X-Git-Url: http://git.ozlabs.org/?p=ccan;a=blobdiff_plain;f=ccan%2Fopt%2Fparse.c;h=d227f7bca2ab679033dd920fe426983dc0ea9314;hp=7b7c02c5936f5af60d7b702979473fdf2dbd1820;hb=HEAD;hpb=e34192d580958aaffff3754a4e2bf1eccbb489f8 diff --git a/ccan/opt/parse.c b/ccan/opt/parse.c index 7b7c02c5..b932bf33 100644 --- a/ccan/opt/parse.c +++ b/ccan/opt/parse.c @@ -1,3 +1,4 @@ +/* Licensed under GPLv2+ - see LICENSE file for details */ /* Actual code to parse commandline. */ #include #include @@ -13,7 +14,8 @@ /tmp/opt-example: option requires an argument -- 's' */ static int parse_err(void (*errlog)(const char *fmt, ...), - const char *argv0, const char *arg, unsigned len, + const char *argv0, + const char *arg, unsigned len, const char *problem) { errlog("%s: %.*s: %s", argv0, len, arg, problem); @@ -27,13 +29,63 @@ static void consume_option(int *argc, char *argv[], unsigned optnum) (*argc)--; } +/* This sets the len and o to indicate how far it is into the + * opt_table's names field. */ +static struct opt_table *opt_find_long_extra(const char *arg, + const char **optarg, + unsigned int *len, + const char **o) +{ + unsigned i; + + *optarg = NULL; + for (*o = first_lopt(&i, len); + *o; + *o = next_lopt(*o, &i, len)) { + if (strncmp(arg, *o, *len) != 0) + continue; + if (arg[*len] == '=') + *optarg = arg + *len + 1; + else if (arg[*len] != '\0') + continue; + return &opt_table[i]; + + } + return NULL; +} + +struct opt_table *opt_find_long(const char *arg, const char **optarg) +{ + unsigned len; + const char *o; + + return opt_find_long_extra(arg, optarg ? optarg : &o, &len, &o); +} + +static struct opt_table *opt_find_short_extra(char arg, const char **o) +{ + unsigned i; + for (*o = first_sopt(&i); *o; *o = next_sopt(*o, &i)) { + if (arg == **o) + return &opt_table[i]; + } + return NULL; +} + +struct opt_table *opt_find_short(char arg) +{ + const char *o; + return opt_find_short_extra(arg, &o); +} + /* Returns 1 if argument consumed, 0 if all done, -1 on error. */ -int parse_one(int *argc, char *argv[], unsigned *offset, - void (*errlog)(const char *fmt, ...)) +int parse_one(int *argc, char *argv[], enum opt_type is_early, unsigned *offset, + void (*errlog)(const char *fmt, ...), bool unknown_ok) { - unsigned i, arg, len; + unsigned arg, len; const char *o, *optarg = NULL; - char *problem; + char *problem = NULL; + struct opt_table *ot; if (getenv("POSIXLY_CORRECT")) { /* Don't find options after non-options. */ @@ -57,44 +109,43 @@ int parse_one(int *argc, char *argv[], unsigned *offset, /* Long options start with -- */ if (argv[arg][1] == '-') { assert(*offset == 0); - for (o = first_lopt(&i, &len); o; o = next_lopt(o, &i, &len)) { - if (strncmp(argv[arg] + 2, o, len) != 0) - continue; - if (argv[arg][2 + len] == '=') - optarg = argv[arg] + 2 + len + 1; - else if (argv[arg][2 + len] != '\0') - continue; - break; - } - if (!o) + + ot = opt_find_long_extra(argv[arg]+2, &optarg, &len, &o); + if (!ot) { + if (unknown_ok) + goto ok; return parse_err(errlog, argv[0], argv[arg], strlen(argv[arg]), "unrecognized option"); + } + /* For error messages, we include the leading '--' */ o -= 2; len += 2; } else { - /* offset allows us to handle -abc */ - for (o = first_sopt(&i); o; o = next_sopt(o, &i)) { - if (argv[arg][*offset + 1] != *o) - continue; - (*offset)++; - break; - } - if (!o) + ot = opt_find_short_extra(argv[arg][*offset + 1], &o); + if (!ot) { + if (unknown_ok) { + (*offset)++; + goto ok; + } return parse_err(errlog, argv[0], argv[arg], strlen(argv[arg]), "unrecognized option"); + } + + (*offset)++; /* For error messages, we include the leading '-' */ o--; len = 2; } - if (opt_table[i].type == OPT_NOARG) { + if (ot->type & OPT_NOARG) { if (optarg) return parse_err(errlog, argv[0], o, len, "doesn't allow an argument"); - problem = opt_table[i].cb(opt_table[i].u.arg); + if ((ot->type & OPT_EARLY) == is_early) + problem = ot->cb(ot->u.arg); } else { if (!optarg) { /* Swallow any short options as optarg, eg -afile */ @@ -107,15 +158,17 @@ int parse_one(int *argc, char *argv[], unsigned *offset, if (!optarg) return parse_err(errlog, argv[0], o, len, "requires an argument"); - problem = opt_table[i].cb_arg(optarg, opt_table[i].u.arg); + if ((ot->type & OPT_EARLY) == is_early) + problem = ot->cb_arg(optarg, ot->u.arg); } if (problem) { parse_err(errlog, argv[0], o, len, problem); - free(problem); + opt_alloc.free(problem); return -1; } +ok: /* If no more letters in that short opt, reset offset. */ if (*offset && !argv[arg][*offset + 1]) *offset = 0;