162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <linux/compiler.h>
362306a36Sopenharmony_ci#include <linux/string.h>
462306a36Sopenharmony_ci#include <linux/types.h>
562306a36Sopenharmony_ci#include <stdio.h>
662306a36Sopenharmony_ci#include <stdlib.h>
762306a36Sopenharmony_ci#include <stdint.h>
862306a36Sopenharmony_ci#include <string.h>
962306a36Sopenharmony_ci#include <ctype.h>
1062306a36Sopenharmony_ci#include "subcmd-util.h"
1162306a36Sopenharmony_ci#include "parse-options.h"
1262306a36Sopenharmony_ci#include "subcmd-config.h"
1362306a36Sopenharmony_ci#include "pager.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define OPT_SHORT 1
1662306a36Sopenharmony_ci#define OPT_UNSET 2
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cichar *error_buf;
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistatic int opterror(const struct option *opt, const char *reason, int flags)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	if (flags & OPT_SHORT)
2362306a36Sopenharmony_ci		fprintf(stderr, " Error: switch `%c' %s", opt->short_name, reason);
2462306a36Sopenharmony_ci	else if (flags & OPT_UNSET)
2562306a36Sopenharmony_ci		fprintf(stderr, " Error: option `no-%s' %s", opt->long_name, reason);
2662306a36Sopenharmony_ci	else
2762306a36Sopenharmony_ci		fprintf(stderr, " Error: option `%s' %s", opt->long_name, reason);
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	return -1;
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic const char *skip_prefix(const char *str, const char *prefix)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	size_t len = strlen(prefix);
3562306a36Sopenharmony_ci	return strncmp(str, prefix, len) ? NULL : str + len;
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic void optwarning(const struct option *opt, const char *reason, int flags)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	if (flags & OPT_SHORT)
4162306a36Sopenharmony_ci		fprintf(stderr, " Warning: switch `%c' %s", opt->short_name, reason);
4262306a36Sopenharmony_ci	else if (flags & OPT_UNSET)
4362306a36Sopenharmony_ci		fprintf(stderr, " Warning: option `no-%s' %s", opt->long_name, reason);
4462306a36Sopenharmony_ci	else
4562306a36Sopenharmony_ci		fprintf(stderr, " Warning: option `%s' %s", opt->long_name, reason);
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic int get_arg(struct parse_opt_ctx_t *p, const struct option *opt,
4962306a36Sopenharmony_ci		   int flags, const char **arg)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	const char *res;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	if (p->opt) {
5462306a36Sopenharmony_ci		res = p->opt;
5562306a36Sopenharmony_ci		p->opt = NULL;
5662306a36Sopenharmony_ci	} else if ((opt->flags & PARSE_OPT_LASTARG_DEFAULT) && (p->argc == 1 ||
5762306a36Sopenharmony_ci		    **(p->argv + 1) == '-')) {
5862306a36Sopenharmony_ci		res = (const char *)opt->defval;
5962306a36Sopenharmony_ci	} else if (p->argc > 1) {
6062306a36Sopenharmony_ci		p->argc--;
6162306a36Sopenharmony_ci		res = *++p->argv;
6262306a36Sopenharmony_ci	} else
6362306a36Sopenharmony_ci		return opterror(opt, "requires a value", flags);
6462306a36Sopenharmony_ci	if (arg)
6562306a36Sopenharmony_ci		*arg = res;
6662306a36Sopenharmony_ci	return 0;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic int get_value(struct parse_opt_ctx_t *p,
7062306a36Sopenharmony_ci		     const struct option *opt, int flags)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	const char *s, *arg = NULL;
7362306a36Sopenharmony_ci	const int unset = flags & OPT_UNSET;
7462306a36Sopenharmony_ci	int err;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	if (unset && p->opt)
7762306a36Sopenharmony_ci		return opterror(opt, "takes no value", flags);
7862306a36Sopenharmony_ci	if (unset && (opt->flags & PARSE_OPT_NONEG))
7962306a36Sopenharmony_ci		return opterror(opt, "isn't available", flags);
8062306a36Sopenharmony_ci	if (opt->flags & PARSE_OPT_DISABLED)
8162306a36Sopenharmony_ci		return opterror(opt, "is not usable", flags);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	if (opt->flags & PARSE_OPT_EXCLUSIVE) {
8462306a36Sopenharmony_ci		if (p->excl_opt && p->excl_opt != opt) {
8562306a36Sopenharmony_ci			char msg[128];
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci			if (((flags & OPT_SHORT) && p->excl_opt->short_name) ||
8862306a36Sopenharmony_ci			    p->excl_opt->long_name == NULL) {
8962306a36Sopenharmony_ci				snprintf(msg, sizeof(msg), "cannot be used with switch `%c'",
9062306a36Sopenharmony_ci					 p->excl_opt->short_name);
9162306a36Sopenharmony_ci			} else {
9262306a36Sopenharmony_ci				snprintf(msg, sizeof(msg), "cannot be used with %s",
9362306a36Sopenharmony_ci					 p->excl_opt->long_name);
9462306a36Sopenharmony_ci			}
9562306a36Sopenharmony_ci			opterror(opt, msg, flags);
9662306a36Sopenharmony_ci			return -3;
9762306a36Sopenharmony_ci		}
9862306a36Sopenharmony_ci		p->excl_opt = opt;
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci	if (!(flags & OPT_SHORT) && p->opt) {
10162306a36Sopenharmony_ci		switch (opt->type) {
10262306a36Sopenharmony_ci		case OPTION_CALLBACK:
10362306a36Sopenharmony_ci			if (!(opt->flags & PARSE_OPT_NOARG))
10462306a36Sopenharmony_ci				break;
10562306a36Sopenharmony_ci			/* FALLTHROUGH */
10662306a36Sopenharmony_ci		case OPTION_BOOLEAN:
10762306a36Sopenharmony_ci		case OPTION_INCR:
10862306a36Sopenharmony_ci		case OPTION_BIT:
10962306a36Sopenharmony_ci		case OPTION_SET_UINT:
11062306a36Sopenharmony_ci		case OPTION_SET_PTR:
11162306a36Sopenharmony_ci			return opterror(opt, "takes no value", flags);
11262306a36Sopenharmony_ci		case OPTION_END:
11362306a36Sopenharmony_ci		case OPTION_ARGUMENT:
11462306a36Sopenharmony_ci		case OPTION_GROUP:
11562306a36Sopenharmony_ci		case OPTION_STRING:
11662306a36Sopenharmony_ci		case OPTION_INTEGER:
11762306a36Sopenharmony_ci		case OPTION_UINTEGER:
11862306a36Sopenharmony_ci		case OPTION_LONG:
11962306a36Sopenharmony_ci		case OPTION_ULONG:
12062306a36Sopenharmony_ci		case OPTION_U64:
12162306a36Sopenharmony_ci		default:
12262306a36Sopenharmony_ci			break;
12362306a36Sopenharmony_ci		}
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	if (opt->flags & PARSE_OPT_NOBUILD) {
12762306a36Sopenharmony_ci		char reason[128];
12862306a36Sopenharmony_ci		bool noarg = false;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci		err = snprintf(reason, sizeof(reason),
13162306a36Sopenharmony_ci				opt->flags & PARSE_OPT_CANSKIP ?
13262306a36Sopenharmony_ci					"is being ignored because %s " :
13362306a36Sopenharmony_ci					"is not available because %s",
13462306a36Sopenharmony_ci				opt->build_opt);
13562306a36Sopenharmony_ci		reason[sizeof(reason) - 1] = '\0';
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci		if (err < 0)
13862306a36Sopenharmony_ci			strncpy(reason, opt->flags & PARSE_OPT_CANSKIP ?
13962306a36Sopenharmony_ci					"is being ignored" :
14062306a36Sopenharmony_ci					"is not available",
14162306a36Sopenharmony_ci					sizeof(reason));
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci		if (!(opt->flags & PARSE_OPT_CANSKIP))
14462306a36Sopenharmony_ci			return opterror(opt, reason, flags);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci		err = 0;
14762306a36Sopenharmony_ci		if (unset)
14862306a36Sopenharmony_ci			noarg = true;
14962306a36Sopenharmony_ci		if (opt->flags & PARSE_OPT_NOARG)
15062306a36Sopenharmony_ci			noarg = true;
15162306a36Sopenharmony_ci		if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
15262306a36Sopenharmony_ci			noarg = true;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci		switch (opt->type) {
15562306a36Sopenharmony_ci		case OPTION_BOOLEAN:
15662306a36Sopenharmony_ci		case OPTION_INCR:
15762306a36Sopenharmony_ci		case OPTION_BIT:
15862306a36Sopenharmony_ci		case OPTION_SET_UINT:
15962306a36Sopenharmony_ci		case OPTION_SET_PTR:
16062306a36Sopenharmony_ci		case OPTION_END:
16162306a36Sopenharmony_ci		case OPTION_ARGUMENT:
16262306a36Sopenharmony_ci		case OPTION_GROUP:
16362306a36Sopenharmony_ci			noarg = true;
16462306a36Sopenharmony_ci			break;
16562306a36Sopenharmony_ci		case OPTION_CALLBACK:
16662306a36Sopenharmony_ci		case OPTION_STRING:
16762306a36Sopenharmony_ci		case OPTION_INTEGER:
16862306a36Sopenharmony_ci		case OPTION_UINTEGER:
16962306a36Sopenharmony_ci		case OPTION_LONG:
17062306a36Sopenharmony_ci		case OPTION_ULONG:
17162306a36Sopenharmony_ci		case OPTION_U64:
17262306a36Sopenharmony_ci		default:
17362306a36Sopenharmony_ci			break;
17462306a36Sopenharmony_ci		}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci		if (!noarg)
17762306a36Sopenharmony_ci			err = get_arg(p, opt, flags, NULL);
17862306a36Sopenharmony_ci		if (err)
17962306a36Sopenharmony_ci			return err;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci		optwarning(opt, reason, flags);
18262306a36Sopenharmony_ci		return 0;
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	switch (opt->type) {
18662306a36Sopenharmony_ci	case OPTION_BIT:
18762306a36Sopenharmony_ci		if (unset)
18862306a36Sopenharmony_ci			*(int *)opt->value &= ~opt->defval;
18962306a36Sopenharmony_ci		else
19062306a36Sopenharmony_ci			*(int *)opt->value |= opt->defval;
19162306a36Sopenharmony_ci		return 0;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	case OPTION_BOOLEAN:
19462306a36Sopenharmony_ci		*(bool *)opt->value = unset ? false : true;
19562306a36Sopenharmony_ci		if (opt->set)
19662306a36Sopenharmony_ci			*(bool *)opt->set = true;
19762306a36Sopenharmony_ci		return 0;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	case OPTION_INCR:
20062306a36Sopenharmony_ci		*(int *)opt->value = unset ? 0 : *(int *)opt->value + 1;
20162306a36Sopenharmony_ci		return 0;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	case OPTION_SET_UINT:
20462306a36Sopenharmony_ci		*(unsigned int *)opt->value = unset ? 0 : opt->defval;
20562306a36Sopenharmony_ci		return 0;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	case OPTION_SET_PTR:
20862306a36Sopenharmony_ci		*(void **)opt->value = unset ? NULL : (void *)opt->defval;
20962306a36Sopenharmony_ci		return 0;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	case OPTION_STRING:
21262306a36Sopenharmony_ci		err = 0;
21362306a36Sopenharmony_ci		if (unset)
21462306a36Sopenharmony_ci			*(const char **)opt->value = NULL;
21562306a36Sopenharmony_ci		else if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
21662306a36Sopenharmony_ci			*(const char **)opt->value = (const char *)opt->defval;
21762306a36Sopenharmony_ci		else
21862306a36Sopenharmony_ci			err = get_arg(p, opt, flags, (const char **)opt->value);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci		if (opt->set)
22162306a36Sopenharmony_ci			*(bool *)opt->set = true;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci		/* PARSE_OPT_NOEMPTY: Allow NULL but disallow empty string. */
22462306a36Sopenharmony_ci		if (opt->flags & PARSE_OPT_NOEMPTY) {
22562306a36Sopenharmony_ci			const char *val = *(const char **)opt->value;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci			if (!val)
22862306a36Sopenharmony_ci				return err;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci			/* Similar to unset if we are given an empty string. */
23162306a36Sopenharmony_ci			if (val[0] == '\0') {
23262306a36Sopenharmony_ci				*(const char **)opt->value = NULL;
23362306a36Sopenharmony_ci				return 0;
23462306a36Sopenharmony_ci			}
23562306a36Sopenharmony_ci		}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci		return err;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	case OPTION_CALLBACK:
24062306a36Sopenharmony_ci		if (opt->set)
24162306a36Sopenharmony_ci			*(bool *)opt->set = true;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci		if (unset)
24462306a36Sopenharmony_ci			return (*opt->callback)(opt, NULL, 1) ? (-1) : 0;
24562306a36Sopenharmony_ci		if (opt->flags & PARSE_OPT_NOARG)
24662306a36Sopenharmony_ci			return (*opt->callback)(opt, NULL, 0) ? (-1) : 0;
24762306a36Sopenharmony_ci		if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
24862306a36Sopenharmony_ci			return (*opt->callback)(opt, NULL, 0) ? (-1) : 0;
24962306a36Sopenharmony_ci		if (get_arg(p, opt, flags, &arg))
25062306a36Sopenharmony_ci			return -1;
25162306a36Sopenharmony_ci		return (*opt->callback)(opt, arg, 0) ? (-1) : 0;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	case OPTION_INTEGER:
25462306a36Sopenharmony_ci		if (unset) {
25562306a36Sopenharmony_ci			*(int *)opt->value = 0;
25662306a36Sopenharmony_ci			return 0;
25762306a36Sopenharmony_ci		}
25862306a36Sopenharmony_ci		if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
25962306a36Sopenharmony_ci			*(int *)opt->value = opt->defval;
26062306a36Sopenharmony_ci			return 0;
26162306a36Sopenharmony_ci		}
26262306a36Sopenharmony_ci		if (get_arg(p, opt, flags, &arg))
26362306a36Sopenharmony_ci			return -1;
26462306a36Sopenharmony_ci		*(int *)opt->value = strtol(arg, (char **)&s, 10);
26562306a36Sopenharmony_ci		if (*s)
26662306a36Sopenharmony_ci			return opterror(opt, "expects a numerical value", flags);
26762306a36Sopenharmony_ci		return 0;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	case OPTION_UINTEGER:
27062306a36Sopenharmony_ci		if (unset) {
27162306a36Sopenharmony_ci			*(unsigned int *)opt->value = 0;
27262306a36Sopenharmony_ci			return 0;
27362306a36Sopenharmony_ci		}
27462306a36Sopenharmony_ci		if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
27562306a36Sopenharmony_ci			*(unsigned int *)opt->value = opt->defval;
27662306a36Sopenharmony_ci			return 0;
27762306a36Sopenharmony_ci		}
27862306a36Sopenharmony_ci		if (get_arg(p, opt, flags, &arg))
27962306a36Sopenharmony_ci			return -1;
28062306a36Sopenharmony_ci		if (arg[0] == '-')
28162306a36Sopenharmony_ci			return opterror(opt, "expects an unsigned numerical value", flags);
28262306a36Sopenharmony_ci		*(unsigned int *)opt->value = strtol(arg, (char **)&s, 10);
28362306a36Sopenharmony_ci		if (*s)
28462306a36Sopenharmony_ci			return opterror(opt, "expects a numerical value", flags);
28562306a36Sopenharmony_ci		return 0;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	case OPTION_LONG:
28862306a36Sopenharmony_ci		if (unset) {
28962306a36Sopenharmony_ci			*(long *)opt->value = 0;
29062306a36Sopenharmony_ci			return 0;
29162306a36Sopenharmony_ci		}
29262306a36Sopenharmony_ci		if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
29362306a36Sopenharmony_ci			*(long *)opt->value = opt->defval;
29462306a36Sopenharmony_ci			return 0;
29562306a36Sopenharmony_ci		}
29662306a36Sopenharmony_ci		if (get_arg(p, opt, flags, &arg))
29762306a36Sopenharmony_ci			return -1;
29862306a36Sopenharmony_ci		*(long *)opt->value = strtol(arg, (char **)&s, 10);
29962306a36Sopenharmony_ci		if (*s)
30062306a36Sopenharmony_ci			return opterror(opt, "expects a numerical value", flags);
30162306a36Sopenharmony_ci		return 0;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	case OPTION_ULONG:
30462306a36Sopenharmony_ci		if (unset) {
30562306a36Sopenharmony_ci			*(unsigned long *)opt->value = 0;
30662306a36Sopenharmony_ci			return 0;
30762306a36Sopenharmony_ci		}
30862306a36Sopenharmony_ci		if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
30962306a36Sopenharmony_ci			*(unsigned long *)opt->value = opt->defval;
31062306a36Sopenharmony_ci			return 0;
31162306a36Sopenharmony_ci		}
31262306a36Sopenharmony_ci		if (get_arg(p, opt, flags, &arg))
31362306a36Sopenharmony_ci			return -1;
31462306a36Sopenharmony_ci		*(unsigned long *)opt->value = strtoul(arg, (char **)&s, 10);
31562306a36Sopenharmony_ci		if (*s)
31662306a36Sopenharmony_ci			return opterror(opt, "expects a numerical value", flags);
31762306a36Sopenharmony_ci		return 0;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	case OPTION_U64:
32062306a36Sopenharmony_ci		if (unset) {
32162306a36Sopenharmony_ci			*(u64 *)opt->value = 0;
32262306a36Sopenharmony_ci			return 0;
32362306a36Sopenharmony_ci		}
32462306a36Sopenharmony_ci		if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
32562306a36Sopenharmony_ci			*(u64 *)opt->value = opt->defval;
32662306a36Sopenharmony_ci			return 0;
32762306a36Sopenharmony_ci		}
32862306a36Sopenharmony_ci		if (get_arg(p, opt, flags, &arg))
32962306a36Sopenharmony_ci			return -1;
33062306a36Sopenharmony_ci		if (arg[0] == '-')
33162306a36Sopenharmony_ci			return opterror(opt, "expects an unsigned numerical value", flags);
33262306a36Sopenharmony_ci		*(u64 *)opt->value = strtoull(arg, (char **)&s, 10);
33362306a36Sopenharmony_ci		if (*s)
33462306a36Sopenharmony_ci			return opterror(opt, "expects a numerical value", flags);
33562306a36Sopenharmony_ci		return 0;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	case OPTION_END:
33862306a36Sopenharmony_ci	case OPTION_ARGUMENT:
33962306a36Sopenharmony_ci	case OPTION_GROUP:
34062306a36Sopenharmony_ci	default:
34162306a36Sopenharmony_ci		die("should not happen, someone must be hit on the forehead");
34262306a36Sopenharmony_ci	}
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_cistatic int parse_short_opt(struct parse_opt_ctx_t *p, const struct option *options)
34662306a36Sopenharmony_ci{
34762306a36Sopenharmony_ciretry:
34862306a36Sopenharmony_ci	for (; options->type != OPTION_END; options++) {
34962306a36Sopenharmony_ci		if (options->short_name == *p->opt) {
35062306a36Sopenharmony_ci			p->opt = p->opt[1] ? p->opt + 1 : NULL;
35162306a36Sopenharmony_ci			return get_value(p, options, OPT_SHORT);
35262306a36Sopenharmony_ci		}
35362306a36Sopenharmony_ci	}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	if (options->parent) {
35662306a36Sopenharmony_ci		options = options->parent;
35762306a36Sopenharmony_ci		goto retry;
35862306a36Sopenharmony_ci	}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	return -2;
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_cistatic int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
36462306a36Sopenharmony_ci                          const struct option *options)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	const char *arg_end = strchr(arg, '=');
36762306a36Sopenharmony_ci	const struct option *abbrev_option = NULL, *ambiguous_option = NULL;
36862306a36Sopenharmony_ci	int abbrev_flags = 0, ambiguous_flags = 0;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	if (!arg_end)
37162306a36Sopenharmony_ci		arg_end = arg + strlen(arg);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ciretry:
37462306a36Sopenharmony_ci	for (; options->type != OPTION_END; options++) {
37562306a36Sopenharmony_ci		const char *rest;
37662306a36Sopenharmony_ci		int flags = 0;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci		if (!options->long_name)
37962306a36Sopenharmony_ci			continue;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci		rest = skip_prefix(arg, options->long_name);
38262306a36Sopenharmony_ci		if (options->type == OPTION_ARGUMENT) {
38362306a36Sopenharmony_ci			if (!rest)
38462306a36Sopenharmony_ci				continue;
38562306a36Sopenharmony_ci			if (*rest == '=')
38662306a36Sopenharmony_ci				return opterror(options, "takes no value", flags);
38762306a36Sopenharmony_ci			if (*rest)
38862306a36Sopenharmony_ci				continue;
38962306a36Sopenharmony_ci			p->out[p->cpidx++] = arg - 2;
39062306a36Sopenharmony_ci			return 0;
39162306a36Sopenharmony_ci		}
39262306a36Sopenharmony_ci		if (!rest) {
39362306a36Sopenharmony_ci			if (strstarts(options->long_name, "no-")) {
39462306a36Sopenharmony_ci				/*
39562306a36Sopenharmony_ci				 * The long name itself starts with "no-", so
39662306a36Sopenharmony_ci				 * accept the option without "no-" so that users
39762306a36Sopenharmony_ci				 * do not have to enter "no-no-" to get the
39862306a36Sopenharmony_ci				 * negation.
39962306a36Sopenharmony_ci				 */
40062306a36Sopenharmony_ci				rest = skip_prefix(arg, options->long_name + 3);
40162306a36Sopenharmony_ci				if (rest) {
40262306a36Sopenharmony_ci					flags |= OPT_UNSET;
40362306a36Sopenharmony_ci					goto match;
40462306a36Sopenharmony_ci				}
40562306a36Sopenharmony_ci				/* Abbreviated case */
40662306a36Sopenharmony_ci				if (strstarts(options->long_name + 3, arg)) {
40762306a36Sopenharmony_ci					flags |= OPT_UNSET;
40862306a36Sopenharmony_ci					goto is_abbreviated;
40962306a36Sopenharmony_ci				}
41062306a36Sopenharmony_ci			}
41162306a36Sopenharmony_ci			/* abbreviated? */
41262306a36Sopenharmony_ci			if (!strncmp(options->long_name, arg, arg_end - arg)) {
41362306a36Sopenharmony_ciis_abbreviated:
41462306a36Sopenharmony_ci				if (abbrev_option) {
41562306a36Sopenharmony_ci					/*
41662306a36Sopenharmony_ci					 * If this is abbreviated, it is
41762306a36Sopenharmony_ci					 * ambiguous. So when there is no
41862306a36Sopenharmony_ci					 * exact match later, we need to
41962306a36Sopenharmony_ci					 * error out.
42062306a36Sopenharmony_ci					 */
42162306a36Sopenharmony_ci					ambiguous_option = abbrev_option;
42262306a36Sopenharmony_ci					ambiguous_flags = abbrev_flags;
42362306a36Sopenharmony_ci				}
42462306a36Sopenharmony_ci				if (!(flags & OPT_UNSET) && *arg_end)
42562306a36Sopenharmony_ci					p->opt = arg_end + 1;
42662306a36Sopenharmony_ci				abbrev_option = options;
42762306a36Sopenharmony_ci				abbrev_flags = flags;
42862306a36Sopenharmony_ci				continue;
42962306a36Sopenharmony_ci			}
43062306a36Sopenharmony_ci			/* negated and abbreviated very much? */
43162306a36Sopenharmony_ci			if (strstarts("no-", arg)) {
43262306a36Sopenharmony_ci				flags |= OPT_UNSET;
43362306a36Sopenharmony_ci				goto is_abbreviated;
43462306a36Sopenharmony_ci			}
43562306a36Sopenharmony_ci			/* negated? */
43662306a36Sopenharmony_ci			if (strncmp(arg, "no-", 3))
43762306a36Sopenharmony_ci				continue;
43862306a36Sopenharmony_ci			flags |= OPT_UNSET;
43962306a36Sopenharmony_ci			rest = skip_prefix(arg + 3, options->long_name);
44062306a36Sopenharmony_ci			/* abbreviated and negated? */
44162306a36Sopenharmony_ci			if (!rest && strstarts(options->long_name, arg + 3))
44262306a36Sopenharmony_ci				goto is_abbreviated;
44362306a36Sopenharmony_ci			if (!rest)
44462306a36Sopenharmony_ci				continue;
44562306a36Sopenharmony_ci		}
44662306a36Sopenharmony_cimatch:
44762306a36Sopenharmony_ci		if (*rest) {
44862306a36Sopenharmony_ci			if (*rest != '=')
44962306a36Sopenharmony_ci				continue;
45062306a36Sopenharmony_ci			p->opt = rest + 1;
45162306a36Sopenharmony_ci		}
45262306a36Sopenharmony_ci		return get_value(p, options, flags);
45362306a36Sopenharmony_ci	}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	if (ambiguous_option) {
45662306a36Sopenharmony_ci		 fprintf(stderr,
45762306a36Sopenharmony_ci			 " Error: Ambiguous option: %s (could be --%s%s or --%s%s)\n",
45862306a36Sopenharmony_ci			 arg,
45962306a36Sopenharmony_ci			 (ambiguous_flags & OPT_UNSET) ?  "no-" : "",
46062306a36Sopenharmony_ci			 ambiguous_option->long_name,
46162306a36Sopenharmony_ci			 (abbrev_flags & OPT_UNSET) ?  "no-" : "",
46262306a36Sopenharmony_ci			 abbrev_option->long_name);
46362306a36Sopenharmony_ci		 return -1;
46462306a36Sopenharmony_ci	}
46562306a36Sopenharmony_ci	if (abbrev_option)
46662306a36Sopenharmony_ci		return get_value(p, abbrev_option, abbrev_flags);
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	if (options->parent) {
46962306a36Sopenharmony_ci		options = options->parent;
47062306a36Sopenharmony_ci		goto retry;
47162306a36Sopenharmony_ci	}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	return -2;
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_cistatic void check_typos(const char *arg, const struct option *options)
47762306a36Sopenharmony_ci{
47862306a36Sopenharmony_ci	if (strlen(arg) < 3)
47962306a36Sopenharmony_ci		return;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	if (strstarts(arg, "no-")) {
48262306a36Sopenharmony_ci		fprintf(stderr, " Error: did you mean `--%s` (with two dashes ?)\n", arg);
48362306a36Sopenharmony_ci		exit(129);
48462306a36Sopenharmony_ci	}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	for (; options->type != OPTION_END; options++) {
48762306a36Sopenharmony_ci		if (!options->long_name)
48862306a36Sopenharmony_ci			continue;
48962306a36Sopenharmony_ci		if (strstarts(options->long_name, arg)) {
49062306a36Sopenharmony_ci			fprintf(stderr, " Error: did you mean `--%s` (with two dashes ?)\n", arg);
49162306a36Sopenharmony_ci			exit(129);
49262306a36Sopenharmony_ci		}
49362306a36Sopenharmony_ci	}
49462306a36Sopenharmony_ci}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_cistatic void parse_options_start(struct parse_opt_ctx_t *ctx,
49762306a36Sopenharmony_ci				int argc, const char **argv, int flags)
49862306a36Sopenharmony_ci{
49962306a36Sopenharmony_ci	memset(ctx, 0, sizeof(*ctx));
50062306a36Sopenharmony_ci	ctx->argc = argc - 1;
50162306a36Sopenharmony_ci	ctx->argv = argv + 1;
50262306a36Sopenharmony_ci	ctx->out  = argv;
50362306a36Sopenharmony_ci	ctx->cpidx = ((flags & PARSE_OPT_KEEP_ARGV0) != 0);
50462306a36Sopenharmony_ci	ctx->flags = flags;
50562306a36Sopenharmony_ci	if ((flags & PARSE_OPT_KEEP_UNKNOWN) &&
50662306a36Sopenharmony_ci	    (flags & PARSE_OPT_STOP_AT_NON_OPTION))
50762306a36Sopenharmony_ci		die("STOP_AT_NON_OPTION and KEEP_UNKNOWN don't go together");
50862306a36Sopenharmony_ci}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_cistatic int usage_with_options_internal(const char * const *,
51162306a36Sopenharmony_ci				       const struct option *, int,
51262306a36Sopenharmony_ci				       struct parse_opt_ctx_t *);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_cistatic int parse_options_step(struct parse_opt_ctx_t *ctx,
51562306a36Sopenharmony_ci			      const struct option *options,
51662306a36Sopenharmony_ci			      const char * const usagestr[])
51762306a36Sopenharmony_ci{
51862306a36Sopenharmony_ci	int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP);
51962306a36Sopenharmony_ci	int excl_short_opt = 1;
52062306a36Sopenharmony_ci	const char *arg;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	/* we must reset ->opt, unknown short option leave it dangling */
52362306a36Sopenharmony_ci	ctx->opt = NULL;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	for (; ctx->argc; ctx->argc--, ctx->argv++) {
52662306a36Sopenharmony_ci		arg = ctx->argv[0];
52762306a36Sopenharmony_ci		if (*arg != '-' || !arg[1]) {
52862306a36Sopenharmony_ci			if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION)
52962306a36Sopenharmony_ci				break;
53062306a36Sopenharmony_ci			ctx->out[ctx->cpidx++] = ctx->argv[0];
53162306a36Sopenharmony_ci			continue;
53262306a36Sopenharmony_ci		}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci		if (arg[1] != '-') {
53562306a36Sopenharmony_ci			ctx->opt = ++arg;
53662306a36Sopenharmony_ci			if (internal_help && *ctx->opt == 'h') {
53762306a36Sopenharmony_ci				return usage_with_options_internal(usagestr, options, 0, ctx);
53862306a36Sopenharmony_ci			}
53962306a36Sopenharmony_ci			switch (parse_short_opt(ctx, options)) {
54062306a36Sopenharmony_ci			case -1:
54162306a36Sopenharmony_ci				return parse_options_usage(usagestr, options, arg, 1);
54262306a36Sopenharmony_ci			case -2:
54362306a36Sopenharmony_ci				goto unknown;
54462306a36Sopenharmony_ci			case -3:
54562306a36Sopenharmony_ci				goto exclusive;
54662306a36Sopenharmony_ci			default:
54762306a36Sopenharmony_ci				break;
54862306a36Sopenharmony_ci			}
54962306a36Sopenharmony_ci			if (ctx->opt)
55062306a36Sopenharmony_ci				check_typos(arg, options);
55162306a36Sopenharmony_ci			while (ctx->opt) {
55262306a36Sopenharmony_ci				if (internal_help && *ctx->opt == 'h')
55362306a36Sopenharmony_ci					return usage_with_options_internal(usagestr, options, 0, ctx);
55462306a36Sopenharmony_ci				arg = ctx->opt;
55562306a36Sopenharmony_ci				switch (parse_short_opt(ctx, options)) {
55662306a36Sopenharmony_ci				case -1:
55762306a36Sopenharmony_ci					return parse_options_usage(usagestr, options, arg, 1);
55862306a36Sopenharmony_ci				case -2:
55962306a36Sopenharmony_ci					/* fake a short option thing to hide the fact that we may have
56062306a36Sopenharmony_ci					 * started to parse aggregated stuff
56162306a36Sopenharmony_ci					 *
56262306a36Sopenharmony_ci					 * This is leaky, too bad.
56362306a36Sopenharmony_ci					 */
56462306a36Sopenharmony_ci					ctx->argv[0] = strdup(ctx->opt - 1);
56562306a36Sopenharmony_ci					*(char *)ctx->argv[0] = '-';
56662306a36Sopenharmony_ci					goto unknown;
56762306a36Sopenharmony_ci				case -3:
56862306a36Sopenharmony_ci					goto exclusive;
56962306a36Sopenharmony_ci				default:
57062306a36Sopenharmony_ci					break;
57162306a36Sopenharmony_ci				}
57262306a36Sopenharmony_ci			}
57362306a36Sopenharmony_ci			continue;
57462306a36Sopenharmony_ci		}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci		if (!arg[2]) { /* "--" */
57762306a36Sopenharmony_ci			if (!(ctx->flags & PARSE_OPT_KEEP_DASHDASH)) {
57862306a36Sopenharmony_ci				ctx->argc--;
57962306a36Sopenharmony_ci				ctx->argv++;
58062306a36Sopenharmony_ci			}
58162306a36Sopenharmony_ci			break;
58262306a36Sopenharmony_ci		}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci		arg += 2;
58562306a36Sopenharmony_ci		if (internal_help && !strcmp(arg, "help-all"))
58662306a36Sopenharmony_ci			return usage_with_options_internal(usagestr, options, 1, ctx);
58762306a36Sopenharmony_ci		if (internal_help && !strcmp(arg, "help"))
58862306a36Sopenharmony_ci			return usage_with_options_internal(usagestr, options, 0, ctx);
58962306a36Sopenharmony_ci		if (!strcmp(arg, "list-opts"))
59062306a36Sopenharmony_ci			return PARSE_OPT_LIST_OPTS;
59162306a36Sopenharmony_ci		if (!strcmp(arg, "list-cmds"))
59262306a36Sopenharmony_ci			return PARSE_OPT_LIST_SUBCMDS;
59362306a36Sopenharmony_ci		switch (parse_long_opt(ctx, arg, options)) {
59462306a36Sopenharmony_ci		case -1:
59562306a36Sopenharmony_ci			return parse_options_usage(usagestr, options, arg, 0);
59662306a36Sopenharmony_ci		case -2:
59762306a36Sopenharmony_ci			goto unknown;
59862306a36Sopenharmony_ci		case -3:
59962306a36Sopenharmony_ci			excl_short_opt = 0;
60062306a36Sopenharmony_ci			goto exclusive;
60162306a36Sopenharmony_ci		default:
60262306a36Sopenharmony_ci			break;
60362306a36Sopenharmony_ci		}
60462306a36Sopenharmony_ci		continue;
60562306a36Sopenharmony_ciunknown:
60662306a36Sopenharmony_ci		if (!(ctx->flags & PARSE_OPT_KEEP_UNKNOWN))
60762306a36Sopenharmony_ci			return PARSE_OPT_UNKNOWN;
60862306a36Sopenharmony_ci		ctx->out[ctx->cpidx++] = ctx->argv[0];
60962306a36Sopenharmony_ci		ctx->opt = NULL;
61062306a36Sopenharmony_ci	}
61162306a36Sopenharmony_ci	return PARSE_OPT_DONE;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ciexclusive:
61462306a36Sopenharmony_ci	parse_options_usage(usagestr, options, arg, excl_short_opt);
61562306a36Sopenharmony_ci	if ((excl_short_opt && ctx->excl_opt->short_name) ||
61662306a36Sopenharmony_ci	    ctx->excl_opt->long_name == NULL) {
61762306a36Sopenharmony_ci		char opt = ctx->excl_opt->short_name;
61862306a36Sopenharmony_ci		parse_options_usage(NULL, options, &opt, 1);
61962306a36Sopenharmony_ci	} else {
62062306a36Sopenharmony_ci		parse_options_usage(NULL, options, ctx->excl_opt->long_name, 0);
62162306a36Sopenharmony_ci	}
62262306a36Sopenharmony_ci	return PARSE_OPT_HELP;
62362306a36Sopenharmony_ci}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_cistatic int parse_options_end(struct parse_opt_ctx_t *ctx)
62662306a36Sopenharmony_ci{
62762306a36Sopenharmony_ci	memmove(ctx->out + ctx->cpidx, ctx->argv, ctx->argc * sizeof(*ctx->out));
62862306a36Sopenharmony_ci	ctx->out[ctx->cpidx + ctx->argc] = NULL;
62962306a36Sopenharmony_ci	return ctx->cpidx + ctx->argc;
63062306a36Sopenharmony_ci}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ciint parse_options_subcommand(int argc, const char **argv, const struct option *options,
63362306a36Sopenharmony_ci			const char *const subcommands[], const char *usagestr[], int flags)
63462306a36Sopenharmony_ci{
63562306a36Sopenharmony_ci	struct parse_opt_ctx_t ctx;
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	/* build usage string if it's not provided */
63862306a36Sopenharmony_ci	if (subcommands && !usagestr[0]) {
63962306a36Sopenharmony_ci		char *buf = NULL;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci		astrcatf(&buf, "%s %s [<options>] {", subcmd_config.exec_name, argv[0]);
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci		for (int i = 0; subcommands[i]; i++) {
64462306a36Sopenharmony_ci			if (i)
64562306a36Sopenharmony_ci				astrcat(&buf, "|");
64662306a36Sopenharmony_ci			astrcat(&buf, subcommands[i]);
64762306a36Sopenharmony_ci		}
64862306a36Sopenharmony_ci		astrcat(&buf, "}");
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci		usagestr[0] = buf;
65162306a36Sopenharmony_ci	}
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	parse_options_start(&ctx, argc, argv, flags);
65462306a36Sopenharmony_ci	switch (parse_options_step(&ctx, options, usagestr)) {
65562306a36Sopenharmony_ci	case PARSE_OPT_HELP:
65662306a36Sopenharmony_ci		exit(129);
65762306a36Sopenharmony_ci	case PARSE_OPT_DONE:
65862306a36Sopenharmony_ci		break;
65962306a36Sopenharmony_ci	case PARSE_OPT_LIST_OPTS:
66062306a36Sopenharmony_ci		while (options->type != OPTION_END) {
66162306a36Sopenharmony_ci			if (options->long_name)
66262306a36Sopenharmony_ci				printf("--%s ", options->long_name);
66362306a36Sopenharmony_ci			options++;
66462306a36Sopenharmony_ci		}
66562306a36Sopenharmony_ci		putchar('\n');
66662306a36Sopenharmony_ci		exit(130);
66762306a36Sopenharmony_ci	case PARSE_OPT_LIST_SUBCMDS:
66862306a36Sopenharmony_ci		if (subcommands) {
66962306a36Sopenharmony_ci			for (int i = 0; subcommands[i]; i++)
67062306a36Sopenharmony_ci				printf("%s ", subcommands[i]);
67162306a36Sopenharmony_ci		}
67262306a36Sopenharmony_ci		putchar('\n');
67362306a36Sopenharmony_ci		exit(130);
67462306a36Sopenharmony_ci	default: /* PARSE_OPT_UNKNOWN */
67562306a36Sopenharmony_ci		if (ctx.argv[0][1] == '-')
67662306a36Sopenharmony_ci			astrcatf(&error_buf, "unknown option `%s'",
67762306a36Sopenharmony_ci				 ctx.argv[0] + 2);
67862306a36Sopenharmony_ci		else
67962306a36Sopenharmony_ci			astrcatf(&error_buf, "unknown switch `%c'", *ctx.opt);
68062306a36Sopenharmony_ci		usage_with_options(usagestr, options);
68162306a36Sopenharmony_ci	}
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	return parse_options_end(&ctx);
68462306a36Sopenharmony_ci}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ciint parse_options(int argc, const char **argv, const struct option *options,
68762306a36Sopenharmony_ci		  const char * const usagestr[], int flags)
68862306a36Sopenharmony_ci{
68962306a36Sopenharmony_ci	return parse_options_subcommand(argc, argv, options, NULL,
69062306a36Sopenharmony_ci					(const char **) usagestr, flags);
69162306a36Sopenharmony_ci}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci#define USAGE_OPTS_WIDTH 24
69462306a36Sopenharmony_ci#define USAGE_GAP         2
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_cistatic void print_option_help(const struct option *opts, int full)
69762306a36Sopenharmony_ci{
69862306a36Sopenharmony_ci	size_t pos;
69962306a36Sopenharmony_ci	int pad;
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	if (opts->type == OPTION_GROUP) {
70262306a36Sopenharmony_ci		fputc('\n', stderr);
70362306a36Sopenharmony_ci		if (*opts->help)
70462306a36Sopenharmony_ci			fprintf(stderr, "%s\n", opts->help);
70562306a36Sopenharmony_ci		return;
70662306a36Sopenharmony_ci	}
70762306a36Sopenharmony_ci	if (!full && (opts->flags & PARSE_OPT_HIDDEN))
70862306a36Sopenharmony_ci		return;
70962306a36Sopenharmony_ci	if (opts->flags & PARSE_OPT_DISABLED)
71062306a36Sopenharmony_ci		return;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	pos = fprintf(stderr, "    ");
71362306a36Sopenharmony_ci	if (opts->short_name)
71462306a36Sopenharmony_ci		pos += fprintf(stderr, "-%c", opts->short_name);
71562306a36Sopenharmony_ci	else
71662306a36Sopenharmony_ci		pos += fprintf(stderr, "    ");
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	if (opts->long_name && opts->short_name)
71962306a36Sopenharmony_ci		pos += fprintf(stderr, ", ");
72062306a36Sopenharmony_ci	if (opts->long_name)
72162306a36Sopenharmony_ci		pos += fprintf(stderr, "--%s", opts->long_name);
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	switch (opts->type) {
72462306a36Sopenharmony_ci	case OPTION_ARGUMENT:
72562306a36Sopenharmony_ci		break;
72662306a36Sopenharmony_ci	case OPTION_LONG:
72762306a36Sopenharmony_ci	case OPTION_ULONG:
72862306a36Sopenharmony_ci	case OPTION_U64:
72962306a36Sopenharmony_ci	case OPTION_INTEGER:
73062306a36Sopenharmony_ci	case OPTION_UINTEGER:
73162306a36Sopenharmony_ci		if (opts->flags & PARSE_OPT_OPTARG)
73262306a36Sopenharmony_ci			if (opts->long_name)
73362306a36Sopenharmony_ci				pos += fprintf(stderr, "[=<n>]");
73462306a36Sopenharmony_ci			else
73562306a36Sopenharmony_ci				pos += fprintf(stderr, "[<n>]");
73662306a36Sopenharmony_ci		else
73762306a36Sopenharmony_ci			pos += fprintf(stderr, " <n>");
73862306a36Sopenharmony_ci		break;
73962306a36Sopenharmony_ci	case OPTION_CALLBACK:
74062306a36Sopenharmony_ci		if (opts->flags & PARSE_OPT_NOARG)
74162306a36Sopenharmony_ci			break;
74262306a36Sopenharmony_ci		/* FALLTHROUGH */
74362306a36Sopenharmony_ci	case OPTION_STRING:
74462306a36Sopenharmony_ci		if (opts->argh) {
74562306a36Sopenharmony_ci			if (opts->flags & PARSE_OPT_OPTARG)
74662306a36Sopenharmony_ci				if (opts->long_name)
74762306a36Sopenharmony_ci					pos += fprintf(stderr, "[=<%s>]", opts->argh);
74862306a36Sopenharmony_ci				else
74962306a36Sopenharmony_ci					pos += fprintf(stderr, "[<%s>]", opts->argh);
75062306a36Sopenharmony_ci			else
75162306a36Sopenharmony_ci				pos += fprintf(stderr, " <%s>", opts->argh);
75262306a36Sopenharmony_ci		} else {
75362306a36Sopenharmony_ci			if (opts->flags & PARSE_OPT_OPTARG)
75462306a36Sopenharmony_ci				if (opts->long_name)
75562306a36Sopenharmony_ci					pos += fprintf(stderr, "[=...]");
75662306a36Sopenharmony_ci				else
75762306a36Sopenharmony_ci					pos += fprintf(stderr, "[...]");
75862306a36Sopenharmony_ci			else
75962306a36Sopenharmony_ci				pos += fprintf(stderr, " ...");
76062306a36Sopenharmony_ci		}
76162306a36Sopenharmony_ci		break;
76262306a36Sopenharmony_ci	default: /* OPTION_{BIT,BOOLEAN,SET_UINT,SET_PTR} */
76362306a36Sopenharmony_ci	case OPTION_END:
76462306a36Sopenharmony_ci	case OPTION_GROUP:
76562306a36Sopenharmony_ci	case OPTION_BIT:
76662306a36Sopenharmony_ci	case OPTION_BOOLEAN:
76762306a36Sopenharmony_ci	case OPTION_INCR:
76862306a36Sopenharmony_ci	case OPTION_SET_UINT:
76962306a36Sopenharmony_ci	case OPTION_SET_PTR:
77062306a36Sopenharmony_ci		break;
77162306a36Sopenharmony_ci	}
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	if (pos <= USAGE_OPTS_WIDTH)
77462306a36Sopenharmony_ci		pad = USAGE_OPTS_WIDTH - pos;
77562306a36Sopenharmony_ci	else {
77662306a36Sopenharmony_ci		fputc('\n', stderr);
77762306a36Sopenharmony_ci		pad = USAGE_OPTS_WIDTH;
77862306a36Sopenharmony_ci	}
77962306a36Sopenharmony_ci	fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help);
78062306a36Sopenharmony_ci	if (opts->flags & PARSE_OPT_NOBUILD)
78162306a36Sopenharmony_ci		fprintf(stderr, "%*s(not built-in because %s)\n",
78262306a36Sopenharmony_ci			USAGE_OPTS_WIDTH + USAGE_GAP, "",
78362306a36Sopenharmony_ci			opts->build_opt);
78462306a36Sopenharmony_ci}
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_cistatic int option__cmp(const void *va, const void *vb)
78762306a36Sopenharmony_ci{
78862306a36Sopenharmony_ci	const struct option *a = va, *b = vb;
78962306a36Sopenharmony_ci	int sa = tolower(a->short_name), sb = tolower(b->short_name), ret;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	if (sa == 0)
79262306a36Sopenharmony_ci		sa = 'z' + 1;
79362306a36Sopenharmony_ci	if (sb == 0)
79462306a36Sopenharmony_ci		sb = 'z' + 1;
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	ret = sa - sb;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	if (ret == 0) {
79962306a36Sopenharmony_ci		const char *la = a->long_name ?: "",
80062306a36Sopenharmony_ci			   *lb = b->long_name ?: "";
80162306a36Sopenharmony_ci		ret = strcmp(la, lb);
80262306a36Sopenharmony_ci	}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	return ret;
80562306a36Sopenharmony_ci}
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_cistatic struct option *options__order(const struct option *opts)
80862306a36Sopenharmony_ci{
80962306a36Sopenharmony_ci	int nr_opts = 0, nr_group = 0, len;
81062306a36Sopenharmony_ci	const struct option *o = opts;
81162306a36Sopenharmony_ci	struct option *opt, *ordered, *group;
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	for (o = opts; o->type != OPTION_END; o++)
81462306a36Sopenharmony_ci		++nr_opts;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	len = sizeof(*o) * (nr_opts + 1);
81762306a36Sopenharmony_ci	ordered = malloc(len);
81862306a36Sopenharmony_ci	if (!ordered)
81962306a36Sopenharmony_ci		goto out;
82062306a36Sopenharmony_ci	memcpy(ordered, opts, len);
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	/* sort each option group individually */
82362306a36Sopenharmony_ci	for (opt = group = ordered; opt->type != OPTION_END; opt++) {
82462306a36Sopenharmony_ci		if (opt->type == OPTION_GROUP) {
82562306a36Sopenharmony_ci			qsort(group, nr_group, sizeof(*opt), option__cmp);
82662306a36Sopenharmony_ci			group = opt + 1;
82762306a36Sopenharmony_ci			nr_group = 0;
82862306a36Sopenharmony_ci			continue;
82962306a36Sopenharmony_ci		}
83062306a36Sopenharmony_ci		nr_group++;
83162306a36Sopenharmony_ci	}
83262306a36Sopenharmony_ci	qsort(group, nr_group, sizeof(*opt), option__cmp);
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ciout:
83562306a36Sopenharmony_ci	return ordered;
83662306a36Sopenharmony_ci}
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_cistatic bool option__in_argv(const struct option *opt, const struct parse_opt_ctx_t *ctx)
83962306a36Sopenharmony_ci{
84062306a36Sopenharmony_ci	int i;
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	for (i = 1; i < ctx->argc; ++i) {
84362306a36Sopenharmony_ci		const char *arg = ctx->argv[i];
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci		if (arg[0] != '-') {
84662306a36Sopenharmony_ci			if (arg[1] == '\0') {
84762306a36Sopenharmony_ci				if (arg[0] == opt->short_name)
84862306a36Sopenharmony_ci					return true;
84962306a36Sopenharmony_ci				continue;
85062306a36Sopenharmony_ci			}
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci			if (opt->long_name && strcmp(opt->long_name, arg) == 0)
85362306a36Sopenharmony_ci				return true;
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci			if (opt->help && strcasestr(opt->help, arg) != NULL)
85662306a36Sopenharmony_ci				return true;
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci			continue;
85962306a36Sopenharmony_ci		}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci		if (arg[1] == opt->short_name ||
86262306a36Sopenharmony_ci		    (arg[1] == '-' && opt->long_name && strcmp(opt->long_name, arg + 2) == 0))
86362306a36Sopenharmony_ci			return true;
86462306a36Sopenharmony_ci	}
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	return false;
86762306a36Sopenharmony_ci}
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_cistatic int usage_with_options_internal(const char * const *usagestr,
87062306a36Sopenharmony_ci				       const struct option *opts, int full,
87162306a36Sopenharmony_ci				       struct parse_opt_ctx_t *ctx)
87262306a36Sopenharmony_ci{
87362306a36Sopenharmony_ci	struct option *ordered;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	if (!usagestr)
87662306a36Sopenharmony_ci		return PARSE_OPT_HELP;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	setup_pager();
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	if (error_buf) {
88162306a36Sopenharmony_ci		fprintf(stderr, "  Error: %s\n", error_buf);
88262306a36Sopenharmony_ci		zfree(&error_buf);
88362306a36Sopenharmony_ci	}
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	fprintf(stderr, "\n Usage: %s\n", *usagestr++);
88662306a36Sopenharmony_ci	while (*usagestr && **usagestr)
88762306a36Sopenharmony_ci		fprintf(stderr, "    or: %s\n", *usagestr++);
88862306a36Sopenharmony_ci	while (*usagestr) {
88962306a36Sopenharmony_ci		fprintf(stderr, "%s%s\n",
89062306a36Sopenharmony_ci				**usagestr ? "    " : "",
89162306a36Sopenharmony_ci				*usagestr);
89262306a36Sopenharmony_ci		usagestr++;
89362306a36Sopenharmony_ci	}
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	if (opts->type != OPTION_GROUP)
89662306a36Sopenharmony_ci		fputc('\n', stderr);
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	ordered = options__order(opts);
89962306a36Sopenharmony_ci	if (ordered)
90062306a36Sopenharmony_ci		opts = ordered;
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	for (  ; opts->type != OPTION_END; opts++) {
90362306a36Sopenharmony_ci		if (ctx && ctx->argc > 1 && !option__in_argv(opts, ctx))
90462306a36Sopenharmony_ci			continue;
90562306a36Sopenharmony_ci		print_option_help(opts, full);
90662306a36Sopenharmony_ci	}
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	fputc('\n', stderr);
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	free(ordered);
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	return PARSE_OPT_HELP;
91362306a36Sopenharmony_ci}
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_civoid usage_with_options(const char * const *usagestr,
91662306a36Sopenharmony_ci			const struct option *opts)
91762306a36Sopenharmony_ci{
91862306a36Sopenharmony_ci	usage_with_options_internal(usagestr, opts, 0, NULL);
91962306a36Sopenharmony_ci	exit(129);
92062306a36Sopenharmony_ci}
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_civoid usage_with_options_msg(const char * const *usagestr,
92362306a36Sopenharmony_ci			    const struct option *opts, const char *fmt, ...)
92462306a36Sopenharmony_ci{
92562306a36Sopenharmony_ci	va_list ap;
92662306a36Sopenharmony_ci	char *tmp = error_buf;
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	va_start(ap, fmt);
92962306a36Sopenharmony_ci	if (vasprintf(&error_buf, fmt, ap) == -1)
93062306a36Sopenharmony_ci		die("vasprintf failed");
93162306a36Sopenharmony_ci	va_end(ap);
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	free(tmp);
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	usage_with_options_internal(usagestr, opts, 0, NULL);
93662306a36Sopenharmony_ci	exit(129);
93762306a36Sopenharmony_ci}
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ciint parse_options_usage(const char * const *usagestr,
94062306a36Sopenharmony_ci			const struct option *opts,
94162306a36Sopenharmony_ci			const char *optstr, bool short_opt)
94262306a36Sopenharmony_ci{
94362306a36Sopenharmony_ci	if (!usagestr)
94462306a36Sopenharmony_ci		goto opt;
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	fprintf(stderr, "\n Usage: %s\n", *usagestr++);
94762306a36Sopenharmony_ci	while (*usagestr && **usagestr)
94862306a36Sopenharmony_ci		fprintf(stderr, "    or: %s\n", *usagestr++);
94962306a36Sopenharmony_ci	while (*usagestr) {
95062306a36Sopenharmony_ci		fprintf(stderr, "%s%s\n",
95162306a36Sopenharmony_ci				**usagestr ? "    " : "",
95262306a36Sopenharmony_ci				*usagestr);
95362306a36Sopenharmony_ci		usagestr++;
95462306a36Sopenharmony_ci	}
95562306a36Sopenharmony_ci	fputc('\n', stderr);
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ciopt:
95862306a36Sopenharmony_ci	for (  ; opts->type != OPTION_END; opts++) {
95962306a36Sopenharmony_ci		if (short_opt) {
96062306a36Sopenharmony_ci			if (opts->short_name == *optstr) {
96162306a36Sopenharmony_ci				print_option_help(opts, 0);
96262306a36Sopenharmony_ci				break;
96362306a36Sopenharmony_ci			}
96462306a36Sopenharmony_ci			continue;
96562306a36Sopenharmony_ci		}
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci		if (opts->long_name == NULL)
96862306a36Sopenharmony_ci			continue;
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci		if (strstarts(opts->long_name, optstr))
97162306a36Sopenharmony_ci			print_option_help(opts, 0);
97262306a36Sopenharmony_ci		if (strstarts("no-", optstr) &&
97362306a36Sopenharmony_ci		    strstarts(opts->long_name, optstr + 3))
97462306a36Sopenharmony_ci			print_option_help(opts, 0);
97562306a36Sopenharmony_ci	}
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	return PARSE_OPT_HELP;
97862306a36Sopenharmony_ci}
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ciint parse_opt_verbosity_cb(const struct option *opt,
98262306a36Sopenharmony_ci			   const char *arg __maybe_unused,
98362306a36Sopenharmony_ci			   int unset)
98462306a36Sopenharmony_ci{
98562306a36Sopenharmony_ci	int *target = opt->value;
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	if (unset)
98862306a36Sopenharmony_ci		/* --no-quiet, --no-verbose */
98962306a36Sopenharmony_ci		*target = 0;
99062306a36Sopenharmony_ci	else if (opt->short_name == 'v') {
99162306a36Sopenharmony_ci		if (*target >= 0)
99262306a36Sopenharmony_ci			(*target)++;
99362306a36Sopenharmony_ci		else
99462306a36Sopenharmony_ci			*target = 1;
99562306a36Sopenharmony_ci	} else {
99662306a36Sopenharmony_ci		if (*target <= 0)
99762306a36Sopenharmony_ci			(*target)--;
99862306a36Sopenharmony_ci		else
99962306a36Sopenharmony_ci			*target = -1;
100062306a36Sopenharmony_ci	}
100162306a36Sopenharmony_ci	return 0;
100262306a36Sopenharmony_ci}
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_cistatic struct option *
100562306a36Sopenharmony_cifind_option(struct option *opts, int shortopt, const char *longopt)
100662306a36Sopenharmony_ci{
100762306a36Sopenharmony_ci	for (; opts->type != OPTION_END; opts++) {
100862306a36Sopenharmony_ci		if ((shortopt && opts->short_name == shortopt) ||
100962306a36Sopenharmony_ci		    (opts->long_name && longopt &&
101062306a36Sopenharmony_ci		     !strcmp(opts->long_name, longopt)))
101162306a36Sopenharmony_ci			return opts;
101262306a36Sopenharmony_ci	}
101362306a36Sopenharmony_ci	return NULL;
101462306a36Sopenharmony_ci}
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_civoid set_option_flag(struct option *opts, int shortopt, const char *longopt,
101762306a36Sopenharmony_ci		     int flag)
101862306a36Sopenharmony_ci{
101962306a36Sopenharmony_ci	struct option *opt = find_option(opts, shortopt, longopt);
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	if (opt)
102262306a36Sopenharmony_ci		opt->flags |= flag;
102362306a36Sopenharmony_ci	return;
102462306a36Sopenharmony_ci}
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_civoid set_option_nobuild(struct option *opts, int shortopt,
102762306a36Sopenharmony_ci			const char *longopt,
102862306a36Sopenharmony_ci			const char *build_opt,
102962306a36Sopenharmony_ci			bool can_skip)
103062306a36Sopenharmony_ci{
103162306a36Sopenharmony_ci	struct option *opt = find_option(opts, shortopt, longopt);
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	if (!opt)
103462306a36Sopenharmony_ci		return;
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	opt->flags |= PARSE_OPT_NOBUILD;
103762306a36Sopenharmony_ci	opt->flags |= can_skip ? PARSE_OPT_CANSKIP : 0;
103862306a36Sopenharmony_ci	opt->build_opt = build_opt;
103962306a36Sopenharmony_ci}
1040