diff --git a/parse-options.c b/parse-options.c index c2cbca25cc6bb0..62e9b1cc683176 100644 --- a/parse-options.c +++ b/parse-options.c @@ -43,8 +43,42 @@ static void fix_filename(const char *prefix, const char **file) *file = xstrdup(prefix_filename(prefix, strlen(prefix), *file)); } +static int opt_command_mode_error(const struct option *opt, + const struct option *all_opts, + int flags) +{ + const struct option *that; + struct strbuf message = STRBUF_INIT; + struct strbuf that_name = STRBUF_INIT; + + /* + * Find the other option that was used to set the variable + * already, and report that this is not compatible with it. + */ + for (that = all_opts; that->type != OPTION_END; that++) { + if (that == opt || + that->type != OPTION_CMDMODE || + that->value != opt->value || + that->defval != *(int *)opt->value) + continue; + + if (that->long_name) + strbuf_addf(&that_name, "--%s", that->long_name); + else + strbuf_addf(&that_name, "-%c", that->short_name); + strbuf_addf(&message, ": incompatible with %s", that_name.buf); + strbuf_release(&that_name); + opterror(opt, message.buf, flags); + strbuf_release(&message); + return -1; + } + return opterror(opt, ": incompatible with something else", flags); +} + static int get_value(struct parse_opt_ctx_t *p, - const struct option *opt, int flags) + const struct option *opt, + const struct option *all_opts, + int flags) { const char *s, *arg; const int unset = flags & OPT_UNSET; @@ -83,6 +117,16 @@ static int get_value(struct parse_opt_ctx_t *p, *(int *)opt->value = unset ? 0 : opt->defval; return 0; + case OPTION_CMDMODE: + /* + * Giving the same mode option twice, although is unnecessary, + * is not a grave error, so let it pass. + */ + if (*(int *)opt->value && *(int *)opt->value != opt->defval) + return opt_command_mode_error(opt, all_opts, flags); + *(int *)opt->value = opt->defval; + return 0; + case OPTION_SET_PTR: *(void **)opt->value = unset ? NULL : (void *)opt->defval; return 0; @@ -143,12 +187,13 @@ static int get_value(struct parse_opt_ctx_t *p, static int parse_short_opt(struct parse_opt_ctx_t *p, const struct option *options) { + const struct option *all_opts = options; const struct option *numopt = NULL; for (; options->type != OPTION_END; options++) { if (options->short_name == *p->opt) { p->opt = p->opt[1] ? p->opt + 1 : NULL; - return get_value(p, options, OPT_SHORT); + return get_value(p, options, all_opts, OPT_SHORT); } /* @@ -177,6 +222,7 @@ static int parse_short_opt(struct parse_opt_ctx_t *p, const struct option *optio static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg, const struct option *options) { + const struct option *all_opts = options; const char *arg_end = strchr(arg, '='); const struct option *abbrev_option = NULL, *ambiguous_option = NULL; int abbrev_flags = 0, ambiguous_flags = 0; @@ -253,7 +299,7 @@ static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg, continue; p->opt = rest + 1; } - return get_value(p, options, flags ^ opt_flags); + return get_value(p, options, all_opts, flags ^ opt_flags); } if (ambiguous_option) @@ -265,18 +311,20 @@ static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg, (abbrev_flags & OPT_UNSET) ? "no-" : "", abbrev_option->long_name); if (abbrev_option) - return get_value(p, abbrev_option, abbrev_flags); + return get_value(p, abbrev_option, all_opts, abbrev_flags); return -2; } static int parse_nodash_opt(struct parse_opt_ctx_t *p, const char *arg, const struct option *options) { + const struct option *all_opts = options; + for (; options->type != OPTION_END; options++) { if (!(options->flags & PARSE_OPT_NODASH)) continue; if (options->short_name == arg[0] && arg[1] == '\0') - return get_value(p, options, OPT_SHORT); + return get_value(p, options, all_opts, OPT_SHORT); } return -2; } diff --git a/parse-options.h b/parse-options.h index c378b75b13317d..2404e06732366d 100644 --- a/parse-options.h +++ b/parse-options.h @@ -13,6 +13,7 @@ enum parse_opt_type { OPTION_COUNTUP, OPTION_SET_INT, OPTION_SET_PTR, + OPTION_CMDMODE, /* options with arguments (usually) */ OPTION_STRING, OPTION_INTEGER, @@ -130,6 +131,8 @@ struct option { #define OPT_BOOL(s, l, v, h) OPT_SET_INT(s, l, v, h, 1) #define OPT_SET_PTR(s, l, v, h, p) { OPTION_SET_PTR, (s), (l), (v), NULL, \ (h), PARSE_OPT_NOARG, NULL, (p) } +#define OPT_CMDMODE(s, l, v, h, i) { OPTION_CMDMODE, (s), (l), (v), NULL, \ + (h), PARSE_OPT_NOARG|PARSE_OPT_NONEG, NULL, (i) } #define OPT_INTEGER(s, l, v, h) { OPTION_INTEGER, (s), (l), (v), N_("n"), (h) } #define OPT_STRING(s, l, v, a, h) { OPTION_STRING, (s), (l), (v), (a), (h) } #define OPT_STRING_LIST(s, l, v, a, h) \