parse-options: add OPT_CMDMODE()

This can be used to define a set of mutually exclusive "command mode" options, and automatically catch use of more than one from that set as an error. Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Jul 30, 2013 at 12:06 UTC 1158826394e162c5d45a4043ea86b04b24215555
2 files changed +56 -5
parse-options.c
+53 -5
@@ -43,8 +43,42 @@ static void fix_filename(const char *prefix, const char **file)
43 *file = xstrdup(prefix_filename(prefix, strlen(prefix), *file));
44 }
45
46 +static int opt_command_mode_error(const struct option *opt,
47 + const struct option *all_opts,
48 + int flags)
49 +{
50 + const struct option *that;
51 + struct strbuf message = STRBUF_INIT;
52 + struct strbuf that_name = STRBUF_INIT;
53 +
54 + /*
55 + * Find the other option that was used to set the variable
56 + * already, and report that this is not compatible with it.
57 + */
58 + for (that = all_opts; that->type != OPTION_END; that++) {
59 + if (that == opt ||
60 + that->type != OPTION_CMDMODE ||
61 + that->value != opt->value ||
62 + that->defval != *(int *)opt->value)
63 + continue;
64 +
65 + if (that->long_name)
66 + strbuf_addf(&that_name, "--%s", that->long_name);
67 + else
68 + strbuf_addf(&that_name, "-%c", that->short_name);
69 + strbuf_addf(&message, ": incompatible with %s", that_name.buf);
70 + strbuf_release(&that_name);
71 + opterror(opt, message.buf, flags);
72 + strbuf_release(&message);
73 + return -1;
74 + }
75 + return opterror(opt, ": incompatible with something else", flags);
76 +}
77 +
78 static int get_value(struct parse_opt_ctx_t *p,
47 - const struct option *opt, int flags)
79 + const struct option *opt,
80 + const struct option *all_opts,
81 + int flags)
82 {
83 const char *s, *arg;
84 const int unset = flags & OPT_UNSET;
@@ -83,6 +117,16 @@ static int get_value(struct parse_opt_ctx_t *p,
117 *(int *)opt->value = unset ? 0 : opt->defval;
118 return 0;
119
120 + case OPTION_CMDMODE:
121 + /*
122 + * Giving the same mode option twice, although is unnecessary,
123 + * is not a grave error, so let it pass.
124 + */
125 + if (*(int *)opt->value && *(int *)opt->value != opt->defval)
126 + return opt_command_mode_error(opt, all_opts, flags);
127 + *(int *)opt->value = opt->defval;
128 + return 0;
129 +
130 case OPTION_SET_PTR:
131 *(void **)opt->value = unset ? NULL : (void *)opt->defval;
132 return 0;
@@ -143,12 +187,13 @@ static int get_value(struct parse_opt_ctx_t *p,
187
188 static int parse_short_opt(struct parse_opt_ctx_t *p, const struct option *options)
189 {
190 + const struct option *all_opts = options;
191 const struct option *numopt = NULL;
192
193 for (; options->type != OPTION_END; options++) {
194 if (options->short_name == *p->opt) {
195 p->opt = p->opt[1] ? p->opt + 1 : NULL;
151 - return get_value(p, options, OPT_SHORT);
196 + return get_value(p, options, all_opts, OPT_SHORT);
197 }
198
199 /*
@@ -177,6 +222,7 @@ static int parse_short_opt(struct parse_opt_ctx_t *p, const struct option *optio
222 static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
223 const struct option *options)
224 {
225 + const struct option *all_opts = options;
226 const char *arg_end = strchr(arg, '=');
227 const struct option *abbrev_option = NULL, *ambiguous_option = NULL;
228 int abbrev_flags = 0, ambiguous_flags = 0;
@@ -253,7 +299,7 @@ is_abbreviated:
299 continue;
300 p->opt = rest + 1;
301 }
256 - return get_value(p, options, flags ^ opt_flags);
302 + return get_value(p, options, all_opts, flags ^ opt_flags);
303 }
304
305 if (ambiguous_option)
@@ -265,18 +311,20 @@ is_abbreviated:
311 (abbrev_flags & OPT_UNSET) ? "no-" : "",
312 abbrev_option->long_name);
313 if (abbrev_option)
268 - return get_value(p, abbrev_option, abbrev_flags);
314 + return get_value(p, abbrev_option, all_opts, abbrev_flags);
315 return -2;
316 }
317
318 static int parse_nodash_opt(struct parse_opt_ctx_t *p, const char *arg,
319 const struct option *options)
320 {
321 + const struct option *all_opts = options;
322 +
323 for (; options->type != OPTION_END; options++) {
324 if (!(options->flags & PARSE_OPT_NODASH))
325 continue;
326 if (options->short_name == arg[0] && arg[1] == '\0')
279 - return get_value(p, options, OPT_SHORT);
327 + return get_value(p, options, all_opts, OPT_SHORT);
328 }
329 return -2;
330 }
parse-options.h
+3
@@ -13,6 +13,7 @@ enum parse_opt_type {
13 OPTION_COUNTUP,
14 OPTION_SET_INT,
15 OPTION_SET_PTR,
16 + OPTION_CMDMODE,
17 /* options with arguments (usually) */
18 OPTION_STRING,
19 OPTION_INTEGER,
@@ -130,6 +131,8 @@ struct option {
131 #define OPT_BOOL(s, l, v, h) OPT_SET_INT(s, l, v, h, 1)
132 #define OPT_SET_PTR(s, l, v, h, p) { OPTION_SET_PTR, (s), (l), (v), NULL, \
133 (h), PARSE_OPT_NOARG, NULL, (p) }
134 +#define OPT_CMDMODE(s, l, v, h, i) { OPTION_CMDMODE, (s), (l), (v), NULL, \
135 + (h), PARSE_OPT_NOARG|PARSE_OPT_NONEG, NULL, (i) }
136 #define OPT_INTEGER(s, l, v, h) { OPTION_INTEGER, (s), (l), (v), N_("n"), (h) }
137 #define OPT_STRING(s, l, v, a, h) { OPTION_STRING, (s), (l), (v), (a), (h) }
138 #define OPT_STRING_LIST(s, l, v, a, h) \