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 @@ is_abbreviated: 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 @@ is_abbreviated: 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; }