@@ -261,6 +261,35 @@ static enum parse_opt_result parse_short_opt(struct parse_opt_ctx_t *p,
261261 return PARSE_OPT_UNKNOWN ;
262262}
263263
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+
264293static enum parse_opt_result parse_long_opt (
265294 struct parse_opt_ctx_t * p , const char * arg ,
266295 const struct option * options )
@@ -298,7 +327,8 @@ static enum parse_opt_result parse_long_opt(
298327 if (!(p -> flags & PARSE_OPT_KEEP_UNKNOWN ) &&
299328 !strncmp (long_name , arg , arg_end - arg )) {
300329is_abbreviated :
301- if (abbrev_option ) {
330+ if (abbrev_option &&
331+ !is_alias (p , abbrev_option , options )) {
302332 /*
303333 * If this is abbreviated, it is
304334 * ambiguous. So when there is no
@@ -447,6 +477,10 @@ static void parse_options_check(const struct option *opts)
447477 if (opts -> callback )
448478 BUG ("OPTION_LOWLEVEL_CALLBACK needs no high level callback" );
449479 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." );
450484 default :
451485 ; /* ok. (usually accepts an argument) */
452486 }
@@ -458,11 +492,10 @@ static void parse_options_check(const struct option *opts)
458492 exit (128 );
459493}
460494
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 )
464498{
465- memset (ctx , 0 , sizeof (* ctx ));
466499 ctx -> argc = argc ;
467500 ctx -> argv = argv ;
468501 if (!(flags & PARSE_OPT_ONE_SHOT )) {
@@ -484,6 +517,14 @@ void parse_options_start(struct parse_opt_ctx_t *ctx,
484517 parse_options_check (options );
485518}
486519
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+
487528static void show_negated_gitcomp (const struct option * opts , int nr_noopts )
488529{
489530 int printed_dashdash = 0 ;
@@ -575,6 +616,83 @@ static int show_gitcomp(const struct option *opts)
575616 return PARSE_OPT_COMPLETE ;
576617}
577618
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+
578696static int usage_with_options_internal (struct parse_opt_ctx_t * ,
579697 const char * const * ,
580698 const struct option * , int , int );
@@ -714,11 +832,16 @@ int parse_options(int argc, const char **argv, const char *prefix,
714832 int flags )
715833{
716834 struct parse_opt_ctx_t ctx ;
835+ struct option * real_options ;
717836
718837 disallow_abbreviated_options =
719838 git_env_bool ("GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS" , 0 );
720839
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 );
722845 switch (parse_options_step (& ctx , options , usagestr )) {
723846 case PARSE_OPT_HELP :
724847 case PARSE_OPT_ERROR :
@@ -741,6 +864,8 @@ int parse_options(int argc, const char **argv, const char *prefix,
741864 }
742865
743866 precompose_argv (argc , argv );
867+ free (real_options );
868+ free (ctx .alias_groups );
744869 return parse_options_end (& ctx );
745870}
746871
@@ -835,6 +960,12 @@ static int usage_with_options_internal(struct parse_opt_ctx_t *ctx,
835960 fputc ('\n' , outfile );
836961 pad = USAGE_OPTS_WIDTH ;
837962 }
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+ }
838969 fprintf (outfile , "%*s%s\n" , pad + USAGE_GAP , "" , _ (opts -> help ));
839970 }
840971 fputc ('\n' , outfile );
0 commit comments