Line | Count | Source |
1 | | #include "git-compat-util.h" |
2 | | #include "parse-options.h" |
3 | | #include "abspath.h" |
4 | | #include "parse.h" |
5 | | #include "gettext.h" |
6 | | #include "strbuf.h" |
7 | | #include "string-list.h" |
8 | | #include "utf8.h" |
9 | | |
10 | | static int disallow_abbreviated_options; |
11 | | |
12 | | enum opt_parsed { |
13 | | OPT_LONG = 0, |
14 | | OPT_SHORT = 1<<0, |
15 | | OPT_UNSET = 1<<1, |
16 | | }; |
17 | | |
18 | | static void optbug(const struct option *opt, const char *reason) |
19 | 0 | { |
20 | 0 | if (opt->long_name && opt->short_name) |
21 | 0 | bug("switch '%c' (--%s) %s", opt->short_name, |
22 | 0 | opt->long_name, reason); |
23 | 0 | else if (opt->long_name) |
24 | 0 | bug("option '%s' %s", opt->long_name, reason); |
25 | 0 | else |
26 | 0 | bug("switch '%c' %s", opt->short_name, reason); |
27 | 0 | } |
28 | | |
29 | | static const char *optname(const struct option *opt, enum opt_parsed flags) |
30 | 0 | { |
31 | 0 | static struct strbuf sb = STRBUF_INIT; |
32 | |
|
33 | 0 | strbuf_reset(&sb); |
34 | 0 | if (flags & OPT_SHORT) |
35 | 0 | strbuf_addf(&sb, "switch `%c'", opt->short_name); |
36 | 0 | else if (flags & OPT_UNSET) |
37 | 0 | strbuf_addf(&sb, "option `no-%s'", opt->long_name); |
38 | 0 | else if (flags == OPT_LONG) |
39 | 0 | strbuf_addf(&sb, "option `%s'", opt->long_name); |
40 | 0 | else |
41 | 0 | BUG("optname() got unknown flags %d", flags); |
42 | | |
43 | 0 | return sb.buf; |
44 | 0 | } |
45 | | |
46 | | static enum parse_opt_result get_arg(struct parse_opt_ctx_t *p, |
47 | | const struct option *opt, |
48 | | enum opt_parsed flags, const char **arg) |
49 | 0 | { |
50 | 0 | if (p->opt) { |
51 | 0 | *arg = p->opt; |
52 | 0 | p->opt = NULL; |
53 | 0 | } else if (p->argc == 1 && (opt->flags & PARSE_OPT_LASTARG_DEFAULT)) { |
54 | 0 | *arg = (const char *)opt->defval; |
55 | 0 | } else if (p->argc > 1) { |
56 | 0 | p->argc--; |
57 | 0 | *arg = *++p->argv; |
58 | 0 | } else |
59 | 0 | return error(_("%s requires a value"), optname(opt, flags)); |
60 | 0 | return 0; |
61 | 0 | } |
62 | | |
63 | | static char *fix_filename(const char *prefix, const char *file) |
64 | 0 | { |
65 | 0 | if (!file || !*file) |
66 | 0 | return NULL; |
67 | 0 | else |
68 | 0 | return prefix_filename_except_for_dash(prefix, file); |
69 | 0 | } |
70 | | |
71 | | static int do_get_int_value(const void *value, size_t precision, intmax_t *ret) |
72 | 0 | { |
73 | 0 | switch (precision) { |
74 | 0 | case sizeof(int8_t): |
75 | 0 | *ret = *(int8_t *)value; |
76 | 0 | return 0; |
77 | 0 | case sizeof(int16_t): |
78 | 0 | *ret = *(int16_t *)value; |
79 | 0 | return 0; |
80 | 0 | case sizeof(int32_t): |
81 | 0 | *ret = *(int32_t *)value; |
82 | 0 | return 0; |
83 | 0 | case sizeof(int64_t): |
84 | 0 | *ret = *(int64_t *)value; |
85 | 0 | return 0; |
86 | 0 | default: |
87 | 0 | return -1; |
88 | 0 | } |
89 | 0 | } |
90 | | |
91 | | static intmax_t get_int_value(const struct option *opt, enum opt_parsed flags) |
92 | 0 | { |
93 | 0 | intmax_t ret; |
94 | 0 | if (do_get_int_value(opt->value, opt->precision, &ret)) |
95 | 0 | BUG("invalid precision for option %s", optname(opt, flags)); |
96 | 0 | return ret; |
97 | 0 | } |
98 | | |
99 | | static enum parse_opt_result set_int_value(const struct option *opt, |
100 | | enum opt_parsed flags, |
101 | | intmax_t value) |
102 | 0 | { |
103 | 0 | switch (opt->precision) { |
104 | 0 | case sizeof(int8_t): |
105 | 0 | *(int8_t *)opt->value = value; |
106 | 0 | return 0; |
107 | 0 | case sizeof(int16_t): |
108 | 0 | *(int16_t *)opt->value = value; |
109 | 0 | return 0; |
110 | 0 | case sizeof(int32_t): |
111 | 0 | *(int32_t *)opt->value = value; |
112 | 0 | return 0; |
113 | 0 | case sizeof(int64_t): |
114 | 0 | *(int64_t *)opt->value = value; |
115 | 0 | return 0; |
116 | 0 | default: |
117 | 0 | BUG("invalid precision for option %s", optname(opt, flags)); |
118 | 0 | } |
119 | 0 | } |
120 | | |
121 | | static int signed_int_fits(intmax_t value, size_t precision) |
122 | 0 | { |
123 | 0 | size_t bits = precision * CHAR_BIT; |
124 | 0 | intmax_t upper_bound = INTMAX_MAX >> (bitsizeof(intmax_t) - bits); |
125 | 0 | intmax_t lower_bound = -upper_bound - 1; |
126 | 0 | return lower_bound <= value && value <= upper_bound; |
127 | 0 | } |
128 | | |
129 | | static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p, |
130 | | const struct option *opt, |
131 | | enum opt_parsed flags, |
132 | | const char **argp) |
133 | 0 | { |
134 | 0 | const char *arg; |
135 | 0 | const int unset = flags & OPT_UNSET; |
136 | |
|
137 | 0 | if (unset && p->opt) |
138 | 0 | return error(_("%s takes no value"), optname(opt, flags)); |
139 | 0 | if (unset && (opt->flags & PARSE_OPT_NONEG)) |
140 | 0 | return error(_("%s isn't available"), optname(opt, flags)); |
141 | 0 | if (!(flags & OPT_SHORT) && p->opt && (opt->flags & PARSE_OPT_NOARG)) |
142 | 0 | return error(_("%s takes no value"), optname(opt, flags)); |
143 | | |
144 | 0 | switch (opt->type) { |
145 | 0 | case OPTION_LOWLEVEL_CALLBACK: |
146 | 0 | return opt->ll_callback(p, opt, NULL, unset); |
147 | | |
148 | 0 | case OPTION_BIT: |
149 | 0 | { |
150 | 0 | intmax_t value = get_int_value(opt, flags); |
151 | 0 | if (unset) |
152 | 0 | value &= ~opt->defval; |
153 | 0 | else |
154 | 0 | value |= opt->defval; |
155 | 0 | return set_int_value(opt, flags, value); |
156 | 0 | } |
157 | | |
158 | 0 | case OPTION_NEGBIT: |
159 | 0 | { |
160 | 0 | intmax_t value = get_int_value(opt, flags); |
161 | 0 | if (unset) |
162 | 0 | value |= opt->defval; |
163 | 0 | else |
164 | 0 | value &= ~opt->defval; |
165 | 0 | return set_int_value(opt, flags, value); |
166 | 0 | } |
167 | | |
168 | 0 | case OPTION_BITOP: |
169 | 0 | { |
170 | 0 | intmax_t value = get_int_value(opt, flags); |
171 | 0 | if (unset) |
172 | 0 | BUG("BITOP can't have unset form"); |
173 | 0 | value &= ~opt->extra; |
174 | 0 | value |= opt->defval; |
175 | 0 | return set_int_value(opt, flags, value); |
176 | 0 | } |
177 | | |
178 | 0 | case OPTION_COUNTUP: |
179 | 0 | { |
180 | 0 | size_t bits = CHAR_BIT * opt->precision; |
181 | 0 | intmax_t upper_bound = INTMAX_MAX >> (bitsizeof(intmax_t) - bits); |
182 | 0 | intmax_t value = get_int_value(opt, flags); |
183 | |
|
184 | 0 | if (value < 0) |
185 | 0 | value = 0; |
186 | 0 | if (unset) |
187 | 0 | value = 0; |
188 | 0 | else if (value < upper_bound) |
189 | 0 | value++; |
190 | 0 | else |
191 | 0 | return error(_("value for %s exceeds %"PRIdMAX), |
192 | 0 | optname(opt, flags), upper_bound); |
193 | 0 | return set_int_value(opt, flags, value); |
194 | 0 | } |
195 | | |
196 | 0 | case OPTION_SET_INT: |
197 | 0 | return set_int_value(opt, flags, unset ? 0 : opt->defval); |
198 | | |
199 | 0 | case OPTION_STRING: |
200 | 0 | if (unset) |
201 | 0 | *(const char **)opt->value = NULL; |
202 | 0 | else if (opt->flags & PARSE_OPT_OPTARG && !p->opt) |
203 | 0 | *(const char **)opt->value = (const char *)opt->defval; |
204 | 0 | else |
205 | 0 | return get_arg(p, opt, flags, (const char **)opt->value); |
206 | 0 | return 0; |
207 | | |
208 | 0 | case OPTION_FILENAME: |
209 | 0 | { |
210 | 0 | const char *value; |
211 | 0 | bool is_optional; |
212 | |
|
213 | 0 | if (unset) |
214 | 0 | value = NULL; |
215 | 0 | else if (opt->flags & PARSE_OPT_OPTARG && !p->opt) |
216 | 0 | value = (const char *)opt->defval; |
217 | 0 | else { |
218 | 0 | int err = get_arg(p, opt, flags, &value); |
219 | 0 | if (err) |
220 | 0 | return err; |
221 | 0 | } |
222 | 0 | if (!value) |
223 | 0 | return 0; |
224 | | |
225 | 0 | is_optional = skip_prefix(value, ":(optional)", &value); |
226 | 0 | value = fix_filename(p->prefix, value); |
227 | 0 | if (is_optional && is_missing_file(value)) { |
228 | 0 | free((char *)value); |
229 | 0 | } else { |
230 | 0 | FREE_AND_NULL(*(char **)opt->value); |
231 | 0 | *(const char **)opt->value = value; |
232 | 0 | } |
233 | 0 | return 0; |
234 | 0 | } |
235 | 0 | case OPTION_CALLBACK: |
236 | 0 | { |
237 | 0 | const char *p_arg = NULL; |
238 | 0 | int p_unset; |
239 | |
|
240 | 0 | if (unset) |
241 | 0 | p_unset = 1; |
242 | 0 | else if (opt->flags & PARSE_OPT_NOARG) |
243 | 0 | p_unset = 0; |
244 | 0 | else if (opt->flags & PARSE_OPT_OPTARG && !p->opt) |
245 | 0 | p_unset = 0; |
246 | 0 | else if (get_arg(p, opt, flags, &arg)) |
247 | 0 | return -1; |
248 | 0 | else { |
249 | 0 | p_unset = 0; |
250 | 0 | p_arg = arg; |
251 | 0 | } |
252 | 0 | if (opt->flags & PARSE_OPT_CMDMODE) |
253 | 0 | *argp = p_arg; |
254 | 0 | if (opt->callback) |
255 | 0 | return (*opt->callback)(opt, p_arg, p_unset) ? (-1) : 0; |
256 | 0 | else |
257 | 0 | return (*opt->ll_callback)(p, opt, p_arg, p_unset); |
258 | 0 | } |
259 | 0 | case OPTION_INTEGER: |
260 | 0 | { |
261 | 0 | intmax_t upper_bound = INTMAX_MAX >> (bitsizeof(intmax_t) - CHAR_BIT * opt->precision); |
262 | 0 | intmax_t lower_bound = -upper_bound - 1; |
263 | 0 | intmax_t value; |
264 | |
|
265 | 0 | if (unset) { |
266 | 0 | value = 0; |
267 | 0 | } else if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { |
268 | 0 | value = opt->defval; |
269 | 0 | } else if (get_arg(p, opt, flags, &arg)) { |
270 | 0 | return -1; |
271 | 0 | } else if (!*arg) { |
272 | 0 | return error(_("%s expects a numerical value"), |
273 | 0 | optname(opt, flags)); |
274 | 0 | } else if (!git_parse_signed(arg, &value, upper_bound)) { |
275 | 0 | if (errno == ERANGE) |
276 | 0 | return error(_("value %s for %s not in range [%"PRIdMAX",%"PRIdMAX"]"), |
277 | 0 | arg, optname(opt, flags), lower_bound, upper_bound); |
278 | | |
279 | 0 | return error(_("%s expects an integer value with an optional k/m/g suffix"), |
280 | 0 | optname(opt, flags)); |
281 | 0 | } |
282 | | |
283 | 0 | if (value < lower_bound) |
284 | 0 | return error(_("value %s for %s not in range [%"PRIdMAX",%"PRIdMAX"]"), |
285 | 0 | arg, optname(opt, flags), (intmax_t)lower_bound, (intmax_t)upper_bound); |
286 | | |
287 | 0 | return set_int_value(opt, flags, value); |
288 | 0 | } |
289 | 0 | case OPTION_UNSIGNED: |
290 | 0 | { |
291 | 0 | uintmax_t upper_bound = UINTMAX_MAX >> (bitsizeof(uintmax_t) - CHAR_BIT * opt->precision); |
292 | 0 | uintmax_t value; |
293 | |
|
294 | 0 | if (unset) { |
295 | 0 | value = 0; |
296 | 0 | } else if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { |
297 | 0 | value = opt->defval; |
298 | 0 | } else if (get_arg(p, opt, flags, &arg)) { |
299 | 0 | return -1; |
300 | 0 | } else if (!*arg) { |
301 | 0 | return error(_("%s expects a numerical value"), |
302 | 0 | optname(opt, flags)); |
303 | 0 | } else if (!git_parse_unsigned(arg, &value, upper_bound)) { |
304 | 0 | if (errno == ERANGE) |
305 | 0 | return error(_("value %s for %s not in range [%"PRIdMAX",%"PRIdMAX"]"), |
306 | 0 | arg, optname(opt, flags), (uintmax_t) 0, upper_bound); |
307 | | |
308 | 0 | return error(_("%s expects a non-negative integer value" |
309 | 0 | " with an optional k/m/g suffix"), |
310 | 0 | optname(opt, flags)); |
311 | 0 | } |
312 | | |
313 | 0 | switch (opt->precision) { |
314 | 0 | case 1: |
315 | 0 | *(uint8_t *)opt->value = value; |
316 | 0 | return 0; |
317 | 0 | case 2: |
318 | 0 | *(uint16_t *)opt->value = value; |
319 | 0 | return 0; |
320 | 0 | case 4: |
321 | 0 | *(uint32_t *)opt->value = value; |
322 | 0 | return 0; |
323 | 0 | case 8: |
324 | 0 | *(uint64_t *)opt->value = value; |
325 | 0 | return 0; |
326 | 0 | default: |
327 | 0 | BUG("invalid precision for option %s", |
328 | 0 | optname(opt, flags)); |
329 | 0 | } |
330 | 0 | } |
331 | | |
332 | 0 | default: |
333 | 0 | BUG("opt->type %d should not happen", opt->type); |
334 | 0 | } |
335 | 0 | } |
336 | | |
337 | | struct parse_opt_cmdmode_list { |
338 | | intmax_t value; |
339 | | void *value_ptr; |
340 | | size_t precision; |
341 | | const struct option *opt; |
342 | | const char *arg; |
343 | | enum opt_parsed flags; |
344 | | struct parse_opt_cmdmode_list *next; |
345 | | }; |
346 | | |
347 | | static void build_cmdmode_list(struct parse_opt_ctx_t *ctx, |
348 | | const struct option *opts) |
349 | 0 | { |
350 | 0 | ctx->cmdmode_list = NULL; |
351 | |
|
352 | 0 | for (; opts->type != OPTION_END; opts++) { |
353 | 0 | struct parse_opt_cmdmode_list *elem = ctx->cmdmode_list; |
354 | 0 | void *value_ptr = opts->value; |
355 | |
|
356 | 0 | if (!(opts->flags & PARSE_OPT_CMDMODE) || !value_ptr) |
357 | 0 | continue; |
358 | | |
359 | 0 | while (elem && elem->value_ptr != value_ptr) |
360 | 0 | elem = elem->next; |
361 | 0 | if (elem) |
362 | 0 | continue; |
363 | | |
364 | 0 | CALLOC_ARRAY(elem, 1); |
365 | 0 | elem->value_ptr = value_ptr; |
366 | 0 | elem->precision = opts->precision; |
367 | 0 | if (do_get_int_value(value_ptr, opts->precision, &elem->value)) |
368 | 0 | optbug(opts, "has invalid precision"); |
369 | 0 | elem->next = ctx->cmdmode_list; |
370 | 0 | ctx->cmdmode_list = elem; |
371 | 0 | } |
372 | 0 | BUG_if_bug("invalid 'struct option'"); |
373 | 0 | } |
374 | | |
375 | | static char *optnamearg(const struct option *opt, const char *arg, |
376 | | enum opt_parsed flags) |
377 | 0 | { |
378 | 0 | if (flags & OPT_SHORT) |
379 | 0 | return xstrfmt("-%c%s", opt->short_name, arg ? arg : ""); |
380 | 0 | return xstrfmt("--%s%s%s%s", flags & OPT_UNSET ? "no-" : "", |
381 | 0 | opt->long_name, arg ? "=" : "", arg ? arg : ""); |
382 | 0 | } |
383 | | |
384 | | static enum parse_opt_result get_value(struct parse_opt_ctx_t *p, |
385 | | const struct option *opt, |
386 | | enum opt_parsed flags) |
387 | 0 | { |
388 | 0 | const char *arg = NULL; |
389 | 0 | enum parse_opt_result result = do_get_value(p, opt, flags, &arg); |
390 | 0 | struct parse_opt_cmdmode_list *elem = p->cmdmode_list; |
391 | 0 | char *opt_name, *other_opt_name; |
392 | |
|
393 | 0 | for (; elem; elem = elem->next) { |
394 | 0 | intmax_t new_value; |
395 | |
|
396 | 0 | if (do_get_int_value(elem->value_ptr, elem->precision, |
397 | 0 | &new_value)) |
398 | 0 | BUG("impossible: invalid precision"); |
399 | | |
400 | 0 | if (new_value == elem->value) |
401 | 0 | continue; |
402 | | |
403 | 0 | if (elem->opt && |
404 | 0 | (elem->opt->flags | opt->flags) & PARSE_OPT_CMDMODE) |
405 | 0 | break; |
406 | | |
407 | 0 | elem->opt = opt; |
408 | 0 | elem->arg = arg; |
409 | 0 | elem->flags = flags; |
410 | 0 | elem->value = new_value; |
411 | 0 | } |
412 | | |
413 | 0 | if (result || !elem) |
414 | 0 | return result; |
415 | | |
416 | 0 | opt_name = optnamearg(opt, arg, flags); |
417 | 0 | other_opt_name = optnamearg(elem->opt, elem->arg, elem->flags); |
418 | 0 | error(_("options '%s' and '%s' cannot be used together"), |
419 | 0 | opt_name, other_opt_name); |
420 | 0 | free(opt_name); |
421 | 0 | free(other_opt_name); |
422 | 0 | return -1; |
423 | 0 | } |
424 | | |
425 | | static enum parse_opt_result parse_short_opt(struct parse_opt_ctx_t *p, |
426 | | const struct option *options) |
427 | 0 | { |
428 | 0 | const struct option *numopt = NULL; |
429 | |
|
430 | 0 | for (; options->type != OPTION_END; options++) { |
431 | 0 | if (options->short_name == *p->opt) { |
432 | 0 | p->opt = p->opt[1] ? p->opt + 1 : NULL; |
433 | 0 | return get_value(p, options, OPT_SHORT); |
434 | 0 | } |
435 | | |
436 | | /* |
437 | | * Handle the numerical option later, explicit one-digit |
438 | | * options take precedence over it. |
439 | | */ |
440 | 0 | if (options->type == OPTION_NUMBER) |
441 | 0 | numopt = options; |
442 | 0 | } |
443 | 0 | if (numopt && isdigit(*p->opt)) { |
444 | 0 | size_t len = 1; |
445 | 0 | char *arg; |
446 | 0 | int rc; |
447 | |
|
448 | 0 | while (isdigit(p->opt[len])) |
449 | 0 | len++; |
450 | 0 | arg = xmemdupz(p->opt, len); |
451 | 0 | p->opt = p->opt[len] ? p->opt + len : NULL; |
452 | 0 | if (numopt->callback) |
453 | 0 | rc = (*numopt->callback)(numopt, arg, 0) ? (-1) : 0; |
454 | 0 | else |
455 | 0 | rc = (*numopt->ll_callback)(p, numopt, arg, 0); |
456 | 0 | free(arg); |
457 | 0 | return rc; |
458 | 0 | } |
459 | 0 | return PARSE_OPT_UNKNOWN; |
460 | 0 | } |
461 | | |
462 | | static int has_string(const char *it, const char **array) |
463 | 0 | { |
464 | 0 | while (*array) |
465 | 0 | if (!strcmp(it, *(array++))) |
466 | 0 | return 1; |
467 | 0 | return 0; |
468 | 0 | } |
469 | | |
470 | | static int is_alias(struct parse_opt_ctx_t *ctx, |
471 | | const struct option *one_opt, |
472 | | const struct option *another_opt) |
473 | 0 | { |
474 | 0 | const char **group; |
475 | |
|
476 | 0 | if (!ctx->alias_groups) |
477 | 0 | return 0; |
478 | | |
479 | 0 | if (!one_opt->long_name || !another_opt->long_name) |
480 | 0 | return 0; |
481 | | |
482 | 0 | for (group = ctx->alias_groups; *group; group += 3) { |
483 | | /* it and other are from the same family? */ |
484 | 0 | if (has_string(one_opt->long_name, group) && |
485 | 0 | has_string(another_opt->long_name, group)) |
486 | 0 | return 1; |
487 | 0 | } |
488 | 0 | return 0; |
489 | 0 | } |
490 | | |
491 | | struct parsed_option { |
492 | | const struct option *option; |
493 | | enum opt_parsed flags; |
494 | | }; |
495 | | |
496 | | static void register_abbrev(struct parse_opt_ctx_t *p, |
497 | | const struct option *option, enum opt_parsed flags, |
498 | | struct parsed_option *abbrev, |
499 | | struct parsed_option *ambiguous) |
500 | 0 | { |
501 | 0 | if (p->flags & PARSE_OPT_KEEP_UNKNOWN_OPT) |
502 | 0 | return; |
503 | 0 | if (abbrev->option && |
504 | 0 | !(abbrev->flags == flags && is_alias(p, abbrev->option, option))) { |
505 | | /* |
506 | | * If this is abbreviated, it is |
507 | | * ambiguous. So when there is no |
508 | | * exact match later, we need to |
509 | | * error out. |
510 | | */ |
511 | 0 | ambiguous->option = abbrev->option; |
512 | 0 | ambiguous->flags = abbrev->flags; |
513 | 0 | } |
514 | 0 | abbrev->option = option; |
515 | 0 | abbrev->flags = flags; |
516 | 0 | } |
517 | | |
518 | | static enum parse_opt_result parse_long_opt( |
519 | | struct parse_opt_ctx_t *p, const char *arg, |
520 | | const struct option *options) |
521 | 0 | { |
522 | 0 | const char *arg_end = strchrnul(arg, '='); |
523 | 0 | const char *arg_start = arg; |
524 | 0 | enum opt_parsed flags = OPT_LONG; |
525 | 0 | int arg_starts_with_no_no = 0; |
526 | 0 | struct parsed_option abbrev = { .option = NULL, .flags = OPT_LONG }; |
527 | 0 | struct parsed_option ambiguous = { .option = NULL, .flags = OPT_LONG }; |
528 | |
|
529 | 0 | if (skip_prefix(arg_start, "no-", &arg_start)) { |
530 | 0 | if (skip_prefix(arg_start, "no-", &arg_start)) |
531 | 0 | arg_starts_with_no_no = 1; |
532 | 0 | else |
533 | 0 | flags |= OPT_UNSET; |
534 | 0 | } |
535 | |
|
536 | 0 | for (; options->type != OPTION_END; options++) { |
537 | 0 | const char *rest, *long_name = options->long_name; |
538 | 0 | enum opt_parsed opt_flags = OPT_LONG; |
539 | 0 | int allow_unset = !(options->flags & PARSE_OPT_NONEG); |
540 | |
|
541 | 0 | if (options->type == OPTION_SUBCOMMAND) |
542 | 0 | continue; |
543 | 0 | if (!long_name) |
544 | 0 | continue; |
545 | | |
546 | 0 | if (skip_prefix(long_name, "no-", &long_name)) |
547 | 0 | opt_flags |= OPT_UNSET; |
548 | 0 | else if (arg_starts_with_no_no) |
549 | 0 | continue; |
550 | | |
551 | 0 | if (((flags ^ opt_flags) & OPT_UNSET) && !allow_unset) |
552 | 0 | continue; |
553 | | |
554 | 0 | if (skip_prefix(arg_start, long_name, &rest)) { |
555 | 0 | if (*rest == '=') |
556 | 0 | p->opt = rest + 1; |
557 | 0 | else if (*rest) |
558 | 0 | continue; |
559 | 0 | return get_value(p, options, flags ^ opt_flags); |
560 | 0 | } |
561 | | |
562 | | /* abbreviated? */ |
563 | 0 | if (!strncmp(long_name, arg_start, arg_end - arg_start)) |
564 | 0 | register_abbrev(p, options, flags ^ opt_flags, |
565 | 0 | &abbrev, &ambiguous); |
566 | | |
567 | | /* negated and abbreviated very much? */ |
568 | 0 | if (allow_unset && starts_with("no-", arg)) |
569 | 0 | register_abbrev(p, options, OPT_UNSET ^ opt_flags, |
570 | 0 | &abbrev, &ambiguous); |
571 | 0 | } |
572 | | |
573 | 0 | if (disallow_abbreviated_options && (ambiguous.option || abbrev.option)) |
574 | 0 | die("disallowed abbreviated or ambiguous option '%.*s'", |
575 | 0 | (int)(arg_end - arg), arg); |
576 | | |
577 | 0 | if (ambiguous.option) { |
578 | 0 | error(_("ambiguous option: %s " |
579 | 0 | "(could be --%s%s or --%s%s)"), |
580 | 0 | arg, |
581 | 0 | (ambiguous.flags & OPT_UNSET) ? "no-" : "", |
582 | 0 | ambiguous.option->long_name, |
583 | 0 | (abbrev.flags & OPT_UNSET) ? "no-" : "", |
584 | 0 | abbrev.option->long_name); |
585 | 0 | return PARSE_OPT_HELP; |
586 | 0 | } |
587 | 0 | if (abbrev.option) { |
588 | 0 | if (*arg_end) |
589 | 0 | p->opt = arg_end + 1; |
590 | 0 | return get_value(p, abbrev.option, abbrev.flags); |
591 | 0 | } |
592 | 0 | return PARSE_OPT_UNKNOWN; |
593 | 0 | } |
594 | | |
595 | | static enum parse_opt_result parse_nodash_opt(struct parse_opt_ctx_t *p, |
596 | | const char *arg, |
597 | | const struct option *options) |
598 | 0 | { |
599 | 0 | for (; options->type != OPTION_END; options++) { |
600 | 0 | if (!(options->flags & PARSE_OPT_NODASH)) |
601 | 0 | continue; |
602 | 0 | if (options->short_name == arg[0] && arg[1] == '\0') |
603 | 0 | return get_value(p, options, OPT_SHORT); |
604 | 0 | } |
605 | 0 | return PARSE_OPT_ERROR; |
606 | 0 | } |
607 | | |
608 | | static enum parse_opt_result parse_subcommand(const char *arg, |
609 | | const struct option *options) |
610 | 0 | { |
611 | 0 | for (; options->type != OPTION_END; options++) |
612 | 0 | if (options->type == OPTION_SUBCOMMAND && |
613 | 0 | !strcmp(options->long_name, arg)) { |
614 | 0 | *(parse_opt_subcommand_fn **)options->value = options->subcommand_fn; |
615 | 0 | return PARSE_OPT_SUBCOMMAND; |
616 | 0 | } |
617 | | |
618 | 0 | return PARSE_OPT_UNKNOWN; |
619 | 0 | } |
620 | | |
621 | | static void check_typos(const char *arg, const struct option *options) |
622 | 0 | { |
623 | 0 | if (strlen(arg) < 3) |
624 | 0 | return; |
625 | | |
626 | 0 | if (starts_with(arg, "no-")) { |
627 | 0 | error(_("did you mean `--%s` (with two dashes)?"), arg); |
628 | 0 | exit(129); |
629 | 0 | } |
630 | | |
631 | 0 | for (; options->type != OPTION_END; options++) { |
632 | 0 | if (!options->long_name) |
633 | 0 | continue; |
634 | 0 | if (starts_with(options->long_name, arg)) { |
635 | 0 | error(_("did you mean `--%s` (with two dashes)?"), arg); |
636 | 0 | exit(129); |
637 | 0 | } |
638 | 0 | } |
639 | 0 | } |
640 | | |
641 | | static void parse_options_check(const struct option *opts) |
642 | 0 | { |
643 | 0 | char short_opts[128]; |
644 | 0 | void *subcommand_value = NULL; |
645 | |
|
646 | 0 | memset(short_opts, '\0', sizeof(short_opts)); |
647 | 0 | for (; opts->type != OPTION_END; opts++) { |
648 | 0 | if ((opts->flags & PARSE_OPT_LASTARG_DEFAULT) && |
649 | 0 | (opts->flags & PARSE_OPT_OPTARG)) |
650 | 0 | optbug(opts, "uses incompatible flags " |
651 | 0 | "LASTARG_DEFAULT and OPTARG"); |
652 | 0 | if (opts->short_name) { |
653 | 0 | if (0x7F <= opts->short_name) |
654 | 0 | optbug(opts, "invalid short name"); |
655 | 0 | else if (short_opts[opts->short_name]++) |
656 | 0 | optbug(opts, "short name already used"); |
657 | 0 | } |
658 | 0 | if (opts->flags & PARSE_OPT_NODASH && |
659 | 0 | ((opts->flags & PARSE_OPT_OPTARG) || |
660 | 0 | !(opts->flags & PARSE_OPT_NOARG) || |
661 | 0 | !(opts->flags & PARSE_OPT_NONEG) || |
662 | 0 | opts->long_name)) |
663 | 0 | optbug(opts, "uses feature " |
664 | 0 | "not supported for dashless options"); |
665 | 0 | if (opts->type == OPTION_SET_INT && !opts->defval && |
666 | 0 | opts->long_name && !(opts->flags & PARSE_OPT_NONEG)) |
667 | 0 | optbug(opts, "OPTION_SET_INT 0 should not be negatable"); |
668 | 0 | switch (opts->type) { |
669 | 0 | case OPTION_SET_INT: |
670 | 0 | case OPTION_BIT: |
671 | 0 | case OPTION_NEGBIT: |
672 | 0 | case OPTION_BITOP: |
673 | 0 | case OPTION_COUNTUP: |
674 | 0 | if (!signed_int_fits(opts->defval, opts->precision)) |
675 | 0 | optbug(opts, "has invalid defval"); |
676 | | /* fallthru */ |
677 | 0 | case OPTION_NUMBER: |
678 | 0 | if ((opts->flags & PARSE_OPT_OPTARG) || |
679 | 0 | !(opts->flags & PARSE_OPT_NOARG)) |
680 | 0 | optbug(opts, "should not accept an argument"); |
681 | 0 | break; |
682 | 0 | case OPTION_CALLBACK: |
683 | 0 | if (!opts->callback && !opts->ll_callback) |
684 | 0 | optbug(opts, "OPTION_CALLBACK needs one callback"); |
685 | 0 | else if (opts->callback && opts->ll_callback) |
686 | 0 | optbug(opts, "OPTION_CALLBACK can't have two callbacks"); |
687 | 0 | break; |
688 | 0 | case OPTION_LOWLEVEL_CALLBACK: |
689 | 0 | if (!opts->ll_callback) |
690 | 0 | optbug(opts, "OPTION_LOWLEVEL_CALLBACK needs a callback"); |
691 | 0 | if (opts->callback) |
692 | 0 | optbug(opts, "OPTION_LOWLEVEL_CALLBACK needs no high level callback"); |
693 | 0 | break; |
694 | 0 | case OPTION_ALIAS: |
695 | 0 | optbug(opts, "OPT_ALIAS() should not remain at this point. " |
696 | 0 | "Are you using parse_options_step() directly?\n" |
697 | 0 | "That case is not supported yet."); |
698 | 0 | break; |
699 | 0 | case OPTION_SUBCOMMAND: |
700 | 0 | if (!opts->value || !opts->subcommand_fn) |
701 | 0 | optbug(opts, "OPTION_SUBCOMMAND needs a value and a subcommand function"); |
702 | 0 | if (!subcommand_value) |
703 | 0 | subcommand_value = opts->value; |
704 | 0 | else if (subcommand_value != opts->value) |
705 | 0 | optbug(opts, "all OPTION_SUBCOMMANDs need the same value"); |
706 | 0 | break; |
707 | 0 | default: |
708 | 0 | ; /* ok. (usually accepts an argument) */ |
709 | 0 | } |
710 | 0 | if (opts->argh && |
711 | 0 | strcspn(opts->argh, " _") != strlen(opts->argh)) |
712 | 0 | optbug(opts, "multi-word argh should use dash to separate words"); |
713 | 0 | } |
714 | 0 | BUG_if_bug("invalid 'struct option'"); |
715 | 0 | } |
716 | | |
717 | | static int has_subcommands(const struct option *options) |
718 | 0 | { |
719 | 0 | for (; options->type != OPTION_END; options++) |
720 | 0 | if (options->type == OPTION_SUBCOMMAND) |
721 | 0 | return 1; |
722 | 0 | return 0; |
723 | 0 | } |
724 | | |
725 | | static void parse_options_start_1(struct parse_opt_ctx_t *ctx, |
726 | | int argc, const char **argv, const char *prefix, |
727 | | const struct option *options, |
728 | | enum parse_opt_flags flags) |
729 | 0 | { |
730 | 0 | ctx->argc = argc; |
731 | 0 | ctx->argv = argv; |
732 | 0 | if (!(flags & PARSE_OPT_ONE_SHOT)) { |
733 | 0 | ctx->argc--; |
734 | 0 | ctx->argv++; |
735 | 0 | } |
736 | 0 | ctx->total = ctx->argc; |
737 | 0 | ctx->out = argv; |
738 | 0 | ctx->prefix = prefix; |
739 | 0 | ctx->cpidx = ((flags & PARSE_OPT_KEEP_ARGV0) != 0); |
740 | 0 | ctx->flags = flags; |
741 | 0 | ctx->has_subcommands = has_subcommands(options); |
742 | 0 | if (!ctx->has_subcommands && (flags & PARSE_OPT_SUBCOMMAND_OPTIONAL)) |
743 | 0 | BUG("Using PARSE_OPT_SUBCOMMAND_OPTIONAL without subcommands"); |
744 | 0 | if (ctx->has_subcommands) { |
745 | 0 | if (flags & PARSE_OPT_STOP_AT_NON_OPTION) |
746 | 0 | BUG("subcommands are incompatible with PARSE_OPT_STOP_AT_NON_OPTION"); |
747 | 0 | if (!(flags & PARSE_OPT_SUBCOMMAND_OPTIONAL)) { |
748 | 0 | if (flags & PARSE_OPT_KEEP_UNKNOWN_OPT) |
749 | 0 | BUG("subcommands are incompatible with PARSE_OPT_KEEP_UNKNOWN_OPT unless in combination with PARSE_OPT_SUBCOMMAND_OPTIONAL"); |
750 | 0 | if (flags & PARSE_OPT_KEEP_DASHDASH) |
751 | 0 | BUG("subcommands are incompatible with PARSE_OPT_KEEP_DASHDASH unless in combination with PARSE_OPT_SUBCOMMAND_OPTIONAL"); |
752 | 0 | } |
753 | 0 | } |
754 | 0 | if ((flags & PARSE_OPT_KEEP_UNKNOWN_OPT) && |
755 | 0 | (flags & PARSE_OPT_STOP_AT_NON_OPTION) && |
756 | 0 | !(flags & PARSE_OPT_ONE_SHOT)) |
757 | 0 | BUG("STOP_AT_NON_OPTION and KEEP_UNKNOWN don't go together"); |
758 | 0 | if ((flags & PARSE_OPT_ONE_SHOT) && |
759 | 0 | (flags & PARSE_OPT_KEEP_ARGV0)) |
760 | 0 | BUG("Can't keep argv0 if you don't have it"); |
761 | 0 | parse_options_check(options); |
762 | 0 | build_cmdmode_list(ctx, options); |
763 | 0 | } |
764 | | |
765 | | void parse_options_start(struct parse_opt_ctx_t *ctx, |
766 | | int argc, const char **argv, const char *prefix, |
767 | | const struct option *options, |
768 | | enum parse_opt_flags flags) |
769 | 0 | { |
770 | 0 | memset(ctx, 0, sizeof(*ctx)); |
771 | 0 | parse_options_start_1(ctx, argc, argv, prefix, options, flags); |
772 | 0 | } |
773 | | |
774 | | static void show_negated_gitcomp(const struct option *opts, int show_all, |
775 | | int nr_noopts) |
776 | 0 | { |
777 | 0 | int printed_dashdash = 0; |
778 | |
|
779 | 0 | for (; opts->type != OPTION_END; opts++) { |
780 | 0 | int has_unset_form = 0; |
781 | 0 | const char *name; |
782 | |
|
783 | 0 | if (!opts->long_name) |
784 | 0 | continue; |
785 | 0 | if (!show_all && |
786 | 0 | (opts->flags & (PARSE_OPT_HIDDEN | PARSE_OPT_NOCOMPLETE))) |
787 | 0 | continue; |
788 | 0 | if (opts->flags & PARSE_OPT_NONEG) |
789 | 0 | continue; |
790 | | |
791 | 0 | switch (opts->type) { |
792 | 0 | case OPTION_STRING: |
793 | 0 | case OPTION_FILENAME: |
794 | 0 | case OPTION_INTEGER: |
795 | 0 | case OPTION_UNSIGNED: |
796 | 0 | case OPTION_CALLBACK: |
797 | 0 | case OPTION_BIT: |
798 | 0 | case OPTION_NEGBIT: |
799 | 0 | case OPTION_COUNTUP: |
800 | 0 | case OPTION_SET_INT: |
801 | 0 | has_unset_form = 1; |
802 | 0 | break; |
803 | 0 | default: |
804 | 0 | break; |
805 | 0 | } |
806 | 0 | if (!has_unset_form) |
807 | 0 | continue; |
808 | | |
809 | 0 | if (skip_prefix(opts->long_name, "no-", &name)) { |
810 | 0 | if (nr_noopts < 0) |
811 | 0 | printf(" --%s", name); |
812 | 0 | } else if (nr_noopts >= 0) { |
813 | 0 | if (nr_noopts && !printed_dashdash) { |
814 | 0 | printf(" --"); |
815 | 0 | printed_dashdash = 1; |
816 | 0 | } |
817 | 0 | printf(" --no-%s", opts->long_name); |
818 | 0 | nr_noopts++; |
819 | 0 | } |
820 | 0 | } |
821 | 0 | } |
822 | | |
823 | | static int show_gitcomp(const struct option *opts, int show_all) |
824 | 0 | { |
825 | 0 | const struct option *original_opts = opts; |
826 | 0 | int nr_noopts = 0; |
827 | |
|
828 | 0 | for (; opts->type != OPTION_END; opts++) { |
829 | 0 | const char *prefix = "--"; |
830 | 0 | const char *suffix = ""; |
831 | |
|
832 | 0 | if (!opts->long_name) |
833 | 0 | continue; |
834 | 0 | if (!show_all && |
835 | 0 | (opts->flags & (PARSE_OPT_HIDDEN | PARSE_OPT_NOCOMPLETE | PARSE_OPT_FROM_ALIAS))) |
836 | 0 | continue; |
837 | | |
838 | 0 | switch (opts->type) { |
839 | 0 | case OPTION_SUBCOMMAND: |
840 | 0 | prefix = ""; |
841 | 0 | break; |
842 | 0 | case OPTION_GROUP: |
843 | 0 | continue; |
844 | 0 | case OPTION_STRING: |
845 | 0 | case OPTION_FILENAME: |
846 | 0 | case OPTION_INTEGER: |
847 | 0 | case OPTION_UNSIGNED: |
848 | 0 | case OPTION_CALLBACK: |
849 | 0 | if (opts->flags & PARSE_OPT_NOARG) |
850 | 0 | break; |
851 | 0 | if (opts->flags & PARSE_OPT_OPTARG) |
852 | 0 | break; |
853 | 0 | if (opts->flags & PARSE_OPT_LASTARG_DEFAULT) |
854 | 0 | break; |
855 | 0 | suffix = "="; |
856 | 0 | break; |
857 | 0 | default: |
858 | 0 | break; |
859 | 0 | } |
860 | 0 | if (opts->flags & PARSE_OPT_COMP_ARG) |
861 | 0 | suffix = "="; |
862 | 0 | if (starts_with(opts->long_name, "no-")) |
863 | 0 | nr_noopts++; |
864 | 0 | printf("%s%s%s%s", opts == original_opts ? "" : " ", |
865 | 0 | prefix, opts->long_name, suffix); |
866 | 0 | } |
867 | 0 | show_negated_gitcomp(original_opts, show_all, -1); |
868 | 0 | show_negated_gitcomp(original_opts, show_all, nr_noopts); |
869 | 0 | fputc('\n', stdout); |
870 | 0 | return PARSE_OPT_COMPLETE; |
871 | 0 | } |
872 | | |
873 | | /* |
874 | | * Scan and may produce a new option[] array, which should be used |
875 | | * instead of the original 'options'. |
876 | | * |
877 | | * Right now this is only used to preprocess and substitute |
878 | | * OPTION_ALIAS. |
879 | | * |
880 | | * The returned options should be freed using free_preprocessed_options. |
881 | | */ |
882 | | static struct option *preprocess_options(struct parse_opt_ctx_t *ctx, |
883 | | const struct option *options) |
884 | 0 | { |
885 | 0 | struct option *newopt; |
886 | 0 | int i, nr, alias; |
887 | 0 | int nr_aliases = 0; |
888 | |
|
889 | 0 | for (nr = 0; options[nr].type != OPTION_END; nr++) { |
890 | 0 | if (options[nr].type == OPTION_ALIAS) |
891 | 0 | nr_aliases++; |
892 | 0 | } |
893 | |
|
894 | 0 | if (!nr_aliases) |
895 | 0 | return NULL; |
896 | | |
897 | 0 | DUP_ARRAY(newopt, options, nr + 1); |
898 | | |
899 | | /* each alias has two string pointers and NULL */ |
900 | 0 | CALLOC_ARRAY(ctx->alias_groups, 3 * (nr_aliases + 1)); |
901 | |
|
902 | 0 | for (alias = 0, i = 0; i < nr; i++) { |
903 | 0 | int short_name; |
904 | 0 | const char *long_name; |
905 | 0 | const char *source; |
906 | 0 | struct strbuf help = STRBUF_INIT; |
907 | 0 | int j; |
908 | |
|
909 | 0 | if (newopt[i].type != OPTION_ALIAS) |
910 | 0 | continue; |
911 | | |
912 | 0 | short_name = newopt[i].short_name; |
913 | 0 | long_name = newopt[i].long_name; |
914 | 0 | source = newopt[i].value; |
915 | |
|
916 | 0 | if (!long_name) |
917 | 0 | BUG("An alias must have long option name"); |
918 | 0 | strbuf_addf(&help, _("alias of --%s"), source); |
919 | |
|
920 | 0 | for (j = 0; j < nr; j++) { |
921 | 0 | const char *name = options[j].long_name; |
922 | |
|
923 | 0 | if (!name || strcmp(name, source)) |
924 | 0 | continue; |
925 | | |
926 | 0 | if (options[j].type == OPTION_ALIAS) |
927 | 0 | BUG("No please. Nested aliases are not supported."); |
928 | | |
929 | 0 | memcpy(newopt + i, options + j, sizeof(*newopt)); |
930 | 0 | newopt[i].short_name = short_name; |
931 | 0 | newopt[i].long_name = long_name; |
932 | 0 | newopt[i].help = strbuf_detach(&help, NULL); |
933 | 0 | newopt[i].flags |= PARSE_OPT_FROM_ALIAS; |
934 | 0 | break; |
935 | 0 | } |
936 | | |
937 | 0 | if (j == nr) |
938 | 0 | BUG("could not find source option '%s' of alias '%s'", |
939 | 0 | source, newopt[i].long_name); |
940 | 0 | ctx->alias_groups[alias * 3 + 0] = newopt[i].long_name; |
941 | 0 | ctx->alias_groups[alias * 3 + 1] = options[j].long_name; |
942 | 0 | ctx->alias_groups[alias * 3 + 2] = NULL; |
943 | 0 | alias++; |
944 | 0 | } |
945 | | |
946 | 0 | return newopt; |
947 | 0 | } |
948 | | |
949 | | static void free_preprocessed_options(struct option *options) |
950 | 0 | { |
951 | 0 | int i; |
952 | |
|
953 | 0 | if (!options) |
954 | 0 | return; |
955 | | |
956 | 0 | for (i = 0; options[i].type != OPTION_END; i++) { |
957 | 0 | if (options[i].flags & PARSE_OPT_FROM_ALIAS) |
958 | 0 | free((void *)options[i].help); |
959 | 0 | } |
960 | 0 | free(options); |
961 | 0 | } |
962 | | |
963 | 0 | #define USAGE_NORMAL 0 |
964 | 0 | #define USAGE_FULL 1 |
965 | 0 | #define USAGE_TO_STDOUT 0 |
966 | 0 | #define USAGE_TO_STDERR 1 |
967 | | |
968 | | static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t *, |
969 | | const char * const *, |
970 | | const struct option *, |
971 | | int full_usage, |
972 | | int usage_to_stderr); |
973 | | |
974 | | enum parse_opt_result parse_options_step(struct parse_opt_ctx_t *ctx, |
975 | | const struct option *options, |
976 | | const char * const usagestr[]) |
977 | 0 | { |
978 | 0 | int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP); |
979 | | |
980 | | /* we must reset ->opt, unknown short option leave it dangling */ |
981 | 0 | ctx->opt = NULL; |
982 | |
|
983 | 0 | for (; ctx->argc; ctx->argc--, ctx->argv++) { |
984 | 0 | const char *arg = ctx->argv[0]; |
985 | |
|
986 | 0 | if (ctx->flags & PARSE_OPT_ONE_SHOT && |
987 | 0 | ctx->argc != ctx->total) |
988 | 0 | break; |
989 | | |
990 | 0 | if (*arg != '-' || !arg[1]) { |
991 | 0 | if (parse_nodash_opt(ctx, arg, options) == 0) |
992 | 0 | continue; |
993 | 0 | if (!ctx->has_subcommands) { |
994 | 0 | if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION) |
995 | 0 | return PARSE_OPT_NON_OPTION; |
996 | 0 | ctx->out[ctx->cpidx++] = ctx->argv[0]; |
997 | 0 | continue; |
998 | 0 | } |
999 | 0 | switch (parse_subcommand(arg, options)) { |
1000 | 0 | case PARSE_OPT_SUBCOMMAND: |
1001 | 0 | return PARSE_OPT_SUBCOMMAND; |
1002 | 0 | case PARSE_OPT_UNKNOWN: |
1003 | 0 | if (ctx->flags & PARSE_OPT_SUBCOMMAND_OPTIONAL) |
1004 | | /* |
1005 | | * arg is neither a short or long |
1006 | | * option nor a subcommand. Since |
1007 | | * this command has a default |
1008 | | * operation mode, we have to treat |
1009 | | * this arg and all remaining args |
1010 | | * as args meant to that default |
1011 | | * operation mode. |
1012 | | * So we are done parsing. |
1013 | | */ |
1014 | 0 | return PARSE_OPT_DONE; |
1015 | 0 | error(_("unknown subcommand: `%s'"), arg); |
1016 | 0 | usage_with_options(usagestr, options); |
1017 | 0 | case PARSE_OPT_COMPLETE: |
1018 | 0 | case PARSE_OPT_HELP: |
1019 | 0 | case PARSE_OPT_ERROR: |
1020 | 0 | case PARSE_OPT_DONE: |
1021 | 0 | case PARSE_OPT_NON_OPTION: |
1022 | | /* Impossible. */ |
1023 | 0 | BUG("parse_subcommand() cannot return these"); |
1024 | 0 | } |
1025 | 0 | } |
1026 | | |
1027 | | /* lone -h asks for help */ |
1028 | 0 | if (internal_help && ctx->total == 1 && !strcmp(arg + 1, "h")) |
1029 | 0 | goto show_usage; |
1030 | | |
1031 | | /* |
1032 | | * lone --git-completion-helper and --git-completion-helper-all |
1033 | | * are asked by git-completion.bash |
1034 | | */ |
1035 | 0 | if (ctx->total == 1 && !strcmp(arg, "--git-completion-helper")) |
1036 | 0 | return show_gitcomp(options, 0); |
1037 | 0 | if (ctx->total == 1 && !strcmp(arg, "--git-completion-helper-all")) |
1038 | 0 | return show_gitcomp(options, 1); |
1039 | | |
1040 | 0 | if (arg[1] != '-') { |
1041 | 0 | ctx->opt = arg + 1; |
1042 | 0 | switch (parse_short_opt(ctx, options)) { |
1043 | 0 | case PARSE_OPT_ERROR: |
1044 | 0 | return PARSE_OPT_ERROR; |
1045 | 0 | case PARSE_OPT_UNKNOWN: |
1046 | 0 | if (ctx->opt) |
1047 | 0 | check_typos(arg + 1, options); |
1048 | 0 | if (internal_help && *ctx->opt == 'h') |
1049 | 0 | goto show_usage; |
1050 | 0 | goto unknown; |
1051 | 0 | case PARSE_OPT_NON_OPTION: |
1052 | 0 | case PARSE_OPT_SUBCOMMAND: |
1053 | 0 | case PARSE_OPT_HELP: |
1054 | 0 | case PARSE_OPT_COMPLETE: |
1055 | 0 | BUG("parse_short_opt() cannot return these"); |
1056 | 0 | case PARSE_OPT_DONE: |
1057 | 0 | break; |
1058 | 0 | } |
1059 | 0 | if (ctx->opt) |
1060 | 0 | check_typos(arg + 1, options); |
1061 | 0 | while (ctx->opt) { |
1062 | 0 | switch (parse_short_opt(ctx, options)) { |
1063 | 0 | case PARSE_OPT_ERROR: |
1064 | 0 | return PARSE_OPT_ERROR; |
1065 | 0 | case PARSE_OPT_UNKNOWN: |
1066 | 0 | if (internal_help && *ctx->opt == 'h') |
1067 | 0 | goto show_usage; |
1068 | | |
1069 | | /* fake a short option thing to hide the fact that we may have |
1070 | | * started to parse aggregated stuff |
1071 | | * |
1072 | | * This is leaky, too bad. |
1073 | | */ |
1074 | 0 | ctx->argv[0] = xstrdup(ctx->opt - 1); |
1075 | 0 | *(char *)ctx->argv[0] = '-'; |
1076 | 0 | goto unknown; |
1077 | 0 | case PARSE_OPT_NON_OPTION: |
1078 | 0 | case PARSE_OPT_SUBCOMMAND: |
1079 | 0 | case PARSE_OPT_COMPLETE: |
1080 | 0 | case PARSE_OPT_HELP: |
1081 | 0 | BUG("parse_short_opt() cannot return these"); |
1082 | 0 | case PARSE_OPT_DONE: |
1083 | 0 | break; |
1084 | 0 | } |
1085 | 0 | } |
1086 | 0 | continue; |
1087 | 0 | } |
1088 | | |
1089 | 0 | if (!arg[2] /* "--" */) { |
1090 | 0 | if (!(ctx->flags & PARSE_OPT_KEEP_DASHDASH)) { |
1091 | 0 | ctx->argc--; |
1092 | 0 | ctx->argv++; |
1093 | 0 | } |
1094 | 0 | break; |
1095 | 0 | } else if (!strcmp(arg + 2, "end-of-options")) { |
1096 | 0 | if (!(ctx->flags & PARSE_OPT_KEEP_UNKNOWN_OPT)) { |
1097 | 0 | ctx->argc--; |
1098 | 0 | ctx->argv++; |
1099 | 0 | } |
1100 | 0 | break; |
1101 | 0 | } |
1102 | | |
1103 | 0 | if (internal_help && !strcmp(arg + 2, "help-all")) |
1104 | 0 | return usage_with_options_internal(ctx, usagestr, options, |
1105 | 0 | USAGE_FULL, USAGE_TO_STDOUT); |
1106 | 0 | if (internal_help && !strcmp(arg + 2, "help")) |
1107 | 0 | goto show_usage; |
1108 | 0 | switch (parse_long_opt(ctx, arg + 2, options)) { |
1109 | 0 | case PARSE_OPT_ERROR: |
1110 | 0 | return PARSE_OPT_ERROR; |
1111 | 0 | case PARSE_OPT_UNKNOWN: |
1112 | 0 | goto unknown; |
1113 | 0 | case PARSE_OPT_HELP: |
1114 | 0 | goto show_usage; |
1115 | 0 | case PARSE_OPT_NON_OPTION: |
1116 | 0 | case PARSE_OPT_SUBCOMMAND: |
1117 | 0 | case PARSE_OPT_COMPLETE: |
1118 | 0 | BUG("parse_long_opt() cannot return these"); |
1119 | 0 | case PARSE_OPT_DONE: |
1120 | 0 | break; |
1121 | 0 | } |
1122 | 0 | continue; |
1123 | 0 | unknown: |
1124 | 0 | if (ctx->flags & PARSE_OPT_ONE_SHOT) |
1125 | 0 | break; |
1126 | 0 | if (ctx->has_subcommands && |
1127 | 0 | (ctx->flags & PARSE_OPT_SUBCOMMAND_OPTIONAL) && |
1128 | 0 | (ctx->flags & PARSE_OPT_KEEP_UNKNOWN_OPT)) { |
1129 | | /* |
1130 | | * Found an unknown option given to a command with |
1131 | | * subcommands that has a default operation mode: |
1132 | | * we treat this option and all remaining args as |
1133 | | * arguments meant to that default operation mode. |
1134 | | * So we are done parsing. |
1135 | | */ |
1136 | 0 | return PARSE_OPT_DONE; |
1137 | 0 | } |
1138 | 0 | if (!(ctx->flags & PARSE_OPT_KEEP_UNKNOWN_OPT)) |
1139 | 0 | return PARSE_OPT_UNKNOWN; |
1140 | 0 | ctx->out[ctx->cpidx++] = ctx->argv[0]; |
1141 | 0 | ctx->opt = NULL; |
1142 | 0 | } |
1143 | 0 | return PARSE_OPT_DONE; |
1144 | | |
1145 | 0 | show_usage: |
1146 | 0 | return usage_with_options_internal(ctx, usagestr, options, |
1147 | 0 | USAGE_NORMAL, USAGE_TO_STDOUT); |
1148 | 0 | } |
1149 | | |
1150 | | int parse_options_end(struct parse_opt_ctx_t *ctx) |
1151 | 0 | { |
1152 | 0 | if (ctx->flags & PARSE_OPT_ONE_SHOT) |
1153 | 0 | return ctx->total - ctx->argc; |
1154 | | |
1155 | 0 | MOVE_ARRAY(ctx->out + ctx->cpidx, ctx->argv, ctx->argc); |
1156 | 0 | ctx->out[ctx->cpidx + ctx->argc] = NULL; |
1157 | 0 | return ctx->cpidx + ctx->argc; |
1158 | 0 | } |
1159 | | |
1160 | | int parse_options(int argc, const char **argv, |
1161 | | const char *prefix, |
1162 | | const struct option *options, |
1163 | | const char * const usagestr[], |
1164 | | enum parse_opt_flags flags) |
1165 | 0 | { |
1166 | 0 | struct parse_opt_ctx_t ctx; |
1167 | 0 | struct option *real_options; |
1168 | |
|
1169 | 0 | disallow_abbreviated_options = |
1170 | 0 | git_env_bool("GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS", 0); |
1171 | |
|
1172 | 0 | memset(&ctx, 0, sizeof(ctx)); |
1173 | 0 | real_options = preprocess_options(&ctx, options); |
1174 | 0 | if (real_options) |
1175 | 0 | options = real_options; |
1176 | 0 | parse_options_start_1(&ctx, argc, argv, prefix, options, flags); |
1177 | 0 | switch (parse_options_step(&ctx, options, usagestr)) { |
1178 | 0 | case PARSE_OPT_HELP: |
1179 | 0 | case PARSE_OPT_ERROR: |
1180 | 0 | exit(129); |
1181 | 0 | case PARSE_OPT_COMPLETE: |
1182 | 0 | exit(0); |
1183 | 0 | case PARSE_OPT_NON_OPTION: |
1184 | 0 | case PARSE_OPT_SUBCOMMAND: |
1185 | 0 | break; |
1186 | 0 | case PARSE_OPT_DONE: |
1187 | 0 | if (ctx.has_subcommands && |
1188 | 0 | !(flags & PARSE_OPT_SUBCOMMAND_OPTIONAL)) { |
1189 | 0 | error(_("need a subcommand")); |
1190 | 0 | usage_with_options(usagestr, options); |
1191 | 0 | } |
1192 | 0 | break; |
1193 | 0 | case PARSE_OPT_UNKNOWN: |
1194 | 0 | if (ctx.argv[0][1] == '-') { |
1195 | 0 | error(_("unknown option `%s'"), ctx.argv[0] + 2); |
1196 | 0 | } else if (isascii(*ctx.opt)) { |
1197 | 0 | error(_("unknown switch `%c'"), *ctx.opt); |
1198 | 0 | } else { |
1199 | 0 | error(_("unknown non-ascii option in string: `%s'"), |
1200 | 0 | ctx.argv[0]); |
1201 | 0 | } |
1202 | 0 | usage_with_options(usagestr, options); |
1203 | 0 | } |
1204 | | |
1205 | 0 | precompose_argv_prefix(argc, argv, NULL); |
1206 | 0 | free_preprocessed_options(real_options); |
1207 | 0 | free(ctx.alias_groups); |
1208 | 0 | for (struct parse_opt_cmdmode_list *elem = ctx.cmdmode_list; elem;) { |
1209 | 0 | struct parse_opt_cmdmode_list *next = elem->next; |
1210 | 0 | free(elem); |
1211 | 0 | elem = next; |
1212 | 0 | } |
1213 | 0 | return parse_options_end(&ctx); |
1214 | 0 | } |
1215 | | |
1216 | | static int usage_argh(const struct option *opts, FILE *outfile) |
1217 | 0 | { |
1218 | 0 | const char *s; |
1219 | 0 | int literal = (opts->flags & PARSE_OPT_LITERAL_ARGHELP) || |
1220 | 0 | !opts->argh || !!strpbrk(opts->argh, "()<>[]|"); |
1221 | 0 | if (opts->flags & PARSE_OPT_OPTARG) |
1222 | 0 | if (opts->long_name) |
1223 | | /* |
1224 | | * TRANSLATORS: The "<%s>" part of this string |
1225 | | * stands for an optional value given to a command |
1226 | | * line option in the long form, and "<>" is there |
1227 | | * as a convention to signal that it is a |
1228 | | * placeholder (i.e. the user should substitute it |
1229 | | * with the real value). If your language uses a |
1230 | | * different convention, you can change "<%s>" part |
1231 | | * to match yours, e.g. it might use "|%s|" instead, |
1232 | | * or if the alphabet is different enough it may use |
1233 | | * "%s" without any placeholder signal. Most |
1234 | | * translations leave this message as is. |
1235 | | */ |
1236 | 0 | s = literal ? "[=%s]" : _("[=<%s>]"); |
1237 | 0 | else |
1238 | | /* |
1239 | | * TRANSLATORS: The "<%s>" part of this string |
1240 | | * stands for an optional value given to a command |
1241 | | * line option in the short form, and "<>" is there |
1242 | | * as a convention to signal that it is a |
1243 | | * placeholder (i.e. the user should substitute it |
1244 | | * with the real value). If your language uses a |
1245 | | * different convention, you can change "<%s>" part |
1246 | | * to match yours, e.g. it might use "|%s|" instead, |
1247 | | * or if the alphabet is different enough it may use |
1248 | | * "%s" without any placeholder signal. Most |
1249 | | * translations leave this message as is. |
1250 | | */ |
1251 | 0 | s = literal ? "[%s]" : _("[<%s>]"); |
1252 | 0 | else |
1253 | | /* |
1254 | | * TRANSLATORS: The "<%s>" part of this string stands for a |
1255 | | * value given to a command line option, and "<>" is there |
1256 | | * as a convention to signal that it is a placeholder |
1257 | | * (i.e. the user should substitute it with the real value). |
1258 | | * If your language uses a different convention, you can |
1259 | | * change "<%s>" part to match yours, e.g. it might use |
1260 | | * "|%s|" instead, or if the alphabet is different enough it |
1261 | | * may use "%s" without any placeholder signal. Most |
1262 | | * translations leave this message as is. |
1263 | | */ |
1264 | 0 | s = literal ? " %s" : _(" <%s>"); |
1265 | 0 | return utf8_fprintf(outfile, s, opts->argh ? _(opts->argh) : _("...")); |
1266 | 0 | } |
1267 | | |
1268 | | static int usage_indent(FILE *outfile) |
1269 | 0 | { |
1270 | 0 | return fprintf(outfile, " "); |
1271 | 0 | } |
1272 | | |
1273 | 0 | #define USAGE_OPTS_WIDTH 26 |
1274 | | |
1275 | | static void usage_padding(FILE *outfile, size_t pos) |
1276 | 0 | { |
1277 | 0 | if (pos < USAGE_OPTS_WIDTH) |
1278 | 0 | fprintf(outfile, "%*s", USAGE_OPTS_WIDTH - (int)pos, ""); |
1279 | 0 | else |
1280 | 0 | fprintf(outfile, "\n%*s", USAGE_OPTS_WIDTH, ""); |
1281 | 0 | } |
1282 | | |
1283 | | static const struct option *find_option_by_long_name(const struct option *opts, |
1284 | | const char *long_name) |
1285 | 0 | { |
1286 | 0 | for (; opts->type != OPTION_END; opts++) { |
1287 | 0 | if (opts->long_name && !strcmp(opts->long_name, long_name)) |
1288 | 0 | return opts; |
1289 | 0 | } |
1290 | 0 | return NULL; |
1291 | 0 | } |
1292 | | |
1293 | | static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t *ctx, |
1294 | | const char * const *usagestr, |
1295 | | const struct option *opts, |
1296 | | int full, int err) |
1297 | 0 | { |
1298 | 0 | const struct option *all_opts = opts; |
1299 | 0 | FILE *outfile = err ? stderr : stdout; |
1300 | 0 | int need_newline; |
1301 | |
|
1302 | 0 | const char *usage_prefix = _("usage: %s"); |
1303 | | /* |
1304 | | * The translation could be anything, but we can count on |
1305 | | * msgfmt(1)'s --check option to have asserted that "%s" is in |
1306 | | * the translation. So compute the length of the "usage: " |
1307 | | * part. We are assuming that the translator wasn't overly |
1308 | | * clever and used e.g. "%1$s" instead of "%s", there's only |
1309 | | * one "%s" in "usage_prefix" above, so there's no reason to |
1310 | | * do so even with a RTL language. |
1311 | | */ |
1312 | 0 | size_t usage_len = strlen(usage_prefix) - strlen("%s"); |
1313 | | /* |
1314 | | * TRANSLATORS: the colon here should align with the |
1315 | | * one in "usage: %s" translation. |
1316 | | */ |
1317 | 0 | const char *or_prefix = _(" or: %s"); |
1318 | | /* |
1319 | | * TRANSLATORS: You should only need to translate this format |
1320 | | * string if your language is a RTL language (e.g. Arabic, |
1321 | | * Hebrew etc.), not if it's a LTR language (e.g. German, |
1322 | | * Russian, Chinese etc.). |
1323 | | * |
1324 | | * When a translated usage string has an embedded "\n" it's |
1325 | | * because options have wrapped to the next line. The line |
1326 | | * after the "\n" will then be padded to align with the |
1327 | | * command name, such as N_("git cmd [opt]\n<8 |
1328 | | * spaces>[opt2]"), where the 8 spaces are the same length as |
1329 | | * "git cmd ". |
1330 | | * |
1331 | | * This format string prints out that already-translated |
1332 | | * line. The "%*s" is whitespace padding to account for the |
1333 | | * padding at the start of the line that we add in this |
1334 | | * function. The "%s" is a line in the (hopefully already |
1335 | | * translated) N_() usage string, which contained embedded |
1336 | | * newlines before we split it up. |
1337 | | */ |
1338 | 0 | const char *usage_continued = _("%*s%s"); |
1339 | 0 | const char *prefix = usage_prefix; |
1340 | 0 | int saw_empty_line = 0; |
1341 | |
|
1342 | 0 | if (!usagestr) |
1343 | 0 | return PARSE_OPT_HELP; |
1344 | | |
1345 | 0 | if (!err && ctx && ctx->flags & PARSE_OPT_SHELL_EVAL) |
1346 | 0 | fprintf(outfile, "cat <<\\EOF\n"); |
1347 | |
|
1348 | 0 | while (*usagestr) { |
1349 | 0 | const char *str = _(*usagestr++); |
1350 | 0 | struct string_list list = STRING_LIST_INIT_DUP; |
1351 | 0 | unsigned int j; |
1352 | |
|
1353 | 0 | if (!saw_empty_line && !*str) |
1354 | 0 | saw_empty_line = 1; |
1355 | |
|
1356 | 0 | string_list_split(&list, str, "\n", -1); |
1357 | 0 | for (j = 0; j < list.nr; j++) { |
1358 | 0 | const char *line = list.items[j].string; |
1359 | |
|
1360 | 0 | if (saw_empty_line && *line) |
1361 | 0 | fprintf_ln(outfile, _(" %s"), line); |
1362 | 0 | else if (saw_empty_line) |
1363 | 0 | fputc('\n', outfile); |
1364 | 0 | else if (!j) |
1365 | 0 | fprintf_ln(outfile, prefix, line); |
1366 | 0 | else |
1367 | 0 | fprintf_ln(outfile, usage_continued, |
1368 | 0 | (int)usage_len, "", line); |
1369 | 0 | } |
1370 | 0 | string_list_clear(&list, 0); |
1371 | |
|
1372 | 0 | prefix = or_prefix; |
1373 | 0 | } |
1374 | |
|
1375 | 0 | need_newline = 1; |
1376 | |
|
1377 | 0 | for (; opts->type != OPTION_END; opts++) { |
1378 | 0 | size_t pos; |
1379 | 0 | const char *cp, *np; |
1380 | 0 | const char *positive_name = NULL; |
1381 | |
|
1382 | 0 | if (opts->type == OPTION_SUBCOMMAND) |
1383 | 0 | continue; |
1384 | 0 | if (opts->type == OPTION_GROUP) { |
1385 | 0 | fputc('\n', outfile); |
1386 | 0 | need_newline = 0; |
1387 | 0 | if (*opts->help) |
1388 | 0 | fprintf(outfile, "%s\n", _(opts->help)); |
1389 | 0 | continue; |
1390 | 0 | } |
1391 | 0 | if (!full && (opts->flags & PARSE_OPT_HIDDEN)) |
1392 | 0 | continue; |
1393 | | |
1394 | 0 | if (need_newline) { |
1395 | 0 | fputc('\n', outfile); |
1396 | 0 | need_newline = 0; |
1397 | 0 | } |
1398 | |
|
1399 | 0 | pos = usage_indent(outfile); |
1400 | 0 | if (opts->short_name) { |
1401 | 0 | if (opts->flags & PARSE_OPT_NODASH) |
1402 | 0 | pos += fprintf(outfile, "%c", opts->short_name); |
1403 | 0 | else |
1404 | 0 | pos += fprintf(outfile, "-%c", opts->short_name); |
1405 | 0 | } |
1406 | 0 | if (opts->long_name && opts->short_name) |
1407 | 0 | pos += fprintf(outfile, ", "); |
1408 | 0 | if (opts->long_name) { |
1409 | 0 | const char *long_name = opts->long_name; |
1410 | 0 | if ((opts->flags & PARSE_OPT_NONEG) || |
1411 | 0 | skip_prefix(long_name, "no-", &positive_name)) |
1412 | 0 | pos += fprintf(outfile, "--%s", long_name); |
1413 | 0 | else |
1414 | 0 | pos += fprintf(outfile, "--[no-]%s", long_name); |
1415 | 0 | } |
1416 | |
|
1417 | 0 | if (opts->type == OPTION_NUMBER) |
1418 | 0 | pos += utf8_fprintf(outfile, _("-NUM")); |
1419 | |
|
1420 | 0 | if ((opts->flags & PARSE_OPT_LITERAL_ARGHELP) || |
1421 | 0 | !(opts->flags & PARSE_OPT_NOARG)) |
1422 | 0 | pos += usage_argh(opts, outfile); |
1423 | |
|
1424 | 0 | if (opts->type == OPTION_ALIAS) { |
1425 | 0 | usage_padding(outfile, pos); |
1426 | 0 | fprintf_ln(outfile, _("alias of --%s"), |
1427 | 0 | (const char *)opts->value); |
1428 | 0 | continue; |
1429 | 0 | } |
1430 | | |
1431 | 0 | for (cp = opts->help ? _(opts->help) : ""; *cp; cp = np) { |
1432 | 0 | np = strchrnul(cp, '\n'); |
1433 | 0 | if (*np) |
1434 | 0 | np++; |
1435 | 0 | usage_padding(outfile, pos); |
1436 | 0 | fwrite(cp, 1, np - cp, outfile); |
1437 | 0 | pos = 0; |
1438 | 0 | } |
1439 | 0 | fputc('\n', outfile); |
1440 | |
|
1441 | 0 | if (positive_name) { |
1442 | 0 | if (find_option_by_long_name(all_opts, positive_name)) |
1443 | 0 | continue; |
1444 | 0 | pos = usage_indent(outfile); |
1445 | 0 | pos += fprintf(outfile, "--%s", positive_name); |
1446 | 0 | usage_padding(outfile, pos); |
1447 | 0 | fprintf_ln(outfile, _("opposite of --no-%s"), |
1448 | 0 | positive_name); |
1449 | 0 | } |
1450 | 0 | } |
1451 | 0 | fputc('\n', outfile); |
1452 | |
|
1453 | 0 | if (!err && ctx && ctx->flags & PARSE_OPT_SHELL_EVAL) |
1454 | 0 | fputs("EOF\n", outfile); |
1455 | |
|
1456 | 0 | return PARSE_OPT_HELP; |
1457 | 0 | } |
1458 | | |
1459 | | void NORETURN usage_with_options(const char * const *usagestr, |
1460 | | const struct option *opts) |
1461 | 0 | { |
1462 | 0 | usage_with_options_internal(NULL, usagestr, opts, |
1463 | 0 | USAGE_NORMAL, USAGE_TO_STDERR); |
1464 | 0 | exit(129); |
1465 | 0 | } |
1466 | | |
1467 | | void show_usage_with_options_if_asked(int ac, const char **av, |
1468 | | const char * const *usagestr, |
1469 | | const struct option *opts) |
1470 | 0 | { |
1471 | 0 | if (ac == 2) { |
1472 | 0 | if (!strcmp(av[1], "-h")) { |
1473 | 0 | usage_with_options_internal(NULL, usagestr, opts, |
1474 | 0 | USAGE_NORMAL, USAGE_TO_STDOUT); |
1475 | 0 | exit(129); |
1476 | 0 | } else if (!strcmp(av[1], "--help-all")) { |
1477 | 0 | usage_with_options_internal(NULL, usagestr, opts, |
1478 | 0 | USAGE_FULL, USAGE_TO_STDOUT); |
1479 | 0 | exit(129); |
1480 | 0 | } |
1481 | 0 | } |
1482 | 0 | } |
1483 | | |
1484 | | void NORETURN usage_msg_opt(const char *msg, |
1485 | | const char * const *usagestr, |
1486 | | const struct option *options) |
1487 | 0 | { |
1488 | 0 | die_message("%s\n", msg); /* The extra \n is intentional */ |
1489 | 0 | usage_with_options(usagestr, options); |
1490 | 0 | } |
1491 | | |
1492 | | void NORETURN usage_msg_optf(const char * const fmt, |
1493 | | const char * const *usagestr, |
1494 | | const struct option *options, ...) |
1495 | 0 | { |
1496 | 0 | struct strbuf msg = STRBUF_INIT; |
1497 | 0 | va_list ap; |
1498 | 0 | va_start(ap, options); |
1499 | 0 | strbuf_vaddf(&msg, fmt, ap); |
1500 | 0 | va_end(ap); |
1501 | |
|
1502 | 0 | usage_msg_opt(msg.buf, usagestr, options); |
1503 | 0 | } |
1504 | | |
1505 | | void die_for_incompatible_opt4(int opt1, const char *opt1_name, |
1506 | | int opt2, const char *opt2_name, |
1507 | | int opt3, const char *opt3_name, |
1508 | | int opt4, const char *opt4_name) |
1509 | 0 | { |
1510 | 0 | int count = 0; |
1511 | 0 | const char *options[4]; |
1512 | |
|
1513 | 0 | if (opt1) |
1514 | 0 | options[count++] = opt1_name; |
1515 | 0 | if (opt2) |
1516 | 0 | options[count++] = opt2_name; |
1517 | 0 | if (opt3) |
1518 | 0 | options[count++] = opt3_name; |
1519 | 0 | if (opt4) |
1520 | 0 | options[count++] = opt4_name; |
1521 | 0 | switch (count) { |
1522 | 0 | case 4: |
1523 | 0 | die(_("options '%s', '%s', '%s', and '%s' cannot be used together"), |
1524 | 0 | opt1_name, opt2_name, opt3_name, opt4_name); |
1525 | 0 | break; |
1526 | 0 | case 3: |
1527 | 0 | die(_("options '%s', '%s', and '%s' cannot be used together"), |
1528 | 0 | options[0], options[1], options[2]); |
1529 | 0 | break; |
1530 | 0 | case 2: |
1531 | 0 | die(_("options '%s' and '%s' cannot be used together"), |
1532 | 0 | options[0], options[1]); |
1533 | 0 | break; |
1534 | 0 | default: |
1535 | 0 | break; |
1536 | 0 | } |
1537 | 0 | } |