]> git.ipfire.org Git - thirdparty/git.git/blobdiff - parse-options.c
Merge branch 'js/ci-use-macos-13'
[thirdparty/git.git] / parse-options.c
index 093eaf2db80f82d86260d929bb514f3d84c39efa..e0c94b0546b5487c5b324c4c2b76d668289e8b10 100644 (file)
@@ -70,42 +70,10 @@ static void fix_filename(const char *prefix, char **file)
                *file = prefix_filename_except_for_dash(prefix, *file);
 }
 
-static enum parse_opt_result opt_command_mode_error(
-       const struct option *opt,
-       const struct option *all_opts,
-       enum opt_parsed flags)
-{
-       const struct option *that;
-       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->flags & PARSE_OPT_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);
-               error(_("%s is incompatible with %s"),
-                     optname(opt, flags), that_name.buf);
-               strbuf_release(&that_name);
-               return PARSE_OPT_ERROR;
-       }
-       return error(_("%s : incompatible with something else"),
-                    optname(opt, flags));
-}
-
-static enum parse_opt_result get_value(struct parse_opt_ctx_t *p,
-                                      const struct option *opt,
-                                      const struct option *all_opts,
-                                      enum opt_parsed flags)
+static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p,
+                                         const struct option *opt,
+                                         enum opt_parsed flags,
+                                         const char **argp)
 {
        const char *s, *arg;
        const int unset = flags & OPT_UNSET;
@@ -118,14 +86,6 @@ static enum parse_opt_result get_value(struct parse_opt_ctx_t *p,
        if (!(flags & OPT_SHORT) && p->opt && (opt->flags & PARSE_OPT_NOARG))
                return error(_("%s takes no value"), optname(opt, flags));
 
-       /*
-        * Giving the same mode option twice, although unnecessary,
-        * is not a grave error, so let it pass.
-        */
-       if ((opt->flags & PARSE_OPT_CMDMODE) &&
-           *(int *)opt->value && *(int *)opt->value != opt->defval)
-               return opt_command_mode_error(opt, all_opts, flags);
-
        switch (opt->type) {
        case OPTION_LOWLEVEL_CALLBACK:
                return opt->ll_callback(p, opt, NULL, unset);
@@ -200,6 +160,8 @@ static enum parse_opt_result get_value(struct parse_opt_ctx_t *p,
                        p_unset = 0;
                        p_arg = arg;
                }
+               if (opt->flags & PARSE_OPT_CMDMODE)
+                       *argp = p_arg;
                if (opt->callback)
                        return (*opt->callback)(opt, p_arg, p_unset) ? (-1) : 0;
                else
@@ -247,16 +209,91 @@ static enum parse_opt_result get_value(struct parse_opt_ctx_t *p,
        }
 }
 
+struct parse_opt_cmdmode_list {
+       int value, *value_ptr;
+       const struct option *opt;
+       const char *arg;
+       enum opt_parsed flags;
+       struct parse_opt_cmdmode_list *next;
+};
+
+static void build_cmdmode_list(struct parse_opt_ctx_t *ctx,
+                              const struct option *opts)
+{
+       ctx->cmdmode_list = NULL;
+
+       for (; opts->type != OPTION_END; opts++) {
+               struct parse_opt_cmdmode_list *elem = ctx->cmdmode_list;
+               int *value_ptr = opts->value;
+
+               if (!(opts->flags & PARSE_OPT_CMDMODE) || !value_ptr)
+                       continue;
+
+               while (elem && elem->value_ptr != value_ptr)
+                       elem = elem->next;
+               if (elem)
+                       continue;
+
+               CALLOC_ARRAY(elem, 1);
+               elem->value_ptr = value_ptr;
+               elem->value = *value_ptr;
+               elem->next = ctx->cmdmode_list;
+               ctx->cmdmode_list = elem;
+       }
+}
+
+static char *optnamearg(const struct option *opt, const char *arg,
+                       enum opt_parsed flags)
+{
+       if (flags & OPT_SHORT)
+               return xstrfmt("-%c%s", opt->short_name, arg ? arg : "");
+       return xstrfmt("--%s%s%s%s", flags & OPT_UNSET ? "no-" : "",
+                      opt->long_name, arg ? "=" : "", arg ? arg : "");
+}
+
+static enum parse_opt_result get_value(struct parse_opt_ctx_t *p,
+                                      const struct option *opt,
+                                      enum opt_parsed flags)
+{
+       const char *arg = NULL;
+       enum parse_opt_result result = do_get_value(p, opt, flags, &arg);
+       struct parse_opt_cmdmode_list *elem = p->cmdmode_list;
+       char *opt_name, *other_opt_name;
+
+       for (; elem; elem = elem->next) {
+               if (*elem->value_ptr == elem->value)
+                       continue;
+
+               if (elem->opt &&
+                   (elem->opt->flags | opt->flags) & PARSE_OPT_CMDMODE)
+                       break;
+
+               elem->opt = opt;
+               elem->arg = arg;
+               elem->flags = flags;
+               elem->value = *elem->value_ptr;
+       }
+
+       if (result || !elem)
+               return result;
+
+       opt_name = optnamearg(opt, arg, flags);
+       other_opt_name = optnamearg(elem->opt, elem->arg, elem->flags);
+       error(_("%s is incompatible with %s"), opt_name, other_opt_name);
+       free(opt_name);
+       free(other_opt_name);
+       return -1;
+}
+
 static enum parse_opt_result 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, all_opts, OPT_SHORT);
+                       return get_value(p, options, OPT_SHORT);
                }
 
                /*
@@ -318,7 +355,6 @@ static enum parse_opt_result 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 = strchrnul(arg, '=');
        const struct option *abbrev_option = NULL, *ambiguous_option = NULL;
        enum opt_parsed abbrev_flags = OPT_LONG, ambiguous_flags = OPT_LONG;
@@ -387,7 +423,7 @@ is_abbreviated:
                                continue;
                        p->opt = rest + 1;
                }
-               return get_value(p, options, all_opts, flags ^ opt_flags);
+               return get_value(p, options, flags ^ opt_flags);
        }
 
        if (disallow_abbreviated_options && (ambiguous_option || abbrev_option))
@@ -405,7 +441,7 @@ is_abbreviated:
                return PARSE_OPT_HELP;
        }
        if (abbrev_option)
-               return get_value(p, abbrev_option, all_opts, abbrev_flags);
+               return get_value(p, abbrev_option, abbrev_flags);
        return PARSE_OPT_UNKNOWN;
 }
 
@@ -413,13 +449,11 @@ static enum parse_opt_result 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, all_opts, OPT_SHORT);
+                       return get_value(p, options, OPT_SHORT);
        }
        return PARSE_OPT_ERROR;
 }
@@ -574,6 +608,7 @@ static void parse_options_start_1(struct parse_opt_ctx_t *ctx,
            (flags & PARSE_OPT_KEEP_ARGV0))
                BUG("Can't keep argv0 if you don't have it");
        parse_options_check(options);
+       build_cmdmode_list(ctx, options);
 }
 
 void parse_options_start(struct parse_opt_ctx_t *ctx,
@@ -1006,6 +1041,11 @@ int parse_options(int argc, const char **argv,
        precompose_argv_prefix(argc, argv, NULL);
        free_preprocessed_options(real_options);
        free(ctx.alias_groups);
+       for (struct parse_opt_cmdmode_list *elem = ctx.cmdmode_list; elem;) {
+               struct parse_opt_cmdmode_list *next = elem->next;
+               free(elem);
+               elem = next;
+       }
        return parse_options_end(&ctx);
 }