Mercurial > hg > toybox
changeset 415:2cbbd4c5eaaa
Add <>= to lib/args.c, with documentation.
author | Rob Landley <rob@landley.net> |
---|---|
date | Sun, 29 Jan 2012 13:54:13 -0600 |
parents | 9204821e6b95 |
children | e9f6e7f25854 |
files | lib/args.c www/code.html |
diffstat | 2 files changed, 93 insertions(+), 24 deletions(-) [+] |
line wrap: on
line diff
--- a/lib/args.c Sat Jan 28 12:07:45 2012 -0600 +++ b/lib/args.c Sun Jan 29 13:54:13 2012 -0600 @@ -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 @@ 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 @@ }; // Parse one command line option. - static int gotflag(struct getoptflagstate *gof) { int type; @@ -138,8 +146,23 @@ 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 @@ 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 @@ 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 @@ 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 @@ // 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 @@ // 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 @@ // 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 @@ 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;
--- a/www/code.html Sat Jan 28 12:07:45 2012 -0600 +++ b/www/code.html Sun Jan 29 13:54:13 2012 -0600 @@ -356,7 +356,7 @@ 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 @@ <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 @@ <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