diff options
Diffstat (limited to 'tools/lib/subcmd/parse-options.c')
| -rw-r--r-- | tools/lib/subcmd/parse-options.c | 983 | 
1 files changed, 983 insertions, 0 deletions
diff --git a/tools/lib/subcmd/parse-options.c b/tools/lib/subcmd/parse-options.c new file mode 100644 index 000000000000..981bb4481fd5 --- /dev/null +++ b/tools/lib/subcmd/parse-options.c @@ -0,0 +1,983 @@ +#include <linux/compiler.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); + +		/* 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; +		*(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; +		*(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) +{ +	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); +		} +	} +	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); + +	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 (!prefixcmp(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 (!prefixcmp(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 (!prefixcmp("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 && !prefixcmp(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)", +			 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); +	return -2; +} + +static void check_typos(const char *arg, const struct option *options) +{ +	if (strlen(arg) < 3) +		return; + +	if (!prefixcmp(arg, "no-")) { +		fprintf(stderr, " Error: did you mean `--%s` (with two dashes ?)", arg); +		exit(129); +	} + +	for (; options->type != OPTION_END; options++) { +		if (!options->long_name) +			continue; +		if (!prefixcmp(options->long_name, arg)) { +			fprintf(stderr, " Error: did you mean `--%s` (with two dashes ?)", 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 (!prefixcmp(opts->long_name, optstr)) +			print_option_help(opts, 0); +		if (!prefixcmp("no-", optstr) && +		    !prefixcmp(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; +}  | 
