git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v2 3/6] parse-options: factor out register_abbrev() and struct parsed_option

From
René Scharfe <l.s.r@web.de>
Date
Mar 3, 2024, 12:19 UTC
Message-ID
<20240303121944.20627-4-l.s.r@web.de>
In-Reply-To
<20240303121944.20627-1-l.s.r@web.de>

Add a function, register_abbrev(), for storing the necessary details for remembering an abbreviated and thus potentially ambiguous option. Call it instead of sharing the code using goto, to make the control flow more explicit.

Conveniently collect these details in the new struct parsed_option to reduce the number of necessary function arguments.

Signed-off-by: René Scharfe <l.s.r@web.de>
---
 parse-options.c | 83 +++++++++++++++++++++++++++++--------------------
 1 file changed, 49 insertions(+), 34 deletions(-)
diff --git a/parse-options.c b/parse-options.c
index 056c6b30e9..398ebaef14 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -350,14 +350,40 @@ static int is_alias(struct parse_opt_ctx_t *ctx,
 	return 0;
 }

+struct parsed_option {
+	const struct option *option;
+	enum opt_parsed flags;
+};
+
+static void register_abbrev(struct parse_opt_ctx_t *p,
+			    const struct option *option, enum opt_parsed flags,
+			    struct parsed_option *abbrev,
+			    struct parsed_option *ambiguous)
+{
+	if (p->flags & PARSE_OPT_KEEP_UNKNOWN_OPT)
+		return;
+	if (abbrev->option &&
+	    !is_alias(p, abbrev->option, option)) {
+		/*
+		 * If this is abbreviated, it is
+		 * ambiguous. So when there is no
+		 * exact match later, we need to
+		 * error out.
+		 */
+		ambiguous->option = abbrev->option;
+		ambiguous->flags = abbrev->flags;
+	}
+	abbrev->option = option;
+	abbrev->flags = flags;
+}
+
 static enum parse_opt_result parse_long_opt(
 	struct parse_opt_ctx_t *p, const char *arg,
 	const struct option *options)
 {
 	const char *arg_end = strchrnul(arg, '=');
-	const struct option *abbrev_option = NULL, *ambiguous_option = NULL;
-	enum opt_parsed abbrev_flags = OPT_LONG, ambiguous_flags = OPT_LONG;
-	int allow_abbrev = !(p->flags & PARSE_OPT_KEEP_UNKNOWN_OPT);
+	struct parsed_option abbrev = { .option = NULL, .flags = OPT_LONG };
+	struct parsed_option ambiguous = { .option = NULL, .flags = OPT_LONG };

 	for (; options->type != OPTION_END; options++) {
 		const char *rest, *long_name = options->long_name;
@@ -377,31 +403,20 @@ static enum parse_opt_result parse_long_opt(
 			rest = NULL;
 		if (!rest) {
 			/* abbreviated? */
-			if (allow_abbrev &&
-			    !strncmp(long_name, arg, arg_end - arg)) {
-is_abbreviated:
-				if (abbrev_option &&
-				    !is_alias(p, abbrev_option, options)) {
-					/*
-					 * If this is abbreviated, it is
-					 * ambiguous. So when there is no
-					 * exact match later, we need to
-					 * error out.
-					 */
-					ambiguous_option = abbrev_option;
-					ambiguous_flags = abbrev_flags;
-				}
-				abbrev_option = options;
-				abbrev_flags = flags ^ opt_flags;
+			if (!strncmp(long_name, arg, arg_end - arg)) {
+				register_abbrev(p, options, flags ^ opt_flags,
+						&abbrev, &ambiguous);
 				continue;
 			}
 			/* negation allowed? */
 			if (options->flags & PARSE_OPT_NONEG)
 				continue;
 			/* negated and abbreviated very much? */
-			if (allow_abbrev && starts_with("no-", arg)) {
+			if (starts_with("no-", arg)) {
 				flags |= OPT_UNSET;
-				goto is_abbreviated;
+				register_abbrev(p, options, flags ^ opt_flags,
+						&abbrev, &ambiguous);
+				continue;
 			}
 			/* negated? */
 			if (!starts_with(arg, "no-"))
@@ -409,12 +424,12 @@ static enum parse_opt_result parse_long_opt(
 			flags |= OPT_UNSET;
 			if (!skip_prefix(arg + 3, long_name, &rest)) {
 				/* abbreviated and negated? */
-				if (allow_abbrev &&
-				    !strncmp(long_name, arg + 3,
+				if (!strncmp(long_name, arg + 3,
 					     arg_end - arg - 3))
-					goto is_abbreviated;
-				else
-					continue;
+					register_abbrev(p, options,
+							flags ^ opt_flags,
+							&abbrev, &ambiguous);
+				continue;
 			}
 		}
 		if (*rest) {
@@ -425,24 +440,24 @@ static enum parse_opt_result parse_long_opt(
 		return get_value(p, options, flags ^ opt_flags);
 	}

-	if (disallow_abbreviated_options && (ambiguous_option || abbrev_option))
+	if (disallow_abbreviated_options && (ambiguous.option || abbrev.option))
 		die("disallowed abbreviated or ambiguous option '%.*s'",
 		    (int)(arg_end - arg), arg);

-	if (ambiguous_option) {
+	if (ambiguous.option) {
 		error(_("ambiguous option: %s "
 			"(could be --%s%s or --%s%s)"),
 			arg,
-			(ambiguous_flags & OPT_UNSET) ?  "no-" : "",
-			ambiguous_option->long_name,
-			(abbrev_flags & OPT_UNSET) ?  "no-" : "",
-			abbrev_option->long_name);
+			(ambiguous.flags & OPT_UNSET) ?  "no-" : "",
+			ambiguous.option->long_name,
+			(abbrev.flags & OPT_UNSET) ?  "no-" : "",
+			abbrev.option->long_name);
 		return PARSE_OPT_HELP;
 	}
-	if (abbrev_option) {
+	if (abbrev.option) {
 		if (*arg_end)
 			p->opt = arg_end + 1;
-		return get_value(p, abbrev_option, abbrev_flags);
+		return get_value(p, abbrev.option, abbrev.flags);
 	}
 	return PARSE_OPT_UNKNOWN;
 }
--
2.44.0
Previous: René ScharfeNext: Josh Steadmon
Message 19 of 23 in “parse-options: long option parsing fixes and cleanup”
  1. 0/6 parse-options: long option parsing fixes and cleanupRené Scharfe, Feb 24, 2024
  2. 2/6 parse-options: set arg of abbreviated option lazilyRené Scharfe, Feb 24, 2024
  3. 3/6 parse-options: factor out register_abbrev() and struct parsed_optionRené Scharfe, Feb 24, 2024
  4. Junio C HamanoFeb 24, 2024
  5. 1/6 parse-options: recognize abbreviated negated option with argRené Scharfe, Feb 24, 2024
  6. 6/6 parse-options: rearrange long_name matching codeRené Scharfe, Feb 24, 2024
  7. 5/6 parse-options: normalize arg and long_name before comparisonRené Scharfe, Feb 24, 2024
  8. 4/6 parse-options: detect ambiguous self-negationRené Scharfe, Feb 24, 2024
  9. René ScharfeFeb 24, 2024
  10. 0/6 parse-options: long option parsing fixes and cleanupRené Scharfe, Mar 3, 2024
  11. 6/6 parse-options: rearrange long_name matching codeRené Scharfe, Mar 3, 2024
  12. Josh SteadmonMar 12, 2024
  13. 5/6 parse-options: normalize arg and long_name before comparisonRené Scharfe, Mar 3, 2024
  14. Josh SteadmonMar 12, 2024
  15. 2/6 parse-options: set arg of abbreviated option lazilyRené Scharfe, Mar 3, 2024
  16. 1/6 parse-options: recognize abbreviated negated option with argRené Scharfe, Mar 3, 2024
  17. Josh SteadmonMar 12, 2024
  18. 4/6 parse-options: detect ambiguous self-negationRené Scharfe, Mar 3, 2024
  19. 3/6 parse-options: factor out register_abbrev() and struct parsed_optionRené Scharfe, Mar 3, 2024
  20. Josh SteadmonMar 12, 2024
  21. Junio C HamanoMar 13, 2024
  22. René ScharfeMar 13, 2024
  23. Josh SteadmonMar 12, 2024

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.