diff options
-rw-r--r-- | lib/args.c | 85 | ||||
-rw-r--r-- | www/code.html | 32 |
2 files changed, 93 insertions, 24 deletions
@@ -15,7 +15,12 @@ // : plus a string argument, keep most recent if more than one // * plus a string argument, appended to a list // # plus a signed long argument -// {LOW,HIGH} - allowed range TODO +// <LOW - die if less than LOW +// >HIGH - die if greater than HIGH +// =DEFAULT - value if not specified +// . plus a double precision floating point argument (with CFG_TOYBOX_FLOAT) +// Chop this out with USE_TOYBOX_FLOAT() around option string +// 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. @@ -28,8 +33,8 @@ // [yz] needs at least one of y or z. TODO // at the beginning: // ^ stop at first nonoption argument -// <0 at least # leftover arguments needed (default 0) -// >9 at most # leftover arguments needed (default MAX_INT) +// <0 die if less than leftover arguments (default 0) +// >9 die if > # leftover arguments (default MAX_INT) // ? Allow unknown arguments (pass them through to command). // & first argument has imaginary dash (ala tar/ps) // If given twice, all arguments have imaginary dash @@ -77,6 +82,10 @@ struct opts { int c; // Short argument character int flags; // |=1, ^=2 char type; // Type of arguments to store + union { + long l; + FLOAT f; + } val[3]; // low, high, default - range of allowed values }; struct longopts { @@ -98,7 +107,6 @@ struct getoptflagstate }; // Parse one command line option. - static int gotflag(struct getoptflagstate *gof) { int type; @@ -138,8 +146,23 @@ static int gotflag(struct getoptflagstate *gof) while (*list) list=&((*list)->next); *list = xzalloc(sizeof(struct arg_list)); (*list)->arg = arg; - } else if (type == '#') *(opt->arg) = atolx((char *)arg); - else if (type == '@') ++*(opt->arg); + } else if (type == '#') { + long l = atolx(arg); + if (l < opt->val[0].l) + error_exit("-%c < %ld", opt->c, opt->val[0].l); + if (l > opt->val[1].l) + error_exit("-%c > %ld", opt->c, opt->val[1].l); + + *(opt->arg) = l; + } else if (CFG_TOYBOX_FLOAT && type == '.') { + FLOAT *f = (FLOAT *)(opt->arg); + + *f = strtod(arg, &arg); + if (opt->val[0].l != LONG_MIN && *f < opt->val[0].f) + error_exit("-%c < %lf", opt->c, (double)opt->val[0].f); + if (opt->val[1].l != LONG_MAX && *f > opt->val[1].f) + error_exit("-%c > %lf", opt->c, (double)opt->val[1].f); + } else if (type == '@') ++*(opt->arg); if (!gof->nodash_now) gof->arg = ""; } @@ -152,11 +175,12 @@ static int gotflag(struct getoptflagstate *gof) void parse_optflaglist(struct getoptflagstate *gof) { - char *options = toys.which->options, *plustildenot = "+~!"; + char *options = toys.which->options, *plustildenot = "+~!", *limits = "<>="; long *nextarg = (long *)&this; struct opts *new = 0; + int i; - // Parse option format + // Parse option format string bzero(gof, sizeof(struct getoptflagstate)); gof->maxargs = INT_MAX; if (!options) return; @@ -172,8 +196,8 @@ void parse_optflaglist(struct getoptflagstate *gof) options++; } - // Parse the rest of the option characters into a linked list - // of options with attributes. + // Parse the rest of the option string into a linked list + // of options with attributes. if (!*options) gof->stopearly++; while (*options) { @@ -184,6 +208,8 @@ void parse_optflaglist(struct getoptflagstate *gof) new = xzalloc(sizeof(struct opts)); new->next = gof->opts; gof->opts = new; + new->val[0].l = LONG_MIN; + new->val[1].l = LONG_MAX; ++*(new->edx); } // Each option must start with "(" or an option character. (Bare @@ -195,7 +221,7 @@ void parse_optflaglist(struct getoptflagstate *gof) // Find the end of the longopt for (end = ++options; *end && *end != ')'; end++); if (CFG_TOYBOX_DEBUG && !*end) - error_exit("Bug1 in get_opt"); + error_exit("(longopt) didn't end"); // Allocate and init a new struct longopts lo = xmalloc(sizeof(struct longopts)); @@ -211,26 +237,39 @@ void parse_optflaglist(struct getoptflagstate *gof) // If this is the start of a new option that wasn't a longopt, - } else if (strchr(":*#@", *options)) { + } else if (strchr(":*#@.", *options)) { if (CFG_TOYBOX_DEBUG && new->type) - error_exit("Bug4 in get_opt"); + error_exit("multiple types %c:%c%c", new->c, new->type, *options); new->type = *options; } else if (0 != (temp = strchr(plustildenot, *options))) { - int i=0, idx = temp - plustildenot; + int idx = temp - plustildenot; struct opts *opt; if (!*++options && CFG_TOYBOX_DEBUG) - error_exit("Bug2 in get_opt"); + error_exit("+~! no target"); // Find this option flag (in previously parsed struct opt) - for (opt = new; ; opt = opt->next) { - if (CFG_TOYBOX_DEBUG && !opt) error_exit("Bug3 in get_opt"); + for (i=0, opt = new; ; opt = opt->next) { + if (CFG_TOYBOX_DEBUG && !opt) + error_exit("+~! unknown target"); if (opt->c == *options) break; i++; } new->edx[idx] |= 1<<i; - } else if (*options == '[') { + } else if (*options == '[') { // TODO } else if (*options == '|') new->flags |= 1; else if (*options == '^') new->flags |= 2; + // bounds checking + else if (0 != (temp = strchr(limits, *options))) { + i = temp - limits; + if (new->type == '#') { + long l = strtol(++options, &temp, 10); + if (temp != options) new->val[i].l = l; + } else if (CFG_TOYBOX_FLOAT && new->type == '.') { + FLOAT f = strtod(++options, &temp); + if (temp != options) new->val[i].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 @@ -248,16 +287,15 @@ void parse_optflaglist(struct getoptflagstate *gof) // Initialize enable/disable/exclude masks and pointers to store arguments. // (We have to calculate all this ahead of time because longopts jump into - // the middle of the list.) + // the middle of the list. We have to do this after creating the list + // because we reverse direction: last entry created gets first global slot.) int pos = 0; for (new = gof->opts; new; new = new->next) { - int i; - for (i=0;i<3;i++) new->edx[i] <<= pos; pos++; if (new->type) { new->arg = (void *)nextarg; - *(nextarg++) = 0; + *(nextarg++) = new->val[2].l; } } } @@ -268,6 +306,9 @@ void get_optflags(void) long saveflags; char *letters[]={"s",""}; + // Option parsing is a two stage process: parse the option string into + // a struct opts list, then use that list to process argv[]; + if (CFG_HELP) toys.exithelp++; // Allocate memory for optargs saveflags = 0; diff --git a/www/code.html b/www/code.html index 541b1019..6eacb40d 100644 --- a/www/code.html +++ b/www/code.html @@ -356,7 +356,7 @@ code at compile time via the optimizer's dead code elimination (which removes from the binary any code that cannot be reached). This saves space without cluttering the code with #ifdefs or leading to configuration dependent build breaks. (See the 1992 Usenix paper -<a href=http://www.chris-lott.org/resources/cstyle/ifdefs.pdf>#ifdef +<a href=http://doc.cat-v.org/henry_spencer/ifdef_considered_harmful.pdf>#ifdef Considered Harmful</a> for more information.)</p> <p>USE_SYMBOL(code) evaluates to the code in parentheses when the symbol @@ -547,8 +547,20 @@ argument letter, indicating the option takes an additional argument:</p> <ul> <li><b>:</b> - plus a string argument, keep most recent if more than one.</li> <li><b>*</b> - plus a string argument, appended to a linked list.</li> -<li><b>#</b> - plus a singed long argument. A {LOW,HIGH} range can also be appended to restrict allowed values of argument.</li> <li><b>@</b> - plus an occurrence counter (stored in a long)</li> +<li><b>#</b> - plus a signed long argument. +<li><b>.</b> - plus a floating point argument (if CFG_TOYBOX_FLOAT).</li> +<ul>The following can be appended to a float or double: +<li><b><123</b> - error if argument is less than this</li> +<li><b>>123</b> - error if argument is greater than this</li> +<li><b>=123</b> - default value if argument not supplied</li> +</ul> +<ul><li>Option parsing only understands <>= after . when CFG_TOYBOX_FLOAT +is enabled. (Otherwise the code to determine where floating point constants +end drops out. When disabled, it can reserve a global data slot for the +argument so offsets won't change, but will never fill it out.). You can handle +this by using the USE_BLAH() macros with C string concatenation, ala: +"abc." USE_TOYBOX_FLOAT("<1.23>4.56=7.89") "def"</li></ul> </ul> <p>Arguments may occur with or without a space (I.E. "-a 42" or "-a42"). @@ -614,6 +626,22 @@ optflag, but letters are never control characters.)</p> <li><b>[yz]</b> this option requires at least one of y or z to also be enabled.</li> </ul> +<p>The following may be appended to a float or double:</p> + +<ul> +<li><b><123</b> - error if argument is less than this</li> +<li><b>>123</b> - error if argument is greater than this</li> +<li><b>=123</b> - default value if argument not supplied</li> +</ul> + +<p>Option parsing only understands <>= after . when CFG_TOYBOX_FLOAT +is enabled. (Otherwise the code to determine where floating point constants +end drops out. When disabled, it can reserve a global data slot for the +argument so offsets won't change, but will never fill it out.). You can handle +this by using the USE_BLAH() macros with C string concatenation, ala:</p> + +<blockquote>"abc." USE_TOYBOX_FLOAT("<1.23>4.56=7.89") "def"</blockquote> + <p><b>--longopts</b></p> <p>The optflags string can contain long options, which are enclosed in |