parse-options: factor out register_abbrev() and struct parsed_option

Add a function, register_abbrev(), for storing the necessary details for remembering an abbreviated and thus potentially ambiguous option. Call it instead of sharing the code using goto, to make the control flow more explicit. Conveniently collect these details in the new struct parsed_option to reduce the number of necessary function arguments. Signed-off-by: René Scharfe <l.s.r@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

René Scharfe committed Mar 3, 2024 at 13:19 UTC cb46c3faf843fa46ce2df22baeb02ad2f60da6fb
1 file changed +49 -34
parse-options.c
+49 -34
@@ -350,14 +350,40 @@ static int is_alias(struct parse_opt_ctx_t *ctx,
350 return 0;
351 }
352
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 + !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 +
380 static enum parse_opt_result parse_long_opt(
381 struct parse_opt_ctx_t *p, const char *arg,
382 const struct option *options)
383 {
384 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 + struct parsed_option abbrev = { .option = NULL, .flags = OPT_LONG };
386 + struct parsed_option ambiguous = { .option = NULL, .flags = OPT_LONG };
387
388 for (; options->type != OPTION_END; options++) {
389 const char *rest, *long_name = options->long_name;
@@ -377,31 +403,20 @@ static enum parse_opt_result parse_long_opt(
403 rest = NULL;
404 if (!rest) {
405 /* 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 - abbrev_option = options;
395 - abbrev_flags = flags ^ opt_flags;
406 + if (!strncmp(long_name, arg, arg_end - arg)) {
407 + register_abbrev(p, options, flags ^ opt_flags,
408 + &abbrev, &ambiguous);
409 continue;
410 }
411 /* negation allowed? */
412 if (options->flags & PARSE_OPT_NONEG)
413 continue;
414 /* negated and abbreviated very much? */
402 - if (allow_abbrev && starts_with("no-", arg)) {
415 + if (starts_with("no-", arg)) {
416 flags |= OPT_UNSET;
404 - goto is_abbreviated;
417 + register_abbrev(p, options, flags ^ opt_flags,
418 + &abbrev, &ambiguous);
419 + continue;
420 }
421 /* negated? */
422 if (!starts_with(arg, "no-"))
@@ -409,12 +424,12 @@ is_abbreviated:
424 flags |= OPT_UNSET;
425 if (!skip_prefix(arg + 3, long_name, &rest)) {
426 /* abbreviated and negated? */
412 - if (allow_abbrev &&
413 - !strncmp(long_name, arg + 3,
427 + if (!strncmp(long_name, arg + 3,
428 arg_end - arg - 3))
415 - goto is_abbreviated;
416 - else
417 - continue;
429 + register_abbrev(p, options,
430 + flags ^ opt_flags,
431 + &abbrev, &ambiguous);
432 + continue;
433 }
434 }
435 if (*rest) {
@@ -425,24 +440,24 @@ is_abbreviated:
440 return get_value(p, options, flags ^ opt_flags);
441 }
442
428 - if (disallow_abbreviated_options && (ambiguous_option || abbrev_option))
443 + if (disallow_abbreviated_options && (ambiguous.option || abbrev.option))
444 die("disallowed abbreviated or ambiguous option '%.*s'",
445 (int)(arg_end - arg), arg);
446
432 - if (ambiguous_option) {
447 + if (ambiguous.option) {
448 error(_("ambiguous option: %s "
449 "(could be --%s%s or --%s%s)"),
450 arg,
436 - (ambiguous_flags & OPT_UNSET) ? "no-" : "",
437 - ambiguous_option->long_name,
438 - (abbrev_flags & OPT_UNSET) ? "no-" : "",
439 - abbrev_option->long_name);
451 + (ambiguous.flags & OPT_UNSET) ? "no-" : "",
452 + ambiguous.option->long_name,
453 + (abbrev.flags & OPT_UNSET) ? "no-" : "",
454 + abbrev.option->long_name);
455 return PARSE_OPT_HELP;
456 }
442 - if (abbrev_option) {
457 + if (abbrev.option) {
458 if (*arg_end)
459 p->opt = arg_end + 1;
445 - return get_value(p, abbrev_option, abbrev_flags);
460 + return get_value(p, abbrev.option, abbrev.flags);
461 }
462 return PARSE_OPT_UNKNOWN;
463 }