diff options
author | Rob Landley <rob@landley.net> | 2013-09-01 07:50:32 -0500 |
---|---|---|
committer | Rob Landley <rob@landley.net> | 2013-09-01 07:50:32 -0500 |
commit | b7162a47e71a69e30bb9103b5caae72e5097f68f (patch) | |
tree | bebe96c75bc437365ca06ee5ef2120850aeda4b8 | |
parent | c166faf9c14b377f7005d97abd4f0658b3bbc7ce (diff) | |
download | toybox-b7162a47e71a69e30bb9103b5caae72e5097f68f.tar.gz |
Improve --longopt parsing: general bugfixes, better error reporting, new ; option for optional arguments only suppliable with =.
-rw-r--r-- | lib/args.c | 56 | ||||
-rw-r--r-- | toys/other/hello.c | 3 |
2 files changed, 35 insertions, 24 deletions
@@ -23,7 +23,8 @@ // Same <LOW>HIGH=DEFAULT as # // @ plus an occurrence counter (which is a long) // (longopt) -// | this is required. If more than one marked, only one required. TODO +// | this is required. If more than one marked, only one required. +// ; long option's argument is optional, can only be supplied with --opt= // ^ Stop parsing after encountering this argument // " " (space char) the "plus an argument" must be separate // I.E. "-j 3" not "-j3". So "kill -stop" != "kill -s top" @@ -140,7 +141,10 @@ static int gotflag(struct getoptflagstate *gof, struct opts *opt) } // Does this option take an argument? - gof->arg++; + if (!gof->arg) { + if (opt->flags & 8) return 0; + gof->arg = ""; + } else gof->arg++; type = opt->type; if (type == '@') ++*(opt->arg); @@ -151,9 +155,17 @@ static int gotflag(struct getoptflagstate *gof, struct opts *opt) // to make "tar xCjfv blah1 blah2 thingy" work like // "tar -x -C blah1 -j -f blah2 -v thingy" - if (gof->nodash_now || !arg[0]) arg = toys.argv[++gof->argc]; - // TODO: The following line doesn't display --longopt correctly - if (!arg) error_exit("Missing argument to -%c", opt->c); + if (gof->nodash_now || (!arg[0] && !(opt->flags & 8))) + arg = toys.argv[++gof->argc]; + if (!arg) { + char *s = "Missing argument to "; + struct longopts *lo; + + if (opt->c != -1) error_exit("%s-%c", s, opt->c); + + for (lo = gof->longopts; lo->opt != opt; lo = lo->next); + error_exit("%s--%.*s", s, lo->len, lo->str); + } if (type == ':') *(opt->arg) = (long)arg; else if (type == '*') { @@ -230,27 +242,27 @@ void parse_optflaglist(struct getoptflagstate *gof) } // Each option must start with "(" or an option character. (Bare // longopts only come at the start of the string.) - if (*options == '(') { + if (*options == '(' && new->c != -1) { char *end; - struct longopts *lo = xmalloc(sizeof(struct longopts)); + struct longopts *lo; // Find the end of the longopt for (end = ++options; *end && *end != ')'; end++); if (CFG_TOYBOX_DEBUG && !*end) error_exit("(longopt) didn't end"); // init a new struct longopts + lo = xmalloc(sizeof(struct longopts)); lo->next = gof->longopts; lo->opt = new; lo->str = options; lo->len = end-options; gof->longopts = lo; - options = end; + options = ++end; // Mark this struct opt as used, even when no short opt. - if (!new->c) { - new->c = -1; - new = 0; - } + if (!new->c) new->c = -1; + + continue; // If this is the start of a new option that wasn't a longopt, @@ -258,7 +270,7 @@ void parse_optflaglist(struct getoptflagstate *gof) if (CFG_TOYBOX_DEBUG && new->type) error_exit("multiple types %c:%c%c", new->c, new->type, *options); new->type = *options; - } else if (-1 != (idx = stridx("|^ ", *options))) new->flags |= 1<<idx; + } else if (-1 != (idx = stridx("|^ ;", *options))) new->flags |= 1<<idx; // bounds checking else if (-1 != (idx = stridx("<>=", *options))) { if (new->type == '#') { @@ -269,14 +281,13 @@ void parse_optflaglist(struct getoptflagstate *gof) if (temp != options) new->val[idx].f = f; } else if (CFG_TOYBOX_DEBUG) error_exit("<>= only after .#"); options = --temp; - } // At this point, we've hit the end of the previous option. The // current character is the start of a new option. If we've already // assigned an option to this struct, loop to allocate a new one. // (It'll get back here afterwards and fall through to next else.) - else if (new->c) { - new = NULL; + } else if (new->c) { + new = 0; continue; // Claim this option, loop to see what's after it. @@ -382,16 +393,15 @@ void get_optflags(void) gof.stopearly += 2; continue; } - // Handle --longopt + // do we match a known --longopt? for (lo = gof.longopts; lo; lo = lo->next) { if (!strncmp(gof.arg, lo->str, lo->len)) { - if (gof.arg[lo->len]) { - if (gof.arg[lo->len]=='=' && lo->opt->type) gof.arg += lo->len; - else continue; - } + if (!gof.arg[lo->len]) gof.arg = 0; + else if (gof.arg[lo->len] == '=' && lo->opt->type) + gof.arg += lo->len; + else continue; // It's a match. - gof.arg = ""; catch = lo->opt; break; } @@ -399,7 +409,7 @@ void get_optflags(void) // Should we handle this --longopt as a non-option argument? if (!lo && gof.noerror) { - gof.arg-=2; + gof.arg -= 2; goto notflag; } diff --git a/toys/other/hello.c b/toys/other/hello.c index a6bc69eb..e87cd6c9 100644 --- a/toys/other/hello.c +++ b/toys/other/hello.c @@ -7,7 +7,7 @@ // Accept many different kinds of command line argument: -USE_HELLO(NEWTOY(hello, "(walrus)(blubber):e@d*c#b:a", TOYFLAG_USR|TOYFLAG_BIN)) +USE_HELLO(NEWTOY(hello, "(walrus)(blubber):;(also):e@d*c#b:a", TOYFLAG_USR|TOYFLAG_BIN)) config HELLO bool "hello" @@ -31,6 +31,7 @@ GLOBALS( long c_number; struct arg_list *d_list; long e_count; + char *also_string; char *blubber_string; int more_globals; |