@@ -261,6 +261,35 @@ static enum parse_opt_result parse_short_opt(struct parse_opt_ctx_t *p,
261
261
return PARSE_OPT_UNKNOWN ;
262
262
}
263
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
+
264
293
static enum parse_opt_result parse_long_opt (
265
294
struct parse_opt_ctx_t * p , const char * arg ,
266
295
const struct option * options )
@@ -298,7 +327,8 @@ static enum parse_opt_result parse_long_opt(
298
327
if (!(p -> flags & PARSE_OPT_KEEP_UNKNOWN ) &&
299
328
!strncmp (long_name , arg , arg_end - arg )) {
300
329
is_abbreviated :
301
- if (abbrev_option ) {
330
+ if (abbrev_option &&
331
+ !is_alias (p , abbrev_option , options )) {
302
332
/*
303
333
* If this is abbreviated, it is
304
334
* ambiguous. So when there is no
@@ -447,6 +477,10 @@ static void parse_options_check(const struct option *opts)
447
477
if (opts -> callback )
448
478
BUG ("OPTION_LOWLEVEL_CALLBACK needs no high level callback" );
449
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." );
450
484
default :
451
485
; /* ok. (usually accepts an argument) */
452
486
}
@@ -458,11 +492,10 @@ static void parse_options_check(const struct option *opts)
458
492
exit (128 );
459
493
}
460
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 )
464
498
{
465
- memset (ctx , 0 , sizeof (* ctx ));
466
499
ctx -> argc = argc ;
467
500
ctx -> argv = argv ;
468
501
if (!(flags & PARSE_OPT_ONE_SHOT )) {
@@ -484,6 +517,14 @@ void parse_options_start(struct parse_opt_ctx_t *ctx,
484
517
parse_options_check (options );
485
518
}
486
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
+
487
528
static void show_negated_gitcomp (const struct option * opts , int nr_noopts )
488
529
{
489
530
int printed_dashdash = 0 ;
@@ -575,6 +616,83 @@ static int show_gitcomp(const struct option *opts)
575
616
return PARSE_OPT_COMPLETE ;
576
617
}
577
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
+
578
696
static int usage_with_options_internal (struct parse_opt_ctx_t * ,
579
697
const char * const * ,
580
698
const struct option * , int , int );
@@ -714,11 +832,16 @@ int parse_options(int argc, const char **argv, const char *prefix,
714
832
int flags )
715
833
{
716
834
struct parse_opt_ctx_t ctx ;
835
+ struct option * real_options ;
717
836
718
837
disallow_abbreviated_options =
719
838
git_env_bool ("GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS" , 0 );
720
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 );
722
845
switch (parse_options_step (& ctx , options , usagestr )) {
723
846
case PARSE_OPT_HELP :
724
847
case PARSE_OPT_ERROR :
@@ -741,6 +864,8 @@ int parse_options(int argc, const char **argv, const char *prefix,
741
864
}
742
865
743
866
precompose_argv (argc , argv );
867
+ free (real_options );
868
+ free (ctx .alias_groups );
744
869
return parse_options_end (& ctx );
745
870
}
746
871
@@ -835,6 +960,12 @@ static int usage_with_options_internal(struct parse_opt_ctx_t *ctx,
835
960
fputc ('\n' , outfile );
836
961
pad = USAGE_OPTS_WIDTH ;
837
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
+ }
838
969
fprintf (outfile , "%*s%s\n" , pad + USAGE_GAP , "" , _ (opts -> help ));
839
970
}
840
971
fputc ('\n' , outfile );
0 commit comments