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>&lt;123</b> - error if argument is less than this</li>
+<li><b>&gt;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>&lt;123</b> - error if argument is less than this</li>
+<li><b>&gt;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