12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007 |
- // SPDX-License-Identifier: GPL-2.0
- #include <linux/compiler.h>
- #include <linux/string.h>
- #include <linux/types.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <stdint.h>
- #include <string.h>
- #include <ctype.h>
- #include "subcmd-util.h"
- #include "parse-options.h"
- #include "subcmd-config.h"
- #include "pager.h"
- #define OPT_SHORT 1
- #define OPT_UNSET 2
- char *error_buf;
- static int opterror(const struct option *opt, const char *reason, int flags)
- {
- if (flags & OPT_SHORT)
- fprintf(stderr, " Error: switch `%c' %s", opt->short_name, reason);
- else if (flags & OPT_UNSET)
- fprintf(stderr, " Error: option `no-%s' %s", opt->long_name, reason);
- else
- fprintf(stderr, " Error: option `%s' %s", opt->long_name, reason);
- return -1;
- }
- static const char *skip_prefix(const char *str, const char *prefix)
- {
- size_t len = strlen(prefix);
- return strncmp(str, prefix, len) ? NULL : str + len;
- }
- static void optwarning(const struct option *opt, const char *reason, int flags)
- {
- if (flags & OPT_SHORT)
- fprintf(stderr, " Warning: switch `%c' %s", opt->short_name, reason);
- else if (flags & OPT_UNSET)
- fprintf(stderr, " Warning: option `no-%s' %s", opt->long_name, reason);
- else
- fprintf(stderr, " Warning: option `%s' %s", opt->long_name, reason);
- }
- static int get_arg(struct parse_opt_ctx_t *p, const struct option *opt,
- int flags, const char **arg)
- {
- const char *res;
- if (p->opt) {
- res = p->opt;
- p->opt = NULL;
- } else if ((opt->flags & PARSE_OPT_LASTARG_DEFAULT) && (p->argc == 1 ||
- **(p->argv + 1) == '-')) {
- res = (const char *)opt->defval;
- } else if (p->argc > 1) {
- p->argc--;
- res = *++p->argv;
- } else
- return opterror(opt, "requires a value", flags);
- if (arg)
- *arg = res;
- return 0;
- }
- static int get_value(struct parse_opt_ctx_t *p,
- const struct option *opt, int flags)
- {
- const char *s, *arg = NULL;
- const int unset = flags & OPT_UNSET;
- int err;
- if (unset && p->opt)
- return opterror(opt, "takes no value", flags);
- if (unset && (opt->flags & PARSE_OPT_NONEG))
- return opterror(opt, "isn't available", flags);
- if (opt->flags & PARSE_OPT_DISABLED)
- return opterror(opt, "is not usable", flags);
- if (opt->flags & PARSE_OPT_EXCLUSIVE) {
- if (p->excl_opt && p->excl_opt != opt) {
- char msg[128];
- if (((flags & OPT_SHORT) && p->excl_opt->short_name) ||
- p->excl_opt->long_name == NULL) {
- snprintf(msg, sizeof(msg), "cannot be used with switch `%c'",
- p->excl_opt->short_name);
- } else {
- snprintf(msg, sizeof(msg), "cannot be used with %s",
- p->excl_opt->long_name);
- }
- opterror(opt, msg, flags);
- return -3;
- }
- p->excl_opt = opt;
- }
- if (!(flags & OPT_SHORT) && p->opt) {
- switch (opt->type) {
- case OPTION_CALLBACK:
- if (!(opt->flags & PARSE_OPT_NOARG))
- break;
- /* FALLTHROUGH */
- case OPTION_BOOLEAN:
- case OPTION_INCR:
- case OPTION_BIT:
- case OPTION_SET_UINT:
- case OPTION_SET_PTR:
- return opterror(opt, "takes no value", flags);
- case OPTION_END:
- case OPTION_ARGUMENT:
- case OPTION_GROUP:
- case OPTION_STRING:
- case OPTION_INTEGER:
- case OPTION_UINTEGER:
- case OPTION_LONG:
- case OPTION_U64:
- default:
- break;
- }
- }
- if (opt->flags & PARSE_OPT_NOBUILD) {
- char reason[128];
- bool noarg = false;
- err = snprintf(reason, sizeof(reason),
- opt->flags & PARSE_OPT_CANSKIP ?
- "is being ignored because %s " :
- "is not available because %s",
- opt->build_opt);
- reason[sizeof(reason) - 1] = '\0';
- if (err < 0)
- strncpy(reason, opt->flags & PARSE_OPT_CANSKIP ?
- "is being ignored" :
- "is not available",
- sizeof(reason));
- if (!(opt->flags & PARSE_OPT_CANSKIP))
- return opterror(opt, reason, flags);
- err = 0;
- if (unset)
- noarg = true;
- if (opt->flags & PARSE_OPT_NOARG)
- noarg = true;
- if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
- noarg = true;
- switch (opt->type) {
- case OPTION_BOOLEAN:
- case OPTION_INCR:
- case OPTION_BIT:
- case OPTION_SET_UINT:
- case OPTION_SET_PTR:
- case OPTION_END:
- case OPTION_ARGUMENT:
- case OPTION_GROUP:
- noarg = true;
- break;
- case OPTION_CALLBACK:
- case OPTION_STRING:
- case OPTION_INTEGER:
- case OPTION_UINTEGER:
- case OPTION_LONG:
- case OPTION_U64:
- default:
- break;
- }
- if (!noarg)
- err = get_arg(p, opt, flags, NULL);
- if (err)
- return err;
- optwarning(opt, reason, flags);
- return 0;
- }
- switch (opt->type) {
- case OPTION_BIT:
- if (unset)
- *(int *)opt->value &= ~opt->defval;
- else
- *(int *)opt->value |= opt->defval;
- return 0;
- case OPTION_BOOLEAN:
- *(bool *)opt->value = unset ? false : true;
- if (opt->set)
- *(bool *)opt->set = true;
- return 0;
- case OPTION_INCR:
- *(int *)opt->value = unset ? 0 : *(int *)opt->value + 1;
- return 0;
- case OPTION_SET_UINT:
- *(unsigned int *)opt->value = unset ? 0 : opt->defval;
- return 0;
- case OPTION_SET_PTR:
- *(void **)opt->value = unset ? NULL : (void *)opt->defval;
- return 0;
- case OPTION_STRING:
- err = 0;
- if (unset)
- *(const char **)opt->value = NULL;
- else if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
- *(const char **)opt->value = (const char *)opt->defval;
- else
- err = get_arg(p, opt, flags, (const char **)opt->value);
- if (opt->set)
- *(bool *)opt->set = true;
- /* PARSE_OPT_NOEMPTY: Allow NULL but disallow empty string. */
- if (opt->flags & PARSE_OPT_NOEMPTY) {
- const char *val = *(const char **)opt->value;
- if (!val)
- return err;
- /* Similar to unset if we are given an empty string. */
- if (val[0] == '\0') {
- *(const char **)opt->value = NULL;
- return 0;
- }
- }
- return err;
- case OPTION_CALLBACK:
- if (unset)
- return (*opt->callback)(opt, NULL, 1) ? (-1) : 0;
- if (opt->flags & PARSE_OPT_NOARG)
- return (*opt->callback)(opt, NULL, 0) ? (-1) : 0;
- if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
- return (*opt->callback)(opt, NULL, 0) ? (-1) : 0;
- if (get_arg(p, opt, flags, &arg))
- return -1;
- return (*opt->callback)(opt, arg, 0) ? (-1) : 0;
- case OPTION_INTEGER:
- if (unset) {
- *(int *)opt->value = 0;
- return 0;
- }
- if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
- *(int *)opt->value = opt->defval;
- return 0;
- }
- if (get_arg(p, opt, flags, &arg))
- return -1;
- *(int *)opt->value = strtol(arg, (char **)&s, 10);
- if (*s)
- return opterror(opt, "expects a numerical value", flags);
- return 0;
- case OPTION_UINTEGER:
- if (unset) {
- *(unsigned int *)opt->value = 0;
- return 0;
- }
- if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
- *(unsigned int *)opt->value = opt->defval;
- return 0;
- }
- if (get_arg(p, opt, flags, &arg))
- return -1;
- if (arg[0] == '-')
- return opterror(opt, "expects an unsigned numerical value", flags);
- *(unsigned int *)opt->value = strtol(arg, (char **)&s, 10);
- if (*s)
- return opterror(opt, "expects a numerical value", flags);
- return 0;
- case OPTION_LONG:
- if (unset) {
- *(long *)opt->value = 0;
- return 0;
- }
- if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
- *(long *)opt->value = opt->defval;
- return 0;
- }
- if (get_arg(p, opt, flags, &arg))
- return -1;
- *(long *)opt->value = strtol(arg, (char **)&s, 10);
- if (*s)
- return opterror(opt, "expects a numerical value", flags);
- return 0;
- case OPTION_U64:
- if (unset) {
- *(u64 *)opt->value = 0;
- return 0;
- }
- if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
- *(u64 *)opt->value = opt->defval;
- return 0;
- }
- if (get_arg(p, opt, flags, &arg))
- return -1;
- if (arg[0] == '-')
- return opterror(opt, "expects an unsigned numerical value", flags);
- *(u64 *)opt->value = strtoull(arg, (char **)&s, 10);
- if (*s)
- return opterror(opt, "expects a numerical value", flags);
- return 0;
- case OPTION_END:
- case OPTION_ARGUMENT:
- case OPTION_GROUP:
- default:
- die("should not happen, someone must be hit on the forehead");
- }
- }
- static int parse_short_opt(struct parse_opt_ctx_t *p, const struct option *options)
- {
- retry:
- for (; options->type != OPTION_END; options++) {
- if (options->short_name == *p->opt) {
- p->opt = p->opt[1] ? p->opt + 1 : NULL;
- return get_value(p, options, OPT_SHORT);
- }
- }
- if (options->parent) {
- options = options->parent;
- goto retry;
- }
- return -2;
- }
- static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
- const struct option *options)
- {
- const char *arg_end = strchr(arg, '=');
- const struct option *abbrev_option = NULL, *ambiguous_option = NULL;
- int abbrev_flags = 0, ambiguous_flags = 0;
- if (!arg_end)
- arg_end = arg + strlen(arg);
- retry:
- for (; options->type != OPTION_END; options++) {
- const char *rest;
- int flags = 0;
- if (!options->long_name)
- continue;
- rest = skip_prefix(arg, options->long_name);
- if (options->type == OPTION_ARGUMENT) {
- if (!rest)
- continue;
- if (*rest == '=')
- return opterror(options, "takes no value", flags);
- if (*rest)
- continue;
- p->out[p->cpidx++] = arg - 2;
- return 0;
- }
- if (!rest) {
- if (strstarts(options->long_name, "no-")) {
- /*
- * The long name itself starts with "no-", so
- * accept the option without "no-" so that users
- * do not have to enter "no-no-" to get the
- * negation.
- */
- rest = skip_prefix(arg, options->long_name + 3);
- if (rest) {
- flags |= OPT_UNSET;
- goto match;
- }
- /* Abbreviated case */
- if (strstarts(options->long_name + 3, arg)) {
- flags |= OPT_UNSET;
- goto is_abbreviated;
- }
- }
- /* abbreviated? */
- if (!strncmp(options->long_name, arg, arg_end - arg)) {
- is_abbreviated:
- if (abbrev_option) {
- /*
- * If this is abbreviated, it is
- * ambiguous. So when there is no
- * exact match later, we need to
- * error out.
- */
- ambiguous_option = abbrev_option;
- ambiguous_flags = abbrev_flags;
- }
- if (!(flags & OPT_UNSET) && *arg_end)
- p->opt = arg_end + 1;
- abbrev_option = options;
- abbrev_flags = flags;
- continue;
- }
- /* negated and abbreviated very much? */
- if (strstarts("no-", arg)) {
- flags |= OPT_UNSET;
- goto is_abbreviated;
- }
- /* negated? */
- if (strncmp(arg, "no-", 3))
- continue;
- flags |= OPT_UNSET;
- rest = skip_prefix(arg + 3, options->long_name);
- /* abbreviated and negated? */
- if (!rest && strstarts(options->long_name, arg + 3))
- goto is_abbreviated;
- if (!rest)
- continue;
- }
- match:
- if (*rest) {
- if (*rest != '=')
- continue;
- p->opt = rest + 1;
- }
- return get_value(p, options, flags);
- }
- if (ambiguous_option) {
- fprintf(stderr,
- " Error: Ambiguous option: %s (could be --%s%s or --%s%s)\n",
- arg,
- (ambiguous_flags & OPT_UNSET) ? "no-" : "",
- ambiguous_option->long_name,
- (abbrev_flags & OPT_UNSET) ? "no-" : "",
- abbrev_option->long_name);
- return -1;
- }
- if (abbrev_option)
- return get_value(p, abbrev_option, abbrev_flags);
- if (options->parent) {
- options = options->parent;
- goto retry;
- }
- return -2;
- }
- static void check_typos(const char *arg, const struct option *options)
- {
- if (strlen(arg) < 3)
- return;
- if (strstarts(arg, "no-")) {
- fprintf(stderr, " Error: did you mean `--%s` (with two dashes ?)\n", arg);
- exit(129);
- }
- for (; options->type != OPTION_END; options++) {
- if (!options->long_name)
- continue;
- if (strstarts(options->long_name, arg)) {
- fprintf(stderr, " Error: did you mean `--%s` (with two dashes ?)\n", arg);
- exit(129);
- }
- }
- }
- static void parse_options_start(struct parse_opt_ctx_t *ctx,
- int argc, const char **argv, int flags)
- {
- memset(ctx, 0, sizeof(*ctx));
- ctx->argc = argc - 1;
- ctx->argv = argv + 1;
- ctx->out = argv;
- ctx->cpidx = ((flags & PARSE_OPT_KEEP_ARGV0) != 0);
- ctx->flags = flags;
- if ((flags & PARSE_OPT_KEEP_UNKNOWN) &&
- (flags & PARSE_OPT_STOP_AT_NON_OPTION))
- die("STOP_AT_NON_OPTION and KEEP_UNKNOWN don't go together");
- }
- static int usage_with_options_internal(const char * const *,
- const struct option *, int,
- struct parse_opt_ctx_t *);
- static int parse_options_step(struct parse_opt_ctx_t *ctx,
- const struct option *options,
- const char * const usagestr[])
- {
- int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP);
- int excl_short_opt = 1;
- const char *arg;
- /* we must reset ->opt, unknown short option leave it dangling */
- ctx->opt = NULL;
- for (; ctx->argc; ctx->argc--, ctx->argv++) {
- arg = ctx->argv[0];
- if (*arg != '-' || !arg[1]) {
- if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION)
- break;
- ctx->out[ctx->cpidx++] = ctx->argv[0];
- continue;
- }
- if (arg[1] != '-') {
- ctx->opt = ++arg;
- if (internal_help && *ctx->opt == 'h') {
- return usage_with_options_internal(usagestr, options, 0, ctx);
- }
- switch (parse_short_opt(ctx, options)) {
- case -1:
- return parse_options_usage(usagestr, options, arg, 1);
- case -2:
- goto unknown;
- case -3:
- goto exclusive;
- default:
- break;
- }
- if (ctx->opt)
- check_typos(arg, options);
- while (ctx->opt) {
- if (internal_help && *ctx->opt == 'h')
- return usage_with_options_internal(usagestr, options, 0, ctx);
- arg = ctx->opt;
- switch (parse_short_opt(ctx, options)) {
- case -1:
- return parse_options_usage(usagestr, options, arg, 1);
- case -2:
- /* fake a short option thing to hide the fact that we may have
- * started to parse aggregated stuff
- *
- * This is leaky, too bad.
- */
- ctx->argv[0] = strdup(ctx->opt - 1);
- *(char *)ctx->argv[0] = '-';
- goto unknown;
- case -3:
- goto exclusive;
- default:
- break;
- }
- }
- continue;
- }
- if (!arg[2]) { /* "--" */
- if (!(ctx->flags & PARSE_OPT_KEEP_DASHDASH)) {
- ctx->argc--;
- ctx->argv++;
- }
- break;
- }
- arg += 2;
- if (internal_help && !strcmp(arg, "help-all"))
- return usage_with_options_internal(usagestr, options, 1, ctx);
- if (internal_help && !strcmp(arg, "help"))
- return usage_with_options_internal(usagestr, options, 0, ctx);
- if (!strcmp(arg, "list-opts"))
- return PARSE_OPT_LIST_OPTS;
- if (!strcmp(arg, "list-cmds"))
- return PARSE_OPT_LIST_SUBCMDS;
- switch (parse_long_opt(ctx, arg, options)) {
- case -1:
- return parse_options_usage(usagestr, options, arg, 0);
- case -2:
- goto unknown;
- case -3:
- excl_short_opt = 0;
- goto exclusive;
- default:
- break;
- }
- continue;
- unknown:
- if (!(ctx->flags & PARSE_OPT_KEEP_UNKNOWN))
- return PARSE_OPT_UNKNOWN;
- ctx->out[ctx->cpidx++] = ctx->argv[0];
- ctx->opt = NULL;
- }
- return PARSE_OPT_DONE;
- exclusive:
- parse_options_usage(usagestr, options, arg, excl_short_opt);
- if ((excl_short_opt && ctx->excl_opt->short_name) ||
- ctx->excl_opt->long_name == NULL) {
- char opt = ctx->excl_opt->short_name;
- parse_options_usage(NULL, options, &opt, 1);
- } else {
- parse_options_usage(NULL, options, ctx->excl_opt->long_name, 0);
- }
- return PARSE_OPT_HELP;
- }
- static int parse_options_end(struct parse_opt_ctx_t *ctx)
- {
- memmove(ctx->out + ctx->cpidx, ctx->argv, ctx->argc * sizeof(*ctx->out));
- ctx->out[ctx->cpidx + ctx->argc] = NULL;
- return ctx->cpidx + ctx->argc;
- }
- int parse_options_subcommand(int argc, const char **argv, const struct option *options,
- const char *const subcommands[], const char *usagestr[], int flags)
- {
- struct parse_opt_ctx_t ctx;
- /* build usage string if it's not provided */
- if (subcommands && !usagestr[0]) {
- char *buf = NULL;
- astrcatf(&buf, "%s %s [<options>] {", subcmd_config.exec_name, argv[0]);
- for (int i = 0; subcommands[i]; i++) {
- if (i)
- astrcat(&buf, "|");
- astrcat(&buf, subcommands[i]);
- }
- astrcat(&buf, "}");
- usagestr[0] = buf;
- }
- parse_options_start(&ctx, argc, argv, flags);
- switch (parse_options_step(&ctx, options, usagestr)) {
- case PARSE_OPT_HELP:
- exit(129);
- case PARSE_OPT_DONE:
- break;
- case PARSE_OPT_LIST_OPTS:
- while (options->type != OPTION_END) {
- if (options->long_name)
- printf("--%s ", options->long_name);
- options++;
- }
- putchar('\n');
- exit(130);
- case PARSE_OPT_LIST_SUBCMDS:
- if (subcommands) {
- for (int i = 0; subcommands[i]; i++)
- printf("%s ", subcommands[i]);
- }
- putchar('\n');
- exit(130);
- default: /* PARSE_OPT_UNKNOWN */
- if (ctx.argv[0][1] == '-')
- astrcatf(&error_buf, "unknown option `%s'",
- ctx.argv[0] + 2);
- else
- astrcatf(&error_buf, "unknown switch `%c'", *ctx.opt);
- usage_with_options(usagestr, options);
- }
- return parse_options_end(&ctx);
- }
- int parse_options(int argc, const char **argv, const struct option *options,
- const char * const usagestr[], int flags)
- {
- return parse_options_subcommand(argc, argv, options, NULL,
- (const char **) usagestr, flags);
- }
- #define USAGE_OPTS_WIDTH 24
- #define USAGE_GAP 2
- static void print_option_help(const struct option *opts, int full)
- {
- size_t pos;
- int pad;
- if (opts->type == OPTION_GROUP) {
- fputc('\n', stderr);
- if (*opts->help)
- fprintf(stderr, "%s\n", opts->help);
- return;
- }
- if (!full && (opts->flags & PARSE_OPT_HIDDEN))
- return;
- if (opts->flags & PARSE_OPT_DISABLED)
- return;
- pos = fprintf(stderr, " ");
- if (opts->short_name)
- pos += fprintf(stderr, "-%c", opts->short_name);
- else
- pos += fprintf(stderr, " ");
- if (opts->long_name && opts->short_name)
- pos += fprintf(stderr, ", ");
- if (opts->long_name)
- pos += fprintf(stderr, "--%s", opts->long_name);
- switch (opts->type) {
- case OPTION_ARGUMENT:
- break;
- case OPTION_LONG:
- case OPTION_U64:
- case OPTION_INTEGER:
- case OPTION_UINTEGER:
- if (opts->flags & PARSE_OPT_OPTARG)
- if (opts->long_name)
- pos += fprintf(stderr, "[=<n>]");
- else
- pos += fprintf(stderr, "[<n>]");
- else
- pos += fprintf(stderr, " <n>");
- break;
- case OPTION_CALLBACK:
- if (opts->flags & PARSE_OPT_NOARG)
- break;
- /* FALLTHROUGH */
- case OPTION_STRING:
- if (opts->argh) {
- if (opts->flags & PARSE_OPT_OPTARG)
- if (opts->long_name)
- pos += fprintf(stderr, "[=<%s>]", opts->argh);
- else
- pos += fprintf(stderr, "[<%s>]", opts->argh);
- else
- pos += fprintf(stderr, " <%s>", opts->argh);
- } else {
- if (opts->flags & PARSE_OPT_OPTARG)
- if (opts->long_name)
- pos += fprintf(stderr, "[=...]");
- else
- pos += fprintf(stderr, "[...]");
- else
- pos += fprintf(stderr, " ...");
- }
- break;
- default: /* OPTION_{BIT,BOOLEAN,SET_UINT,SET_PTR} */
- case OPTION_END:
- case OPTION_GROUP:
- case OPTION_BIT:
- case OPTION_BOOLEAN:
- case OPTION_INCR:
- case OPTION_SET_UINT:
- case OPTION_SET_PTR:
- break;
- }
- if (pos <= USAGE_OPTS_WIDTH)
- pad = USAGE_OPTS_WIDTH - pos;
- else {
- fputc('\n', stderr);
- pad = USAGE_OPTS_WIDTH;
- }
- fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help);
- if (opts->flags & PARSE_OPT_NOBUILD)
- fprintf(stderr, "%*s(not built-in because %s)\n",
- USAGE_OPTS_WIDTH + USAGE_GAP, "",
- opts->build_opt);
- }
- static int option__cmp(const void *va, const void *vb)
- {
- const struct option *a = va, *b = vb;
- int sa = tolower(a->short_name), sb = tolower(b->short_name), ret;
- if (sa == 0)
- sa = 'z' + 1;
- if (sb == 0)
- sb = 'z' + 1;
- ret = sa - sb;
- if (ret == 0) {
- const char *la = a->long_name ?: "",
- *lb = b->long_name ?: "";
- ret = strcmp(la, lb);
- }
- return ret;
- }
- static struct option *options__order(const struct option *opts)
- {
- int nr_opts = 0, len;
- const struct option *o = opts;
- struct option *ordered;
- for (o = opts; o->type != OPTION_END; o++)
- ++nr_opts;
- len = sizeof(*o) * (nr_opts + 1);
- ordered = malloc(len);
- if (!ordered)
- goto out;
- memcpy(ordered, opts, len);
- qsort(ordered, nr_opts, sizeof(*o), option__cmp);
- out:
- return ordered;
- }
- static bool option__in_argv(const struct option *opt, const struct parse_opt_ctx_t *ctx)
- {
- int i;
- for (i = 1; i < ctx->argc; ++i) {
- const char *arg = ctx->argv[i];
- if (arg[0] != '-') {
- if (arg[1] == '\0') {
- if (arg[0] == opt->short_name)
- return true;
- continue;
- }
- if (opt->long_name && strcmp(opt->long_name, arg) == 0)
- return true;
- if (opt->help && strcasestr(opt->help, arg) != NULL)
- return true;
- continue;
- }
- if (arg[1] == opt->short_name ||
- (arg[1] == '-' && opt->long_name && strcmp(opt->long_name, arg + 2) == 0))
- return true;
- }
- return false;
- }
- static int usage_with_options_internal(const char * const *usagestr,
- const struct option *opts, int full,
- struct parse_opt_ctx_t *ctx)
- {
- struct option *ordered;
- if (!usagestr)
- return PARSE_OPT_HELP;
- setup_pager();
- if (error_buf) {
- fprintf(stderr, " Error: %s\n", error_buf);
- zfree(&error_buf);
- }
- fprintf(stderr, "\n Usage: %s\n", *usagestr++);
- while (*usagestr && **usagestr)
- fprintf(stderr, " or: %s\n", *usagestr++);
- while (*usagestr) {
- fprintf(stderr, "%s%s\n",
- **usagestr ? " " : "",
- *usagestr);
- usagestr++;
- }
- if (opts->type != OPTION_GROUP)
- fputc('\n', stderr);
- ordered = options__order(opts);
- if (ordered)
- opts = ordered;
- for ( ; opts->type != OPTION_END; opts++) {
- if (ctx && ctx->argc > 1 && !option__in_argv(opts, ctx))
- continue;
- print_option_help(opts, full);
- }
- fputc('\n', stderr);
- free(ordered);
- return PARSE_OPT_HELP;
- }
- void usage_with_options(const char * const *usagestr,
- const struct option *opts)
- {
- usage_with_options_internal(usagestr, opts, 0, NULL);
- exit(129);
- }
- void usage_with_options_msg(const char * const *usagestr,
- const struct option *opts, const char *fmt, ...)
- {
- va_list ap;
- char *tmp = error_buf;
- va_start(ap, fmt);
- if (vasprintf(&error_buf, fmt, ap) == -1)
- die("vasprintf failed");
- va_end(ap);
- free(tmp);
- usage_with_options_internal(usagestr, opts, 0, NULL);
- exit(129);
- }
- int parse_options_usage(const char * const *usagestr,
- const struct option *opts,
- const char *optstr, bool short_opt)
- {
- if (!usagestr)
- goto opt;
- fprintf(stderr, "\n Usage: %s\n", *usagestr++);
- while (*usagestr && **usagestr)
- fprintf(stderr, " or: %s\n", *usagestr++);
- while (*usagestr) {
- fprintf(stderr, "%s%s\n",
- **usagestr ? " " : "",
- *usagestr);
- usagestr++;
- }
- fputc('\n', stderr);
- opt:
- for ( ; opts->type != OPTION_END; opts++) {
- if (short_opt) {
- if (opts->short_name == *optstr) {
- print_option_help(opts, 0);
- break;
- }
- continue;
- }
- if (opts->long_name == NULL)
- continue;
- if (strstarts(opts->long_name, optstr))
- print_option_help(opts, 0);
- if (strstarts("no-", optstr) &&
- strstarts(opts->long_name, optstr + 3))
- print_option_help(opts, 0);
- }
- return PARSE_OPT_HELP;
- }
- int parse_opt_verbosity_cb(const struct option *opt,
- const char *arg __maybe_unused,
- int unset)
- {
- int *target = opt->value;
- if (unset)
- /* --no-quiet, --no-verbose */
- *target = 0;
- else if (opt->short_name == 'v') {
- if (*target >= 0)
- (*target)++;
- else
- *target = 1;
- } else {
- if (*target <= 0)
- (*target)--;
- else
- *target = -1;
- }
- return 0;
- }
- static struct option *
- find_option(struct option *opts, int shortopt, const char *longopt)
- {
- for (; opts->type != OPTION_END; opts++) {
- if ((shortopt && opts->short_name == shortopt) ||
- (opts->long_name && longopt &&
- !strcmp(opts->long_name, longopt)))
- return opts;
- }
- return NULL;
- }
- void set_option_flag(struct option *opts, int shortopt, const char *longopt,
- int flag)
- {
- struct option *opt = find_option(opts, shortopt, longopt);
- if (opt)
- opt->flags |= flag;
- return;
- }
- void set_option_nobuild(struct option *opts, int shortopt,
- const char *longopt,
- const char *build_opt,
- bool can_skip)
- {
- struct option *opt = find_option(opts, shortopt, longopt);
- if (!opt)
- return;
- opt->flags |= PARSE_OPT_NOBUILD;
- opt->flags |= can_skip ? PARSE_OPT_CANSKIP : 0;
- opt->build_opt = build_opt;
- }
|