Mercurial > hg > toybox
changeset 1036:a25239480e7a draft
Improve --longopt parsing: general bugfixes, better error reporting, new ; option for optional arguments only suppliable with =.
author | Rob Landley <rob@landley.net> |
---|---|
date | Sun, 01 Sep 2013 07:50:32 -0500 |
parents | 6b4529a50b72 |
children | af1780148f7c |
files | lib/args.c toys/other/hello.c |
diffstat | 2 files changed, 35 insertions(+), 24 deletions(-) [+] |
line wrap: on
line diff
--- a/lib/args.c Sun Sep 01 07:25:37 2013 -0500 +++ b/lib/args.c Sun Sep 01 07:50:32 2013 -0500 @@ -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 @@ } // 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 @@ // 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 @@ } // 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 @@ 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 @@ 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 @@ 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 @@ // Should we handle this --longopt as a non-option argument? if (!lo && gof.noerror) { - gof.arg-=2; + gof.arg -= 2; goto notflag; }
--- a/toys/other/hello.c Sun Sep 01 07:25:37 2013 -0500 +++ b/toys/other/hello.c Sun Sep 01 07:50:32 2013 -0500 @@ -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 @@ long c_number; struct arg_list *d_list; long e_count; + char *also_string; char *blubber_string; int more_globals;