1052 lines · c
1// SPDX-License-Identifier: GPL-2.02#include <linux/compiler.h>3#include <linux/string.h>4#include <linux/types.h>5#include <stdio.h>6#include <stdlib.h>7#include <stdint.h>8#include <string.h>9#include <ctype.h>10#include "subcmd-util.h"11#include "parse-options.h"12#include "subcmd-config.h"13#include "pager.h"14 15#define OPT_SHORT 116#define OPT_UNSET 217 18char *error_buf;19 20static int opterror(const struct option *opt, const char *reason, int flags)21{22 if (flags & OPT_SHORT)23 fprintf(stderr, " Error: switch `%c' %s", opt->short_name, reason);24 else if (flags & OPT_UNSET)25 fprintf(stderr, " Error: option `no-%s' %s", opt->long_name, reason);26 else27 fprintf(stderr, " Error: option `%s' %s", opt->long_name, reason);28 29 return -1;30}31 32static const char *skip_prefix(const char *str, const char *prefix)33{34 size_t len = strlen(prefix);35 return strncmp(str, prefix, len) ? NULL : str + len;36}37 38static void optwarning(const struct option *opt, const char *reason, int flags)39{40 if (flags & OPT_SHORT)41 fprintf(stderr, " Warning: switch `%c' %s", opt->short_name, reason);42 else if (flags & OPT_UNSET)43 fprintf(stderr, " Warning: option `no-%s' %s", opt->long_name, reason);44 else45 fprintf(stderr, " Warning: option `%s' %s", opt->long_name, reason);46}47 48static int get_arg(struct parse_opt_ctx_t *p, const struct option *opt,49 int flags, const char **arg)50{51 const char *res;52 53 if (p->opt) {54 res = p->opt;55 p->opt = NULL;56 } else if ((opt->flags & PARSE_OPT_LASTARG_DEFAULT) && (p->argc == 1 ||57 **(p->argv + 1) == '-')) {58 res = (const char *)opt->defval;59 } else if (p->argc > 1) {60 p->argc--;61 res = *++p->argv;62 } else63 return opterror(opt, "requires a value", flags);64 if (arg)65 *arg = res;66 return 0;67}68 69static int get_value(struct parse_opt_ctx_t *p,70 const struct option *opt, int flags)71{72 const char *s, *arg = NULL;73 const int unset = flags & OPT_UNSET;74 int err;75 76 if (unset && p->opt)77 return opterror(opt, "takes no value", flags);78 if (unset && (opt->flags & PARSE_OPT_NONEG))79 return opterror(opt, "isn't available", flags);80 if (opt->flags & PARSE_OPT_DISABLED)81 return opterror(opt, "is not usable", flags);82 83 if (opt->flags & PARSE_OPT_EXCLUSIVE) {84 if (p->excl_opt && p->excl_opt != opt) {85 char msg[128];86 87 if (((flags & OPT_SHORT) && p->excl_opt->short_name) ||88 p->excl_opt->long_name == NULL) {89 snprintf(msg, sizeof(msg), "cannot be used with switch `%c'",90 p->excl_opt->short_name);91 } else {92 snprintf(msg, sizeof(msg), "cannot be used with %s",93 p->excl_opt->long_name);94 }95 opterror(opt, msg, flags);96 return -3;97 }98 p->excl_opt = opt;99 }100 if (!(flags & OPT_SHORT) && p->opt) {101 switch (opt->type) {102 case OPTION_CALLBACK:103 if (!(opt->flags & PARSE_OPT_NOARG))104 break;105 /* FALLTHROUGH */106 case OPTION_BOOLEAN:107 case OPTION_INCR:108 case OPTION_BIT:109 case OPTION_SET_UINT:110 case OPTION_SET_PTR:111 return opterror(opt, "takes no value", flags);112 case OPTION_END:113 case OPTION_ARGUMENT:114 case OPTION_GROUP:115 case OPTION_STRING:116 case OPTION_INTEGER:117 case OPTION_UINTEGER:118 case OPTION_LONG:119 case OPTION_ULONG:120 case OPTION_U64:121 default:122 break;123 }124 }125 126 if (opt->flags & PARSE_OPT_NOBUILD) {127 char reason[128];128 bool noarg = false;129 130 err = snprintf(reason, sizeof(reason),131 opt->flags & PARSE_OPT_CANSKIP ?132 "is being ignored because %s " :133 "is not available because %s",134 opt->build_opt);135 reason[sizeof(reason) - 1] = '\0';136 137 if (err < 0)138 strncpy(reason, opt->flags & PARSE_OPT_CANSKIP ?139 "is being ignored" :140 "is not available",141 sizeof(reason));142 143 if (!(opt->flags & PARSE_OPT_CANSKIP))144 return opterror(opt, reason, flags);145 146 err = 0;147 if (unset)148 noarg = true;149 if (opt->flags & PARSE_OPT_NOARG)150 noarg = true;151 if (opt->flags & PARSE_OPT_OPTARG && !p->opt)152 noarg = true;153 154 switch (opt->type) {155 case OPTION_BOOLEAN:156 case OPTION_INCR:157 case OPTION_BIT:158 case OPTION_SET_UINT:159 case OPTION_SET_PTR:160 case OPTION_END:161 case OPTION_ARGUMENT:162 case OPTION_GROUP:163 noarg = true;164 break;165 case OPTION_CALLBACK:166 case OPTION_STRING:167 case OPTION_INTEGER:168 case OPTION_UINTEGER:169 case OPTION_LONG:170 case OPTION_ULONG:171 case OPTION_U64:172 default:173 break;174 }175 176 if (!noarg)177 err = get_arg(p, opt, flags, NULL);178 if (err)179 return err;180 181 optwarning(opt, reason, flags);182 return 0;183 }184 185 switch (opt->type) {186 case OPTION_BIT:187 if (unset)188 *(int *)opt->value &= ~opt->defval;189 else190 *(int *)opt->value |= opt->defval;191 return 0;192 193 case OPTION_BOOLEAN:194 *(bool *)opt->value = unset ? false : true;195 if (opt->set)196 *(bool *)opt->set = true;197 return 0;198 199 case OPTION_INCR:200 *(int *)opt->value = unset ? 0 : *(int *)opt->value + 1;201 return 0;202 203 case OPTION_SET_UINT:204 *(unsigned int *)opt->value = unset ? 0 : opt->defval;205 return 0;206 207 case OPTION_SET_PTR:208 *(void **)opt->value = unset ? NULL : (void *)opt->defval;209 return 0;210 211 case OPTION_STRING:212 err = 0;213 if (unset)214 *(const char **)opt->value = NULL;215 else if (opt->flags & PARSE_OPT_OPTARG && !p->opt)216 *(const char **)opt->value = (const char *)opt->defval;217 else218 err = get_arg(p, opt, flags, (const char **)opt->value);219 220 if (opt->set)221 *(bool *)opt->set = true;222 223 /* PARSE_OPT_NOEMPTY: Allow NULL but disallow empty string. */224 if (opt->flags & PARSE_OPT_NOEMPTY) {225 const char *val = *(const char **)opt->value;226 227 if (!val)228 return err;229 230 /* Similar to unset if we are given an empty string. */231 if (val[0] == '\0') {232 *(const char **)opt->value = NULL;233 return 0;234 }235 }236 237 return err;238 239 case OPTION_CALLBACK:240 if (opt->set)241 *(bool *)opt->set = true;242 243 if (unset)244 return (*opt->callback)(opt, NULL, 1) ? (-1) : 0;245 if (opt->flags & PARSE_OPT_NOARG)246 return (*opt->callback)(opt, NULL, 0) ? (-1) : 0;247 if (opt->flags & PARSE_OPT_OPTARG && !p->opt)248 return (*opt->callback)(opt, NULL, 0) ? (-1) : 0;249 if (get_arg(p, opt, flags, &arg))250 return -1;251 return (*opt->callback)(opt, arg, 0) ? (-1) : 0;252 253 case OPTION_INTEGER:254 if (unset) {255 *(int *)opt->value = 0;256 return 0;257 }258 if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {259 *(int *)opt->value = opt->defval;260 return 0;261 }262 if (get_arg(p, opt, flags, &arg))263 return -1;264 *(int *)opt->value = strtol(arg, (char **)&s, 10);265 if (*s)266 return opterror(opt, "expects a numerical value", flags);267 return 0;268 269 case OPTION_UINTEGER:270 if (unset) {271 *(unsigned int *)opt->value = 0;272 return 0;273 }274 if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {275 *(unsigned int *)opt->value = opt->defval;276 return 0;277 }278 if (get_arg(p, opt, flags, &arg))279 return -1;280 if (arg[0] == '-')281 return opterror(opt, "expects an unsigned numerical value", flags);282 *(unsigned int *)opt->value = strtol(arg, (char **)&s, 10);283 if (*s)284 return opterror(opt, "expects a numerical value", flags);285 return 0;286 287 case OPTION_LONG:288 if (unset) {289 *(long *)opt->value = 0;290 return 0;291 }292 if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {293 *(long *)opt->value = opt->defval;294 return 0;295 }296 if (get_arg(p, opt, flags, &arg))297 return -1;298 *(long *)opt->value = strtol(arg, (char **)&s, 10);299 if (*s)300 return opterror(opt, "expects a numerical value", flags);301 return 0;302 303 case OPTION_ULONG:304 if (unset) {305 *(unsigned long *)opt->value = 0;306 return 0;307 }308 if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {309 *(unsigned long *)opt->value = opt->defval;310 return 0;311 }312 if (get_arg(p, opt, flags, &arg))313 return -1;314 *(unsigned long *)opt->value = strtoul(arg, (char **)&s, 10);315 if (*s)316 return opterror(opt, "expects a numerical value", flags);317 return 0;318 319 case OPTION_U64:320 if (unset) {321 *(u64 *)opt->value = 0;322 return 0;323 }324 if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {325 *(u64 *)opt->value = opt->defval;326 return 0;327 }328 if (get_arg(p, opt, flags, &arg))329 return -1;330 if (arg[0] == '-')331 return opterror(opt, "expects an unsigned numerical value", flags);332 *(u64 *)opt->value = strtoull(arg, (char **)&s, 10);333 if (*s)334 return opterror(opt, "expects a numerical value", flags);335 return 0;336 337 case OPTION_END:338 case OPTION_ARGUMENT:339 case OPTION_GROUP:340 default:341 die("should not happen, someone must be hit on the forehead");342 }343}344 345static int parse_short_opt(struct parse_opt_ctx_t *p, const struct option *options)346{347retry:348 for (; options->type != OPTION_END; options++) {349 if (options->short_name == *p->opt) {350 p->opt = p->opt[1] ? p->opt + 1 : NULL;351 return get_value(p, options, OPT_SHORT);352 }353 }354 355 if (options->parent) {356 options = options->parent;357 goto retry;358 }359 360 return -2;361}362 363static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,364 const struct option *options)365{366 const char *arg_end = strchr(arg, '=');367 const struct option *abbrev_option = NULL, *ambiguous_option = NULL;368 int abbrev_flags = 0, ambiguous_flags = 0;369 370 if (!arg_end)371 arg_end = arg + strlen(arg);372 373retry:374 for (; options->type != OPTION_END; options++) {375 const char *rest;376 int flags = 0;377 378 if (!options->long_name)379 continue;380 381 rest = skip_prefix(arg, options->long_name);382 if (options->type == OPTION_ARGUMENT) {383 if (!rest)384 continue;385 if (*rest == '=')386 return opterror(options, "takes no value", flags);387 if (*rest)388 continue;389 p->out[p->cpidx++] = arg - 2;390 return 0;391 }392 if (!rest) {393 if (strstarts(options->long_name, "no-")) {394 /*395 * The long name itself starts with "no-", so396 * accept the option without "no-" so that users397 * do not have to enter "no-no-" to get the398 * negation.399 */400 rest = skip_prefix(arg, options->long_name + 3);401 if (rest) {402 flags |= OPT_UNSET;403 goto match;404 }405 /* Abbreviated case */406 if (strstarts(options->long_name + 3, arg)) {407 flags |= OPT_UNSET;408 goto is_abbreviated;409 }410 }411 /* abbreviated? */412 if (!strncmp(options->long_name, arg, arg_end - arg)) {413is_abbreviated:414 if (abbrev_option) {415 /*416 * If this is abbreviated, it is417 * ambiguous. So when there is no418 * exact match later, we need to419 * error out.420 */421 ambiguous_option = abbrev_option;422 ambiguous_flags = abbrev_flags;423 }424 if (!(flags & OPT_UNSET) && *arg_end)425 p->opt = arg_end + 1;426 abbrev_option = options;427 abbrev_flags = flags;428 continue;429 }430 /* negated and abbreviated very much? */431 if (strstarts("no-", arg)) {432 flags |= OPT_UNSET;433 goto is_abbreviated;434 }435 /* negated? */436 if (strncmp(arg, "no-", 3))437 continue;438 flags |= OPT_UNSET;439 rest = skip_prefix(arg + 3, options->long_name);440 /* abbreviated and negated? */441 if (!rest && strstarts(options->long_name, arg + 3))442 goto is_abbreviated;443 if (!rest)444 continue;445 }446match:447 if (*rest) {448 if (*rest != '=')449 continue;450 p->opt = rest + 1;451 }452 return get_value(p, options, flags);453 }454 455 if (ambiguous_option) {456 fprintf(stderr,457 " Error: Ambiguous option: %s (could be --%s%s or --%s%s)\n",458 arg,459 (ambiguous_flags & OPT_UNSET) ? "no-" : "",460 ambiguous_option->long_name,461 (abbrev_flags & OPT_UNSET) ? "no-" : "",462 abbrev_option->long_name);463 return -1;464 }465 if (abbrev_option)466 return get_value(p, abbrev_option, abbrev_flags);467 468 if (options->parent) {469 options = options->parent;470 goto retry;471 }472 473 return -2;474}475 476static void check_typos(const char *arg, const struct option *options)477{478 if (strlen(arg) < 3)479 return;480 481 if (strstarts(arg, "no-")) {482 fprintf(stderr, " Error: did you mean `--%s` (with two dashes ?)\n", arg);483 exit(129);484 }485 486 for (; options->type != OPTION_END; options++) {487 if (!options->long_name)488 continue;489 if (strstarts(options->long_name, arg)) {490 fprintf(stderr, " Error: did you mean `--%s` (with two dashes ?)\n", arg);491 exit(129);492 }493 }494}495 496static void parse_options_start(struct parse_opt_ctx_t *ctx,497 int argc, const char **argv, int flags)498{499 memset(ctx, 0, sizeof(*ctx));500 ctx->argc = argc - 1;501 ctx->argv = argv + 1;502 ctx->out = argv;503 ctx->cpidx = ((flags & PARSE_OPT_KEEP_ARGV0) != 0);504 ctx->flags = flags;505 if ((flags & PARSE_OPT_KEEP_UNKNOWN) &&506 (flags & PARSE_OPT_STOP_AT_NON_OPTION))507 die("STOP_AT_NON_OPTION and KEEP_UNKNOWN don't go together");508}509 510static int usage_with_options_internal(const char * const *,511 const struct option *, int,512 struct parse_opt_ctx_t *);513 514static int parse_options_step(struct parse_opt_ctx_t *ctx,515 const struct option *options,516 const char * const usagestr[])517{518 int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP);519 int excl_short_opt = 1;520 const char *arg;521 522 /* we must reset ->opt, unknown short option leave it dangling */523 ctx->opt = NULL;524 525 for (; ctx->argc; ctx->argc--, ctx->argv++) {526 arg = ctx->argv[0];527 if (*arg != '-' || !arg[1]) {528 if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION)529 break;530 ctx->out[ctx->cpidx++] = ctx->argv[0];531 continue;532 }533 534 if (arg[1] != '-') {535 ctx->opt = ++arg;536 if (internal_help && *ctx->opt == 'h') {537 return usage_with_options_internal(usagestr, options, 0, ctx);538 }539 switch (parse_short_opt(ctx, options)) {540 case -1:541 return parse_options_usage(usagestr, options, arg, 1);542 case -2:543 goto unknown;544 case -3:545 goto exclusive;546 default:547 break;548 }549 if (ctx->opt)550 check_typos(arg, options);551 while (ctx->opt) {552 if (internal_help && *ctx->opt == 'h')553 return usage_with_options_internal(usagestr, options, 0, ctx);554 arg = ctx->opt;555 switch (parse_short_opt(ctx, options)) {556 case -1:557 return parse_options_usage(usagestr, options, arg, 1);558 case -2:559 /* fake a short option thing to hide the fact that we may have560 * started to parse aggregated stuff561 *562 * This is leaky, too bad.563 */564 ctx->argv[0] = strdup(ctx->opt - 1);565 *(char *)ctx->argv[0] = '-';566 goto unknown;567 case -3:568 goto exclusive;569 default:570 break;571 }572 }573 continue;574 }575 576 if (!arg[2]) { /* "--" */577 if (!(ctx->flags & PARSE_OPT_KEEP_DASHDASH)) {578 ctx->argc--;579 ctx->argv++;580 }581 break;582 }583 584 arg += 2;585 if (internal_help && !strcmp(arg, "help-all"))586 return usage_with_options_internal(usagestr, options, 1, ctx);587 if (internal_help && !strcmp(arg, "help"))588 return usage_with_options_internal(usagestr, options, 0, ctx);589 if (!strcmp(arg, "list-opts"))590 return PARSE_OPT_LIST_OPTS;591 if (!strcmp(arg, "list-cmds"))592 return PARSE_OPT_LIST_SUBCMDS;593 switch (parse_long_opt(ctx, arg, options)) {594 case -1:595 return parse_options_usage(usagestr, options, arg, 0);596 case -2:597 goto unknown;598 case -3:599 excl_short_opt = 0;600 goto exclusive;601 default:602 break;603 }604 continue;605unknown:606 if (!(ctx->flags & PARSE_OPT_KEEP_UNKNOWN))607 return PARSE_OPT_UNKNOWN;608 ctx->out[ctx->cpidx++] = ctx->argv[0];609 ctx->opt = NULL;610 }611 return PARSE_OPT_DONE;612 613exclusive:614 parse_options_usage(usagestr, options, arg, excl_short_opt);615 if ((excl_short_opt && ctx->excl_opt->short_name) ||616 ctx->excl_opt->long_name == NULL) {617 char opt = ctx->excl_opt->short_name;618 parse_options_usage(NULL, options, &opt, 1);619 } else {620 parse_options_usage(NULL, options, ctx->excl_opt->long_name, 0);621 }622 return PARSE_OPT_HELP;623}624 625static int parse_options_end(struct parse_opt_ctx_t *ctx)626{627 memmove(ctx->out + ctx->cpidx, ctx->argv, ctx->argc * sizeof(*ctx->out));628 ctx->out[ctx->cpidx + ctx->argc] = NULL;629 return ctx->cpidx + ctx->argc;630}631 632int parse_options_subcommand(int argc, const char **argv, const struct option *options,633 const char *const subcommands[], const char *usagestr[], int flags)634{635 struct parse_opt_ctx_t ctx;636 637 /* build usage string if it's not provided */638 if (subcommands && !usagestr[0]) {639 char *buf = NULL;640 641 astrcatf(&buf, "%s %s [<options>] {", subcmd_config.exec_name, argv[0]);642 643 for (int i = 0; subcommands[i]; i++) {644 if (i)645 astrcat(&buf, "|");646 astrcat(&buf, subcommands[i]);647 }648 astrcat(&buf, "}");649 650 usagestr[0] = buf;651 }652 653 parse_options_start(&ctx, argc, argv, flags);654 switch (parse_options_step(&ctx, options, usagestr)) {655 case PARSE_OPT_HELP:656 exit(129);657 case PARSE_OPT_DONE:658 break;659 case PARSE_OPT_LIST_OPTS:660 while (options->type != OPTION_END) {661 if (options->long_name)662 printf("--%s ", options->long_name);663 options++;664 }665 putchar('\n');666 exit(130);667 case PARSE_OPT_LIST_SUBCMDS:668 if (subcommands) {669 for (int i = 0; subcommands[i]; i++)670 printf("%s ", subcommands[i]);671 }672 putchar('\n');673 exit(130);674 default: /* PARSE_OPT_UNKNOWN */675 if (ctx.argv[0][1] == '-')676 astrcatf(&error_buf, "unknown option `%s'",677 ctx.argv[0] + 2);678 else679 astrcatf(&error_buf, "unknown switch `%c'", *ctx.opt);680 usage_with_options(usagestr, options);681 }682 683 return parse_options_end(&ctx);684}685 686int parse_options(int argc, const char **argv, const struct option *options,687 const char * const usagestr[], int flags)688{689 return parse_options_subcommand(argc, argv, options, NULL,690 (const char **) usagestr, flags);691}692 693#define USAGE_OPTS_WIDTH 24694#define USAGE_GAP 2695 696static void print_option_help(const struct option *opts, int full)697{698 size_t pos;699 int pad;700 701 if (opts->type == OPTION_GROUP) {702 fputc('\n', stderr);703 if (*opts->help)704 fprintf(stderr, "%s\n", opts->help);705 return;706 }707 if (!full && (opts->flags & PARSE_OPT_HIDDEN))708 return;709 if (opts->flags & PARSE_OPT_DISABLED)710 return;711 712 pos = fprintf(stderr, " ");713 if (opts->short_name)714 pos += fprintf(stderr, "-%c", opts->short_name);715 else716 pos += fprintf(stderr, " ");717 718 if (opts->long_name && opts->short_name)719 pos += fprintf(stderr, ", ");720 if (opts->long_name)721 pos += fprintf(stderr, "--%s", opts->long_name);722 723 switch (opts->type) {724 case OPTION_ARGUMENT:725 break;726 case OPTION_LONG:727 case OPTION_ULONG:728 case OPTION_U64:729 case OPTION_INTEGER:730 case OPTION_UINTEGER:731 if (opts->flags & PARSE_OPT_OPTARG)732 if (opts->long_name)733 pos += fprintf(stderr, "[=<n>]");734 else735 pos += fprintf(stderr, "[<n>]");736 else737 pos += fprintf(stderr, " <n>");738 break;739 case OPTION_CALLBACK:740 if (opts->flags & PARSE_OPT_NOARG)741 break;742 /* FALLTHROUGH */743 case OPTION_STRING:744 if (opts->argh) {745 if (opts->flags & PARSE_OPT_OPTARG)746 if (opts->long_name)747 pos += fprintf(stderr, "[=<%s>]", opts->argh);748 else749 pos += fprintf(stderr, "[<%s>]", opts->argh);750 else751 pos += fprintf(stderr, " <%s>", opts->argh);752 } else {753 if (opts->flags & PARSE_OPT_OPTARG)754 if (opts->long_name)755 pos += fprintf(stderr, "[=...]");756 else757 pos += fprintf(stderr, "[...]");758 else759 pos += fprintf(stderr, " ...");760 }761 break;762 default: /* OPTION_{BIT,BOOLEAN,SET_UINT,SET_PTR} */763 case OPTION_END:764 case OPTION_GROUP:765 case OPTION_BIT:766 case OPTION_BOOLEAN:767 case OPTION_INCR:768 case OPTION_SET_UINT:769 case OPTION_SET_PTR:770 break;771 }772 773 if (pos <= USAGE_OPTS_WIDTH)774 pad = USAGE_OPTS_WIDTH - pos;775 else {776 fputc('\n', stderr);777 pad = USAGE_OPTS_WIDTH;778 }779 fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help);780 if (opts->flags & PARSE_OPT_NOBUILD)781 fprintf(stderr, "%*s(not built-in because %s)\n",782 USAGE_OPTS_WIDTH + USAGE_GAP, "",783 opts->build_opt);784}785 786static int option__cmp(const void *va, const void *vb)787{788 const struct option *a = va, *b = vb;789 int sa = tolower(a->short_name), sb = tolower(b->short_name), ret;790 791 if (sa == 0)792 sa = 'z' + 1;793 if (sb == 0)794 sb = 'z' + 1;795 796 ret = sa - sb;797 798 if (ret == 0) {799 const char *la = a->long_name ?: "",800 *lb = b->long_name ?: "";801 ret = strcmp(la, lb);802 }803 804 return ret;805}806 807static struct option *options__order(const struct option *opts)808{809 int nr_opts = 0, nr_group = 0, nr_parent = 0, len;810 const struct option *o, *p = opts;811 struct option *opt, *ordered = NULL, *group;812 813 /* flatten the options that have parents */814 for (p = opts; p != NULL; p = o->parent) {815 for (o = p; o->type != OPTION_END; o++)816 ++nr_opts;817 818 /*819 * the length is given by the number of options plus a null820 * terminator for the last loop iteration.821 */822 len = sizeof(*o) * (nr_opts + !o->parent);823 group = realloc(ordered, len);824 if (!group)825 goto out;826 ordered = group;827 memcpy(&ordered[nr_parent], p, sizeof(*o) * (nr_opts - nr_parent));828 829 nr_parent = nr_opts;830 }831 /* copy the last OPTION_END */832 memcpy(&ordered[nr_opts], o, sizeof(*o));833 834 /* sort each option group individually */835 for (opt = group = ordered; opt->type != OPTION_END; opt++) {836 if (opt->type == OPTION_GROUP) {837 qsort(group, nr_group, sizeof(*opt), option__cmp);838 group = opt + 1;839 nr_group = 0;840 continue;841 }842 nr_group++;843 }844 qsort(group, nr_group, sizeof(*opt), option__cmp);845 846out:847 return ordered;848}849 850static bool option__in_argv(const struct option *opt, const struct parse_opt_ctx_t *ctx)851{852 int i;853 854 for (i = 1; i < ctx->argc; ++i) {855 const char *arg = ctx->argv[i];856 857 if (arg[0] != '-') {858 if (arg[1] == '\0') {859 if (arg[0] == opt->short_name)860 return true;861 continue;862 }863 864 if (opt->long_name && strcmp(opt->long_name, arg) == 0)865 return true;866 867 if (opt->help && strcasestr(opt->help, arg) != NULL)868 return true;869 870 continue;871 }872 873 if (arg[1] == opt->short_name ||874 (arg[1] == '-' && opt->long_name && strcmp(opt->long_name, arg + 2) == 0))875 return true;876 }877 878 return false;879}880 881static int usage_with_options_internal(const char * const *usagestr,882 const struct option *opts, int full,883 struct parse_opt_ctx_t *ctx)884{885 struct option *ordered;886 887 if (!usagestr)888 return PARSE_OPT_HELP;889 890 setup_pager();891 892 if (error_buf) {893 fprintf(stderr, " Error: %s\n", error_buf);894 zfree(&error_buf);895 }896 897 fprintf(stderr, "\n Usage: %s\n", *usagestr++);898 while (*usagestr && **usagestr)899 fprintf(stderr, " or: %s\n", *usagestr++);900 while (*usagestr) {901 fprintf(stderr, "%s%s\n",902 **usagestr ? " " : "",903 *usagestr);904 usagestr++;905 }906 907 if (opts->type != OPTION_GROUP)908 fputc('\n', stderr);909 910 ordered = options__order(opts);911 if (ordered)912 opts = ordered;913 914 for ( ; opts->type != OPTION_END; opts++) {915 if (ctx && ctx->argc > 1 && !option__in_argv(opts, ctx))916 continue;917 print_option_help(opts, full);918 }919 920 fputc('\n', stderr);921 922 free(ordered);923 924 return PARSE_OPT_HELP;925}926 927void usage_with_options(const char * const *usagestr,928 const struct option *opts)929{930 usage_with_options_internal(usagestr, opts, 0, NULL);931 exit(129);932}933 934void usage_with_options_msg(const char * const *usagestr,935 const struct option *opts, const char *fmt, ...)936{937 va_list ap;938 char *tmp = error_buf;939 940 va_start(ap, fmt);941 if (vasprintf(&error_buf, fmt, ap) == -1)942 die("vasprintf failed");943 va_end(ap);944 945 free(tmp);946 947 usage_with_options_internal(usagestr, opts, 0, NULL);948 exit(129);949}950 951int parse_options_usage(const char * const *usagestr,952 const struct option *opts,953 const char *optstr, bool short_opt)954{955 if (!usagestr)956 goto opt;957 958 fprintf(stderr, "\n Usage: %s\n", *usagestr++);959 while (*usagestr && **usagestr)960 fprintf(stderr, " or: %s\n", *usagestr++);961 while (*usagestr) {962 fprintf(stderr, "%s%s\n",963 **usagestr ? " " : "",964 *usagestr);965 usagestr++;966 }967 fputc('\n', stderr);968 969opt:970 for ( ; opts->type != OPTION_END; opts++) {971 if (short_opt) {972 if (opts->short_name == *optstr) {973 print_option_help(opts, 0);974 break;975 }976 continue;977 }978 979 if (opts->long_name == NULL)980 continue;981 982 if (strstarts(opts->long_name, optstr))983 print_option_help(opts, 0);984 if (strstarts("no-", optstr) &&985 strstarts(opts->long_name, optstr + 3))986 print_option_help(opts, 0);987 }988 989 return PARSE_OPT_HELP;990}991 992 993int parse_opt_verbosity_cb(const struct option *opt,994 const char *arg __maybe_unused,995 int unset)996{997 int *target = opt->value;998 999 if (unset)1000 /* --no-quiet, --no-verbose */1001 *target = 0;1002 else if (opt->short_name == 'v') {1003 if (*target >= 0)1004 (*target)++;1005 else1006 *target = 1;1007 } else {1008 if (*target <= 0)1009 (*target)--;1010 else1011 *target = -1;1012 }1013 return 0;1014}1015 1016static struct option *1017find_option(struct option *opts, int shortopt, const char *longopt)1018{1019 for (; opts->type != OPTION_END; opts++) {1020 if ((shortopt && opts->short_name == shortopt) ||1021 (opts->long_name && longopt &&1022 !strcmp(opts->long_name, longopt)))1023 return opts;1024 }1025 return NULL;1026}1027 1028void set_option_flag(struct option *opts, int shortopt, const char *longopt,1029 int flag)1030{1031 struct option *opt = find_option(opts, shortopt, longopt);1032 1033 if (opt)1034 opt->flags |= flag;1035 return;1036}1037 1038void set_option_nobuild(struct option *opts, int shortopt,1039 const char *longopt,1040 const char *build_opt,1041 bool can_skip)1042{1043 struct option *opt = find_option(opts, shortopt, longopt);1044 1045 if (!opt)1046 return;1047 1048 opt->flags |= PARSE_OPT_NOBUILD;1049 opt->flags |= can_skip ? PARSE_OPT_CANSKIP : 0;1050 opt->build_opt = build_opt;1051}1052