xref: /linux-6.15/tools/lib/subcmd/parse-options.c (revision 7a4ffec9)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
24b6ab94eSJosh Poimboeuf #include <linux/compiler.h>
396395cbbSArnaldo Carvalho de Melo #include <linux/string.h>
44b6ab94eSJosh Poimboeuf #include <linux/types.h>
54b6ab94eSJosh Poimboeuf #include <stdio.h>
64b6ab94eSJosh Poimboeuf #include <stdlib.h>
74b6ab94eSJosh Poimboeuf #include <stdint.h>
84b6ab94eSJosh Poimboeuf #include <string.h>
94b6ab94eSJosh Poimboeuf #include <ctype.h>
104b6ab94eSJosh Poimboeuf #include "subcmd-util.h"
114b6ab94eSJosh Poimboeuf #include "parse-options.h"
124b6ab94eSJosh Poimboeuf #include "subcmd-config.h"
134b6ab94eSJosh Poimboeuf #include "pager.h"
144b6ab94eSJosh Poimboeuf 
154b6ab94eSJosh Poimboeuf #define OPT_SHORT 1
164b6ab94eSJosh Poimboeuf #define OPT_UNSET 2
174b6ab94eSJosh Poimboeuf 
184b6ab94eSJosh Poimboeuf char *error_buf;
194b6ab94eSJosh Poimboeuf 
opterror(const struct option * opt,const char * reason,int flags)204b6ab94eSJosh Poimboeuf static int opterror(const struct option *opt, const char *reason, int flags)
214b6ab94eSJosh Poimboeuf {
224b6ab94eSJosh Poimboeuf 	if (flags & OPT_SHORT)
234b6ab94eSJosh Poimboeuf 		fprintf(stderr, " Error: switch `%c' %s", opt->short_name, reason);
244b6ab94eSJosh Poimboeuf 	else if (flags & OPT_UNSET)
254b6ab94eSJosh Poimboeuf 		fprintf(stderr, " Error: option `no-%s' %s", opt->long_name, reason);
264b6ab94eSJosh Poimboeuf 	else
274b6ab94eSJosh Poimboeuf 		fprintf(stderr, " Error: option `%s' %s", opt->long_name, reason);
284b6ab94eSJosh Poimboeuf 
294b6ab94eSJosh Poimboeuf 	return -1;
304b6ab94eSJosh Poimboeuf }
314b6ab94eSJosh Poimboeuf 
skip_prefix(const char * str,const char * prefix)324b6ab94eSJosh Poimboeuf static const char *skip_prefix(const char *str, const char *prefix)
334b6ab94eSJosh Poimboeuf {
344b6ab94eSJosh Poimboeuf 	size_t len = strlen(prefix);
354b6ab94eSJosh Poimboeuf 	return strncmp(str, prefix, len) ? NULL : str + len;
364b6ab94eSJosh Poimboeuf }
374b6ab94eSJosh Poimboeuf 
optwarning(const struct option * opt,const char * reason,int flags)384b6ab94eSJosh Poimboeuf static void optwarning(const struct option *opt, const char *reason, int flags)
394b6ab94eSJosh Poimboeuf {
404b6ab94eSJosh Poimboeuf 	if (flags & OPT_SHORT)
414b6ab94eSJosh Poimboeuf 		fprintf(stderr, " Warning: switch `%c' %s", opt->short_name, reason);
424b6ab94eSJosh Poimboeuf 	else if (flags & OPT_UNSET)
434b6ab94eSJosh Poimboeuf 		fprintf(stderr, " Warning: option `no-%s' %s", opt->long_name, reason);
444b6ab94eSJosh Poimboeuf 	else
454b6ab94eSJosh Poimboeuf 		fprintf(stderr, " Warning: option `%s' %s", opt->long_name, reason);
464b6ab94eSJosh Poimboeuf }
474b6ab94eSJosh Poimboeuf 
get_arg(struct parse_opt_ctx_t * p,const struct option * opt,int flags,const char ** arg)484b6ab94eSJosh Poimboeuf static int get_arg(struct parse_opt_ctx_t *p, const struct option *opt,
494b6ab94eSJosh Poimboeuf 		   int flags, const char **arg)
504b6ab94eSJosh Poimboeuf {
514b6ab94eSJosh Poimboeuf 	const char *res;
524b6ab94eSJosh Poimboeuf 
534b6ab94eSJosh Poimboeuf 	if (p->opt) {
544b6ab94eSJosh Poimboeuf 		res = p->opt;
554b6ab94eSJosh Poimboeuf 		p->opt = NULL;
564b6ab94eSJosh Poimboeuf 	} else if ((opt->flags & PARSE_OPT_LASTARG_DEFAULT) && (p->argc == 1 ||
574b6ab94eSJosh Poimboeuf 		    **(p->argv + 1) == '-')) {
584b6ab94eSJosh Poimboeuf 		res = (const char *)opt->defval;
594b6ab94eSJosh Poimboeuf 	} else if (p->argc > 1) {
604b6ab94eSJosh Poimboeuf 		p->argc--;
614b6ab94eSJosh Poimboeuf 		res = *++p->argv;
624b6ab94eSJosh Poimboeuf 	} else
634b6ab94eSJosh Poimboeuf 		return opterror(opt, "requires a value", flags);
644b6ab94eSJosh Poimboeuf 	if (arg)
654b6ab94eSJosh Poimboeuf 		*arg = res;
664b6ab94eSJosh Poimboeuf 	return 0;
674b6ab94eSJosh Poimboeuf }
684b6ab94eSJosh Poimboeuf 
get_value(struct parse_opt_ctx_t * p,const struct option * opt,int flags)694b6ab94eSJosh Poimboeuf static int get_value(struct parse_opt_ctx_t *p,
704b6ab94eSJosh Poimboeuf 		     const struct option *opt, int flags)
714b6ab94eSJosh Poimboeuf {
724b6ab94eSJosh Poimboeuf 	const char *s, *arg = NULL;
734b6ab94eSJosh Poimboeuf 	const int unset = flags & OPT_UNSET;
744b6ab94eSJosh Poimboeuf 	int err;
754b6ab94eSJosh Poimboeuf 
764b6ab94eSJosh Poimboeuf 	if (unset && p->opt)
774b6ab94eSJosh Poimboeuf 		return opterror(opt, "takes no value", flags);
784b6ab94eSJosh Poimboeuf 	if (unset && (opt->flags & PARSE_OPT_NONEG))
794b6ab94eSJosh Poimboeuf 		return opterror(opt, "isn't available", flags);
804b6ab94eSJosh Poimboeuf 	if (opt->flags & PARSE_OPT_DISABLED)
814b6ab94eSJosh Poimboeuf 		return opterror(opt, "is not usable", flags);
824b6ab94eSJosh Poimboeuf 
834b6ab94eSJosh Poimboeuf 	if (opt->flags & PARSE_OPT_EXCLUSIVE) {
844b6ab94eSJosh Poimboeuf 		if (p->excl_opt && p->excl_opt != opt) {
854b6ab94eSJosh Poimboeuf 			char msg[128];
864b6ab94eSJosh Poimboeuf 
874b6ab94eSJosh Poimboeuf 			if (((flags & OPT_SHORT) && p->excl_opt->short_name) ||
884b6ab94eSJosh Poimboeuf 			    p->excl_opt->long_name == NULL) {
894b6ab94eSJosh Poimboeuf 				snprintf(msg, sizeof(msg), "cannot be used with switch `%c'",
904b6ab94eSJosh Poimboeuf 					 p->excl_opt->short_name);
914b6ab94eSJosh Poimboeuf 			} else {
924b6ab94eSJosh Poimboeuf 				snprintf(msg, sizeof(msg), "cannot be used with %s",
934b6ab94eSJosh Poimboeuf 					 p->excl_opt->long_name);
944b6ab94eSJosh Poimboeuf 			}
954b6ab94eSJosh Poimboeuf 			opterror(opt, msg, flags);
964b6ab94eSJosh Poimboeuf 			return -3;
974b6ab94eSJosh Poimboeuf 		}
984b6ab94eSJosh Poimboeuf 		p->excl_opt = opt;
994b6ab94eSJosh Poimboeuf 	}
1004b6ab94eSJosh Poimboeuf 	if (!(flags & OPT_SHORT) && p->opt) {
1014b6ab94eSJosh Poimboeuf 		switch (opt->type) {
1024b6ab94eSJosh Poimboeuf 		case OPTION_CALLBACK:
1034b6ab94eSJosh Poimboeuf 			if (!(opt->flags & PARSE_OPT_NOARG))
1044b6ab94eSJosh Poimboeuf 				break;
1054b6ab94eSJosh Poimboeuf 			/* FALLTHROUGH */
1064b6ab94eSJosh Poimboeuf 		case OPTION_BOOLEAN:
1074b6ab94eSJosh Poimboeuf 		case OPTION_INCR:
1084b6ab94eSJosh Poimboeuf 		case OPTION_BIT:
1094b6ab94eSJosh Poimboeuf 		case OPTION_SET_UINT:
1104b6ab94eSJosh Poimboeuf 		case OPTION_SET_PTR:
1114b6ab94eSJosh Poimboeuf 			return opterror(opt, "takes no value", flags);
1124b6ab94eSJosh Poimboeuf 		case OPTION_END:
1134b6ab94eSJosh Poimboeuf 		case OPTION_ARGUMENT:
1144b6ab94eSJosh Poimboeuf 		case OPTION_GROUP:
1154b6ab94eSJosh Poimboeuf 		case OPTION_STRING:
1164b6ab94eSJosh Poimboeuf 		case OPTION_INTEGER:
1174b6ab94eSJosh Poimboeuf 		case OPTION_UINTEGER:
1184b6ab94eSJosh Poimboeuf 		case OPTION_LONG:
1194ba8b3ebSArnaldo Carvalho de Melo 		case OPTION_ULONG:
1204b6ab94eSJosh Poimboeuf 		case OPTION_U64:
1214b6ab94eSJosh Poimboeuf 		default:
1224b6ab94eSJosh Poimboeuf 			break;
1234b6ab94eSJosh Poimboeuf 		}
1244b6ab94eSJosh Poimboeuf 	}
1254b6ab94eSJosh Poimboeuf 
1264b6ab94eSJosh Poimboeuf 	if (opt->flags & PARSE_OPT_NOBUILD) {
1274b6ab94eSJosh Poimboeuf 		char reason[128];
1284b6ab94eSJosh Poimboeuf 		bool noarg = false;
1294b6ab94eSJosh Poimboeuf 
1304b6ab94eSJosh Poimboeuf 		err = snprintf(reason, sizeof(reason),
1314b6ab94eSJosh Poimboeuf 				opt->flags & PARSE_OPT_CANSKIP ?
1324b6ab94eSJosh Poimboeuf 					"is being ignored because %s " :
1334b6ab94eSJosh Poimboeuf 					"is not available because %s",
1344b6ab94eSJosh Poimboeuf 				opt->build_opt);
1354b6ab94eSJosh Poimboeuf 		reason[sizeof(reason) - 1] = '\0';
1364b6ab94eSJosh Poimboeuf 
1374b6ab94eSJosh Poimboeuf 		if (err < 0)
1384b6ab94eSJosh Poimboeuf 			strncpy(reason, opt->flags & PARSE_OPT_CANSKIP ?
1394b6ab94eSJosh Poimboeuf 					"is being ignored" :
1404b6ab94eSJosh Poimboeuf 					"is not available",
1414b6ab94eSJosh Poimboeuf 					sizeof(reason));
1424b6ab94eSJosh Poimboeuf 
1434b6ab94eSJosh Poimboeuf 		if (!(opt->flags & PARSE_OPT_CANSKIP))
1444b6ab94eSJosh Poimboeuf 			return opterror(opt, reason, flags);
1454b6ab94eSJosh Poimboeuf 
1464b6ab94eSJosh Poimboeuf 		err = 0;
1474b6ab94eSJosh Poimboeuf 		if (unset)
1484b6ab94eSJosh Poimboeuf 			noarg = true;
1494b6ab94eSJosh Poimboeuf 		if (opt->flags & PARSE_OPT_NOARG)
1504b6ab94eSJosh Poimboeuf 			noarg = true;
1514b6ab94eSJosh Poimboeuf 		if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
1524b6ab94eSJosh Poimboeuf 			noarg = true;
1534b6ab94eSJosh Poimboeuf 
1544b6ab94eSJosh Poimboeuf 		switch (opt->type) {
1554b6ab94eSJosh Poimboeuf 		case OPTION_BOOLEAN:
1564b6ab94eSJosh Poimboeuf 		case OPTION_INCR:
1574b6ab94eSJosh Poimboeuf 		case OPTION_BIT:
1584b6ab94eSJosh Poimboeuf 		case OPTION_SET_UINT:
1594b6ab94eSJosh Poimboeuf 		case OPTION_SET_PTR:
1604b6ab94eSJosh Poimboeuf 		case OPTION_END:
1614b6ab94eSJosh Poimboeuf 		case OPTION_ARGUMENT:
1624b6ab94eSJosh Poimboeuf 		case OPTION_GROUP:
1634b6ab94eSJosh Poimboeuf 			noarg = true;
1644b6ab94eSJosh Poimboeuf 			break;
1654b6ab94eSJosh Poimboeuf 		case OPTION_CALLBACK:
1664b6ab94eSJosh Poimboeuf 		case OPTION_STRING:
1674b6ab94eSJosh Poimboeuf 		case OPTION_INTEGER:
1684b6ab94eSJosh Poimboeuf 		case OPTION_UINTEGER:
1694b6ab94eSJosh Poimboeuf 		case OPTION_LONG:
1704ba8b3ebSArnaldo Carvalho de Melo 		case OPTION_ULONG:
1714b6ab94eSJosh Poimboeuf 		case OPTION_U64:
1724b6ab94eSJosh Poimboeuf 		default:
1734b6ab94eSJosh Poimboeuf 			break;
1744b6ab94eSJosh Poimboeuf 		}
1754b6ab94eSJosh Poimboeuf 
1764b6ab94eSJosh Poimboeuf 		if (!noarg)
1774b6ab94eSJosh Poimboeuf 			err = get_arg(p, opt, flags, NULL);
1784b6ab94eSJosh Poimboeuf 		if (err)
1794b6ab94eSJosh Poimboeuf 			return err;
1804b6ab94eSJosh Poimboeuf 
1814b6ab94eSJosh Poimboeuf 		optwarning(opt, reason, flags);
1824b6ab94eSJosh Poimboeuf 		return 0;
1834b6ab94eSJosh Poimboeuf 	}
1844b6ab94eSJosh Poimboeuf 
1854b6ab94eSJosh Poimboeuf 	switch (opt->type) {
1864b6ab94eSJosh Poimboeuf 	case OPTION_BIT:
1874b6ab94eSJosh Poimboeuf 		if (unset)
1884b6ab94eSJosh Poimboeuf 			*(int *)opt->value &= ~opt->defval;
1894b6ab94eSJosh Poimboeuf 		else
1904b6ab94eSJosh Poimboeuf 			*(int *)opt->value |= opt->defval;
1914b6ab94eSJosh Poimboeuf 		return 0;
1924b6ab94eSJosh Poimboeuf 
1934b6ab94eSJosh Poimboeuf 	case OPTION_BOOLEAN:
1944b6ab94eSJosh Poimboeuf 		*(bool *)opt->value = unset ? false : true;
1954b6ab94eSJosh Poimboeuf 		if (opt->set)
1964b6ab94eSJosh Poimboeuf 			*(bool *)opt->set = true;
1974b6ab94eSJosh Poimboeuf 		return 0;
1984b6ab94eSJosh Poimboeuf 
1994b6ab94eSJosh Poimboeuf 	case OPTION_INCR:
2004b6ab94eSJosh Poimboeuf 		*(int *)opt->value = unset ? 0 : *(int *)opt->value + 1;
2014b6ab94eSJosh Poimboeuf 		return 0;
2024b6ab94eSJosh Poimboeuf 
2034b6ab94eSJosh Poimboeuf 	case OPTION_SET_UINT:
2044b6ab94eSJosh Poimboeuf 		*(unsigned int *)opt->value = unset ? 0 : opt->defval;
2054b6ab94eSJosh Poimboeuf 		return 0;
2064b6ab94eSJosh Poimboeuf 
2074b6ab94eSJosh Poimboeuf 	case OPTION_SET_PTR:
2084b6ab94eSJosh Poimboeuf 		*(void **)opt->value = unset ? NULL : (void *)opt->defval;
2094b6ab94eSJosh Poimboeuf 		return 0;
2104b6ab94eSJosh Poimboeuf 
2114b6ab94eSJosh Poimboeuf 	case OPTION_STRING:
2124b6ab94eSJosh Poimboeuf 		err = 0;
2134b6ab94eSJosh Poimboeuf 		if (unset)
2144b6ab94eSJosh Poimboeuf 			*(const char **)opt->value = NULL;
2154b6ab94eSJosh Poimboeuf 		else if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
2164b6ab94eSJosh Poimboeuf 			*(const char **)opt->value = (const char *)opt->defval;
2174b6ab94eSJosh Poimboeuf 		else
2184b6ab94eSJosh Poimboeuf 			err = get_arg(p, opt, flags, (const char **)opt->value);
2194b6ab94eSJosh Poimboeuf 
220b66fb1daSJiri Olsa 		if (opt->set)
221b66fb1daSJiri Olsa 			*(bool *)opt->set = true;
222b66fb1daSJiri Olsa 
2234b6ab94eSJosh Poimboeuf 		/* PARSE_OPT_NOEMPTY: Allow NULL but disallow empty string. */
2244b6ab94eSJosh Poimboeuf 		if (opt->flags & PARSE_OPT_NOEMPTY) {
2254b6ab94eSJosh Poimboeuf 			const char *val = *(const char **)opt->value;
2264b6ab94eSJosh Poimboeuf 
2274b6ab94eSJosh Poimboeuf 			if (!val)
2284b6ab94eSJosh Poimboeuf 				return err;
2294b6ab94eSJosh Poimboeuf 
2304b6ab94eSJosh Poimboeuf 			/* Similar to unset if we are given an empty string. */
2314b6ab94eSJosh Poimboeuf 			if (val[0] == '\0') {
2324b6ab94eSJosh Poimboeuf 				*(const char **)opt->value = NULL;
2334b6ab94eSJosh Poimboeuf 				return 0;
2344b6ab94eSJosh Poimboeuf 			}
2354b6ab94eSJosh Poimboeuf 		}
2364b6ab94eSJosh Poimboeuf 
2374b6ab94eSJosh Poimboeuf 		return err;
2384b6ab94eSJosh Poimboeuf 
2394b6ab94eSJosh Poimboeuf 	case OPTION_CALLBACK:
240a2db71b9SRavi Bangoria 		if (opt->set)
241a2db71b9SRavi Bangoria 			*(bool *)opt->set = true;
242a2db71b9SRavi Bangoria 
2434b6ab94eSJosh Poimboeuf 		if (unset)
2444b6ab94eSJosh Poimboeuf 			return (*opt->callback)(opt, NULL, 1) ? (-1) : 0;
2454b6ab94eSJosh Poimboeuf 		if (opt->flags & PARSE_OPT_NOARG)
2464b6ab94eSJosh Poimboeuf 			return (*opt->callback)(opt, NULL, 0) ? (-1) : 0;
2474b6ab94eSJosh Poimboeuf 		if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
2484b6ab94eSJosh Poimboeuf 			return (*opt->callback)(opt, NULL, 0) ? (-1) : 0;
2494b6ab94eSJosh Poimboeuf 		if (get_arg(p, opt, flags, &arg))
2504b6ab94eSJosh Poimboeuf 			return -1;
2514b6ab94eSJosh Poimboeuf 		return (*opt->callback)(opt, arg, 0) ? (-1) : 0;
2524b6ab94eSJosh Poimboeuf 
2534b6ab94eSJosh Poimboeuf 	case OPTION_INTEGER:
2544b6ab94eSJosh Poimboeuf 		if (unset) {
2554b6ab94eSJosh Poimboeuf 			*(int *)opt->value = 0;
2564b6ab94eSJosh Poimboeuf 			return 0;
2574b6ab94eSJosh Poimboeuf 		}
2584b6ab94eSJosh Poimboeuf 		if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
2594b6ab94eSJosh Poimboeuf 			*(int *)opt->value = opt->defval;
2604b6ab94eSJosh Poimboeuf 			return 0;
2614b6ab94eSJosh Poimboeuf 		}
2624b6ab94eSJosh Poimboeuf 		if (get_arg(p, opt, flags, &arg))
2634b6ab94eSJosh Poimboeuf 			return -1;
2644b6ab94eSJosh Poimboeuf 		*(int *)opt->value = strtol(arg, (char **)&s, 10);
2654b6ab94eSJosh Poimboeuf 		if (*s)
2664b6ab94eSJosh Poimboeuf 			return opterror(opt, "expects a numerical value", flags);
2674b6ab94eSJosh Poimboeuf 		return 0;
2684b6ab94eSJosh Poimboeuf 
2694b6ab94eSJosh Poimboeuf 	case OPTION_UINTEGER:
2704b6ab94eSJosh Poimboeuf 		if (unset) {
2714b6ab94eSJosh Poimboeuf 			*(unsigned int *)opt->value = 0;
2724b6ab94eSJosh Poimboeuf 			return 0;
2734b6ab94eSJosh Poimboeuf 		}
2744b6ab94eSJosh Poimboeuf 		if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
2754b6ab94eSJosh Poimboeuf 			*(unsigned int *)opt->value = opt->defval;
2764b6ab94eSJosh Poimboeuf 			return 0;
2774b6ab94eSJosh Poimboeuf 		}
2784b6ab94eSJosh Poimboeuf 		if (get_arg(p, opt, flags, &arg))
2794b6ab94eSJosh Poimboeuf 			return -1;
280b9889716SArnaldo Carvalho de Melo 		if (arg[0] == '-')
281b9889716SArnaldo Carvalho de Melo 			return opterror(opt, "expects an unsigned numerical value", flags);
2824b6ab94eSJosh Poimboeuf 		*(unsigned int *)opt->value = strtol(arg, (char **)&s, 10);
2834b6ab94eSJosh Poimboeuf 		if (*s)
2844b6ab94eSJosh Poimboeuf 			return opterror(opt, "expects a numerical value", flags);
2854b6ab94eSJosh Poimboeuf 		return 0;
2864b6ab94eSJosh Poimboeuf 
2874b6ab94eSJosh Poimboeuf 	case OPTION_LONG:
2884b6ab94eSJosh Poimboeuf 		if (unset) {
2894b6ab94eSJosh Poimboeuf 			*(long *)opt->value = 0;
2904b6ab94eSJosh Poimboeuf 			return 0;
2914b6ab94eSJosh Poimboeuf 		}
2924b6ab94eSJosh Poimboeuf 		if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
2934b6ab94eSJosh Poimboeuf 			*(long *)opt->value = opt->defval;
2944b6ab94eSJosh Poimboeuf 			return 0;
2954b6ab94eSJosh Poimboeuf 		}
2964b6ab94eSJosh Poimboeuf 		if (get_arg(p, opt, flags, &arg))
2974b6ab94eSJosh Poimboeuf 			return -1;
2984b6ab94eSJosh Poimboeuf 		*(long *)opt->value = strtol(arg, (char **)&s, 10);
2994b6ab94eSJosh Poimboeuf 		if (*s)
3004b6ab94eSJosh Poimboeuf 			return opterror(opt, "expects a numerical value", flags);
3014b6ab94eSJosh Poimboeuf 		return 0;
3024b6ab94eSJosh Poimboeuf 
3034ba8b3ebSArnaldo Carvalho de Melo 	case OPTION_ULONG:
3044ba8b3ebSArnaldo Carvalho de Melo 		if (unset) {
3054ba8b3ebSArnaldo Carvalho de Melo 			*(unsigned long *)opt->value = 0;
3064ba8b3ebSArnaldo Carvalho de Melo 			return 0;
3074ba8b3ebSArnaldo Carvalho de Melo 		}
3084ba8b3ebSArnaldo Carvalho de Melo 		if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
3094ba8b3ebSArnaldo Carvalho de Melo 			*(unsigned long *)opt->value = opt->defval;
3104ba8b3ebSArnaldo Carvalho de Melo 			return 0;
3114ba8b3ebSArnaldo Carvalho de Melo 		}
3124ba8b3ebSArnaldo Carvalho de Melo 		if (get_arg(p, opt, flags, &arg))
3134ba8b3ebSArnaldo Carvalho de Melo 			return -1;
3144ba8b3ebSArnaldo Carvalho de Melo 		*(unsigned long *)opt->value = strtoul(arg, (char **)&s, 10);
3154ba8b3ebSArnaldo Carvalho de Melo 		if (*s)
3164ba8b3ebSArnaldo Carvalho de Melo 			return opterror(opt, "expects a numerical value", flags);
3174ba8b3ebSArnaldo Carvalho de Melo 		return 0;
3184ba8b3ebSArnaldo Carvalho de Melo 
3194b6ab94eSJosh Poimboeuf 	case OPTION_U64:
3204b6ab94eSJosh Poimboeuf 		if (unset) {
3214b6ab94eSJosh Poimboeuf 			*(u64 *)opt->value = 0;
3224b6ab94eSJosh Poimboeuf 			return 0;
3234b6ab94eSJosh Poimboeuf 		}
3244b6ab94eSJosh Poimboeuf 		if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
3254b6ab94eSJosh Poimboeuf 			*(u64 *)opt->value = opt->defval;
3264b6ab94eSJosh Poimboeuf 			return 0;
3274b6ab94eSJosh Poimboeuf 		}
3284b6ab94eSJosh Poimboeuf 		if (get_arg(p, opt, flags, &arg))
3294b6ab94eSJosh Poimboeuf 			return -1;
330b9889716SArnaldo Carvalho de Melo 		if (arg[0] == '-')
331b9889716SArnaldo Carvalho de Melo 			return opterror(opt, "expects an unsigned numerical value", flags);
3324b6ab94eSJosh Poimboeuf 		*(u64 *)opt->value = strtoull(arg, (char **)&s, 10);
3334b6ab94eSJosh Poimboeuf 		if (*s)
3344b6ab94eSJosh Poimboeuf 			return opterror(opt, "expects a numerical value", flags);
3354b6ab94eSJosh Poimboeuf 		return 0;
3364b6ab94eSJosh Poimboeuf 
3374b6ab94eSJosh Poimboeuf 	case OPTION_END:
3384b6ab94eSJosh Poimboeuf 	case OPTION_ARGUMENT:
3394b6ab94eSJosh Poimboeuf 	case OPTION_GROUP:
3404b6ab94eSJosh Poimboeuf 	default:
3414b6ab94eSJosh Poimboeuf 		die("should not happen, someone must be hit on the forehead");
3424b6ab94eSJosh Poimboeuf 	}
3434b6ab94eSJosh Poimboeuf }
3444b6ab94eSJosh Poimboeuf 
parse_short_opt(struct parse_opt_ctx_t * p,const struct option * options)3454b6ab94eSJosh Poimboeuf static int parse_short_opt(struct parse_opt_ctx_t *p, const struct option *options)
3464b6ab94eSJosh Poimboeuf {
347369a2478SNamhyung Kim retry:
3484b6ab94eSJosh Poimboeuf 	for (; options->type != OPTION_END; options++) {
3494b6ab94eSJosh Poimboeuf 		if (options->short_name == *p->opt) {
3504b6ab94eSJosh Poimboeuf 			p->opt = p->opt[1] ? p->opt + 1 : NULL;
3514b6ab94eSJosh Poimboeuf 			return get_value(p, options, OPT_SHORT);
3524b6ab94eSJosh Poimboeuf 		}
3534b6ab94eSJosh Poimboeuf 	}
354369a2478SNamhyung Kim 
355369a2478SNamhyung Kim 	if (options->parent) {
356369a2478SNamhyung Kim 		options = options->parent;
357369a2478SNamhyung Kim 		goto retry;
358369a2478SNamhyung Kim 	}
359369a2478SNamhyung Kim 
3604b6ab94eSJosh Poimboeuf 	return -2;
3614b6ab94eSJosh Poimboeuf }
3624b6ab94eSJosh Poimboeuf 
parse_long_opt(struct parse_opt_ctx_t * p,const char * arg,const struct option * options)3634b6ab94eSJosh Poimboeuf static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
3644b6ab94eSJosh Poimboeuf                           const struct option *options)
3654b6ab94eSJosh Poimboeuf {
3664b6ab94eSJosh Poimboeuf 	const char *arg_end = strchr(arg, '=');
3674b6ab94eSJosh Poimboeuf 	const struct option *abbrev_option = NULL, *ambiguous_option = NULL;
3684b6ab94eSJosh Poimboeuf 	int abbrev_flags = 0, ambiguous_flags = 0;
3694b6ab94eSJosh Poimboeuf 
3704b6ab94eSJosh Poimboeuf 	if (!arg_end)
3714b6ab94eSJosh Poimboeuf 		arg_end = arg + strlen(arg);
3724b6ab94eSJosh Poimboeuf 
373369a2478SNamhyung Kim retry:
3744b6ab94eSJosh Poimboeuf 	for (; options->type != OPTION_END; options++) {
3754b6ab94eSJosh Poimboeuf 		const char *rest;
3764b6ab94eSJosh Poimboeuf 		int flags = 0;
3774b6ab94eSJosh Poimboeuf 
3784b6ab94eSJosh Poimboeuf 		if (!options->long_name)
3794b6ab94eSJosh Poimboeuf 			continue;
3804b6ab94eSJosh Poimboeuf 
3814b6ab94eSJosh Poimboeuf 		rest = skip_prefix(arg, options->long_name);
3824b6ab94eSJosh Poimboeuf 		if (options->type == OPTION_ARGUMENT) {
3834b6ab94eSJosh Poimboeuf 			if (!rest)
3844b6ab94eSJosh Poimboeuf 				continue;
3854b6ab94eSJosh Poimboeuf 			if (*rest == '=')
3864b6ab94eSJosh Poimboeuf 				return opterror(options, "takes no value", flags);
3874b6ab94eSJosh Poimboeuf 			if (*rest)
3884b6ab94eSJosh Poimboeuf 				continue;
3894b6ab94eSJosh Poimboeuf 			p->out[p->cpidx++] = arg - 2;
3904b6ab94eSJosh Poimboeuf 			return 0;
3914b6ab94eSJosh Poimboeuf 		}
3924b6ab94eSJosh Poimboeuf 		if (!rest) {
3938e99b6d4SArnaldo Carvalho de Melo 			if (strstarts(options->long_name, "no-")) {
3944b6ab94eSJosh Poimboeuf 				/*
3954b6ab94eSJosh Poimboeuf 				 * The long name itself starts with "no-", so
3964b6ab94eSJosh Poimboeuf 				 * accept the option without "no-" so that users
3974b6ab94eSJosh Poimboeuf 				 * do not have to enter "no-no-" to get the
3984b6ab94eSJosh Poimboeuf 				 * negation.
3994b6ab94eSJosh Poimboeuf 				 */
4004b6ab94eSJosh Poimboeuf 				rest = skip_prefix(arg, options->long_name + 3);
4014b6ab94eSJosh Poimboeuf 				if (rest) {
4024b6ab94eSJosh Poimboeuf 					flags |= OPT_UNSET;
4034b6ab94eSJosh Poimboeuf 					goto match;
4044b6ab94eSJosh Poimboeuf 				}
4054b6ab94eSJosh Poimboeuf 				/* Abbreviated case */
4068e99b6d4SArnaldo Carvalho de Melo 				if (strstarts(options->long_name + 3, arg)) {
4074b6ab94eSJosh Poimboeuf 					flags |= OPT_UNSET;
4084b6ab94eSJosh Poimboeuf 					goto is_abbreviated;
4094b6ab94eSJosh Poimboeuf 				}
4104b6ab94eSJosh Poimboeuf 			}
4114b6ab94eSJosh Poimboeuf 			/* abbreviated? */
4124b6ab94eSJosh Poimboeuf 			if (!strncmp(options->long_name, arg, arg_end - arg)) {
4134b6ab94eSJosh Poimboeuf is_abbreviated:
4144b6ab94eSJosh Poimboeuf 				if (abbrev_option) {
4154b6ab94eSJosh Poimboeuf 					/*
4164b6ab94eSJosh Poimboeuf 					 * If this is abbreviated, it is
4174b6ab94eSJosh Poimboeuf 					 * ambiguous. So when there is no
4184b6ab94eSJosh Poimboeuf 					 * exact match later, we need to
4194b6ab94eSJosh Poimboeuf 					 * error out.
4204b6ab94eSJosh Poimboeuf 					 */
4214b6ab94eSJosh Poimboeuf 					ambiguous_option = abbrev_option;
4224b6ab94eSJosh Poimboeuf 					ambiguous_flags = abbrev_flags;
4234b6ab94eSJosh Poimboeuf 				}
4244b6ab94eSJosh Poimboeuf 				if (!(flags & OPT_UNSET) && *arg_end)
4254b6ab94eSJosh Poimboeuf 					p->opt = arg_end + 1;
4264b6ab94eSJosh Poimboeuf 				abbrev_option = options;
4274b6ab94eSJosh Poimboeuf 				abbrev_flags = flags;
4284b6ab94eSJosh Poimboeuf 				continue;
4294b6ab94eSJosh Poimboeuf 			}
4304b6ab94eSJosh Poimboeuf 			/* negated and abbreviated very much? */
4318e99b6d4SArnaldo Carvalho de Melo 			if (strstarts("no-", arg)) {
4324b6ab94eSJosh Poimboeuf 				flags |= OPT_UNSET;
4334b6ab94eSJosh Poimboeuf 				goto is_abbreviated;
4344b6ab94eSJosh Poimboeuf 			}
4354b6ab94eSJosh Poimboeuf 			/* negated? */
4364b6ab94eSJosh Poimboeuf 			if (strncmp(arg, "no-", 3))
4374b6ab94eSJosh Poimboeuf 				continue;
4384b6ab94eSJosh Poimboeuf 			flags |= OPT_UNSET;
4394b6ab94eSJosh Poimboeuf 			rest = skip_prefix(arg + 3, options->long_name);
4404b6ab94eSJosh Poimboeuf 			/* abbreviated and negated? */
4418e99b6d4SArnaldo Carvalho de Melo 			if (!rest && strstarts(options->long_name, arg + 3))
4424b6ab94eSJosh Poimboeuf 				goto is_abbreviated;
4434b6ab94eSJosh Poimboeuf 			if (!rest)
4444b6ab94eSJosh Poimboeuf 				continue;
4454b6ab94eSJosh Poimboeuf 		}
4464b6ab94eSJosh Poimboeuf match:
4474b6ab94eSJosh Poimboeuf 		if (*rest) {
4484b6ab94eSJosh Poimboeuf 			if (*rest != '=')
4494b6ab94eSJosh Poimboeuf 				continue;
4504b6ab94eSJosh Poimboeuf 			p->opt = rest + 1;
4514b6ab94eSJosh Poimboeuf 		}
4524b6ab94eSJosh Poimboeuf 		return get_value(p, options, flags);
4534b6ab94eSJosh Poimboeuf 	}
4544b6ab94eSJosh Poimboeuf 
4554b6ab94eSJosh Poimboeuf 	if (ambiguous_option) {
4564b6ab94eSJosh Poimboeuf 		 fprintf(stderr,
45766f5a077SRavi Bangoria 			 " Error: Ambiguous option: %s (could be --%s%s or --%s%s)\n",
4584b6ab94eSJosh Poimboeuf 			 arg,
4594b6ab94eSJosh Poimboeuf 			 (ambiguous_flags & OPT_UNSET) ?  "no-" : "",
4604b6ab94eSJosh Poimboeuf 			 ambiguous_option->long_name,
4614b6ab94eSJosh Poimboeuf 			 (abbrev_flags & OPT_UNSET) ?  "no-" : "",
4624b6ab94eSJosh Poimboeuf 			 abbrev_option->long_name);
4634b6ab94eSJosh Poimboeuf 		 return -1;
4644b6ab94eSJosh Poimboeuf 	}
4654b6ab94eSJosh Poimboeuf 	if (abbrev_option)
4664b6ab94eSJosh Poimboeuf 		return get_value(p, abbrev_option, abbrev_flags);
467369a2478SNamhyung Kim 
468369a2478SNamhyung Kim 	if (options->parent) {
469369a2478SNamhyung Kim 		options = options->parent;
470369a2478SNamhyung Kim 		goto retry;
471369a2478SNamhyung Kim 	}
472369a2478SNamhyung Kim 
4734b6ab94eSJosh Poimboeuf 	return -2;
4744b6ab94eSJosh Poimboeuf }
4754b6ab94eSJosh Poimboeuf 
check_typos(const char * arg,const struct option * options)4764b6ab94eSJosh Poimboeuf static void check_typos(const char *arg, const struct option *options)
4774b6ab94eSJosh Poimboeuf {
4784b6ab94eSJosh Poimboeuf 	if (strlen(arg) < 3)
4794b6ab94eSJosh Poimboeuf 		return;
4804b6ab94eSJosh Poimboeuf 
4818e99b6d4SArnaldo Carvalho de Melo 	if (strstarts(arg, "no-")) {
48266f5a077SRavi Bangoria 		fprintf(stderr, " Error: did you mean `--%s` (with two dashes ?)\n", arg);
4834b6ab94eSJosh Poimboeuf 		exit(129);
4844b6ab94eSJosh Poimboeuf 	}
4854b6ab94eSJosh Poimboeuf 
4864b6ab94eSJosh Poimboeuf 	for (; options->type != OPTION_END; options++) {
4874b6ab94eSJosh Poimboeuf 		if (!options->long_name)
4884b6ab94eSJosh Poimboeuf 			continue;
4898e99b6d4SArnaldo Carvalho de Melo 		if (strstarts(options->long_name, arg)) {
49066f5a077SRavi Bangoria 			fprintf(stderr, " Error: did you mean `--%s` (with two dashes ?)\n", arg);
4914b6ab94eSJosh Poimboeuf 			exit(129);
4924b6ab94eSJosh Poimboeuf 		}
4934b6ab94eSJosh Poimboeuf 	}
4944b6ab94eSJosh Poimboeuf }
4954b6ab94eSJosh Poimboeuf 
parse_options_start(struct parse_opt_ctx_t * ctx,int argc,const char ** argv,int flags)4964b6ab94eSJosh Poimboeuf static void parse_options_start(struct parse_opt_ctx_t *ctx,
4974b6ab94eSJosh Poimboeuf 				int argc, const char **argv, int flags)
4984b6ab94eSJosh Poimboeuf {
4994b6ab94eSJosh Poimboeuf 	memset(ctx, 0, sizeof(*ctx));
5004b6ab94eSJosh Poimboeuf 	ctx->argc = argc - 1;
5014b6ab94eSJosh Poimboeuf 	ctx->argv = argv + 1;
5024b6ab94eSJosh Poimboeuf 	ctx->out  = argv;
5034b6ab94eSJosh Poimboeuf 	ctx->cpidx = ((flags & PARSE_OPT_KEEP_ARGV0) != 0);
5044b6ab94eSJosh Poimboeuf 	ctx->flags = flags;
5054b6ab94eSJosh Poimboeuf 	if ((flags & PARSE_OPT_KEEP_UNKNOWN) &&
5064b6ab94eSJosh Poimboeuf 	    (flags & PARSE_OPT_STOP_AT_NON_OPTION))
5074b6ab94eSJosh Poimboeuf 		die("STOP_AT_NON_OPTION and KEEP_UNKNOWN don't go together");
5084b6ab94eSJosh Poimboeuf }
5094b6ab94eSJosh Poimboeuf 
5104b6ab94eSJosh Poimboeuf static int usage_with_options_internal(const char * const *,
5114b6ab94eSJosh Poimboeuf 				       const struct option *, int,
5124b6ab94eSJosh Poimboeuf 				       struct parse_opt_ctx_t *);
5134b6ab94eSJosh Poimboeuf 
parse_options_step(struct parse_opt_ctx_t * ctx,const struct option * options,const char * const usagestr[])5144b6ab94eSJosh Poimboeuf static int parse_options_step(struct parse_opt_ctx_t *ctx,
5154b6ab94eSJosh Poimboeuf 			      const struct option *options,
5164b6ab94eSJosh Poimboeuf 			      const char * const usagestr[])
5174b6ab94eSJosh Poimboeuf {
5184b6ab94eSJosh Poimboeuf 	int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP);
5194b6ab94eSJosh Poimboeuf 	int excl_short_opt = 1;
5204b6ab94eSJosh Poimboeuf 	const char *arg;
5214b6ab94eSJosh Poimboeuf 
5224b6ab94eSJosh Poimboeuf 	/* we must reset ->opt, unknown short option leave it dangling */
5234b6ab94eSJosh Poimboeuf 	ctx->opt = NULL;
5244b6ab94eSJosh Poimboeuf 
5254b6ab94eSJosh Poimboeuf 	for (; ctx->argc; ctx->argc--, ctx->argv++) {
5264b6ab94eSJosh Poimboeuf 		arg = ctx->argv[0];
5274b6ab94eSJosh Poimboeuf 		if (*arg != '-' || !arg[1]) {
5284b6ab94eSJosh Poimboeuf 			if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION)
5294b6ab94eSJosh Poimboeuf 				break;
5304b6ab94eSJosh Poimboeuf 			ctx->out[ctx->cpidx++] = ctx->argv[0];
5314b6ab94eSJosh Poimboeuf 			continue;
5324b6ab94eSJosh Poimboeuf 		}
5334b6ab94eSJosh Poimboeuf 
5344b6ab94eSJosh Poimboeuf 		if (arg[1] != '-') {
5354b6ab94eSJosh Poimboeuf 			ctx->opt = ++arg;
5364b6ab94eSJosh Poimboeuf 			if (internal_help && *ctx->opt == 'h') {
5374b6ab94eSJosh Poimboeuf 				return usage_with_options_internal(usagestr, options, 0, ctx);
5384b6ab94eSJosh Poimboeuf 			}
5394b6ab94eSJosh Poimboeuf 			switch (parse_short_opt(ctx, options)) {
5404b6ab94eSJosh Poimboeuf 			case -1:
5414b6ab94eSJosh Poimboeuf 				return parse_options_usage(usagestr, options, arg, 1);
5424b6ab94eSJosh Poimboeuf 			case -2:
5434b6ab94eSJosh Poimboeuf 				goto unknown;
5444b6ab94eSJosh Poimboeuf 			case -3:
5454b6ab94eSJosh Poimboeuf 				goto exclusive;
5464b6ab94eSJosh Poimboeuf 			default:
5474b6ab94eSJosh Poimboeuf 				break;
5484b6ab94eSJosh Poimboeuf 			}
5494b6ab94eSJosh Poimboeuf 			if (ctx->opt)
5504b6ab94eSJosh Poimboeuf 				check_typos(arg, options);
5514b6ab94eSJosh Poimboeuf 			while (ctx->opt) {
5524b6ab94eSJosh Poimboeuf 				if (internal_help && *ctx->opt == 'h')
5534b6ab94eSJosh Poimboeuf 					return usage_with_options_internal(usagestr, options, 0, ctx);
5544b6ab94eSJosh Poimboeuf 				arg = ctx->opt;
5554b6ab94eSJosh Poimboeuf 				switch (parse_short_opt(ctx, options)) {
5564b6ab94eSJosh Poimboeuf 				case -1:
5574b6ab94eSJosh Poimboeuf 					return parse_options_usage(usagestr, options, arg, 1);
5584b6ab94eSJosh Poimboeuf 				case -2:
5594b6ab94eSJosh Poimboeuf 					/* fake a short option thing to hide the fact that we may have
5604b6ab94eSJosh Poimboeuf 					 * started to parse aggregated stuff
5614b6ab94eSJosh Poimboeuf 					 *
5624b6ab94eSJosh Poimboeuf 					 * This is leaky, too bad.
5634b6ab94eSJosh Poimboeuf 					 */
5644b6ab94eSJosh Poimboeuf 					ctx->argv[0] = strdup(ctx->opt - 1);
5654b6ab94eSJosh Poimboeuf 					*(char *)ctx->argv[0] = '-';
5664b6ab94eSJosh Poimboeuf 					goto unknown;
5674b6ab94eSJosh Poimboeuf 				case -3:
5684b6ab94eSJosh Poimboeuf 					goto exclusive;
5694b6ab94eSJosh Poimboeuf 				default:
5704b6ab94eSJosh Poimboeuf 					break;
5714b6ab94eSJosh Poimboeuf 				}
5724b6ab94eSJosh Poimboeuf 			}
5734b6ab94eSJosh Poimboeuf 			continue;
5744b6ab94eSJosh Poimboeuf 		}
5754b6ab94eSJosh Poimboeuf 
5764b6ab94eSJosh Poimboeuf 		if (!arg[2]) { /* "--" */
5774b6ab94eSJosh Poimboeuf 			if (!(ctx->flags & PARSE_OPT_KEEP_DASHDASH)) {
5784b6ab94eSJosh Poimboeuf 				ctx->argc--;
5794b6ab94eSJosh Poimboeuf 				ctx->argv++;
5804b6ab94eSJosh Poimboeuf 			}
5814b6ab94eSJosh Poimboeuf 			break;
5824b6ab94eSJosh Poimboeuf 		}
5834b6ab94eSJosh Poimboeuf 
5844b6ab94eSJosh Poimboeuf 		arg += 2;
5854b6ab94eSJosh Poimboeuf 		if (internal_help && !strcmp(arg, "help-all"))
5864b6ab94eSJosh Poimboeuf 			return usage_with_options_internal(usagestr, options, 1, ctx);
5874b6ab94eSJosh Poimboeuf 		if (internal_help && !strcmp(arg, "help"))
5884b6ab94eSJosh Poimboeuf 			return usage_with_options_internal(usagestr, options, 0, ctx);
5894b6ab94eSJosh Poimboeuf 		if (!strcmp(arg, "list-opts"))
5904b6ab94eSJosh Poimboeuf 			return PARSE_OPT_LIST_OPTS;
5914b6ab94eSJosh Poimboeuf 		if (!strcmp(arg, "list-cmds"))
5924b6ab94eSJosh Poimboeuf 			return PARSE_OPT_LIST_SUBCMDS;
5934b6ab94eSJosh Poimboeuf 		switch (parse_long_opt(ctx, arg, options)) {
5944b6ab94eSJosh Poimboeuf 		case -1:
5954b6ab94eSJosh Poimboeuf 			return parse_options_usage(usagestr, options, arg, 0);
5964b6ab94eSJosh Poimboeuf 		case -2:
5974b6ab94eSJosh Poimboeuf 			goto unknown;
5984b6ab94eSJosh Poimboeuf 		case -3:
5994b6ab94eSJosh Poimboeuf 			excl_short_opt = 0;
6004b6ab94eSJosh Poimboeuf 			goto exclusive;
6014b6ab94eSJosh Poimboeuf 		default:
6024b6ab94eSJosh Poimboeuf 			break;
6034b6ab94eSJosh Poimboeuf 		}
6044b6ab94eSJosh Poimboeuf 		continue;
6054b6ab94eSJosh Poimboeuf unknown:
6064b6ab94eSJosh Poimboeuf 		if (!(ctx->flags & PARSE_OPT_KEEP_UNKNOWN))
6074b6ab94eSJosh Poimboeuf 			return PARSE_OPT_UNKNOWN;
6084b6ab94eSJosh Poimboeuf 		ctx->out[ctx->cpidx++] = ctx->argv[0];
6094b6ab94eSJosh Poimboeuf 		ctx->opt = NULL;
6104b6ab94eSJosh Poimboeuf 	}
6114b6ab94eSJosh Poimboeuf 	return PARSE_OPT_DONE;
6124b6ab94eSJosh Poimboeuf 
6134b6ab94eSJosh Poimboeuf exclusive:
6144b6ab94eSJosh Poimboeuf 	parse_options_usage(usagestr, options, arg, excl_short_opt);
6154b6ab94eSJosh Poimboeuf 	if ((excl_short_opt && ctx->excl_opt->short_name) ||
6164b6ab94eSJosh Poimboeuf 	    ctx->excl_opt->long_name == NULL) {
6174b6ab94eSJosh Poimboeuf 		char opt = ctx->excl_opt->short_name;
6184b6ab94eSJosh Poimboeuf 		parse_options_usage(NULL, options, &opt, 1);
6194b6ab94eSJosh Poimboeuf 	} else {
6204b6ab94eSJosh Poimboeuf 		parse_options_usage(NULL, options, ctx->excl_opt->long_name, 0);
6214b6ab94eSJosh Poimboeuf 	}
6224b6ab94eSJosh Poimboeuf 	return PARSE_OPT_HELP;
6234b6ab94eSJosh Poimboeuf }
6244b6ab94eSJosh Poimboeuf 
parse_options_end(struct parse_opt_ctx_t * ctx)6254b6ab94eSJosh Poimboeuf static int parse_options_end(struct parse_opt_ctx_t *ctx)
6264b6ab94eSJosh Poimboeuf {
6274b6ab94eSJosh Poimboeuf 	memmove(ctx->out + ctx->cpidx, ctx->argv, ctx->argc * sizeof(*ctx->out));
6284b6ab94eSJosh Poimboeuf 	ctx->out[ctx->cpidx + ctx->argc] = NULL;
6294b6ab94eSJosh Poimboeuf 	return ctx->cpidx + ctx->argc;
6304b6ab94eSJosh Poimboeuf }
6314b6ab94eSJosh Poimboeuf 
parse_options_subcommand(int argc,const char ** argv,const struct option * options,const char * const subcommands[],const char * usagestr[],int flags)6324b6ab94eSJosh Poimboeuf int parse_options_subcommand(int argc, const char **argv, const struct option *options,
6334b6ab94eSJosh Poimboeuf 			const char *const subcommands[], const char *usagestr[], int flags)
6344b6ab94eSJosh Poimboeuf {
6354b6ab94eSJosh Poimboeuf 	struct parse_opt_ctx_t ctx;
6364b6ab94eSJosh Poimboeuf 
6374b6ab94eSJosh Poimboeuf 	/* build usage string if it's not provided */
6384b6ab94eSJosh Poimboeuf 	if (subcommands && !usagestr[0]) {
6391a5efc9eSAditya Gupta 		char *buf = NULL;
6401a5efc9eSAditya Gupta 
6414b6ab94eSJosh Poimboeuf 		astrcatf(&buf, "%s %s [<options>] {", subcmd_config.exec_name, argv[0]);
6424b6ab94eSJosh Poimboeuf 
6434b6ab94eSJosh Poimboeuf 		for (int i = 0; subcommands[i]; i++) {
6444b6ab94eSJosh Poimboeuf 			if (i)
6454b6ab94eSJosh Poimboeuf 				astrcat(&buf, "|");
6464b6ab94eSJosh Poimboeuf 			astrcat(&buf, subcommands[i]);
6474b6ab94eSJosh Poimboeuf 		}
6484b6ab94eSJosh Poimboeuf 		astrcat(&buf, "}");
6494b6ab94eSJosh Poimboeuf 
6504b6ab94eSJosh Poimboeuf 		usagestr[0] = buf;
6514b6ab94eSJosh Poimboeuf 	}
6524b6ab94eSJosh Poimboeuf 
6534b6ab94eSJosh Poimboeuf 	parse_options_start(&ctx, argc, argv, flags);
6544b6ab94eSJosh Poimboeuf 	switch (parse_options_step(&ctx, options, usagestr)) {
6554b6ab94eSJosh Poimboeuf 	case PARSE_OPT_HELP:
6564b6ab94eSJosh Poimboeuf 		exit(129);
6574b6ab94eSJosh Poimboeuf 	case PARSE_OPT_DONE:
6584b6ab94eSJosh Poimboeuf 		break;
6594b6ab94eSJosh Poimboeuf 	case PARSE_OPT_LIST_OPTS:
6604b6ab94eSJosh Poimboeuf 		while (options->type != OPTION_END) {
6614b6ab94eSJosh Poimboeuf 			if (options->long_name)
6624b6ab94eSJosh Poimboeuf 				printf("--%s ", options->long_name);
6634b6ab94eSJosh Poimboeuf 			options++;
6644b6ab94eSJosh Poimboeuf 		}
6654b6ab94eSJosh Poimboeuf 		putchar('\n');
6664b6ab94eSJosh Poimboeuf 		exit(130);
6674b6ab94eSJosh Poimboeuf 	case PARSE_OPT_LIST_SUBCMDS:
6684b6ab94eSJosh Poimboeuf 		if (subcommands) {
6694b6ab94eSJosh Poimboeuf 			for (int i = 0; subcommands[i]; i++)
6704b6ab94eSJosh Poimboeuf 				printf("%s ", subcommands[i]);
6714b6ab94eSJosh Poimboeuf 		}
6724b6ab94eSJosh Poimboeuf 		putchar('\n');
6734b6ab94eSJosh Poimboeuf 		exit(130);
6744b6ab94eSJosh Poimboeuf 	default: /* PARSE_OPT_UNKNOWN */
6754b6ab94eSJosh Poimboeuf 		if (ctx.argv[0][1] == '-')
6764b6ab94eSJosh Poimboeuf 			astrcatf(&error_buf, "unknown option `%s'",
6774b6ab94eSJosh Poimboeuf 				 ctx.argv[0] + 2);
6784b6ab94eSJosh Poimboeuf 		else
6794b6ab94eSJosh Poimboeuf 			astrcatf(&error_buf, "unknown switch `%c'", *ctx.opt);
6804b6ab94eSJosh Poimboeuf 		usage_with_options(usagestr, options);
6814b6ab94eSJosh Poimboeuf 	}
6821a5efc9eSAditya Gupta 
6834b6ab94eSJosh Poimboeuf 	return parse_options_end(&ctx);
6844b6ab94eSJosh Poimboeuf }
6854b6ab94eSJosh Poimboeuf 
parse_options(int argc,const char ** argv,const struct option * options,const char * const usagestr[],int flags)6864b6ab94eSJosh Poimboeuf int parse_options(int argc, const char **argv, const struct option *options,
6874b6ab94eSJosh Poimboeuf 		  const char * const usagestr[], int flags)
6884b6ab94eSJosh Poimboeuf {
6894b6ab94eSJosh Poimboeuf 	return parse_options_subcommand(argc, argv, options, NULL,
6904b6ab94eSJosh Poimboeuf 					(const char **) usagestr, flags);
6914b6ab94eSJosh Poimboeuf }
6924b6ab94eSJosh Poimboeuf 
6934b6ab94eSJosh Poimboeuf #define USAGE_OPTS_WIDTH 24
6944b6ab94eSJosh Poimboeuf #define USAGE_GAP         2
6954b6ab94eSJosh Poimboeuf 
print_option_help(const struct option * opts,int full)6964b6ab94eSJosh Poimboeuf static void print_option_help(const struct option *opts, int full)
6974b6ab94eSJosh Poimboeuf {
6984b6ab94eSJosh Poimboeuf 	size_t pos;
6994b6ab94eSJosh Poimboeuf 	int pad;
7004b6ab94eSJosh Poimboeuf 
7014b6ab94eSJosh Poimboeuf 	if (opts->type == OPTION_GROUP) {
7024b6ab94eSJosh Poimboeuf 		fputc('\n', stderr);
7034b6ab94eSJosh Poimboeuf 		if (*opts->help)
7044b6ab94eSJosh Poimboeuf 			fprintf(stderr, "%s\n", opts->help);
7054b6ab94eSJosh Poimboeuf 		return;
7064b6ab94eSJosh Poimboeuf 	}
7074b6ab94eSJosh Poimboeuf 	if (!full && (opts->flags & PARSE_OPT_HIDDEN))
7084b6ab94eSJosh Poimboeuf 		return;
7094b6ab94eSJosh Poimboeuf 	if (opts->flags & PARSE_OPT_DISABLED)
7104b6ab94eSJosh Poimboeuf 		return;
7114b6ab94eSJosh Poimboeuf 
7124b6ab94eSJosh Poimboeuf 	pos = fprintf(stderr, "    ");
7134b6ab94eSJosh Poimboeuf 	if (opts->short_name)
7144b6ab94eSJosh Poimboeuf 		pos += fprintf(stderr, "-%c", opts->short_name);
7154b6ab94eSJosh Poimboeuf 	else
7164b6ab94eSJosh Poimboeuf 		pos += fprintf(stderr, "    ");
7174b6ab94eSJosh Poimboeuf 
7184b6ab94eSJosh Poimboeuf 	if (opts->long_name && opts->short_name)
7194b6ab94eSJosh Poimboeuf 		pos += fprintf(stderr, ", ");
7204b6ab94eSJosh Poimboeuf 	if (opts->long_name)
7214b6ab94eSJosh Poimboeuf 		pos += fprintf(stderr, "--%s", opts->long_name);
7224b6ab94eSJosh Poimboeuf 
7234b6ab94eSJosh Poimboeuf 	switch (opts->type) {
7244b6ab94eSJosh Poimboeuf 	case OPTION_ARGUMENT:
7254b6ab94eSJosh Poimboeuf 		break;
7264b6ab94eSJosh Poimboeuf 	case OPTION_LONG:
7274ba8b3ebSArnaldo Carvalho de Melo 	case OPTION_ULONG:
7284b6ab94eSJosh Poimboeuf 	case OPTION_U64:
7294b6ab94eSJosh Poimboeuf 	case OPTION_INTEGER:
7304b6ab94eSJosh Poimboeuf 	case OPTION_UINTEGER:
7314b6ab94eSJosh Poimboeuf 		if (opts->flags & PARSE_OPT_OPTARG)
7324b6ab94eSJosh Poimboeuf 			if (opts->long_name)
7334b6ab94eSJosh Poimboeuf 				pos += fprintf(stderr, "[=<n>]");
7344b6ab94eSJosh Poimboeuf 			else
7354b6ab94eSJosh Poimboeuf 				pos += fprintf(stderr, "[<n>]");
7364b6ab94eSJosh Poimboeuf 		else
7374b6ab94eSJosh Poimboeuf 			pos += fprintf(stderr, " <n>");
7384b6ab94eSJosh Poimboeuf 		break;
7394b6ab94eSJosh Poimboeuf 	case OPTION_CALLBACK:
7404b6ab94eSJosh Poimboeuf 		if (opts->flags & PARSE_OPT_NOARG)
7414b6ab94eSJosh Poimboeuf 			break;
7424b6ab94eSJosh Poimboeuf 		/* FALLTHROUGH */
7434b6ab94eSJosh Poimboeuf 	case OPTION_STRING:
7444b6ab94eSJosh Poimboeuf 		if (opts->argh) {
7454b6ab94eSJosh Poimboeuf 			if (opts->flags & PARSE_OPT_OPTARG)
7464b6ab94eSJosh Poimboeuf 				if (opts->long_name)
7474b6ab94eSJosh Poimboeuf 					pos += fprintf(stderr, "[=<%s>]", opts->argh);
7484b6ab94eSJosh Poimboeuf 				else
7494b6ab94eSJosh Poimboeuf 					pos += fprintf(stderr, "[<%s>]", opts->argh);
7504b6ab94eSJosh Poimboeuf 			else
7514b6ab94eSJosh Poimboeuf 				pos += fprintf(stderr, " <%s>", opts->argh);
7524b6ab94eSJosh Poimboeuf 		} else {
7534b6ab94eSJosh Poimboeuf 			if (opts->flags & PARSE_OPT_OPTARG)
7544b6ab94eSJosh Poimboeuf 				if (opts->long_name)
7554b6ab94eSJosh Poimboeuf 					pos += fprintf(stderr, "[=...]");
7564b6ab94eSJosh Poimboeuf 				else
7574b6ab94eSJosh Poimboeuf 					pos += fprintf(stderr, "[...]");
7584b6ab94eSJosh Poimboeuf 			else
7594b6ab94eSJosh Poimboeuf 				pos += fprintf(stderr, " ...");
7604b6ab94eSJosh Poimboeuf 		}
7614b6ab94eSJosh Poimboeuf 		break;
7624b6ab94eSJosh Poimboeuf 	default: /* OPTION_{BIT,BOOLEAN,SET_UINT,SET_PTR} */
7634b6ab94eSJosh Poimboeuf 	case OPTION_END:
7644b6ab94eSJosh Poimboeuf 	case OPTION_GROUP:
7654b6ab94eSJosh Poimboeuf 	case OPTION_BIT:
7664b6ab94eSJosh Poimboeuf 	case OPTION_BOOLEAN:
7674b6ab94eSJosh Poimboeuf 	case OPTION_INCR:
7684b6ab94eSJosh Poimboeuf 	case OPTION_SET_UINT:
7694b6ab94eSJosh Poimboeuf 	case OPTION_SET_PTR:
7704b6ab94eSJosh Poimboeuf 		break;
7714b6ab94eSJosh Poimboeuf 	}
7724b6ab94eSJosh Poimboeuf 
7734b6ab94eSJosh Poimboeuf 	if (pos <= USAGE_OPTS_WIDTH)
7744b6ab94eSJosh Poimboeuf 		pad = USAGE_OPTS_WIDTH - pos;
7754b6ab94eSJosh Poimboeuf 	else {
7764b6ab94eSJosh Poimboeuf 		fputc('\n', stderr);
7774b6ab94eSJosh Poimboeuf 		pad = USAGE_OPTS_WIDTH;
7784b6ab94eSJosh Poimboeuf 	}
7794b6ab94eSJosh Poimboeuf 	fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help);
7804b6ab94eSJosh Poimboeuf 	if (opts->flags & PARSE_OPT_NOBUILD)
7814b6ab94eSJosh Poimboeuf 		fprintf(stderr, "%*s(not built-in because %s)\n",
7824b6ab94eSJosh Poimboeuf 			USAGE_OPTS_WIDTH + USAGE_GAP, "",
7834b6ab94eSJosh Poimboeuf 			opts->build_opt);
7844b6ab94eSJosh Poimboeuf }
7854b6ab94eSJosh Poimboeuf 
option__cmp(const void * va,const void * vb)7864b6ab94eSJosh Poimboeuf static int option__cmp(const void *va, const void *vb)
7874b6ab94eSJosh Poimboeuf {
7884b6ab94eSJosh Poimboeuf 	const struct option *a = va, *b = vb;
7894b6ab94eSJosh Poimboeuf 	int sa = tolower(a->short_name), sb = tolower(b->short_name), ret;
7904b6ab94eSJosh Poimboeuf 
7914b6ab94eSJosh Poimboeuf 	if (sa == 0)
7924b6ab94eSJosh Poimboeuf 		sa = 'z' + 1;
7934b6ab94eSJosh Poimboeuf 	if (sb == 0)
7944b6ab94eSJosh Poimboeuf 		sb = 'z' + 1;
7954b6ab94eSJosh Poimboeuf 
7964b6ab94eSJosh Poimboeuf 	ret = sa - sb;
7974b6ab94eSJosh Poimboeuf 
7984b6ab94eSJosh Poimboeuf 	if (ret == 0) {
7994b6ab94eSJosh Poimboeuf 		const char *la = a->long_name ?: "",
8004b6ab94eSJosh Poimboeuf 			   *lb = b->long_name ?: "";
8014b6ab94eSJosh Poimboeuf 		ret = strcmp(la, lb);
8024b6ab94eSJosh Poimboeuf 	}
8034b6ab94eSJosh Poimboeuf 
8044b6ab94eSJosh Poimboeuf 	return ret;
8054b6ab94eSJosh Poimboeuf }
8064b6ab94eSJosh Poimboeuf 
options__order(const struct option * opts)8074b6ab94eSJosh Poimboeuf static struct option *options__order(const struct option *opts)
8084b6ab94eSJosh Poimboeuf {
809ea558c86SNamhyung Kim 	int nr_opts = 0, nr_group = 0, nr_parent = 0, len;
810*7a4ffec9SEder Zulian 	const struct option *o = NULL, *p = opts;
811ea558c86SNamhyung Kim 	struct option *opt, *ordered = NULL, *group;
8124b6ab94eSJosh Poimboeuf 
813ea558c86SNamhyung Kim 	/* flatten the options that have parents */
814ea558c86SNamhyung Kim 	for (p = opts; p != NULL; p = o->parent) {
815ea558c86SNamhyung Kim 		for (o = p; o->type != OPTION_END; o++)
8164b6ab94eSJosh Poimboeuf 			++nr_opts;
8174b6ab94eSJosh Poimboeuf 
818ea558c86SNamhyung Kim 		/*
819ea558c86SNamhyung Kim 		 * the length is given by the number of options plus a null
820ea558c86SNamhyung Kim 		 * terminator for the last loop iteration.
821ea558c86SNamhyung Kim 		 */
822ea558c86SNamhyung Kim 		len = sizeof(*o) * (nr_opts + !o->parent);
823ea558c86SNamhyung Kim 		group = realloc(ordered, len);
824ea558c86SNamhyung Kim 		if (!group)
8254b6ab94eSJosh Poimboeuf 			goto out;
826ea558c86SNamhyung Kim 		ordered = group;
827ea558c86SNamhyung Kim 		memcpy(&ordered[nr_parent], p, sizeof(*o) * (nr_opts - nr_parent));
828ea558c86SNamhyung Kim 
829ea558c86SNamhyung Kim 		nr_parent = nr_opts;
830ea558c86SNamhyung Kim 	}
831ea558c86SNamhyung Kim 	/* copy the last OPTION_END */
832ea558c86SNamhyung Kim 	memcpy(&ordered[nr_opts], o, sizeof(*o));
8334b6ab94eSJosh Poimboeuf 
834aa3d60e0SJosh Poimboeuf 	/* sort each option group individually */
835aa3d60e0SJosh Poimboeuf 	for (opt = group = ordered; opt->type != OPTION_END; opt++) {
836aa3d60e0SJosh Poimboeuf 		if (opt->type == OPTION_GROUP) {
837aa3d60e0SJosh Poimboeuf 			qsort(group, nr_group, sizeof(*opt), option__cmp);
838aa3d60e0SJosh Poimboeuf 			group = opt + 1;
839aa3d60e0SJosh Poimboeuf 			nr_group = 0;
840aa3d60e0SJosh Poimboeuf 			continue;
841aa3d60e0SJosh Poimboeuf 		}
842aa3d60e0SJosh Poimboeuf 		nr_group++;
843aa3d60e0SJosh Poimboeuf 	}
844aa3d60e0SJosh Poimboeuf 	qsort(group, nr_group, sizeof(*opt), option__cmp);
845aa3d60e0SJosh Poimboeuf 
8464b6ab94eSJosh Poimboeuf out:
8474b6ab94eSJosh Poimboeuf 	return ordered;
8484b6ab94eSJosh Poimboeuf }
8494b6ab94eSJosh Poimboeuf 
option__in_argv(const struct option * opt,const struct parse_opt_ctx_t * ctx)8504b6ab94eSJosh Poimboeuf static bool option__in_argv(const struct option *opt, const struct parse_opt_ctx_t *ctx)
8514b6ab94eSJosh Poimboeuf {
8524b6ab94eSJosh Poimboeuf 	int i;
8534b6ab94eSJosh Poimboeuf 
8544b6ab94eSJosh Poimboeuf 	for (i = 1; i < ctx->argc; ++i) {
8554b6ab94eSJosh Poimboeuf 		const char *arg = ctx->argv[i];
8564b6ab94eSJosh Poimboeuf 
8574b6ab94eSJosh Poimboeuf 		if (arg[0] != '-') {
8584b6ab94eSJosh Poimboeuf 			if (arg[1] == '\0') {
8594b6ab94eSJosh Poimboeuf 				if (arg[0] == opt->short_name)
8604b6ab94eSJosh Poimboeuf 					return true;
8614b6ab94eSJosh Poimboeuf 				continue;
8624b6ab94eSJosh Poimboeuf 			}
8634b6ab94eSJosh Poimboeuf 
8644b6ab94eSJosh Poimboeuf 			if (opt->long_name && strcmp(opt->long_name, arg) == 0)
8654b6ab94eSJosh Poimboeuf 				return true;
8664b6ab94eSJosh Poimboeuf 
8674b6ab94eSJosh Poimboeuf 			if (opt->help && strcasestr(opt->help, arg) != NULL)
8684b6ab94eSJosh Poimboeuf 				return true;
8694b6ab94eSJosh Poimboeuf 
8704b6ab94eSJosh Poimboeuf 			continue;
8714b6ab94eSJosh Poimboeuf 		}
8724b6ab94eSJosh Poimboeuf 
8734b6ab94eSJosh Poimboeuf 		if (arg[1] == opt->short_name ||
8744b6ab94eSJosh Poimboeuf 		    (arg[1] == '-' && opt->long_name && strcmp(opt->long_name, arg + 2) == 0))
8754b6ab94eSJosh Poimboeuf 			return true;
8764b6ab94eSJosh Poimboeuf 	}
8774b6ab94eSJosh Poimboeuf 
8784b6ab94eSJosh Poimboeuf 	return false;
8794b6ab94eSJosh Poimboeuf }
8804b6ab94eSJosh Poimboeuf 
usage_with_options_internal(const char * const * usagestr,const struct option * opts,int full,struct parse_opt_ctx_t * ctx)8814b6ab94eSJosh Poimboeuf static int usage_with_options_internal(const char * const *usagestr,
8824b6ab94eSJosh Poimboeuf 				       const struct option *opts, int full,
8834b6ab94eSJosh Poimboeuf 				       struct parse_opt_ctx_t *ctx)
8844b6ab94eSJosh Poimboeuf {
8854b6ab94eSJosh Poimboeuf 	struct option *ordered;
8864b6ab94eSJosh Poimboeuf 
8874b6ab94eSJosh Poimboeuf 	if (!usagestr)
8884b6ab94eSJosh Poimboeuf 		return PARSE_OPT_HELP;
8894b6ab94eSJosh Poimboeuf 
8904b6ab94eSJosh Poimboeuf 	setup_pager();
8914b6ab94eSJosh Poimboeuf 
8924b6ab94eSJosh Poimboeuf 	if (error_buf) {
8934b6ab94eSJosh Poimboeuf 		fprintf(stderr, "  Error: %s\n", error_buf);
8944b6ab94eSJosh Poimboeuf 		zfree(&error_buf);
8954b6ab94eSJosh Poimboeuf 	}
8964b6ab94eSJosh Poimboeuf 
8974b6ab94eSJosh Poimboeuf 	fprintf(stderr, "\n Usage: %s\n", *usagestr++);
8984b6ab94eSJosh Poimboeuf 	while (*usagestr && **usagestr)
8994b6ab94eSJosh Poimboeuf 		fprintf(stderr, "    or: %s\n", *usagestr++);
9004b6ab94eSJosh Poimboeuf 	while (*usagestr) {
9014b6ab94eSJosh Poimboeuf 		fprintf(stderr, "%s%s\n",
9024b6ab94eSJosh Poimboeuf 				**usagestr ? "    " : "",
9034b6ab94eSJosh Poimboeuf 				*usagestr);
9044b6ab94eSJosh Poimboeuf 		usagestr++;
9054b6ab94eSJosh Poimboeuf 	}
9064b6ab94eSJosh Poimboeuf 
9074b6ab94eSJosh Poimboeuf 	if (opts->type != OPTION_GROUP)
9084b6ab94eSJosh Poimboeuf 		fputc('\n', stderr);
9094b6ab94eSJosh Poimboeuf 
9104b6ab94eSJosh Poimboeuf 	ordered = options__order(opts);
9114b6ab94eSJosh Poimboeuf 	if (ordered)
9124b6ab94eSJosh Poimboeuf 		opts = ordered;
9134b6ab94eSJosh Poimboeuf 
9144b6ab94eSJosh Poimboeuf 	for (  ; opts->type != OPTION_END; opts++) {
9154b6ab94eSJosh Poimboeuf 		if (ctx && ctx->argc > 1 && !option__in_argv(opts, ctx))
9164b6ab94eSJosh Poimboeuf 			continue;
9174b6ab94eSJosh Poimboeuf 		print_option_help(opts, full);
9184b6ab94eSJosh Poimboeuf 	}
9194b6ab94eSJosh Poimboeuf 
9204b6ab94eSJosh Poimboeuf 	fputc('\n', stderr);
9214b6ab94eSJosh Poimboeuf 
9224b6ab94eSJosh Poimboeuf 	free(ordered);
9234b6ab94eSJosh Poimboeuf 
9244b6ab94eSJosh Poimboeuf 	return PARSE_OPT_HELP;
9254b6ab94eSJosh Poimboeuf }
9264b6ab94eSJosh Poimboeuf 
usage_with_options(const char * const * usagestr,const struct option * opts)9274b6ab94eSJosh Poimboeuf void usage_with_options(const char * const *usagestr,
9284b6ab94eSJosh Poimboeuf 			const struct option *opts)
9294b6ab94eSJosh Poimboeuf {
9304b6ab94eSJosh Poimboeuf 	usage_with_options_internal(usagestr, opts, 0, NULL);
9314b6ab94eSJosh Poimboeuf 	exit(129);
9324b6ab94eSJosh Poimboeuf }
9334b6ab94eSJosh Poimboeuf 
usage_with_options_msg(const char * const * usagestr,const struct option * opts,const char * fmt,...)9344b6ab94eSJosh Poimboeuf void usage_with_options_msg(const char * const *usagestr,
9354b6ab94eSJosh Poimboeuf 			    const struct option *opts, const char *fmt, ...)
9364b6ab94eSJosh Poimboeuf {
9374b6ab94eSJosh Poimboeuf 	va_list ap;
9384b6ab94eSJosh Poimboeuf 	char *tmp = error_buf;
9394b6ab94eSJosh Poimboeuf 
9404b6ab94eSJosh Poimboeuf 	va_start(ap, fmt);
9414b6ab94eSJosh Poimboeuf 	if (vasprintf(&error_buf, fmt, ap) == -1)
9424b6ab94eSJosh Poimboeuf 		die("vasprintf failed");
9434b6ab94eSJosh Poimboeuf 	va_end(ap);
9444b6ab94eSJosh Poimboeuf 
9454b6ab94eSJosh Poimboeuf 	free(tmp);
9464b6ab94eSJosh Poimboeuf 
9474b6ab94eSJosh Poimboeuf 	usage_with_options_internal(usagestr, opts, 0, NULL);
9484b6ab94eSJosh Poimboeuf 	exit(129);
9494b6ab94eSJosh Poimboeuf }
9504b6ab94eSJosh Poimboeuf 
parse_options_usage(const char * const * usagestr,const struct option * opts,const char * optstr,bool short_opt)9514b6ab94eSJosh Poimboeuf int parse_options_usage(const char * const *usagestr,
9524b6ab94eSJosh Poimboeuf 			const struct option *opts,
9534b6ab94eSJosh Poimboeuf 			const char *optstr, bool short_opt)
9544b6ab94eSJosh Poimboeuf {
9554b6ab94eSJosh Poimboeuf 	if (!usagestr)
9564b6ab94eSJosh Poimboeuf 		goto opt;
9574b6ab94eSJosh Poimboeuf 
9584b6ab94eSJosh Poimboeuf 	fprintf(stderr, "\n Usage: %s\n", *usagestr++);
9594b6ab94eSJosh Poimboeuf 	while (*usagestr && **usagestr)
9604b6ab94eSJosh Poimboeuf 		fprintf(stderr, "    or: %s\n", *usagestr++);
9614b6ab94eSJosh Poimboeuf 	while (*usagestr) {
9624b6ab94eSJosh Poimboeuf 		fprintf(stderr, "%s%s\n",
9634b6ab94eSJosh Poimboeuf 				**usagestr ? "    " : "",
9644b6ab94eSJosh Poimboeuf 				*usagestr);
9654b6ab94eSJosh Poimboeuf 		usagestr++;
9664b6ab94eSJosh Poimboeuf 	}
9674b6ab94eSJosh Poimboeuf 	fputc('\n', stderr);
9684b6ab94eSJosh Poimboeuf 
9694b6ab94eSJosh Poimboeuf opt:
9704b6ab94eSJosh Poimboeuf 	for (  ; opts->type != OPTION_END; opts++) {
9714b6ab94eSJosh Poimboeuf 		if (short_opt) {
9724b6ab94eSJosh Poimboeuf 			if (opts->short_name == *optstr) {
9734b6ab94eSJosh Poimboeuf 				print_option_help(opts, 0);
9744b6ab94eSJosh Poimboeuf 				break;
9754b6ab94eSJosh Poimboeuf 			}
9764b6ab94eSJosh Poimboeuf 			continue;
9774b6ab94eSJosh Poimboeuf 		}
9784b6ab94eSJosh Poimboeuf 
9794b6ab94eSJosh Poimboeuf 		if (opts->long_name == NULL)
9804b6ab94eSJosh Poimboeuf 			continue;
9814b6ab94eSJosh Poimboeuf 
9828e99b6d4SArnaldo Carvalho de Melo 		if (strstarts(opts->long_name, optstr))
9834b6ab94eSJosh Poimboeuf 			print_option_help(opts, 0);
9848e99b6d4SArnaldo Carvalho de Melo 		if (strstarts("no-", optstr) &&
9858e99b6d4SArnaldo Carvalho de Melo 		    strstarts(opts->long_name, optstr + 3))
9864b6ab94eSJosh Poimboeuf 			print_option_help(opts, 0);
9874b6ab94eSJosh Poimboeuf 	}
9884b6ab94eSJosh Poimboeuf 
9894b6ab94eSJosh Poimboeuf 	return PARSE_OPT_HELP;
9904b6ab94eSJosh Poimboeuf }
9914b6ab94eSJosh Poimboeuf 
9924b6ab94eSJosh Poimboeuf 
parse_opt_verbosity_cb(const struct option * opt,const char * arg __maybe_unused,int unset)9934b6ab94eSJosh Poimboeuf int parse_opt_verbosity_cb(const struct option *opt,
9944b6ab94eSJosh Poimboeuf 			   const char *arg __maybe_unused,
9954b6ab94eSJosh Poimboeuf 			   int unset)
9964b6ab94eSJosh Poimboeuf {
9974b6ab94eSJosh Poimboeuf 	int *target = opt->value;
9984b6ab94eSJosh Poimboeuf 
9994b6ab94eSJosh Poimboeuf 	if (unset)
10004b6ab94eSJosh Poimboeuf 		/* --no-quiet, --no-verbose */
10014b6ab94eSJosh Poimboeuf 		*target = 0;
10024b6ab94eSJosh Poimboeuf 	else if (opt->short_name == 'v') {
10034b6ab94eSJosh Poimboeuf 		if (*target >= 0)
10044b6ab94eSJosh Poimboeuf 			(*target)++;
10054b6ab94eSJosh Poimboeuf 		else
10064b6ab94eSJosh Poimboeuf 			*target = 1;
10074b6ab94eSJosh Poimboeuf 	} else {
10084b6ab94eSJosh Poimboeuf 		if (*target <= 0)
10094b6ab94eSJosh Poimboeuf 			(*target)--;
10104b6ab94eSJosh Poimboeuf 		else
10114b6ab94eSJosh Poimboeuf 			*target = -1;
10124b6ab94eSJosh Poimboeuf 	}
10134b6ab94eSJosh Poimboeuf 	return 0;
10144b6ab94eSJosh Poimboeuf }
10154b6ab94eSJosh Poimboeuf 
10164b6ab94eSJosh Poimboeuf static struct option *
find_option(struct option * opts,int shortopt,const char * longopt)10174b6ab94eSJosh Poimboeuf find_option(struct option *opts, int shortopt, const char *longopt)
10184b6ab94eSJosh Poimboeuf {
10194b6ab94eSJosh Poimboeuf 	for (; opts->type != OPTION_END; opts++) {
10204b6ab94eSJosh Poimboeuf 		if ((shortopt && opts->short_name == shortopt) ||
10214b6ab94eSJosh Poimboeuf 		    (opts->long_name && longopt &&
10224b6ab94eSJosh Poimboeuf 		     !strcmp(opts->long_name, longopt)))
10234b6ab94eSJosh Poimboeuf 			return opts;
10244b6ab94eSJosh Poimboeuf 	}
10254b6ab94eSJosh Poimboeuf 	return NULL;
10264b6ab94eSJosh Poimboeuf }
10274b6ab94eSJosh Poimboeuf 
set_option_flag(struct option * opts,int shortopt,const char * longopt,int flag)10284b6ab94eSJosh Poimboeuf void set_option_flag(struct option *opts, int shortopt, const char *longopt,
10294b6ab94eSJosh Poimboeuf 		     int flag)
10304b6ab94eSJosh Poimboeuf {
10314b6ab94eSJosh Poimboeuf 	struct option *opt = find_option(opts, shortopt, longopt);
10324b6ab94eSJosh Poimboeuf 
10334b6ab94eSJosh Poimboeuf 	if (opt)
10344b6ab94eSJosh Poimboeuf 		opt->flags |= flag;
10354b6ab94eSJosh Poimboeuf 	return;
10364b6ab94eSJosh Poimboeuf }
10374b6ab94eSJosh Poimboeuf 
set_option_nobuild(struct option * opts,int shortopt,const char * longopt,const char * build_opt,bool can_skip)10384b6ab94eSJosh Poimboeuf void set_option_nobuild(struct option *opts, int shortopt,
10394b6ab94eSJosh Poimboeuf 			const char *longopt,
10404b6ab94eSJosh Poimboeuf 			const char *build_opt,
10414b6ab94eSJosh Poimboeuf 			bool can_skip)
10424b6ab94eSJosh Poimboeuf {
10434b6ab94eSJosh Poimboeuf 	struct option *opt = find_option(opts, shortopt, longopt);
10444b6ab94eSJosh Poimboeuf 
10454b6ab94eSJosh Poimboeuf 	if (!opt)
10464b6ab94eSJosh Poimboeuf 		return;
10474b6ab94eSJosh Poimboeuf 
10484b6ab94eSJosh Poimboeuf 	opt->flags |= PARSE_OPT_NOBUILD;
10494b6ab94eSJosh Poimboeuf 	opt->flags |= can_skip ? PARSE_OPT_CANSKIP : 0;
10504b6ab94eSJosh Poimboeuf 	opt->build_opt = build_opt;
10514b6ab94eSJosh Poimboeuf }
1052