Am 20.01.24 um 18:55 schrieb Junio C Hamano: > > I agree with you that the structure of that loop and the processing > in it does look confusing. Here's a small simplification. --- >8 --- Subject: [PATCH 2/1] parse-options: simplify positivation handling We accept the positive version of options whose long name starts with "no-" and are defined without the flag PARSE_OPT_NONEG. E.g. git clone has an explicitly defined --no-checkout option and also implicitly accepts --checkout to override it. parse_long_opt() handles that by restarting the option matching with the positive version when it finds that only the current option definition starts with "no-", but not the user-supplied argument. This code is located almost at the end of the matching logic. Avoid the need for a restart by moving the code up. We don't have to check the positive arg against the negative long_name at all -- the "no-" prefix of the latter makes a match impossible. Skip it and toggle OPT_UNSET right away to simplify the control flow. Signed-off-by: René Scharfe <l.s.r@xxxxxx> --- Patch formatted with --function-context for easier review. parse-options.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/parse-options.c b/parse-options.c index 92e96ca6cd..63a99dea6e 100644 --- a/parse-options.c +++ b/parse-options.c @@ -353,95 +353,94 @@ static int is_alias(struct parse_opt_ctx_t *ctx, 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); for (; options->type != OPTION_END; options++) { const char *rest, *long_name = options->long_name; enum opt_parsed flags = OPT_LONG, opt_flags = OPT_LONG; if (options->type == OPTION_SUBCOMMAND) continue; if (!long_name) continue; -again: + if (!starts_with(arg, "no-") && + !(options->flags & PARSE_OPT_NONEG) && + skip_prefix(long_name, "no-", &long_name)) + opt_flags |= OPT_UNSET; + if (!skip_prefix(arg, long_name, &rest)) 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; } if (!(flags & OPT_UNSET) && *arg_end) p->opt = arg_end + 1; abbrev_option = options; abbrev_flags = flags ^ opt_flags; continue; } /* negation allowed? */ if (options->flags & PARSE_OPT_NONEG) continue; /* negated and abbreviated very much? */ if (allow_abbrev && starts_with("no-", arg)) { flags |= OPT_UNSET; goto is_abbreviated; } /* negated? */ - if (!starts_with(arg, "no-")) { - if (skip_prefix(long_name, "no-", &long_name)) { - opt_flags |= OPT_UNSET; - goto again; - } + if (!starts_with(arg, "no-")) continue; - } flags |= OPT_UNSET; if (!skip_prefix(arg + 3, long_name, &rest)) { /* abbreviated and negated? */ if (allow_abbrev && starts_with(long_name, arg + 3)) goto is_abbreviated; else continue; } } if (*rest) { if (*rest != '=') continue; p->opt = rest + 1; } return get_value(p, options, flags ^ opt_flags); } if (disallow_abbreviated_options && (ambiguous_option || abbrev_option)) die("disallowed abbreviated or ambiguous option '%.*s'", (int)(arg_end - arg), arg); 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); return PARSE_OPT_HELP; } if (abbrev_option) return get_value(p, abbrev_option, abbrev_flags); return PARSE_OPT_UNKNOWN; } -- 2.43.0