brintos

brintos / linux-shallow public Read only

0
0
Text · 24.2 KiB · eb896d3 Raw
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