18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <linux/compiler.h> 38c2ecf20Sopenharmony_ci#include <linux/string.h> 48c2ecf20Sopenharmony_ci#include <linux/types.h> 58c2ecf20Sopenharmony_ci#include <stdio.h> 68c2ecf20Sopenharmony_ci#include <stdlib.h> 78c2ecf20Sopenharmony_ci#include <stdint.h> 88c2ecf20Sopenharmony_ci#include <string.h> 98c2ecf20Sopenharmony_ci#include <ctype.h> 108c2ecf20Sopenharmony_ci#include "subcmd-util.h" 118c2ecf20Sopenharmony_ci#include "parse-options.h" 128c2ecf20Sopenharmony_ci#include "subcmd-config.h" 138c2ecf20Sopenharmony_ci#include "pager.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define OPT_SHORT 1 168c2ecf20Sopenharmony_ci#define OPT_UNSET 2 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cichar *error_buf; 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic int opterror(const struct option *opt, const char *reason, int flags) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci if (flags & OPT_SHORT) 238c2ecf20Sopenharmony_ci fprintf(stderr, " Error: switch `%c' %s", opt->short_name, reason); 248c2ecf20Sopenharmony_ci else if (flags & OPT_UNSET) 258c2ecf20Sopenharmony_ci fprintf(stderr, " Error: option `no-%s' %s", opt->long_name, reason); 268c2ecf20Sopenharmony_ci else 278c2ecf20Sopenharmony_ci fprintf(stderr, " Error: option `%s' %s", opt->long_name, reason); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci return -1; 308c2ecf20Sopenharmony_ci} 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic const char *skip_prefix(const char *str, const char *prefix) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci size_t len = strlen(prefix); 358c2ecf20Sopenharmony_ci return strncmp(str, prefix, len) ? NULL : str + len; 368c2ecf20Sopenharmony_ci} 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic void optwarning(const struct option *opt, const char *reason, int flags) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci if (flags & OPT_SHORT) 418c2ecf20Sopenharmony_ci fprintf(stderr, " Warning: switch `%c' %s", opt->short_name, reason); 428c2ecf20Sopenharmony_ci else if (flags & OPT_UNSET) 438c2ecf20Sopenharmony_ci fprintf(stderr, " Warning: option `no-%s' %s", opt->long_name, reason); 448c2ecf20Sopenharmony_ci else 458c2ecf20Sopenharmony_ci fprintf(stderr, " Warning: option `%s' %s", opt->long_name, reason); 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic int get_arg(struct parse_opt_ctx_t *p, const struct option *opt, 498c2ecf20Sopenharmony_ci int flags, const char **arg) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci const char *res; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci if (p->opt) { 548c2ecf20Sopenharmony_ci res = p->opt; 558c2ecf20Sopenharmony_ci p->opt = NULL; 568c2ecf20Sopenharmony_ci } else if ((opt->flags & PARSE_OPT_LASTARG_DEFAULT) && (p->argc == 1 || 578c2ecf20Sopenharmony_ci **(p->argv + 1) == '-')) { 588c2ecf20Sopenharmony_ci res = (const char *)opt->defval; 598c2ecf20Sopenharmony_ci } else if (p->argc > 1) { 608c2ecf20Sopenharmony_ci p->argc--; 618c2ecf20Sopenharmony_ci res = *++p->argv; 628c2ecf20Sopenharmony_ci } else 638c2ecf20Sopenharmony_ci return opterror(opt, "requires a value", flags); 648c2ecf20Sopenharmony_ci if (arg) 658c2ecf20Sopenharmony_ci *arg = res; 668c2ecf20Sopenharmony_ci return 0; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic int get_value(struct parse_opt_ctx_t *p, 708c2ecf20Sopenharmony_ci const struct option *opt, int flags) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci const char *s, *arg = NULL; 738c2ecf20Sopenharmony_ci const int unset = flags & OPT_UNSET; 748c2ecf20Sopenharmony_ci int err; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci if (unset && p->opt) 778c2ecf20Sopenharmony_ci return opterror(opt, "takes no value", flags); 788c2ecf20Sopenharmony_ci if (unset && (opt->flags & PARSE_OPT_NONEG)) 798c2ecf20Sopenharmony_ci return opterror(opt, "isn't available", flags); 808c2ecf20Sopenharmony_ci if (opt->flags & PARSE_OPT_DISABLED) 818c2ecf20Sopenharmony_ci return opterror(opt, "is not usable", flags); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (opt->flags & PARSE_OPT_EXCLUSIVE) { 848c2ecf20Sopenharmony_ci if (p->excl_opt && p->excl_opt != opt) { 858c2ecf20Sopenharmony_ci char msg[128]; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (((flags & OPT_SHORT) && p->excl_opt->short_name) || 888c2ecf20Sopenharmony_ci p->excl_opt->long_name == NULL) { 898c2ecf20Sopenharmony_ci snprintf(msg, sizeof(msg), "cannot be used with switch `%c'", 908c2ecf20Sopenharmony_ci p->excl_opt->short_name); 918c2ecf20Sopenharmony_ci } else { 928c2ecf20Sopenharmony_ci snprintf(msg, sizeof(msg), "cannot be used with %s", 938c2ecf20Sopenharmony_ci p->excl_opt->long_name); 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci opterror(opt, msg, flags); 968c2ecf20Sopenharmony_ci return -3; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci p->excl_opt = opt; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci if (!(flags & OPT_SHORT) && p->opt) { 1018c2ecf20Sopenharmony_ci switch (opt->type) { 1028c2ecf20Sopenharmony_ci case OPTION_CALLBACK: 1038c2ecf20Sopenharmony_ci if (!(opt->flags & PARSE_OPT_NOARG)) 1048c2ecf20Sopenharmony_ci break; 1058c2ecf20Sopenharmony_ci /* FALLTHROUGH */ 1068c2ecf20Sopenharmony_ci case OPTION_BOOLEAN: 1078c2ecf20Sopenharmony_ci case OPTION_INCR: 1088c2ecf20Sopenharmony_ci case OPTION_BIT: 1098c2ecf20Sopenharmony_ci case OPTION_SET_UINT: 1108c2ecf20Sopenharmony_ci case OPTION_SET_PTR: 1118c2ecf20Sopenharmony_ci return opterror(opt, "takes no value", flags); 1128c2ecf20Sopenharmony_ci case OPTION_END: 1138c2ecf20Sopenharmony_ci case OPTION_ARGUMENT: 1148c2ecf20Sopenharmony_ci case OPTION_GROUP: 1158c2ecf20Sopenharmony_ci case OPTION_STRING: 1168c2ecf20Sopenharmony_ci case OPTION_INTEGER: 1178c2ecf20Sopenharmony_ci case OPTION_UINTEGER: 1188c2ecf20Sopenharmony_ci case OPTION_LONG: 1198c2ecf20Sopenharmony_ci case OPTION_ULONG: 1208c2ecf20Sopenharmony_ci case OPTION_U64: 1218c2ecf20Sopenharmony_ci default: 1228c2ecf20Sopenharmony_ci break; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (opt->flags & PARSE_OPT_NOBUILD) { 1278c2ecf20Sopenharmony_ci char reason[128]; 1288c2ecf20Sopenharmony_ci bool noarg = false; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci err = snprintf(reason, sizeof(reason), 1318c2ecf20Sopenharmony_ci opt->flags & PARSE_OPT_CANSKIP ? 1328c2ecf20Sopenharmony_ci "is being ignored because %s " : 1338c2ecf20Sopenharmony_ci "is not available because %s", 1348c2ecf20Sopenharmony_ci opt->build_opt); 1358c2ecf20Sopenharmony_ci reason[sizeof(reason) - 1] = '\0'; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (err < 0) 1388c2ecf20Sopenharmony_ci strncpy(reason, opt->flags & PARSE_OPT_CANSKIP ? 1398c2ecf20Sopenharmony_ci "is being ignored" : 1408c2ecf20Sopenharmony_ci "is not available", 1418c2ecf20Sopenharmony_ci sizeof(reason)); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (!(opt->flags & PARSE_OPT_CANSKIP)) 1448c2ecf20Sopenharmony_ci return opterror(opt, reason, flags); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci err = 0; 1478c2ecf20Sopenharmony_ci if (unset) 1488c2ecf20Sopenharmony_ci noarg = true; 1498c2ecf20Sopenharmony_ci if (opt->flags & PARSE_OPT_NOARG) 1508c2ecf20Sopenharmony_ci noarg = true; 1518c2ecf20Sopenharmony_ci if (opt->flags & PARSE_OPT_OPTARG && !p->opt) 1528c2ecf20Sopenharmony_ci noarg = true; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci switch (opt->type) { 1558c2ecf20Sopenharmony_ci case OPTION_BOOLEAN: 1568c2ecf20Sopenharmony_ci case OPTION_INCR: 1578c2ecf20Sopenharmony_ci case OPTION_BIT: 1588c2ecf20Sopenharmony_ci case OPTION_SET_UINT: 1598c2ecf20Sopenharmony_ci case OPTION_SET_PTR: 1608c2ecf20Sopenharmony_ci case OPTION_END: 1618c2ecf20Sopenharmony_ci case OPTION_ARGUMENT: 1628c2ecf20Sopenharmony_ci case OPTION_GROUP: 1638c2ecf20Sopenharmony_ci noarg = true; 1648c2ecf20Sopenharmony_ci break; 1658c2ecf20Sopenharmony_ci case OPTION_CALLBACK: 1668c2ecf20Sopenharmony_ci case OPTION_STRING: 1678c2ecf20Sopenharmony_ci case OPTION_INTEGER: 1688c2ecf20Sopenharmony_ci case OPTION_UINTEGER: 1698c2ecf20Sopenharmony_ci case OPTION_LONG: 1708c2ecf20Sopenharmony_ci case OPTION_ULONG: 1718c2ecf20Sopenharmony_ci case OPTION_U64: 1728c2ecf20Sopenharmony_ci default: 1738c2ecf20Sopenharmony_ci break; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci if (!noarg) 1778c2ecf20Sopenharmony_ci err = get_arg(p, opt, flags, NULL); 1788c2ecf20Sopenharmony_ci if (err) 1798c2ecf20Sopenharmony_ci return err; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci optwarning(opt, reason, flags); 1828c2ecf20Sopenharmony_ci return 0; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci switch (opt->type) { 1868c2ecf20Sopenharmony_ci case OPTION_BIT: 1878c2ecf20Sopenharmony_ci if (unset) 1888c2ecf20Sopenharmony_ci *(int *)opt->value &= ~opt->defval; 1898c2ecf20Sopenharmony_ci else 1908c2ecf20Sopenharmony_ci *(int *)opt->value |= opt->defval; 1918c2ecf20Sopenharmony_ci return 0; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci case OPTION_BOOLEAN: 1948c2ecf20Sopenharmony_ci *(bool *)opt->value = unset ? false : true; 1958c2ecf20Sopenharmony_ci if (opt->set) 1968c2ecf20Sopenharmony_ci *(bool *)opt->set = true; 1978c2ecf20Sopenharmony_ci return 0; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci case OPTION_INCR: 2008c2ecf20Sopenharmony_ci *(int *)opt->value = unset ? 0 : *(int *)opt->value + 1; 2018c2ecf20Sopenharmony_ci return 0; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci case OPTION_SET_UINT: 2048c2ecf20Sopenharmony_ci *(unsigned int *)opt->value = unset ? 0 : opt->defval; 2058c2ecf20Sopenharmony_ci return 0; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci case OPTION_SET_PTR: 2088c2ecf20Sopenharmony_ci *(void **)opt->value = unset ? NULL : (void *)opt->defval; 2098c2ecf20Sopenharmony_ci return 0; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci case OPTION_STRING: 2128c2ecf20Sopenharmony_ci err = 0; 2138c2ecf20Sopenharmony_ci if (unset) 2148c2ecf20Sopenharmony_ci *(const char **)opt->value = NULL; 2158c2ecf20Sopenharmony_ci else if (opt->flags & PARSE_OPT_OPTARG && !p->opt) 2168c2ecf20Sopenharmony_ci *(const char **)opt->value = (const char *)opt->defval; 2178c2ecf20Sopenharmony_ci else 2188c2ecf20Sopenharmony_ci err = get_arg(p, opt, flags, (const char **)opt->value); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if (opt->set) 2218c2ecf20Sopenharmony_ci *(bool *)opt->set = true; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci /* PARSE_OPT_NOEMPTY: Allow NULL but disallow empty string. */ 2248c2ecf20Sopenharmony_ci if (opt->flags & PARSE_OPT_NOEMPTY) { 2258c2ecf20Sopenharmony_ci const char *val = *(const char **)opt->value; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (!val) 2288c2ecf20Sopenharmony_ci return err; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci /* Similar to unset if we are given an empty string. */ 2318c2ecf20Sopenharmony_ci if (val[0] == '\0') { 2328c2ecf20Sopenharmony_ci *(const char **)opt->value = NULL; 2338c2ecf20Sopenharmony_ci return 0; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci return err; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci case OPTION_CALLBACK: 2408c2ecf20Sopenharmony_ci if (opt->set) 2418c2ecf20Sopenharmony_ci *(bool *)opt->set = true; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci if (unset) 2448c2ecf20Sopenharmony_ci return (*opt->callback)(opt, NULL, 1) ? (-1) : 0; 2458c2ecf20Sopenharmony_ci if (opt->flags & PARSE_OPT_NOARG) 2468c2ecf20Sopenharmony_ci return (*opt->callback)(opt, NULL, 0) ? (-1) : 0; 2478c2ecf20Sopenharmony_ci if (opt->flags & PARSE_OPT_OPTARG && !p->opt) 2488c2ecf20Sopenharmony_ci return (*opt->callback)(opt, NULL, 0) ? (-1) : 0; 2498c2ecf20Sopenharmony_ci if (get_arg(p, opt, flags, &arg)) 2508c2ecf20Sopenharmony_ci return -1; 2518c2ecf20Sopenharmony_ci return (*opt->callback)(opt, arg, 0) ? (-1) : 0; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci case OPTION_INTEGER: 2548c2ecf20Sopenharmony_ci if (unset) { 2558c2ecf20Sopenharmony_ci *(int *)opt->value = 0; 2568c2ecf20Sopenharmony_ci return 0; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { 2598c2ecf20Sopenharmony_ci *(int *)opt->value = opt->defval; 2608c2ecf20Sopenharmony_ci return 0; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci if (get_arg(p, opt, flags, &arg)) 2638c2ecf20Sopenharmony_ci return -1; 2648c2ecf20Sopenharmony_ci *(int *)opt->value = strtol(arg, (char **)&s, 10); 2658c2ecf20Sopenharmony_ci if (*s) 2668c2ecf20Sopenharmony_ci return opterror(opt, "expects a numerical value", flags); 2678c2ecf20Sopenharmony_ci return 0; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci case OPTION_UINTEGER: 2708c2ecf20Sopenharmony_ci if (unset) { 2718c2ecf20Sopenharmony_ci *(unsigned int *)opt->value = 0; 2728c2ecf20Sopenharmony_ci return 0; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { 2758c2ecf20Sopenharmony_ci *(unsigned int *)opt->value = opt->defval; 2768c2ecf20Sopenharmony_ci return 0; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci if (get_arg(p, opt, flags, &arg)) 2798c2ecf20Sopenharmony_ci return -1; 2808c2ecf20Sopenharmony_ci if (arg[0] == '-') 2818c2ecf20Sopenharmony_ci return opterror(opt, "expects an unsigned numerical value", flags); 2828c2ecf20Sopenharmony_ci *(unsigned int *)opt->value = strtol(arg, (char **)&s, 10); 2838c2ecf20Sopenharmony_ci if (*s) 2848c2ecf20Sopenharmony_ci return opterror(opt, "expects a numerical value", flags); 2858c2ecf20Sopenharmony_ci return 0; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci case OPTION_LONG: 2888c2ecf20Sopenharmony_ci if (unset) { 2898c2ecf20Sopenharmony_ci *(long *)opt->value = 0; 2908c2ecf20Sopenharmony_ci return 0; 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { 2938c2ecf20Sopenharmony_ci *(long *)opt->value = opt->defval; 2948c2ecf20Sopenharmony_ci return 0; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci if (get_arg(p, opt, flags, &arg)) 2978c2ecf20Sopenharmony_ci return -1; 2988c2ecf20Sopenharmony_ci *(long *)opt->value = strtol(arg, (char **)&s, 10); 2998c2ecf20Sopenharmony_ci if (*s) 3008c2ecf20Sopenharmony_ci return opterror(opt, "expects a numerical value", flags); 3018c2ecf20Sopenharmony_ci return 0; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci case OPTION_ULONG: 3048c2ecf20Sopenharmony_ci if (unset) { 3058c2ecf20Sopenharmony_ci *(unsigned long *)opt->value = 0; 3068c2ecf20Sopenharmony_ci return 0; 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { 3098c2ecf20Sopenharmony_ci *(unsigned long *)opt->value = opt->defval; 3108c2ecf20Sopenharmony_ci return 0; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci if (get_arg(p, opt, flags, &arg)) 3138c2ecf20Sopenharmony_ci return -1; 3148c2ecf20Sopenharmony_ci *(unsigned long *)opt->value = strtoul(arg, (char **)&s, 10); 3158c2ecf20Sopenharmony_ci if (*s) 3168c2ecf20Sopenharmony_ci return opterror(opt, "expects a numerical value", flags); 3178c2ecf20Sopenharmony_ci return 0; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci case OPTION_U64: 3208c2ecf20Sopenharmony_ci if (unset) { 3218c2ecf20Sopenharmony_ci *(u64 *)opt->value = 0; 3228c2ecf20Sopenharmony_ci return 0; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { 3258c2ecf20Sopenharmony_ci *(u64 *)opt->value = opt->defval; 3268c2ecf20Sopenharmony_ci return 0; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci if (get_arg(p, opt, flags, &arg)) 3298c2ecf20Sopenharmony_ci return -1; 3308c2ecf20Sopenharmony_ci if (arg[0] == '-') 3318c2ecf20Sopenharmony_ci return opterror(opt, "expects an unsigned numerical value", flags); 3328c2ecf20Sopenharmony_ci *(u64 *)opt->value = strtoull(arg, (char **)&s, 10); 3338c2ecf20Sopenharmony_ci if (*s) 3348c2ecf20Sopenharmony_ci return opterror(opt, "expects a numerical value", flags); 3358c2ecf20Sopenharmony_ci return 0; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci case OPTION_END: 3388c2ecf20Sopenharmony_ci case OPTION_ARGUMENT: 3398c2ecf20Sopenharmony_ci case OPTION_GROUP: 3408c2ecf20Sopenharmony_ci default: 3418c2ecf20Sopenharmony_ci die("should not happen, someone must be hit on the forehead"); 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_cistatic int parse_short_opt(struct parse_opt_ctx_t *p, const struct option *options) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ciretry: 3488c2ecf20Sopenharmony_ci for (; options->type != OPTION_END; options++) { 3498c2ecf20Sopenharmony_ci if (options->short_name == *p->opt) { 3508c2ecf20Sopenharmony_ci p->opt = p->opt[1] ? p->opt + 1 : NULL; 3518c2ecf20Sopenharmony_ci return get_value(p, options, OPT_SHORT); 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (options->parent) { 3568c2ecf20Sopenharmony_ci options = options->parent; 3578c2ecf20Sopenharmony_ci goto retry; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci return -2; 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg, 3648c2ecf20Sopenharmony_ci const struct option *options) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci const char *arg_end = strchr(arg, '='); 3678c2ecf20Sopenharmony_ci const struct option *abbrev_option = NULL, *ambiguous_option = NULL; 3688c2ecf20Sopenharmony_ci int abbrev_flags = 0, ambiguous_flags = 0; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (!arg_end) 3718c2ecf20Sopenharmony_ci arg_end = arg + strlen(arg); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ciretry: 3748c2ecf20Sopenharmony_ci for (; options->type != OPTION_END; options++) { 3758c2ecf20Sopenharmony_ci const char *rest; 3768c2ecf20Sopenharmony_ci int flags = 0; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci if (!options->long_name) 3798c2ecf20Sopenharmony_ci continue; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci rest = skip_prefix(arg, options->long_name); 3828c2ecf20Sopenharmony_ci if (options->type == OPTION_ARGUMENT) { 3838c2ecf20Sopenharmony_ci if (!rest) 3848c2ecf20Sopenharmony_ci continue; 3858c2ecf20Sopenharmony_ci if (*rest == '=') 3868c2ecf20Sopenharmony_ci return opterror(options, "takes no value", flags); 3878c2ecf20Sopenharmony_ci if (*rest) 3888c2ecf20Sopenharmony_ci continue; 3898c2ecf20Sopenharmony_ci p->out[p->cpidx++] = arg - 2; 3908c2ecf20Sopenharmony_ci return 0; 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci if (!rest) { 3938c2ecf20Sopenharmony_ci if (strstarts(options->long_name, "no-")) { 3948c2ecf20Sopenharmony_ci /* 3958c2ecf20Sopenharmony_ci * The long name itself starts with "no-", so 3968c2ecf20Sopenharmony_ci * accept the option without "no-" so that users 3978c2ecf20Sopenharmony_ci * do not have to enter "no-no-" to get the 3988c2ecf20Sopenharmony_ci * negation. 3998c2ecf20Sopenharmony_ci */ 4008c2ecf20Sopenharmony_ci rest = skip_prefix(arg, options->long_name + 3); 4018c2ecf20Sopenharmony_ci if (rest) { 4028c2ecf20Sopenharmony_ci flags |= OPT_UNSET; 4038c2ecf20Sopenharmony_ci goto match; 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci /* Abbreviated case */ 4068c2ecf20Sopenharmony_ci if (strstarts(options->long_name + 3, arg)) { 4078c2ecf20Sopenharmony_ci flags |= OPT_UNSET; 4088c2ecf20Sopenharmony_ci goto is_abbreviated; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci /* abbreviated? */ 4128c2ecf20Sopenharmony_ci if (!strncmp(options->long_name, arg, arg_end - arg)) { 4138c2ecf20Sopenharmony_ciis_abbreviated: 4148c2ecf20Sopenharmony_ci if (abbrev_option) { 4158c2ecf20Sopenharmony_ci /* 4168c2ecf20Sopenharmony_ci * If this is abbreviated, it is 4178c2ecf20Sopenharmony_ci * ambiguous. So when there is no 4188c2ecf20Sopenharmony_ci * exact match later, we need to 4198c2ecf20Sopenharmony_ci * error out. 4208c2ecf20Sopenharmony_ci */ 4218c2ecf20Sopenharmony_ci ambiguous_option = abbrev_option; 4228c2ecf20Sopenharmony_ci ambiguous_flags = abbrev_flags; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci if (!(flags & OPT_UNSET) && *arg_end) 4258c2ecf20Sopenharmony_ci p->opt = arg_end + 1; 4268c2ecf20Sopenharmony_ci abbrev_option = options; 4278c2ecf20Sopenharmony_ci abbrev_flags = flags; 4288c2ecf20Sopenharmony_ci continue; 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci /* negated and abbreviated very much? */ 4318c2ecf20Sopenharmony_ci if (strstarts("no-", arg)) { 4328c2ecf20Sopenharmony_ci flags |= OPT_UNSET; 4338c2ecf20Sopenharmony_ci goto is_abbreviated; 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci /* negated? */ 4368c2ecf20Sopenharmony_ci if (strncmp(arg, "no-", 3)) 4378c2ecf20Sopenharmony_ci continue; 4388c2ecf20Sopenharmony_ci flags |= OPT_UNSET; 4398c2ecf20Sopenharmony_ci rest = skip_prefix(arg + 3, options->long_name); 4408c2ecf20Sopenharmony_ci /* abbreviated and negated? */ 4418c2ecf20Sopenharmony_ci if (!rest && strstarts(options->long_name, arg + 3)) 4428c2ecf20Sopenharmony_ci goto is_abbreviated; 4438c2ecf20Sopenharmony_ci if (!rest) 4448c2ecf20Sopenharmony_ci continue; 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_cimatch: 4478c2ecf20Sopenharmony_ci if (*rest) { 4488c2ecf20Sopenharmony_ci if (*rest != '=') 4498c2ecf20Sopenharmony_ci continue; 4508c2ecf20Sopenharmony_ci p->opt = rest + 1; 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci return get_value(p, options, flags); 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci if (ambiguous_option) { 4568c2ecf20Sopenharmony_ci fprintf(stderr, 4578c2ecf20Sopenharmony_ci " Error: Ambiguous option: %s (could be --%s%s or --%s%s)\n", 4588c2ecf20Sopenharmony_ci arg, 4598c2ecf20Sopenharmony_ci (ambiguous_flags & OPT_UNSET) ? "no-" : "", 4608c2ecf20Sopenharmony_ci ambiguous_option->long_name, 4618c2ecf20Sopenharmony_ci (abbrev_flags & OPT_UNSET) ? "no-" : "", 4628c2ecf20Sopenharmony_ci abbrev_option->long_name); 4638c2ecf20Sopenharmony_ci return -1; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci if (abbrev_option) 4668c2ecf20Sopenharmony_ci return get_value(p, abbrev_option, abbrev_flags); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci if (options->parent) { 4698c2ecf20Sopenharmony_ci options = options->parent; 4708c2ecf20Sopenharmony_ci goto retry; 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci return -2; 4748c2ecf20Sopenharmony_ci} 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_cistatic void check_typos(const char *arg, const struct option *options) 4778c2ecf20Sopenharmony_ci{ 4788c2ecf20Sopenharmony_ci if (strlen(arg) < 3) 4798c2ecf20Sopenharmony_ci return; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci if (strstarts(arg, "no-")) { 4828c2ecf20Sopenharmony_ci fprintf(stderr, " Error: did you mean `--%s` (with two dashes ?)\n", arg); 4838c2ecf20Sopenharmony_ci exit(129); 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci for (; options->type != OPTION_END; options++) { 4878c2ecf20Sopenharmony_ci if (!options->long_name) 4888c2ecf20Sopenharmony_ci continue; 4898c2ecf20Sopenharmony_ci if (strstarts(options->long_name, arg)) { 4908c2ecf20Sopenharmony_ci fprintf(stderr, " Error: did you mean `--%s` (with two dashes ?)\n", arg); 4918c2ecf20Sopenharmony_ci exit(129); 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_cistatic void parse_options_start(struct parse_opt_ctx_t *ctx, 4978c2ecf20Sopenharmony_ci int argc, const char **argv, int flags) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci memset(ctx, 0, sizeof(*ctx)); 5008c2ecf20Sopenharmony_ci ctx->argc = argc - 1; 5018c2ecf20Sopenharmony_ci ctx->argv = argv + 1; 5028c2ecf20Sopenharmony_ci ctx->out = argv; 5038c2ecf20Sopenharmony_ci ctx->cpidx = ((flags & PARSE_OPT_KEEP_ARGV0) != 0); 5048c2ecf20Sopenharmony_ci ctx->flags = flags; 5058c2ecf20Sopenharmony_ci if ((flags & PARSE_OPT_KEEP_UNKNOWN) && 5068c2ecf20Sopenharmony_ci (flags & PARSE_OPT_STOP_AT_NON_OPTION)) 5078c2ecf20Sopenharmony_ci die("STOP_AT_NON_OPTION and KEEP_UNKNOWN don't go together"); 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cistatic int usage_with_options_internal(const char * const *, 5118c2ecf20Sopenharmony_ci const struct option *, int, 5128c2ecf20Sopenharmony_ci struct parse_opt_ctx_t *); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_cistatic int parse_options_step(struct parse_opt_ctx_t *ctx, 5158c2ecf20Sopenharmony_ci const struct option *options, 5168c2ecf20Sopenharmony_ci const char * const usagestr[]) 5178c2ecf20Sopenharmony_ci{ 5188c2ecf20Sopenharmony_ci int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP); 5198c2ecf20Sopenharmony_ci int excl_short_opt = 1; 5208c2ecf20Sopenharmony_ci const char *arg; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci /* we must reset ->opt, unknown short option leave it dangling */ 5238c2ecf20Sopenharmony_ci ctx->opt = NULL; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci for (; ctx->argc; ctx->argc--, ctx->argv++) { 5268c2ecf20Sopenharmony_ci arg = ctx->argv[0]; 5278c2ecf20Sopenharmony_ci if (*arg != '-' || !arg[1]) { 5288c2ecf20Sopenharmony_ci if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION) 5298c2ecf20Sopenharmony_ci break; 5308c2ecf20Sopenharmony_ci ctx->out[ctx->cpidx++] = ctx->argv[0]; 5318c2ecf20Sopenharmony_ci continue; 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci if (arg[1] != '-') { 5358c2ecf20Sopenharmony_ci ctx->opt = ++arg; 5368c2ecf20Sopenharmony_ci if (internal_help && *ctx->opt == 'h') { 5378c2ecf20Sopenharmony_ci return usage_with_options_internal(usagestr, options, 0, ctx); 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci switch (parse_short_opt(ctx, options)) { 5408c2ecf20Sopenharmony_ci case -1: 5418c2ecf20Sopenharmony_ci return parse_options_usage(usagestr, options, arg, 1); 5428c2ecf20Sopenharmony_ci case -2: 5438c2ecf20Sopenharmony_ci goto unknown; 5448c2ecf20Sopenharmony_ci case -3: 5458c2ecf20Sopenharmony_ci goto exclusive; 5468c2ecf20Sopenharmony_ci default: 5478c2ecf20Sopenharmony_ci break; 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci if (ctx->opt) 5508c2ecf20Sopenharmony_ci check_typos(arg, options); 5518c2ecf20Sopenharmony_ci while (ctx->opt) { 5528c2ecf20Sopenharmony_ci if (internal_help && *ctx->opt == 'h') 5538c2ecf20Sopenharmony_ci return usage_with_options_internal(usagestr, options, 0, ctx); 5548c2ecf20Sopenharmony_ci arg = ctx->opt; 5558c2ecf20Sopenharmony_ci switch (parse_short_opt(ctx, options)) { 5568c2ecf20Sopenharmony_ci case -1: 5578c2ecf20Sopenharmony_ci return parse_options_usage(usagestr, options, arg, 1); 5588c2ecf20Sopenharmony_ci case -2: 5598c2ecf20Sopenharmony_ci /* fake a short option thing to hide the fact that we may have 5608c2ecf20Sopenharmony_ci * started to parse aggregated stuff 5618c2ecf20Sopenharmony_ci * 5628c2ecf20Sopenharmony_ci * This is leaky, too bad. 5638c2ecf20Sopenharmony_ci */ 5648c2ecf20Sopenharmony_ci ctx->argv[0] = strdup(ctx->opt - 1); 5658c2ecf20Sopenharmony_ci *(char *)ctx->argv[0] = '-'; 5668c2ecf20Sopenharmony_ci goto unknown; 5678c2ecf20Sopenharmony_ci case -3: 5688c2ecf20Sopenharmony_ci goto exclusive; 5698c2ecf20Sopenharmony_ci default: 5708c2ecf20Sopenharmony_ci break; 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci continue; 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci if (!arg[2]) { /* "--" */ 5778c2ecf20Sopenharmony_ci if (!(ctx->flags & PARSE_OPT_KEEP_DASHDASH)) { 5788c2ecf20Sopenharmony_ci ctx->argc--; 5798c2ecf20Sopenharmony_ci ctx->argv++; 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci break; 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci arg += 2; 5858c2ecf20Sopenharmony_ci if (internal_help && !strcmp(arg, "help-all")) 5868c2ecf20Sopenharmony_ci return usage_with_options_internal(usagestr, options, 1, ctx); 5878c2ecf20Sopenharmony_ci if (internal_help && !strcmp(arg, "help")) 5888c2ecf20Sopenharmony_ci return usage_with_options_internal(usagestr, options, 0, ctx); 5898c2ecf20Sopenharmony_ci if (!strcmp(arg, "list-opts")) 5908c2ecf20Sopenharmony_ci return PARSE_OPT_LIST_OPTS; 5918c2ecf20Sopenharmony_ci if (!strcmp(arg, "list-cmds")) 5928c2ecf20Sopenharmony_ci return PARSE_OPT_LIST_SUBCMDS; 5938c2ecf20Sopenharmony_ci switch (parse_long_opt(ctx, arg, options)) { 5948c2ecf20Sopenharmony_ci case -1: 5958c2ecf20Sopenharmony_ci return parse_options_usage(usagestr, options, arg, 0); 5968c2ecf20Sopenharmony_ci case -2: 5978c2ecf20Sopenharmony_ci goto unknown; 5988c2ecf20Sopenharmony_ci case -3: 5998c2ecf20Sopenharmony_ci excl_short_opt = 0; 6008c2ecf20Sopenharmony_ci goto exclusive; 6018c2ecf20Sopenharmony_ci default: 6028c2ecf20Sopenharmony_ci break; 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci continue; 6058c2ecf20Sopenharmony_ciunknown: 6068c2ecf20Sopenharmony_ci if (!(ctx->flags & PARSE_OPT_KEEP_UNKNOWN)) 6078c2ecf20Sopenharmony_ci return PARSE_OPT_UNKNOWN; 6088c2ecf20Sopenharmony_ci ctx->out[ctx->cpidx++] = ctx->argv[0]; 6098c2ecf20Sopenharmony_ci ctx->opt = NULL; 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci return PARSE_OPT_DONE; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ciexclusive: 6148c2ecf20Sopenharmony_ci parse_options_usage(usagestr, options, arg, excl_short_opt); 6158c2ecf20Sopenharmony_ci if ((excl_short_opt && ctx->excl_opt->short_name) || 6168c2ecf20Sopenharmony_ci ctx->excl_opt->long_name == NULL) { 6178c2ecf20Sopenharmony_ci char opt = ctx->excl_opt->short_name; 6188c2ecf20Sopenharmony_ci parse_options_usage(NULL, options, &opt, 1); 6198c2ecf20Sopenharmony_ci } else { 6208c2ecf20Sopenharmony_ci parse_options_usage(NULL, options, ctx->excl_opt->long_name, 0); 6218c2ecf20Sopenharmony_ci } 6228c2ecf20Sopenharmony_ci return PARSE_OPT_HELP; 6238c2ecf20Sopenharmony_ci} 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_cistatic int parse_options_end(struct parse_opt_ctx_t *ctx) 6268c2ecf20Sopenharmony_ci{ 6278c2ecf20Sopenharmony_ci memmove(ctx->out + ctx->cpidx, ctx->argv, ctx->argc * sizeof(*ctx->out)); 6288c2ecf20Sopenharmony_ci ctx->out[ctx->cpidx + ctx->argc] = NULL; 6298c2ecf20Sopenharmony_ci return ctx->cpidx + ctx->argc; 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ciint parse_options_subcommand(int argc, const char **argv, const struct option *options, 6338c2ecf20Sopenharmony_ci const char *const subcommands[], const char *usagestr[], int flags) 6348c2ecf20Sopenharmony_ci{ 6358c2ecf20Sopenharmony_ci struct parse_opt_ctx_t ctx; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci /* build usage string if it's not provided */ 6388c2ecf20Sopenharmony_ci if (subcommands && !usagestr[0]) { 6398c2ecf20Sopenharmony_ci char *buf = NULL; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci astrcatf(&buf, "%s %s [<options>] {", subcmd_config.exec_name, argv[0]); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci for (int i = 0; subcommands[i]; i++) { 6448c2ecf20Sopenharmony_ci if (i) 6458c2ecf20Sopenharmony_ci astrcat(&buf, "|"); 6468c2ecf20Sopenharmony_ci astrcat(&buf, subcommands[i]); 6478c2ecf20Sopenharmony_ci } 6488c2ecf20Sopenharmony_ci astrcat(&buf, "}"); 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci usagestr[0] = buf; 6518c2ecf20Sopenharmony_ci } 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci parse_options_start(&ctx, argc, argv, flags); 6548c2ecf20Sopenharmony_ci switch (parse_options_step(&ctx, options, usagestr)) { 6558c2ecf20Sopenharmony_ci case PARSE_OPT_HELP: 6568c2ecf20Sopenharmony_ci exit(129); 6578c2ecf20Sopenharmony_ci case PARSE_OPT_DONE: 6588c2ecf20Sopenharmony_ci break; 6598c2ecf20Sopenharmony_ci case PARSE_OPT_LIST_OPTS: 6608c2ecf20Sopenharmony_ci while (options->type != OPTION_END) { 6618c2ecf20Sopenharmony_ci if (options->long_name) 6628c2ecf20Sopenharmony_ci printf("--%s ", options->long_name); 6638c2ecf20Sopenharmony_ci options++; 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci putchar('\n'); 6668c2ecf20Sopenharmony_ci exit(130); 6678c2ecf20Sopenharmony_ci case PARSE_OPT_LIST_SUBCMDS: 6688c2ecf20Sopenharmony_ci if (subcommands) { 6698c2ecf20Sopenharmony_ci for (int i = 0; subcommands[i]; i++) 6708c2ecf20Sopenharmony_ci printf("%s ", subcommands[i]); 6718c2ecf20Sopenharmony_ci } 6728c2ecf20Sopenharmony_ci putchar('\n'); 6738c2ecf20Sopenharmony_ci exit(130); 6748c2ecf20Sopenharmony_ci default: /* PARSE_OPT_UNKNOWN */ 6758c2ecf20Sopenharmony_ci if (ctx.argv[0][1] == '-') 6768c2ecf20Sopenharmony_ci astrcatf(&error_buf, "unknown option `%s'", 6778c2ecf20Sopenharmony_ci ctx.argv[0] + 2); 6788c2ecf20Sopenharmony_ci else 6798c2ecf20Sopenharmony_ci astrcatf(&error_buf, "unknown switch `%c'", *ctx.opt); 6808c2ecf20Sopenharmony_ci usage_with_options(usagestr, options); 6818c2ecf20Sopenharmony_ci } 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci return parse_options_end(&ctx); 6848c2ecf20Sopenharmony_ci} 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ciint parse_options(int argc, const char **argv, const struct option *options, 6878c2ecf20Sopenharmony_ci const char * const usagestr[], int flags) 6888c2ecf20Sopenharmony_ci{ 6898c2ecf20Sopenharmony_ci return parse_options_subcommand(argc, argv, options, NULL, 6908c2ecf20Sopenharmony_ci (const char **) usagestr, flags); 6918c2ecf20Sopenharmony_ci} 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci#define USAGE_OPTS_WIDTH 24 6948c2ecf20Sopenharmony_ci#define USAGE_GAP 2 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_cistatic void print_option_help(const struct option *opts, int full) 6978c2ecf20Sopenharmony_ci{ 6988c2ecf20Sopenharmony_ci size_t pos; 6998c2ecf20Sopenharmony_ci int pad; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci if (opts->type == OPTION_GROUP) { 7028c2ecf20Sopenharmony_ci fputc('\n', stderr); 7038c2ecf20Sopenharmony_ci if (*opts->help) 7048c2ecf20Sopenharmony_ci fprintf(stderr, "%s\n", opts->help); 7058c2ecf20Sopenharmony_ci return; 7068c2ecf20Sopenharmony_ci } 7078c2ecf20Sopenharmony_ci if (!full && (opts->flags & PARSE_OPT_HIDDEN)) 7088c2ecf20Sopenharmony_ci return; 7098c2ecf20Sopenharmony_ci if (opts->flags & PARSE_OPT_DISABLED) 7108c2ecf20Sopenharmony_ci return; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci pos = fprintf(stderr, " "); 7138c2ecf20Sopenharmony_ci if (opts->short_name) 7148c2ecf20Sopenharmony_ci pos += fprintf(stderr, "-%c", opts->short_name); 7158c2ecf20Sopenharmony_ci else 7168c2ecf20Sopenharmony_ci pos += fprintf(stderr, " "); 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci if (opts->long_name && opts->short_name) 7198c2ecf20Sopenharmony_ci pos += fprintf(stderr, ", "); 7208c2ecf20Sopenharmony_ci if (opts->long_name) 7218c2ecf20Sopenharmony_ci pos += fprintf(stderr, "--%s", opts->long_name); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci switch (opts->type) { 7248c2ecf20Sopenharmony_ci case OPTION_ARGUMENT: 7258c2ecf20Sopenharmony_ci break; 7268c2ecf20Sopenharmony_ci case OPTION_LONG: 7278c2ecf20Sopenharmony_ci case OPTION_ULONG: 7288c2ecf20Sopenharmony_ci case OPTION_U64: 7298c2ecf20Sopenharmony_ci case OPTION_INTEGER: 7308c2ecf20Sopenharmony_ci case OPTION_UINTEGER: 7318c2ecf20Sopenharmony_ci if (opts->flags & PARSE_OPT_OPTARG) 7328c2ecf20Sopenharmony_ci if (opts->long_name) 7338c2ecf20Sopenharmony_ci pos += fprintf(stderr, "[=<n>]"); 7348c2ecf20Sopenharmony_ci else 7358c2ecf20Sopenharmony_ci pos += fprintf(stderr, "[<n>]"); 7368c2ecf20Sopenharmony_ci else 7378c2ecf20Sopenharmony_ci pos += fprintf(stderr, " <n>"); 7388c2ecf20Sopenharmony_ci break; 7398c2ecf20Sopenharmony_ci case OPTION_CALLBACK: 7408c2ecf20Sopenharmony_ci if (opts->flags & PARSE_OPT_NOARG) 7418c2ecf20Sopenharmony_ci break; 7428c2ecf20Sopenharmony_ci /* FALLTHROUGH */ 7438c2ecf20Sopenharmony_ci case OPTION_STRING: 7448c2ecf20Sopenharmony_ci if (opts->argh) { 7458c2ecf20Sopenharmony_ci if (opts->flags & PARSE_OPT_OPTARG) 7468c2ecf20Sopenharmony_ci if (opts->long_name) 7478c2ecf20Sopenharmony_ci pos += fprintf(stderr, "[=<%s>]", opts->argh); 7488c2ecf20Sopenharmony_ci else 7498c2ecf20Sopenharmony_ci pos += fprintf(stderr, "[<%s>]", opts->argh); 7508c2ecf20Sopenharmony_ci else 7518c2ecf20Sopenharmony_ci pos += fprintf(stderr, " <%s>", opts->argh); 7528c2ecf20Sopenharmony_ci } else { 7538c2ecf20Sopenharmony_ci if (opts->flags & PARSE_OPT_OPTARG) 7548c2ecf20Sopenharmony_ci if (opts->long_name) 7558c2ecf20Sopenharmony_ci pos += fprintf(stderr, "[=...]"); 7568c2ecf20Sopenharmony_ci else 7578c2ecf20Sopenharmony_ci pos += fprintf(stderr, "[...]"); 7588c2ecf20Sopenharmony_ci else 7598c2ecf20Sopenharmony_ci pos += fprintf(stderr, " ..."); 7608c2ecf20Sopenharmony_ci } 7618c2ecf20Sopenharmony_ci break; 7628c2ecf20Sopenharmony_ci default: /* OPTION_{BIT,BOOLEAN,SET_UINT,SET_PTR} */ 7638c2ecf20Sopenharmony_ci case OPTION_END: 7648c2ecf20Sopenharmony_ci case OPTION_GROUP: 7658c2ecf20Sopenharmony_ci case OPTION_BIT: 7668c2ecf20Sopenharmony_ci case OPTION_BOOLEAN: 7678c2ecf20Sopenharmony_ci case OPTION_INCR: 7688c2ecf20Sopenharmony_ci case OPTION_SET_UINT: 7698c2ecf20Sopenharmony_ci case OPTION_SET_PTR: 7708c2ecf20Sopenharmony_ci break; 7718c2ecf20Sopenharmony_ci } 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci if (pos <= USAGE_OPTS_WIDTH) 7748c2ecf20Sopenharmony_ci pad = USAGE_OPTS_WIDTH - pos; 7758c2ecf20Sopenharmony_ci else { 7768c2ecf20Sopenharmony_ci fputc('\n', stderr); 7778c2ecf20Sopenharmony_ci pad = USAGE_OPTS_WIDTH; 7788c2ecf20Sopenharmony_ci } 7798c2ecf20Sopenharmony_ci fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help); 7808c2ecf20Sopenharmony_ci if (opts->flags & PARSE_OPT_NOBUILD) 7818c2ecf20Sopenharmony_ci fprintf(stderr, "%*s(not built-in because %s)\n", 7828c2ecf20Sopenharmony_ci USAGE_OPTS_WIDTH + USAGE_GAP, "", 7838c2ecf20Sopenharmony_ci opts->build_opt); 7848c2ecf20Sopenharmony_ci} 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_cistatic int option__cmp(const void *va, const void *vb) 7878c2ecf20Sopenharmony_ci{ 7888c2ecf20Sopenharmony_ci const struct option *a = va, *b = vb; 7898c2ecf20Sopenharmony_ci int sa = tolower(a->short_name), sb = tolower(b->short_name), ret; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci if (sa == 0) 7928c2ecf20Sopenharmony_ci sa = 'z' + 1; 7938c2ecf20Sopenharmony_ci if (sb == 0) 7948c2ecf20Sopenharmony_ci sb = 'z' + 1; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci ret = sa - sb; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci if (ret == 0) { 7998c2ecf20Sopenharmony_ci const char *la = a->long_name ?: "", 8008c2ecf20Sopenharmony_ci *lb = b->long_name ?: ""; 8018c2ecf20Sopenharmony_ci ret = strcmp(la, lb); 8028c2ecf20Sopenharmony_ci } 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci return ret; 8058c2ecf20Sopenharmony_ci} 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_cistatic struct option *options__order(const struct option *opts) 8088c2ecf20Sopenharmony_ci{ 8098c2ecf20Sopenharmony_ci int nr_opts = 0, len; 8108c2ecf20Sopenharmony_ci const struct option *o = opts; 8118c2ecf20Sopenharmony_ci struct option *ordered; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci for (o = opts; o->type != OPTION_END; o++) 8148c2ecf20Sopenharmony_ci ++nr_opts; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci len = sizeof(*o) * (nr_opts + 1); 8178c2ecf20Sopenharmony_ci ordered = malloc(len); 8188c2ecf20Sopenharmony_ci if (!ordered) 8198c2ecf20Sopenharmony_ci goto out; 8208c2ecf20Sopenharmony_ci memcpy(ordered, opts, len); 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci qsort(ordered, nr_opts, sizeof(*o), option__cmp); 8238c2ecf20Sopenharmony_ciout: 8248c2ecf20Sopenharmony_ci return ordered; 8258c2ecf20Sopenharmony_ci} 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_cistatic bool option__in_argv(const struct option *opt, const struct parse_opt_ctx_t *ctx) 8288c2ecf20Sopenharmony_ci{ 8298c2ecf20Sopenharmony_ci int i; 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci for (i = 1; i < ctx->argc; ++i) { 8328c2ecf20Sopenharmony_ci const char *arg = ctx->argv[i]; 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci if (arg[0] != '-') { 8358c2ecf20Sopenharmony_ci if (arg[1] == '\0') { 8368c2ecf20Sopenharmony_ci if (arg[0] == opt->short_name) 8378c2ecf20Sopenharmony_ci return true; 8388c2ecf20Sopenharmony_ci continue; 8398c2ecf20Sopenharmony_ci } 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci if (opt->long_name && strcmp(opt->long_name, arg) == 0) 8428c2ecf20Sopenharmony_ci return true; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci if (opt->help && strcasestr(opt->help, arg) != NULL) 8458c2ecf20Sopenharmony_ci return true; 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci continue; 8488c2ecf20Sopenharmony_ci } 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci if (arg[1] == opt->short_name || 8518c2ecf20Sopenharmony_ci (arg[1] == '-' && opt->long_name && strcmp(opt->long_name, arg + 2) == 0)) 8528c2ecf20Sopenharmony_ci return true; 8538c2ecf20Sopenharmony_ci } 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci return false; 8568c2ecf20Sopenharmony_ci} 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_cistatic int usage_with_options_internal(const char * const *usagestr, 8598c2ecf20Sopenharmony_ci const struct option *opts, int full, 8608c2ecf20Sopenharmony_ci struct parse_opt_ctx_t *ctx) 8618c2ecf20Sopenharmony_ci{ 8628c2ecf20Sopenharmony_ci struct option *ordered; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci if (!usagestr) 8658c2ecf20Sopenharmony_ci return PARSE_OPT_HELP; 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci setup_pager(); 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci if (error_buf) { 8708c2ecf20Sopenharmony_ci fprintf(stderr, " Error: %s\n", error_buf); 8718c2ecf20Sopenharmony_ci zfree(&error_buf); 8728c2ecf20Sopenharmony_ci } 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci fprintf(stderr, "\n Usage: %s\n", *usagestr++); 8758c2ecf20Sopenharmony_ci while (*usagestr && **usagestr) 8768c2ecf20Sopenharmony_ci fprintf(stderr, " or: %s\n", *usagestr++); 8778c2ecf20Sopenharmony_ci while (*usagestr) { 8788c2ecf20Sopenharmony_ci fprintf(stderr, "%s%s\n", 8798c2ecf20Sopenharmony_ci **usagestr ? " " : "", 8808c2ecf20Sopenharmony_ci *usagestr); 8818c2ecf20Sopenharmony_ci usagestr++; 8828c2ecf20Sopenharmony_ci } 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci if (opts->type != OPTION_GROUP) 8858c2ecf20Sopenharmony_ci fputc('\n', stderr); 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci ordered = options__order(opts); 8888c2ecf20Sopenharmony_ci if (ordered) 8898c2ecf20Sopenharmony_ci opts = ordered; 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci for ( ; opts->type != OPTION_END; opts++) { 8928c2ecf20Sopenharmony_ci if (ctx && ctx->argc > 1 && !option__in_argv(opts, ctx)) 8938c2ecf20Sopenharmony_ci continue; 8948c2ecf20Sopenharmony_ci print_option_help(opts, full); 8958c2ecf20Sopenharmony_ci } 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci fputc('\n', stderr); 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci free(ordered); 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci return PARSE_OPT_HELP; 9028c2ecf20Sopenharmony_ci} 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_civoid usage_with_options(const char * const *usagestr, 9058c2ecf20Sopenharmony_ci const struct option *opts) 9068c2ecf20Sopenharmony_ci{ 9078c2ecf20Sopenharmony_ci usage_with_options_internal(usagestr, opts, 0, NULL); 9088c2ecf20Sopenharmony_ci exit(129); 9098c2ecf20Sopenharmony_ci} 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_civoid usage_with_options_msg(const char * const *usagestr, 9128c2ecf20Sopenharmony_ci const struct option *opts, const char *fmt, ...) 9138c2ecf20Sopenharmony_ci{ 9148c2ecf20Sopenharmony_ci va_list ap; 9158c2ecf20Sopenharmony_ci char *tmp = error_buf; 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci va_start(ap, fmt); 9188c2ecf20Sopenharmony_ci if (vasprintf(&error_buf, fmt, ap) == -1) 9198c2ecf20Sopenharmony_ci die("vasprintf failed"); 9208c2ecf20Sopenharmony_ci va_end(ap); 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci free(tmp); 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci usage_with_options_internal(usagestr, opts, 0, NULL); 9258c2ecf20Sopenharmony_ci exit(129); 9268c2ecf20Sopenharmony_ci} 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ciint parse_options_usage(const char * const *usagestr, 9298c2ecf20Sopenharmony_ci const struct option *opts, 9308c2ecf20Sopenharmony_ci const char *optstr, bool short_opt) 9318c2ecf20Sopenharmony_ci{ 9328c2ecf20Sopenharmony_ci if (!usagestr) 9338c2ecf20Sopenharmony_ci goto opt; 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci fprintf(stderr, "\n Usage: %s\n", *usagestr++); 9368c2ecf20Sopenharmony_ci while (*usagestr && **usagestr) 9378c2ecf20Sopenharmony_ci fprintf(stderr, " or: %s\n", *usagestr++); 9388c2ecf20Sopenharmony_ci while (*usagestr) { 9398c2ecf20Sopenharmony_ci fprintf(stderr, "%s%s\n", 9408c2ecf20Sopenharmony_ci **usagestr ? " " : "", 9418c2ecf20Sopenharmony_ci *usagestr); 9428c2ecf20Sopenharmony_ci usagestr++; 9438c2ecf20Sopenharmony_ci } 9448c2ecf20Sopenharmony_ci fputc('\n', stderr); 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ciopt: 9478c2ecf20Sopenharmony_ci for ( ; opts->type != OPTION_END; opts++) { 9488c2ecf20Sopenharmony_ci if (short_opt) { 9498c2ecf20Sopenharmony_ci if (opts->short_name == *optstr) { 9508c2ecf20Sopenharmony_ci print_option_help(opts, 0); 9518c2ecf20Sopenharmony_ci break; 9528c2ecf20Sopenharmony_ci } 9538c2ecf20Sopenharmony_ci continue; 9548c2ecf20Sopenharmony_ci } 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci if (opts->long_name == NULL) 9578c2ecf20Sopenharmony_ci continue; 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci if (strstarts(opts->long_name, optstr)) 9608c2ecf20Sopenharmony_ci print_option_help(opts, 0); 9618c2ecf20Sopenharmony_ci if (strstarts("no-", optstr) && 9628c2ecf20Sopenharmony_ci strstarts(opts->long_name, optstr + 3)) 9638c2ecf20Sopenharmony_ci print_option_help(opts, 0); 9648c2ecf20Sopenharmony_ci } 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci return PARSE_OPT_HELP; 9678c2ecf20Sopenharmony_ci} 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ciint parse_opt_verbosity_cb(const struct option *opt, 9718c2ecf20Sopenharmony_ci const char *arg __maybe_unused, 9728c2ecf20Sopenharmony_ci int unset) 9738c2ecf20Sopenharmony_ci{ 9748c2ecf20Sopenharmony_ci int *target = opt->value; 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci if (unset) 9778c2ecf20Sopenharmony_ci /* --no-quiet, --no-verbose */ 9788c2ecf20Sopenharmony_ci *target = 0; 9798c2ecf20Sopenharmony_ci else if (opt->short_name == 'v') { 9808c2ecf20Sopenharmony_ci if (*target >= 0) 9818c2ecf20Sopenharmony_ci (*target)++; 9828c2ecf20Sopenharmony_ci else 9838c2ecf20Sopenharmony_ci *target = 1; 9848c2ecf20Sopenharmony_ci } else { 9858c2ecf20Sopenharmony_ci if (*target <= 0) 9868c2ecf20Sopenharmony_ci (*target)--; 9878c2ecf20Sopenharmony_ci else 9888c2ecf20Sopenharmony_ci *target = -1; 9898c2ecf20Sopenharmony_ci } 9908c2ecf20Sopenharmony_ci return 0; 9918c2ecf20Sopenharmony_ci} 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_cistatic struct option * 9948c2ecf20Sopenharmony_cifind_option(struct option *opts, int shortopt, const char *longopt) 9958c2ecf20Sopenharmony_ci{ 9968c2ecf20Sopenharmony_ci for (; opts->type != OPTION_END; opts++) { 9978c2ecf20Sopenharmony_ci if ((shortopt && opts->short_name == shortopt) || 9988c2ecf20Sopenharmony_ci (opts->long_name && longopt && 9998c2ecf20Sopenharmony_ci !strcmp(opts->long_name, longopt))) 10008c2ecf20Sopenharmony_ci return opts; 10018c2ecf20Sopenharmony_ci } 10028c2ecf20Sopenharmony_ci return NULL; 10038c2ecf20Sopenharmony_ci} 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_civoid set_option_flag(struct option *opts, int shortopt, const char *longopt, 10068c2ecf20Sopenharmony_ci int flag) 10078c2ecf20Sopenharmony_ci{ 10088c2ecf20Sopenharmony_ci struct option *opt = find_option(opts, shortopt, longopt); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci if (opt) 10118c2ecf20Sopenharmony_ci opt->flags |= flag; 10128c2ecf20Sopenharmony_ci return; 10138c2ecf20Sopenharmony_ci} 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_civoid set_option_nobuild(struct option *opts, int shortopt, 10168c2ecf20Sopenharmony_ci const char *longopt, 10178c2ecf20Sopenharmony_ci const char *build_opt, 10188c2ecf20Sopenharmony_ci bool can_skip) 10198c2ecf20Sopenharmony_ci{ 10208c2ecf20Sopenharmony_ci struct option *opt = find_option(opts, shortopt, longopt); 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci if (!opt) 10238c2ecf20Sopenharmony_ci return; 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci opt->flags |= PARSE_OPT_NOBUILD; 10268c2ecf20Sopenharmony_ci opt->flags |= can_skip ? PARSE_OPT_CANSKIP : 0; 10278c2ecf20Sopenharmony_ci opt->build_opt = build_opt; 10288c2ecf20Sopenharmony_ci} 1029