261
return PARSE_OPT_UNKNOWN;
262
}
263
264
+static int has_string(const char *it, const char **array)
265
+{
266
+ while (*array)
267
+ if (!strcmp(it, *(array++)))
268
+ return 1;
269
+ return 0;
270
+}
271
+
272
+static int is_alias(struct parse_opt_ctx_t *ctx,
273
+ const struct option *one_opt,
274
+ const struct option *another_opt)
275
+{
276
+ const char **group;
277
+
278
+ if (!ctx->alias_groups)
279
+ return 0;
280
+
281
+ if (!one_opt->long_name || !another_opt->long_name)
282
+ return 0;
283
+
284
+ for (group = ctx->alias_groups; *group; group += 3) {
285
+ /* it and other are from the same family? */
286
+ if (has_string(one_opt->long_name, group) &&
287
+ has_string(another_opt->long_name, group))
288
+ return 1;
289
+ }
290
+ return 0;
291
+}
292
+
293
static enum parse_opt_result parse_long_opt(
294
struct parse_opt_ctx_t *p, const char *arg,
295
const struct option *options)
327
if (!(p->flags & PARSE_OPT_KEEP_UNKNOWN) &&
328
!strncmp(long_name, arg, arg_end - arg)) {
329
is_abbreviated:
301
- if (abbrev_option) {
330
+ if (abbrev_option &&
331
+ !is_alias(p, abbrev_option, options)) {
332
/*
333
* If this is abbreviated, it is
334
* ambiguous. So when there is no
477
if (opts->callback)
478
BUG("OPTION_LOWLEVEL_CALLBACK needs no high level callback");
479
break;
480
+ case OPTION_ALIAS:
481
+ BUG("OPT_ALIAS() should not remain at this point. "
482
+ "Are you using parse_options_step() directly?\n"
483
+ "That case is not supported yet.");
484
default:
485
; /* ok. (usually accepts an argument) */
486
}
492
exit(128);
493
}
494
461
-void parse_options_start(struct parse_opt_ctx_t *ctx,
462
- int argc, const char **argv, const char *prefix,
463
- const struct option *options, int flags)
495
+static void parse_options_start_1(struct parse_opt_ctx_t *ctx,
496
+ int argc, const char **argv, const char *prefix,
497
+ const struct option *options, int flags)
498
{
465
- memset(ctx, 0, sizeof(*ctx));
499
ctx->argc = argc;
500
ctx->argv = argv;
501
if (!(flags & PARSE_OPT_ONE_SHOT)) {
517
parse_options_check(options);
518
}
519
520
+void parse_options_start(struct parse_opt_ctx_t *ctx,
521
+ int argc, const char **argv, const char *prefix,
522
+ const struct option *options, int flags)
523
+{
524
+ memset(ctx, 0, sizeof(*ctx));
525
+ parse_options_start_1(ctx, argc, argv, prefix, options, flags);
526
+}
527
+
528
static void show_negated_gitcomp(const struct option *opts, int nr_noopts)
529
{
530
int printed_dashdash = 0;
616
return PARSE_OPT_COMPLETE;
617
}
618
619
+/*
620
+ * Scan and may produce a new option[] array, which should be used
621
+ * instead of the original 'options'.
622
+ *
623
+ * Right now this is only used to preprocess and substitue
624
+ * OPTION_ALIAS.
625
+ */
626
+static struct option *preprocess_options(struct parse_opt_ctx_t *ctx,
627
+ const struct option *options)
628
+{
629
+ struct option *newopt;
630
+ int i, nr, alias;
631
+ int nr_aliases = 0;
632
+
633
+ for (nr = 0; options[nr].type != OPTION_END; nr++) {
634
+ if (options[nr].type == OPTION_ALIAS)
635
+ nr_aliases++;
636
+ }
637
+
638
+ if (!nr_aliases)
639
+ return NULL;
640
+
641
+ ALLOC_ARRAY(newopt, nr + 1);
642
+ COPY_ARRAY(newopt, options, nr + 1);
643
+
644
+ /* each alias has two string pointers and NULL */
645
+ CALLOC_ARRAY(ctx->alias_groups, 3 * (nr_aliases + 1));
646
+
647
+ for (alias = 0, i = 0; i < nr; i++) {
648
+ int short_name;
649
+ const char *long_name;
650
+ const char *source;
651
+ int j;
652
+
653
+ if (newopt[i].type != OPTION_ALIAS)
654
+ continue;
655
+
656
+ short_name = newopt[i].short_name;
657
+ long_name = newopt[i].long_name;
658
+ source = newopt[i].value;
659
+
660
+ if (!long_name)
661
+ BUG("An alias must have long option name");
662
+
663
+ for (j = 0; j < nr; j++) {
664
+ const char *name = options[j].long_name;
665
+
666
+ if (!name || strcmp(name, source))
667
+ continue;
668
+
669
+ if (options[j].type == OPTION_ALIAS)
670
+ BUG("No please. Nested aliases are not supported.");
671
+
672
+ /*
673
+ * NEEDSWORK: this is a bit inconsistent because
674
+ * usage_with_options() on the original options[] will print
675
+ * help string as "alias of %s" but "git cmd -h" will
676
+ * print the original help string.
677
+ */
678
+ memcpy(newopt + i, options + j, sizeof(*newopt));
679
+ newopt[i].short_name = short_name;
680
+ newopt[i].long_name = long_name;
681
+ break;
682
+ }
683
+
684
+ if (j == nr)
685
+ BUG("could not find source option '%s' of alias '%s'",
686
+ source, newopt[i].long_name);
687
+ ctx->alias_groups[alias * 3 + 0] = newopt[i].long_name;
688
+ ctx->alias_groups[alias * 3 + 1] = options[j].long_name;
689
+ ctx->alias_groups[alias * 3 + 2] = NULL;
690
+ alias++;
691
+ }
692
+
693
+ return newopt;
694
+}
695
+
696
static int usage_with_options_internal(struct parse_opt_ctx_t *,
697
const char * const *,
698
const struct option *, int, int);
832
int flags)
833
{
834
struct parse_opt_ctx_t ctx;
835
+ struct option *real_options;
836
837
disallow_abbreviated_options =
838
git_env_bool("GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS", 0);
839
721
- parse_options_start(&ctx, argc, argv, prefix, options, flags);
840
+ memset(&ctx, 0, sizeof(ctx));
841
+ real_options = preprocess_options(&ctx, options);
842
+ if (real_options)
843
+ options = real_options;
844
+ parse_options_start_1(&ctx, argc, argv, prefix, options, flags);
845
switch (parse_options_step(&ctx, options, usagestr)) {
846
case PARSE_OPT_HELP:
847
case PARSE_OPT_ERROR:
864
}
865
866
precompose_argv(argc, argv);
867
+ free(real_options);
868
+ free(ctx.alias_groups);
869
return parse_options_end(&ctx);
870
}
871
960
fputc('\n', outfile);
961
pad = USAGE_OPTS_WIDTH;
962
}
963
+ if (opts->type == OPTION_ALIAS) {
964
+ fprintf(outfile, "%*s", pad + USAGE_GAP, "");
965
+ fprintf_ln(outfile, _("alias of --%s"),
966
+ (const char *)opts->value);
967
+ continue;
968
+ }
969
fprintf(outfile, "%*s%s\n", pad + USAGE_GAP, "", _(opts->help));
970
}
971
fputc('\n', outfile);