diff --git a/parse-options.c b/parse-options.c index 5224203ffe..6d9fc7bdc7 100644 --- a/parse-options.c +++ b/parse-options.c @@ -5,6 +5,7 @@ #include "gettext.h" #include "strbuf.h" #include "string-list.h" +#include "strmap.h" #include "utf8.h" static int disallow_abbreviated_options; @@ -634,6 +635,7 @@ static void check_typos(const char *arg, const struct option *options) static void parse_options_check(const struct option *opts) { char short_opts[128]; + bool saw_number_option = false; void *subcommand_value = NULL; memset(short_opts, '\0', sizeof(short_opts)); @@ -648,6 +650,11 @@ static void parse_options_check(const struct option *opts) else if (short_opts[opts->short_name]++) optbug(opts, "short name already used"); } + if (opts->type == OPTION_NUMBER) { + if (saw_number_option) + optbug(opts, "duplicate numerical option"); + saw_number_option = true; + } if (opts->flags & PARSE_OPT_NODASH && ((opts->flags & PARSE_OPT_OPTARG) || !(opts->flags & PARSE_OPT_NOARG) || @@ -707,6 +714,20 @@ static void parse_options_check(const struct option *opts) BUG_if_bug("invalid 'struct option'"); } +static void parse_options_check_harder(const struct option *opts) +{ + struct strset long_names = STRSET_INIT; + + for (; opts->type != OPTION_END; opts++) { + if (opts->long_name) { + if (!strset_add(&long_names, opts->long_name)) + optbug(opts, "long name already used"); + } + } + BUG_if_bug("invalid 'struct option'"); + strset_clear(&long_names); +} + static int has_subcommands(const struct option *options) { for (; options->type != OPTION_END; options++) @@ -1324,6 +1345,8 @@ static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t const char *prefix = usage_prefix; int saw_empty_line = 0; + parse_options_check_harder(opts); + if (!usagestr) return PARSE_OPT_HELP;