@@ -350,98 +350,107 @@ static int is_alias(struct parse_opt_ctx_t *ctx,
350350 return 0 ;
351351}
352352
353+ struct parsed_option {
354+ const struct option * option ;
355+ enum opt_parsed flags ;
356+ };
357+
358+ static void register_abbrev (struct parse_opt_ctx_t * p ,
359+ const struct option * option , enum opt_parsed flags ,
360+ struct parsed_option * abbrev ,
361+ struct parsed_option * ambiguous )
362+ {
363+ if (p -> flags & PARSE_OPT_KEEP_UNKNOWN_OPT )
364+ return ;
365+ if (abbrev -> option &&
366+ !(abbrev -> flags == flags && is_alias (p , abbrev -> option , option ))) {
367+ /*
368+ * If this is abbreviated, it is
369+ * ambiguous. So when there is no
370+ * exact match later, we need to
371+ * error out.
372+ */
373+ ambiguous -> option = abbrev -> option ;
374+ ambiguous -> flags = abbrev -> flags ;
375+ }
376+ abbrev -> option = option ;
377+ abbrev -> flags = flags ;
378+ }
379+
353380static enum parse_opt_result parse_long_opt (
354381 struct parse_opt_ctx_t * p , const char * arg ,
355382 const struct option * options )
356383{
357384 const char * arg_end = strchrnul (arg , '=' );
358- const struct option * abbrev_option = NULL , * ambiguous_option = NULL ;
359- enum opt_parsed abbrev_flags = OPT_LONG , ambiguous_flags = OPT_LONG ;
360- int allow_abbrev = !(p -> flags & PARSE_OPT_KEEP_UNKNOWN_OPT );
385+ const char * arg_start = arg ;
386+ enum opt_parsed flags = OPT_LONG ;
387+ int arg_starts_with_no_no = 0 ;
388+ struct parsed_option abbrev = { .option = NULL , .flags = OPT_LONG };
389+ struct parsed_option ambiguous = { .option = NULL , .flags = OPT_LONG };
390+
391+ if (skip_prefix (arg_start , "no-" , & arg_start )) {
392+ if (skip_prefix (arg_start , "no-" , & arg_start ))
393+ arg_starts_with_no_no = 1 ;
394+ else
395+ flags |= OPT_UNSET ;
396+ }
361397
362398 for (; options -> type != OPTION_END ; options ++ ) {
363399 const char * rest , * long_name = options -> long_name ;
364- enum opt_parsed flags = OPT_LONG , opt_flags = OPT_LONG ;
400+ enum opt_parsed opt_flags = OPT_LONG ;
401+ int allow_unset = !(options -> flags & PARSE_OPT_NONEG );
365402
366403 if (options -> type == OPTION_SUBCOMMAND )
367404 continue ;
368405 if (!long_name )
369406 continue ;
370407
371- if (!starts_with (arg , "no-" ) &&
372- !(options -> flags & PARSE_OPT_NONEG ) &&
373- skip_prefix (long_name , "no-" , & long_name ))
408+ if (skip_prefix (long_name , "no-" , & long_name ))
374409 opt_flags |= OPT_UNSET ;
410+ else if (arg_starts_with_no_no )
411+ continue ;
375412
376- if (!skip_prefix (arg , long_name , & rest ))
377- rest = NULL ;
378- if (!rest ) {
379- /* abbreviated? */
380- if (allow_abbrev &&
381- !strncmp (long_name , arg , arg_end - arg )) {
382- is_abbreviated :
383- if (abbrev_option &&
384- !is_alias (p , abbrev_option , options )) {
385- /*
386- * If this is abbreviated, it is
387- * ambiguous. So when there is no
388- * exact match later, we need to
389- * error out.
390- */
391- ambiguous_option = abbrev_option ;
392- ambiguous_flags = abbrev_flags ;
393- }
394- if (!(flags & OPT_UNSET ) && * arg_end )
395- p -> opt = arg_end + 1 ;
396- abbrev_option = options ;
397- abbrev_flags = flags ^ opt_flags ;
398- continue ;
399- }
400- /* negation allowed? */
401- if (options -> flags & PARSE_OPT_NONEG )
402- continue ;
403- /* negated and abbreviated very much? */
404- if (allow_abbrev && starts_with ("no-" , arg )) {
405- flags |= OPT_UNSET ;
406- goto is_abbreviated ;
407- }
408- /* negated? */
409- if (!starts_with (arg , "no-" ))
410- continue ;
411- flags |= OPT_UNSET ;
412- if (!skip_prefix (arg + 3 , long_name , & rest )) {
413- /* abbreviated and negated? */
414- if (allow_abbrev &&
415- starts_with (long_name , arg + 3 ))
416- goto is_abbreviated ;
417- else
418- continue ;
419- }
420- }
421- if (* rest ) {
422- if (* rest != '=' )
413+ if (((flags ^ opt_flags ) & OPT_UNSET ) && !allow_unset )
414+ continue ;
415+
416+ if (skip_prefix (arg_start , long_name , & rest )) {
417+ if (* rest == '=' )
418+ p -> opt = rest + 1 ;
419+ else if (* rest )
423420 continue ;
424- p -> opt = rest + 1 ;
421+ return get_value ( p , options , flags ^ opt_flags ) ;
425422 }
426- return get_value (p , options , flags ^ opt_flags );
423+
424+ /* abbreviated? */
425+ if (!strncmp (long_name , arg_start , arg_end - arg_start ))
426+ register_abbrev (p , options , flags ^ opt_flags ,
427+ & abbrev , & ambiguous );
428+
429+ /* negated and abbreviated very much? */
430+ if (allow_unset && starts_with ("no-" , arg ))
431+ register_abbrev (p , options , OPT_UNSET ^ opt_flags ,
432+ & abbrev , & ambiguous );
427433 }
428434
429- if (disallow_abbreviated_options && (ambiguous_option || abbrev_option ))
435+ if (disallow_abbreviated_options && (ambiguous . option || abbrev . option ))
430436 die ("disallowed abbreviated or ambiguous option '%.*s'" ,
431437 (int )(arg_end - arg ), arg );
432438
433- if (ambiguous_option ) {
439+ if (ambiguous . option ) {
434440 error (_ ("ambiguous option: %s "
435441 "(could be --%s%s or --%s%s)" ),
436442 arg ,
437- (ambiguous_flags & OPT_UNSET ) ? "no-" : "" ,
438- ambiguous_option -> long_name ,
439- (abbrev_flags & OPT_UNSET ) ? "no-" : "" ,
440- abbrev_option -> long_name );
443+ (ambiguous . flags & OPT_UNSET ) ? "no-" : "" ,
444+ ambiguous . option -> long_name ,
445+ (abbrev . flags & OPT_UNSET ) ? "no-" : "" ,
446+ abbrev . option -> long_name );
441447 return PARSE_OPT_HELP ;
442448 }
443- if (abbrev_option )
444- return get_value (p , abbrev_option , abbrev_flags );
449+ if (abbrev .option ) {
450+ if (* arg_end )
451+ p -> opt = arg_end + 1 ;
452+ return get_value (p , abbrev .option , abbrev .flags );
453+ }
445454 return PARSE_OPT_UNKNOWN ;
446455}
447456
0 commit comments