101b6ba3aSAndy Shevchenko #include <stdlib.h> 298ee79f4SPrasad Joshi #include <stdio.h> 398ee79f4SPrasad Joshi #include <string.h> 498ee79f4SPrasad Joshi #include <unistd.h> 598ee79f4SPrasad Joshi 698ee79f4SPrasad Joshi #include <stdbool.h> 798ee79f4SPrasad Joshi 898ee79f4SPrasad Joshi /* user defined includes */ 998ee79f4SPrasad Joshi #include <linux/types.h> 1098ee79f4SPrasad Joshi #include <kvm/util.h> 1198ee79f4SPrasad Joshi #include <kvm/parse-options.h> 1298ee79f4SPrasad Joshi #include <kvm/strbuf.h> 1398ee79f4SPrasad Joshi 1498ee79f4SPrasad Joshi #define OPT_SHORT 1 1598ee79f4SPrasad Joshi #define OPT_UNSET 2 1698ee79f4SPrasad Joshi 1798ee79f4SPrasad Joshi static int opterror(const struct option *opt, const char *reason, int flags) 1898ee79f4SPrasad Joshi { 1998ee79f4SPrasad Joshi if (flags & OPT_SHORT) 20*599ed2a8SCyrill Gorcunov return pr_err("switch `%c' %s", opt->short_name, reason); 2198ee79f4SPrasad Joshi if (flags & OPT_UNSET) 22*599ed2a8SCyrill Gorcunov return pr_err("option `no-%s' %s", opt->long_name, reason); 23*599ed2a8SCyrill Gorcunov return pr_err("option `%s' %s", opt->long_name, reason); 2498ee79f4SPrasad Joshi } 2598ee79f4SPrasad Joshi 2698ee79f4SPrasad Joshi static int get_arg(struct parse_opt_ctx_t *p, const struct option *opt, 2798ee79f4SPrasad Joshi int flags, const char **arg) 2898ee79f4SPrasad Joshi { 2998ee79f4SPrasad Joshi if (p->opt) { 3098ee79f4SPrasad Joshi *arg = p->opt; 3198ee79f4SPrasad Joshi p->opt = NULL; 3298ee79f4SPrasad Joshi } else if ((opt->flags & PARSE_OPT_LASTARG_DEFAULT) && (p->argc == 1 || 3398ee79f4SPrasad Joshi **(p->argv + 1) == '-')) { 3498ee79f4SPrasad Joshi *arg = (const char *)opt->defval; 3598ee79f4SPrasad Joshi } else if (p->argc > 1) { 3698ee79f4SPrasad Joshi p->argc--; 3798ee79f4SPrasad Joshi *arg = *++p->argv; 3898ee79f4SPrasad Joshi } else 3998ee79f4SPrasad Joshi return opterror(opt, "requires a value", flags); 4098ee79f4SPrasad Joshi return 0; 4198ee79f4SPrasad Joshi } 4298ee79f4SPrasad Joshi 43dec94ae9SCyrill Gorcunov static int readnum(const struct option *opt, int flags, 44dec94ae9SCyrill Gorcunov const char *str, char **end) 45dec94ae9SCyrill Gorcunov { 46dec94ae9SCyrill Gorcunov switch (opt->type) { 47dec94ae9SCyrill Gorcunov case OPTION_INTEGER: 4801b6ba3aSAndy Shevchenko *(int *)opt->value = strtol(str, end, 0); 49dec94ae9SCyrill Gorcunov break; 50dec94ae9SCyrill Gorcunov case OPTION_UINTEGER: 5101b6ba3aSAndy Shevchenko *(unsigned int *)opt->value = strtol(str, end, 0); 52dec94ae9SCyrill Gorcunov break; 53dec94ae9SCyrill Gorcunov case OPTION_LONG: 5401b6ba3aSAndy Shevchenko *(long *)opt->value = strtol(str, end, 0); 55dec94ae9SCyrill Gorcunov break; 56dec94ae9SCyrill Gorcunov case OPTION_U64: 5701b6ba3aSAndy Shevchenko *(u64 *)opt->value = strtoull(str, end, 0); 58dec94ae9SCyrill Gorcunov break; 59dec94ae9SCyrill Gorcunov default: 6001b6ba3aSAndy Shevchenko return opterror(opt, "invalid numeric conversion", flags); 61dec94ae9SCyrill Gorcunov } 62dec94ae9SCyrill Gorcunov 63dec94ae9SCyrill Gorcunov return 0; 64dec94ae9SCyrill Gorcunov } 65dec94ae9SCyrill Gorcunov 6698ee79f4SPrasad Joshi static int get_value(struct parse_opt_ctx_t *p, 6798ee79f4SPrasad Joshi const struct option *opt, int flags) 6898ee79f4SPrasad Joshi { 6998ee79f4SPrasad Joshi const char *s, *arg = NULL; 7098ee79f4SPrasad Joshi const int unset = flags & OPT_UNSET; 7198ee79f4SPrasad Joshi 7298ee79f4SPrasad Joshi if (unset && p->opt) 7398ee79f4SPrasad Joshi return opterror(opt, "takes no value", flags); 7498ee79f4SPrasad Joshi if (unset && (opt->flags & PARSE_OPT_NONEG)) 7598ee79f4SPrasad Joshi return opterror(opt, "isn't available", flags); 7698ee79f4SPrasad Joshi 7798ee79f4SPrasad Joshi if (!(flags & OPT_SHORT) && p->opt) { 7898ee79f4SPrasad Joshi switch (opt->type) { 7998ee79f4SPrasad Joshi case OPTION_CALLBACK: 8098ee79f4SPrasad Joshi if (!(opt->flags & PARSE_OPT_NOARG)) 8198ee79f4SPrasad Joshi break; 8298ee79f4SPrasad Joshi /* FALLTHROUGH */ 8398ee79f4SPrasad Joshi case OPTION_BOOLEAN: 8498ee79f4SPrasad Joshi case OPTION_INCR: 8598ee79f4SPrasad Joshi case OPTION_BIT: 8698ee79f4SPrasad Joshi case OPTION_SET_UINT: 8798ee79f4SPrasad Joshi case OPTION_SET_PTR: 8898ee79f4SPrasad Joshi return opterror(opt, "takes no value", flags); 8998ee79f4SPrasad Joshi case OPTION_END: 9098ee79f4SPrasad Joshi case OPTION_ARGUMENT: 9198ee79f4SPrasad Joshi case OPTION_GROUP: 9298ee79f4SPrasad Joshi case OPTION_STRING: 9398ee79f4SPrasad Joshi case OPTION_INTEGER: 9498ee79f4SPrasad Joshi case OPTION_UINTEGER: 9598ee79f4SPrasad Joshi case OPTION_LONG: 9698ee79f4SPrasad Joshi case OPTION_U64: 9798ee79f4SPrasad Joshi default: 9898ee79f4SPrasad Joshi break; 9998ee79f4SPrasad Joshi } 10098ee79f4SPrasad Joshi } 10198ee79f4SPrasad Joshi 10298ee79f4SPrasad Joshi switch (opt->type) { 10398ee79f4SPrasad Joshi case OPTION_BIT: 10498ee79f4SPrasad Joshi if (unset) 10598ee79f4SPrasad Joshi *(int *)opt->value &= ~opt->defval; 10698ee79f4SPrasad Joshi else 10798ee79f4SPrasad Joshi *(int *)opt->value |= opt->defval; 10898ee79f4SPrasad Joshi return 0; 10998ee79f4SPrasad Joshi 11098ee79f4SPrasad Joshi case OPTION_BOOLEAN: 11198ee79f4SPrasad Joshi *(bool *)opt->value = unset ? false : true; 11298ee79f4SPrasad Joshi return 0; 11398ee79f4SPrasad Joshi 11498ee79f4SPrasad Joshi case OPTION_INCR: 11598ee79f4SPrasad Joshi *(int *)opt->value = unset ? 0 : *(int *)opt->value + 1; 11698ee79f4SPrasad Joshi return 0; 11798ee79f4SPrasad Joshi 11898ee79f4SPrasad Joshi case OPTION_SET_UINT: 11998ee79f4SPrasad Joshi *(unsigned int *)opt->value = unset ? 0 : opt->defval; 12098ee79f4SPrasad Joshi return 0; 12198ee79f4SPrasad Joshi 12298ee79f4SPrasad Joshi case OPTION_SET_PTR: 12398ee79f4SPrasad Joshi *(void **)opt->value = unset ? NULL : (void *)opt->defval; 12498ee79f4SPrasad Joshi return 0; 12598ee79f4SPrasad Joshi 12698ee79f4SPrasad Joshi case OPTION_STRING: 12798ee79f4SPrasad Joshi if (unset) 12898ee79f4SPrasad Joshi *(const char **)opt->value = NULL; 12998ee79f4SPrasad Joshi else if (opt->flags & PARSE_OPT_OPTARG && !p->opt) 13098ee79f4SPrasad Joshi *(const char **)opt->value = (const char *)opt->defval; 13198ee79f4SPrasad Joshi else 13298ee79f4SPrasad Joshi return get_arg(p, opt, flags, 13398ee79f4SPrasad Joshi (const char **)opt->value); 13498ee79f4SPrasad Joshi return 0; 13598ee79f4SPrasad Joshi 13698ee79f4SPrasad Joshi case OPTION_CALLBACK: 13798ee79f4SPrasad Joshi if (unset) 13898ee79f4SPrasad Joshi return (*opt->callback)(opt, NULL, 1) ? (-1) : 0; 13998ee79f4SPrasad Joshi if (opt->flags & PARSE_OPT_NOARG) 14098ee79f4SPrasad Joshi return (*opt->callback)(opt, NULL, 0) ? (-1) : 0; 14198ee79f4SPrasad Joshi if (opt->flags & PARSE_OPT_OPTARG && !p->opt) 14298ee79f4SPrasad Joshi return (*opt->callback)(opt, NULL, 0) ? (-1) : 0; 14398ee79f4SPrasad Joshi if (get_arg(p, opt, flags, &arg)) 14498ee79f4SPrasad Joshi return -1; 14598ee79f4SPrasad Joshi return (*opt->callback)(opt, arg, 0) ? (-1) : 0; 14698ee79f4SPrasad Joshi 14798ee79f4SPrasad Joshi case OPTION_INTEGER: 14898ee79f4SPrasad Joshi if (unset) { 14998ee79f4SPrasad Joshi *(int *)opt->value = 0; 15098ee79f4SPrasad Joshi return 0; 15198ee79f4SPrasad Joshi } 15298ee79f4SPrasad Joshi if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { 15398ee79f4SPrasad Joshi *(int *)opt->value = opt->defval; 15498ee79f4SPrasad Joshi return 0; 15598ee79f4SPrasad Joshi } 15698ee79f4SPrasad Joshi if (get_arg(p, opt, flags, &arg)) 15798ee79f4SPrasad Joshi return -1; 158dec94ae9SCyrill Gorcunov return readnum(opt, flags, arg, (char **)&s); 15998ee79f4SPrasad Joshi 16098ee79f4SPrasad Joshi case OPTION_UINTEGER: 16198ee79f4SPrasad Joshi if (unset) { 16298ee79f4SPrasad Joshi *(unsigned int *)opt->value = 0; 16398ee79f4SPrasad Joshi return 0; 16498ee79f4SPrasad Joshi } 16598ee79f4SPrasad Joshi if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { 16698ee79f4SPrasad Joshi *(unsigned int *)opt->value = opt->defval; 16798ee79f4SPrasad Joshi return 0; 16898ee79f4SPrasad Joshi } 16998ee79f4SPrasad Joshi if (get_arg(p, opt, flags, &arg)) 17098ee79f4SPrasad Joshi return -1; 171dec94ae9SCyrill Gorcunov return readnum(opt, flags, arg, (char **)&s); 17298ee79f4SPrasad Joshi 17398ee79f4SPrasad Joshi case OPTION_LONG: 17498ee79f4SPrasad Joshi if (unset) { 17598ee79f4SPrasad Joshi *(long *)opt->value = 0; 17698ee79f4SPrasad Joshi return 0; 17798ee79f4SPrasad Joshi } 17898ee79f4SPrasad Joshi if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { 17998ee79f4SPrasad Joshi *(long *)opt->value = opt->defval; 18098ee79f4SPrasad Joshi return 0; 18198ee79f4SPrasad Joshi } 18298ee79f4SPrasad Joshi if (get_arg(p, opt, flags, &arg)) 18398ee79f4SPrasad Joshi return -1; 184dec94ae9SCyrill Gorcunov return readnum(opt, flags, arg, (char **)&s); 18598ee79f4SPrasad Joshi 18698ee79f4SPrasad Joshi case OPTION_U64: 18798ee79f4SPrasad Joshi if (unset) { 18898ee79f4SPrasad Joshi *(u64 *)opt->value = 0; 18998ee79f4SPrasad Joshi return 0; 19098ee79f4SPrasad Joshi } 19198ee79f4SPrasad Joshi if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { 19298ee79f4SPrasad Joshi *(u64 *)opt->value = opt->defval; 19398ee79f4SPrasad Joshi return 0; 19498ee79f4SPrasad Joshi } 19598ee79f4SPrasad Joshi if (get_arg(p, opt, flags, &arg)) 19698ee79f4SPrasad Joshi return -1; 197dec94ae9SCyrill Gorcunov return readnum(opt, flags, arg, (char **)&s); 19898ee79f4SPrasad Joshi 19998ee79f4SPrasad Joshi case OPTION_END: 20098ee79f4SPrasad Joshi case OPTION_ARGUMENT: 20198ee79f4SPrasad Joshi case OPTION_GROUP: 20298ee79f4SPrasad Joshi default: 20398ee79f4SPrasad Joshi die("should not happen, someone must be hit on the forehead"); 20498ee79f4SPrasad Joshi } 20598ee79f4SPrasad Joshi } 20698ee79f4SPrasad Joshi 20798ee79f4SPrasad Joshi #define USAGE_OPTS_WIDTH 24 20898ee79f4SPrasad Joshi #define USAGE_GAP 2 20998ee79f4SPrasad Joshi 21098ee79f4SPrasad Joshi static int usage_with_options_internal(const char * const *usagestr, 21198ee79f4SPrasad Joshi const struct option *opts, int full) 21298ee79f4SPrasad Joshi { 21398ee79f4SPrasad Joshi if (!usagestr) 21498ee79f4SPrasad Joshi return PARSE_OPT_HELP; 21598ee79f4SPrasad Joshi 21698ee79f4SPrasad Joshi fprintf(stderr, "\n usage: %s\n", *usagestr++); 21798ee79f4SPrasad Joshi while (*usagestr && **usagestr) 21898ee79f4SPrasad Joshi fprintf(stderr, " or: %s\n", *usagestr++); 21998ee79f4SPrasad Joshi while (*usagestr) { 22098ee79f4SPrasad Joshi fprintf(stderr, "%s%s\n", 22198ee79f4SPrasad Joshi **usagestr ? " " : "", 22298ee79f4SPrasad Joshi *usagestr); 22398ee79f4SPrasad Joshi usagestr++; 22498ee79f4SPrasad Joshi } 22598ee79f4SPrasad Joshi 22698ee79f4SPrasad Joshi if (opts->type != OPTION_GROUP) 22798ee79f4SPrasad Joshi fputc('\n', stderr); 22898ee79f4SPrasad Joshi 22998ee79f4SPrasad Joshi for (; opts->type != OPTION_END; opts++) { 23098ee79f4SPrasad Joshi size_t pos; 23198ee79f4SPrasad Joshi int pad; 23298ee79f4SPrasad Joshi 23398ee79f4SPrasad Joshi if (opts->type == OPTION_GROUP) { 23498ee79f4SPrasad Joshi fputc('\n', stderr); 23598ee79f4SPrasad Joshi if (*opts->help) 23698ee79f4SPrasad Joshi fprintf(stderr, "%s\n", opts->help); 23798ee79f4SPrasad Joshi continue; 23898ee79f4SPrasad Joshi } 23998ee79f4SPrasad Joshi if (!full && (opts->flags & PARSE_OPT_HIDDEN)) 24098ee79f4SPrasad Joshi continue; 24198ee79f4SPrasad Joshi 24298ee79f4SPrasad Joshi pos = fprintf(stderr, " "); 24398ee79f4SPrasad Joshi if (opts->short_name) 24498ee79f4SPrasad Joshi pos += fprintf(stderr, "-%c", opts->short_name); 24598ee79f4SPrasad Joshi else 24698ee79f4SPrasad Joshi pos += fprintf(stderr, " "); 24798ee79f4SPrasad Joshi 24898ee79f4SPrasad Joshi if (opts->long_name && opts->short_name) 24998ee79f4SPrasad Joshi pos += fprintf(stderr, ", "); 25098ee79f4SPrasad Joshi if (opts->long_name) 25198ee79f4SPrasad Joshi pos += fprintf(stderr, "--%s", opts->long_name); 25298ee79f4SPrasad Joshi 25398ee79f4SPrasad Joshi switch (opts->type) { 25498ee79f4SPrasad Joshi case OPTION_ARGUMENT: 25598ee79f4SPrasad Joshi break; 25698ee79f4SPrasad Joshi case OPTION_LONG: 25798ee79f4SPrasad Joshi case OPTION_U64: 25898ee79f4SPrasad Joshi case OPTION_INTEGER: 25998ee79f4SPrasad Joshi case OPTION_UINTEGER: 26098ee79f4SPrasad Joshi if (opts->flags & PARSE_OPT_OPTARG) 26198ee79f4SPrasad Joshi if (opts->long_name) 26298ee79f4SPrasad Joshi pos += fprintf(stderr, "[=<n>]"); 26398ee79f4SPrasad Joshi else 26498ee79f4SPrasad Joshi pos += fprintf(stderr, "[<n>]"); 26598ee79f4SPrasad Joshi else 26698ee79f4SPrasad Joshi pos += fprintf(stderr, " <n>"); 26798ee79f4SPrasad Joshi break; 26898ee79f4SPrasad Joshi case OPTION_CALLBACK: 26998ee79f4SPrasad Joshi if (opts->flags & PARSE_OPT_NOARG) 27098ee79f4SPrasad Joshi break; 27198ee79f4SPrasad Joshi /* FALLTHROUGH */ 27298ee79f4SPrasad Joshi case OPTION_STRING: 27398ee79f4SPrasad Joshi if (opts->argh) { 27498ee79f4SPrasad Joshi if (opts->flags & PARSE_OPT_OPTARG) 27598ee79f4SPrasad Joshi if (opts->long_name) 27698ee79f4SPrasad Joshi pos += fprintf(stderr, "[=<%s>]", opts->argh); 27798ee79f4SPrasad Joshi else 27898ee79f4SPrasad Joshi pos += fprintf(stderr, "[<%s>]", opts->argh); 27998ee79f4SPrasad Joshi else 28098ee79f4SPrasad Joshi pos += fprintf(stderr, " <%s>", opts->argh); 28198ee79f4SPrasad Joshi } else { 28298ee79f4SPrasad Joshi if (opts->flags & PARSE_OPT_OPTARG) 28398ee79f4SPrasad Joshi if (opts->long_name) 28498ee79f4SPrasad Joshi pos += fprintf(stderr, "[=...]"); 28598ee79f4SPrasad Joshi else 28698ee79f4SPrasad Joshi pos += fprintf(stderr, "[...]"); 28798ee79f4SPrasad Joshi else 28898ee79f4SPrasad Joshi pos += fprintf(stderr, " ..."); 28998ee79f4SPrasad Joshi } 29098ee79f4SPrasad Joshi break; 29198ee79f4SPrasad Joshi default: /* OPTION_{BIT,BOOLEAN,SET_UINT,SET_PTR} */ 29298ee79f4SPrasad Joshi case OPTION_END: 29398ee79f4SPrasad Joshi case OPTION_GROUP: 29498ee79f4SPrasad Joshi case OPTION_BIT: 29598ee79f4SPrasad Joshi case OPTION_BOOLEAN: 29698ee79f4SPrasad Joshi case OPTION_INCR: 29798ee79f4SPrasad Joshi case OPTION_SET_UINT: 29898ee79f4SPrasad Joshi case OPTION_SET_PTR: 29998ee79f4SPrasad Joshi break; 30098ee79f4SPrasad Joshi } 30198ee79f4SPrasad Joshi if (pos <= USAGE_OPTS_WIDTH) 30298ee79f4SPrasad Joshi pad = USAGE_OPTS_WIDTH - pos; 30398ee79f4SPrasad Joshi else { 30498ee79f4SPrasad Joshi fputc('\n', stderr); 30598ee79f4SPrasad Joshi pad = USAGE_OPTS_WIDTH; 30698ee79f4SPrasad Joshi } 30798ee79f4SPrasad Joshi fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help); 30898ee79f4SPrasad Joshi } 30998ee79f4SPrasad Joshi fputc('\n', stderr); 31098ee79f4SPrasad Joshi 31198ee79f4SPrasad Joshi return PARSE_OPT_HELP; 31298ee79f4SPrasad Joshi } 31398ee79f4SPrasad Joshi 31498ee79f4SPrasad Joshi void usage_with_options(const char * const *usagestr, 31598ee79f4SPrasad Joshi const struct option *opts) 31698ee79f4SPrasad Joshi { 31798ee79f4SPrasad Joshi usage_with_options_internal(usagestr, opts, 0); 31898ee79f4SPrasad Joshi exit(129); 31998ee79f4SPrasad Joshi } 32098ee79f4SPrasad Joshi 32198ee79f4SPrasad Joshi static void check_typos(const char *arg, const struct option *options) 32298ee79f4SPrasad Joshi { 32398ee79f4SPrasad Joshi if (strlen(arg) < 3) 32498ee79f4SPrasad Joshi return; 32598ee79f4SPrasad Joshi 32698ee79f4SPrasad Joshi if (!prefixcmp(arg, "no-")) { 327*599ed2a8SCyrill Gorcunov pr_err("did you mean `--%s` (with two dashes ?)", arg); 32898ee79f4SPrasad Joshi exit(129); 32998ee79f4SPrasad Joshi } 33098ee79f4SPrasad Joshi 33198ee79f4SPrasad Joshi for (; options->type != OPTION_END; options++) { 33298ee79f4SPrasad Joshi if (!options->long_name) 33398ee79f4SPrasad Joshi continue; 33498ee79f4SPrasad Joshi if (!prefixcmp(options->long_name, arg)) { 335*599ed2a8SCyrill Gorcunov pr_err("did you mean `--%s` (with two dashes ?)", arg); 33698ee79f4SPrasad Joshi exit(129); 33798ee79f4SPrasad Joshi } 33898ee79f4SPrasad Joshi } 33998ee79f4SPrasad Joshi } 34098ee79f4SPrasad Joshi 34198ee79f4SPrasad Joshi static int parse_options_usage(const char * const *usagestr, 34298ee79f4SPrasad Joshi const struct option *opts) 34398ee79f4SPrasad Joshi { 34498ee79f4SPrasad Joshi return usage_with_options_internal(usagestr, opts, 0); 34598ee79f4SPrasad Joshi } 34698ee79f4SPrasad Joshi 34798ee79f4SPrasad Joshi static int parse_short_opt(struct parse_opt_ctx_t *p, 34898ee79f4SPrasad Joshi const struct option *options) 34998ee79f4SPrasad Joshi { 35098ee79f4SPrasad Joshi for (; options->type != OPTION_END; options++) { 35198ee79f4SPrasad Joshi if (options->short_name == *p->opt) { 35298ee79f4SPrasad Joshi p->opt = p->opt[1] ? p->opt + 1 : NULL; 35398ee79f4SPrasad Joshi return get_value(p, options, OPT_SHORT); 35498ee79f4SPrasad Joshi } 35598ee79f4SPrasad Joshi } 35698ee79f4SPrasad Joshi return -2; 35798ee79f4SPrasad Joshi } 35898ee79f4SPrasad Joshi 35998ee79f4SPrasad Joshi static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg, 36098ee79f4SPrasad Joshi const struct option *options) 36198ee79f4SPrasad Joshi { 36298ee79f4SPrasad Joshi const char *arg_end = strchr(arg, '='); 36398ee79f4SPrasad Joshi const struct option *abbrev_option = NULL, *ambiguous_option = NULL; 36498ee79f4SPrasad Joshi int abbrev_flags = 0, ambiguous_flags = 0; 36598ee79f4SPrasad Joshi 36698ee79f4SPrasad Joshi if (!arg_end) 36798ee79f4SPrasad Joshi arg_end = arg + strlen(arg); 36898ee79f4SPrasad Joshi 36998ee79f4SPrasad Joshi for (; options->type != OPTION_END; options++) { 37098ee79f4SPrasad Joshi const char *rest; 37198ee79f4SPrasad Joshi int flags = 0; 37298ee79f4SPrasad Joshi 37398ee79f4SPrasad Joshi if (!options->long_name) 37498ee79f4SPrasad Joshi continue; 37598ee79f4SPrasad Joshi 37698ee79f4SPrasad Joshi rest = skip_prefix(arg, options->long_name); 37798ee79f4SPrasad Joshi if (options->type == OPTION_ARGUMENT) { 37898ee79f4SPrasad Joshi if (!rest) 37998ee79f4SPrasad Joshi continue; 38098ee79f4SPrasad Joshi if (*rest == '=') 38198ee79f4SPrasad Joshi return opterror(options, "takes no value", 38298ee79f4SPrasad Joshi flags); 38398ee79f4SPrasad Joshi if (*rest) 38498ee79f4SPrasad Joshi continue; 38598ee79f4SPrasad Joshi p->out[p->cpidx++] = arg - 2; 38698ee79f4SPrasad Joshi return 0; 38798ee79f4SPrasad Joshi } 38898ee79f4SPrasad Joshi if (!rest) { 38998ee79f4SPrasad Joshi /* abbreviated? */ 39098ee79f4SPrasad Joshi if (!strncmp(options->long_name, arg, arg_end - arg)) { 39198ee79f4SPrasad Joshi is_abbreviated: 39298ee79f4SPrasad Joshi if (abbrev_option) { 39398ee79f4SPrasad Joshi /* 39498ee79f4SPrasad Joshi * If this is abbreviated, it is 39598ee79f4SPrasad Joshi * ambiguous. So when there is no 39698ee79f4SPrasad Joshi * exact match later, we need to 39798ee79f4SPrasad Joshi * error out. 39898ee79f4SPrasad Joshi */ 39998ee79f4SPrasad Joshi ambiguous_option = abbrev_option; 40098ee79f4SPrasad Joshi ambiguous_flags = abbrev_flags; 40198ee79f4SPrasad Joshi } 40298ee79f4SPrasad Joshi if (!(flags & OPT_UNSET) && *arg_end) 40398ee79f4SPrasad Joshi p->opt = arg_end + 1; 40498ee79f4SPrasad Joshi abbrev_option = options; 40598ee79f4SPrasad Joshi abbrev_flags = flags; 40698ee79f4SPrasad Joshi continue; 40798ee79f4SPrasad Joshi } 40898ee79f4SPrasad Joshi /* negated and abbreviated very much? */ 40998ee79f4SPrasad Joshi if (!prefixcmp("no-", arg)) { 41098ee79f4SPrasad Joshi flags |= OPT_UNSET; 41198ee79f4SPrasad Joshi goto is_abbreviated; 41298ee79f4SPrasad Joshi } 41398ee79f4SPrasad Joshi /* negated? */ 41498ee79f4SPrasad Joshi if (strncmp(arg, "no-", 3)) 41598ee79f4SPrasad Joshi continue; 41698ee79f4SPrasad Joshi flags |= OPT_UNSET; 41798ee79f4SPrasad Joshi rest = skip_prefix(arg + 3, options->long_name); 41898ee79f4SPrasad Joshi /* abbreviated and negated? */ 41998ee79f4SPrasad Joshi if (!rest && !prefixcmp(options->long_name, arg + 3)) 42098ee79f4SPrasad Joshi goto is_abbreviated; 42198ee79f4SPrasad Joshi if (!rest) 42298ee79f4SPrasad Joshi continue; 42398ee79f4SPrasad Joshi } 42498ee79f4SPrasad Joshi if (*rest) { 42598ee79f4SPrasad Joshi if (*rest != '=') 42698ee79f4SPrasad Joshi continue; 42798ee79f4SPrasad Joshi p->opt = rest + 1; 42898ee79f4SPrasad Joshi } 42998ee79f4SPrasad Joshi return get_value(p, options, flags); 43098ee79f4SPrasad Joshi } 43198ee79f4SPrasad Joshi 43298ee79f4SPrasad Joshi if (ambiguous_option) 433*599ed2a8SCyrill Gorcunov return pr_err("Ambiguous option: %s " 43498ee79f4SPrasad Joshi "(could be --%s%s or --%s%s)", 43598ee79f4SPrasad Joshi arg, 43698ee79f4SPrasad Joshi (ambiguous_flags & OPT_UNSET) ? "no-" : "", 43798ee79f4SPrasad Joshi ambiguous_option->long_name, 43898ee79f4SPrasad Joshi (abbrev_flags & OPT_UNSET) ? "no-" : "", 43998ee79f4SPrasad Joshi abbrev_option->long_name); 44098ee79f4SPrasad Joshi if (abbrev_option) 44198ee79f4SPrasad Joshi return get_value(p, abbrev_option, abbrev_flags); 44298ee79f4SPrasad Joshi return -2; 44398ee79f4SPrasad Joshi } 44498ee79f4SPrasad Joshi 44598ee79f4SPrasad Joshi 44698ee79f4SPrasad Joshi static void parse_options_start(struct parse_opt_ctx_t *ctx, int argc, 44798ee79f4SPrasad Joshi const char **argv, int flags) 44898ee79f4SPrasad Joshi { 44998ee79f4SPrasad Joshi memset(ctx, 0, sizeof(*ctx)); 45098ee79f4SPrasad Joshi ctx->argc = argc; 45198ee79f4SPrasad Joshi ctx->argv = argv; 45298ee79f4SPrasad Joshi ctx->out = argv; 45398ee79f4SPrasad Joshi ctx->cpidx = ((flags & PARSE_OPT_KEEP_ARGV0) != 0); 45498ee79f4SPrasad Joshi ctx->flags = flags; 45598ee79f4SPrasad Joshi if ((flags & PARSE_OPT_KEEP_UNKNOWN) && 45698ee79f4SPrasad Joshi (flags & PARSE_OPT_STOP_AT_NON_OPTION)) 45798ee79f4SPrasad Joshi die("STOP_AT_NON_OPTION and KEEP_UNKNOWN don't go together"); 45898ee79f4SPrasad Joshi } 45998ee79f4SPrasad Joshi 46098ee79f4SPrasad Joshi static int parse_options_end(struct parse_opt_ctx_t *ctx) 46198ee79f4SPrasad Joshi { 46298ee79f4SPrasad Joshi memmove(ctx->out + ctx->cpidx, ctx->argv, ctx->argc * sizeof(*ctx->out)); 46398ee79f4SPrasad Joshi ctx->out[ctx->cpidx + ctx->argc] = NULL; 46498ee79f4SPrasad Joshi return ctx->cpidx + ctx->argc; 46598ee79f4SPrasad Joshi } 46698ee79f4SPrasad Joshi 46798ee79f4SPrasad Joshi 46898ee79f4SPrasad Joshi static int parse_options_step(struct parse_opt_ctx_t *ctx, 46998ee79f4SPrasad Joshi const struct option *options, const char * const usagestr[]) 47098ee79f4SPrasad Joshi { 47198ee79f4SPrasad Joshi int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP); 47298ee79f4SPrasad Joshi 47398ee79f4SPrasad Joshi /* we must reset ->opt, unknown short option leave it dangling */ 47498ee79f4SPrasad Joshi ctx->opt = NULL; 47598ee79f4SPrasad Joshi 47698ee79f4SPrasad Joshi for (; ctx->argc; ctx->argc--, ctx->argv++) { 47798ee79f4SPrasad Joshi const char *arg = ctx->argv[0]; 47898ee79f4SPrasad Joshi 47998ee79f4SPrasad Joshi if (*arg != '-' || !arg[1]) { 48098ee79f4SPrasad Joshi if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION) 48198ee79f4SPrasad Joshi break; 48298ee79f4SPrasad Joshi ctx->out[ctx->cpidx++] = ctx->argv[0]; 48398ee79f4SPrasad Joshi continue; 48498ee79f4SPrasad Joshi } 48598ee79f4SPrasad Joshi 48698ee79f4SPrasad Joshi if (arg[1] != '-') { 48798ee79f4SPrasad Joshi ctx->opt = arg + 1; 48898ee79f4SPrasad Joshi if (internal_help && *ctx->opt == 'h') 48998ee79f4SPrasad Joshi return parse_options_usage(usagestr, options); 49098ee79f4SPrasad Joshi switch (parse_short_opt(ctx, options)) { 49198ee79f4SPrasad Joshi case -1: 49298ee79f4SPrasad Joshi return parse_options_usage(usagestr, options); 49398ee79f4SPrasad Joshi case -2: 49498ee79f4SPrasad Joshi goto unknown; 49598ee79f4SPrasad Joshi default: 49698ee79f4SPrasad Joshi break; 49798ee79f4SPrasad Joshi } 49898ee79f4SPrasad Joshi if (ctx->opt) 49998ee79f4SPrasad Joshi check_typos(arg + 1, options); 50098ee79f4SPrasad Joshi while (ctx->opt) { 50198ee79f4SPrasad Joshi if (internal_help && *ctx->opt == 'h') 50298ee79f4SPrasad Joshi return parse_options_usage(usagestr, 50398ee79f4SPrasad Joshi options); 50498ee79f4SPrasad Joshi switch (parse_short_opt(ctx, options)) { 50598ee79f4SPrasad Joshi case -1: 50698ee79f4SPrasad Joshi return parse_options_usage(usagestr, 50798ee79f4SPrasad Joshi options); 50898ee79f4SPrasad Joshi case -2: 50998ee79f4SPrasad Joshi /* fake a short option thing to hide 51098ee79f4SPrasad Joshi * the fact that we may have 51198ee79f4SPrasad Joshi * started to parse aggregated stuff 51298ee79f4SPrasad Joshi * 51398ee79f4SPrasad Joshi * This is leaky, too bad. 51498ee79f4SPrasad Joshi */ 51598ee79f4SPrasad Joshi ctx->argv[0] = strdup(ctx->opt - 1); 51698ee79f4SPrasad Joshi *(char *)ctx->argv[0] = '-'; 51798ee79f4SPrasad Joshi goto unknown; 51898ee79f4SPrasad Joshi default: 51998ee79f4SPrasad Joshi break; 52098ee79f4SPrasad Joshi } 52198ee79f4SPrasad Joshi } 52298ee79f4SPrasad Joshi continue; 52398ee79f4SPrasad Joshi } 52498ee79f4SPrasad Joshi 52598ee79f4SPrasad Joshi if (!arg[2]) { /* "--" */ 52698ee79f4SPrasad Joshi if (!(ctx->flags & PARSE_OPT_KEEP_DASHDASH)) { 52798ee79f4SPrasad Joshi ctx->argc--; 52898ee79f4SPrasad Joshi ctx->argv++; 52998ee79f4SPrasad Joshi } 53098ee79f4SPrasad Joshi break; 53198ee79f4SPrasad Joshi } 53298ee79f4SPrasad Joshi 53398ee79f4SPrasad Joshi if (internal_help && !strcmp(arg + 2, "help-all")) 53498ee79f4SPrasad Joshi return usage_with_options_internal(usagestr, options, 53598ee79f4SPrasad Joshi 1); 53698ee79f4SPrasad Joshi if (internal_help && !strcmp(arg + 2, "help")) 53798ee79f4SPrasad Joshi return parse_options_usage(usagestr, options); 53898ee79f4SPrasad Joshi switch (parse_long_opt(ctx, arg + 2, options)) { 53998ee79f4SPrasad Joshi case -1: 54098ee79f4SPrasad Joshi return parse_options_usage(usagestr, options); 54198ee79f4SPrasad Joshi case -2: 54298ee79f4SPrasad Joshi goto unknown; 54398ee79f4SPrasad Joshi default: 54498ee79f4SPrasad Joshi break; 54598ee79f4SPrasad Joshi } 54698ee79f4SPrasad Joshi continue; 54798ee79f4SPrasad Joshi unknown: 54898ee79f4SPrasad Joshi if (!(ctx->flags & PARSE_OPT_KEEP_UNKNOWN)) 54998ee79f4SPrasad Joshi return PARSE_OPT_UNKNOWN; 55098ee79f4SPrasad Joshi ctx->out[ctx->cpidx++] = ctx->argv[0]; 55198ee79f4SPrasad Joshi ctx->opt = NULL; 55298ee79f4SPrasad Joshi } 55398ee79f4SPrasad Joshi return PARSE_OPT_DONE; 55498ee79f4SPrasad Joshi } 55598ee79f4SPrasad Joshi 55698ee79f4SPrasad Joshi int parse_options(int argc, const char **argv, const struct option *options, 55798ee79f4SPrasad Joshi const char * const usagestr[], int flags) 55898ee79f4SPrasad Joshi { 55998ee79f4SPrasad Joshi struct parse_opt_ctx_t ctx; 56098ee79f4SPrasad Joshi 56198ee79f4SPrasad Joshi parse_options_start(&ctx, argc, argv, flags); 56298ee79f4SPrasad Joshi switch (parse_options_step(&ctx, options, usagestr)) { 56398ee79f4SPrasad Joshi case PARSE_OPT_HELP: 56498ee79f4SPrasad Joshi exit(129); 56598ee79f4SPrasad Joshi case PARSE_OPT_DONE: 56698ee79f4SPrasad Joshi break; 56798ee79f4SPrasad Joshi default: /* PARSE_OPT_UNKNOWN */ 56898ee79f4SPrasad Joshi if (ctx.argv[0][1] == '-') { 569*599ed2a8SCyrill Gorcunov pr_err("unknown option `%s'", ctx.argv[0] + 2); 57098ee79f4SPrasad Joshi } else { 571*599ed2a8SCyrill Gorcunov pr_err("unknown switch `%c'", *ctx.opt); 57298ee79f4SPrasad Joshi } 57398ee79f4SPrasad Joshi usage_with_options(usagestr, options); 57498ee79f4SPrasad Joshi } 57598ee79f4SPrasad Joshi 57698ee79f4SPrasad Joshi return parse_options_end(&ctx); 57798ee79f4SPrasad Joshi } 578