Split out parse_optflaglist(), and move local variables to optflagstate.
authorRob Landley <rob@landley.net>
Wed, 30 Nov 2011 13:21:23 +0000 (07:21 -0600)
committerRob Landley <rob@landley.net>
Wed, 30 Nov 2011 13:21:23 +0000 (07:21 -0600)
lib/args.c

index 4d4e96e..450472b 100644 (file)
@@ -14,7 +14,8 @@
 //       Note that pointer and long are always the same size, even on 64 bit.
 //     : plus a string argument, keep most recent if more than one
 //     * plus a string argument, appended to a list
-//     # plus a signed long argument (TODO: Bounds checking?)
+//     # plus a signed long argument
+//       {LOW,HIGH} - allowed range TODO
 //     @ plus an occurrence counter (which is a long)
 //     (longopt)
 //     | this is required.  If more than one marked, only one required.
 //       +X enabling this enables X (switch on)
 //       ~X enabling this disables X (switch off)
 //       !X die with error if X already set (x!x die if x supplied twice)
-//       [yz] needs at least one of y or z.
+//       [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)
-//     ? don't show_usage() on unknown argument.
+//     ? Allow unknown arguments (pass them through to command).
 //     & first argument has imaginary dash (ala tar/ps)
 //       If given twice, all arguments have imaginary dash
 
@@ -78,12 +79,20 @@ struct opts {
        char type;         // Type of arguments to store
 };
 
+struct longopts {
+       struct longopts *next;
+       struct opts *opt;
+       char *str;
+       int len;
+};
+
 // State during argument parsing.
 struct getoptflagstate
 {
-       int argc;
+       int argc, minargs, maxargs, nodash;
        char *arg;
        struct opts *opts, *this;
+       struct longopts *longopts;
        int noerror, nodash_now, stopearly;
        uint32_t excludes;
 };
@@ -100,6 +109,8 @@ static int gotflag(struct getoptflagstate *gof)
                if (gof->noerror) return 1;
                error_exit("Unknown option %s", gof->arg);
        }
+
+       // Set flags
        toys.optflags |= opt->edx[0];
        toys.optflags &= ~opt->edx[1];
        gof->excludes = opt->edx[2];
@@ -108,14 +119,14 @@ static int gotflag(struct getoptflagstate *gof)
        // Does this option take an argument?
        gof->arg++;
        type = opt->type;
-       if (type == '@') ++*(opt->arg);
-       else if (type) {
+       if (type) {
                char *arg = gof->arg;
 
                // Handle "-xblah" and "-x blah", but also a third case: "abxc blah"
                // to make "tar xCjfv blah1 blah2 thingy" work like
                // "tar -x -C blah1 -j -f blah2 -v thingy"
-               if (gof->nodash_now || !gof->arg[0]) arg = toys.argv[++gof->argc];
+
+               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);
 
@@ -128,6 +139,7 @@ static int gotflag(struct getoptflagstate *gof)
                        *list = xzalloc(sizeof(struct arg_list));
                        (*list)->arg = arg;
                } else if (type == '#') *(opt->arg) = atolx((char *)arg);
+               else if (type == '@') ++*(opt->arg);
 
                if (!gof->nodash_now) gof->arg = "";
        }
@@ -138,131 +150,131 @@ static int gotflag(struct getoptflagstate *gof)
 
 // Fill out toys.optflags and toys.optargs.
 
-static char *plustildenot = "+~!";
-void get_optflags(void)
+void parse_optflaglist(struct getoptflagstate *gof)
 {
-       int nodash = 0, minargs = 0, maxargs;
-       struct longopts {
-               struct longopts *next;
-               struct opts *opt;
-               char *str;
-               int len;
-       } *longopts = NULL;
-       struct getoptflagstate gof;
-       long *nextarg = (long *)&this, saveflags;
-       char *options = toys.which->options;
-       char *letters[]={"s",""};
-
-       if (CFG_HELP) toys.exithelp++;
-       // Allocate memory for optargs
-       maxargs = 0;
-       while (toys.argv[maxargs++]);
-       toys.optargs = xzalloc(sizeof(char *)*maxargs);
-       maxargs = INT_MAX;
-       bzero(&gof, sizeof(struct getoptflagstate));
+       char *options = toys.which->options, *plustildenot = "+~!";
+       long *nextarg = (long *)&this;
+       struct opts *new = 0;
 
        // Parse option format
-       if (options) {
-
-               // Parse leading special behavior indicators
-               for (;;) {
-                       if (*options == '^') gof.stopearly++;
-                       else if (*options == '<') minargs=*(++options)-'0';
-                       else if (*options == '>') maxargs=*(++options)-'0';
-                       else if (*options == '?') gof.noerror++;
-                       else if (*options == '&') nodash++;
-                       else break;
-                       options++;
-               }
+       bzero(gof, sizeof(struct getoptflagstate));
+       gof->maxargs = INT_MAX;
+       if (!options) return;
+
+       // Parse leading special behavior indicators
+       for (;;) {
+               if (*options == '^') gof->stopearly++;
+               else if (*options == '<') gof->minargs=*(++options)-'0';
+               else if (*options == '>') gof->maxargs=*(++options)-'0';
+               else if (*options == '?') gof->noerror++;
+               else if (*options == '&') gof->nodash++;
+               else break;
+               options++;
+       }
 
-               if (!*options) gof.stopearly++;
-               // Parse rest of opts into array
-               while (*options) {
-                       char *temp;
-
-                       // Allocate a new option entry when necessary
-                       if (!gof.this) {
-                               gof.this = xzalloc(sizeof(struct opts));
-                               gof.this->next = gof.opts;
-                               gof.opts = gof.this;
-                               ++*(gof.this->edx);
-                       }
-                       // Each option must start with "(" or an option character.  (Bare
-                       // longopts only come at the start of the string.)
-                       if (*options == '(') {
-                               char *end;
-                               struct longopts *lo = xmalloc(sizeof(struct longopts));
-
-                               // Find the end of the longopt
-                               for (end = ++options; *end && *end != ')'; end++);
-                               if (CFG_TOYBOX_DEBUG && !*end)
-                                       error_exit("Bug1 in get_opt");
-
-                               // Allocate and init a new struct longopts
-                               lo = xmalloc(sizeof(struct longopts));
-                               lo->next = longopts;
-                               lo->opt = gof.this;
-                               lo->str = options;
-                               lo->len = end-options;
-                               longopts = lo;
-                               options = end;
-
-                               // Mark this as struct opt as used, even when no short opt.
-                               if (!gof.this->c) gof.this->c = -1;
-
-                       // If this is the start of a new option that wasn't a longopt,
-
-                       } else if (strchr(":*#@", *options)) {
-                               if (CFG_TOYBOX_DEBUG && gof.this->type)
-                                       error_exit("Bug4 in get_opt");
-                               gof.this->type = *options;
-                       } else if (0 != (temp = strchr(plustildenot, *options))) {
-                               int i=0, idx = temp - plustildenot;
-                               struct opts *opt;
-
-                               if (!*++options && CFG_TOYBOX_DEBUG)
-                                       error_exit("Bug2 in get_opt");
-                               // Find this option flag (in previously parsed struct opt)
-                               for (opt = gof.this; ; opt = opt->next) {
-                                       if (CFG_TOYBOX_DEBUG && !opt) error_exit("Bug3 in get_opt");
-                                       if (opt->c == *options) break;
-                                       i++;
-                               }
-                               gof.this->edx[idx] |= 1<<i;
-
-                       } else if (*options == '[') {
-                       } else if (*options == '|') gof.this->flags |= 1;
-                       else if (*options == '^') gof.this->flags |= 2;
-
-                       // 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(gof.this->c) {
-                               gof.this = NULL;
-                               continue;
+       // Parse the rest of the option characters into a linked list
+    // of options with attributes.
 
-                       // Claim this option, loop to see what's after it.
-                       } else gof.this->c = *options;
+       if (!*options) gof->stopearly++;
+       while (*options) {
+               char *temp;
 
-                       options++;
+               // Allocate a new list entry when necessary
+               if (!new) {
+                       new = xzalloc(sizeof(struct opts));
+                       new->next = gof->opts;
+                       gof->opts = new;
+                       ++*(new->edx);
                }
+               // Each option must start with "(" or an option character.  (Bare
+               // longopts only come at the start of the string.)
+               if (*options == '(') {
+                       char *end;
+                       struct longopts *lo = xmalloc(sizeof(struct longopts));
+
+                       // Find the end of the longopt
+                       for (end = ++options; *end && *end != ')'; end++);
+                       if (CFG_TOYBOX_DEBUG && !*end)
+                               error_exit("Bug1 in get_opt");
+
+                       // Allocate and 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;
+
+                       // Mark this struct opt as used, even when no short opt.
+                       if (!new->c) new->c = -1;
+
+               // If this is the start of a new option that wasn't a longopt,
+
+               } else if (strchr(":*#@", *options)) {
+                       if (CFG_TOYBOX_DEBUG && new->type)
+                               error_exit("Bug4 in get_opt");
+                       new->type = *options;
+               } else if (0 != (temp = strchr(plustildenot, *options))) {
+                       int i=0, idx = temp - plustildenot;
+                       struct opts *opt;
+
+                       if (!*++options && CFG_TOYBOX_DEBUG)
+                               error_exit("Bug2 in get_opt");
+                       // 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");
+                               if (opt->c == *options) break;
+                               i++;
+                       }
+                       new->edx[idx] |= 1<<i;
+               } else if (*options == '[') {
+               } else if (*options == '|') new->flags |= 1;
+               else if (*options == '^') new->flags |= 2;
+
+               // 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;
+                       continue;
+
+               // Claim this option, loop to see what's after it.
+               } else new->c = *options;
+
+               options++;
        }
 
        // 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.)
-       gof.argc = 0;
-       for (gof.this = gof.opts; gof.this; gof.this = gof.this->next) {
+       int pos = 0;
+       for (new = gof->opts; new; new = new->next) {
                int i;
 
-               for (i=0;i<3;i++) gof.this->edx[i] <<= gof.argc;
-               gof.argc++;
-               if (gof.this->type) {
-                       gof.this->arg = (void *)nextarg;
+               for (i=0;i<3;i++) new->edx[i] <<= pos;
+               pos++;
+               if (new->type) {
+                       new->arg = (void *)nextarg;
                        *(nextarg++) = 0;
                }
        }
+}
+
+void get_optflags(void)
+{
+       struct getoptflagstate gof;
+       long saveflags;
+       char *letters[]={"s",""};
+
+       if (CFG_HELP) toys.exithelp++;
+       // Allocate memory for optargs
+       saveflags = 0;
+       while (toys.argv[saveflags++]);
+       toys.optargs = xzalloc(sizeof(char *)*saveflags);
+
+       parse_optflaglist(&gof);
 
        // Iterate through command line arguments, skipping argv[0]
        for (gof.argc=1; toys.argv[gof.argc]; gof.argc++) {
@@ -291,7 +303,7 @@ void get_optflags(void)
                                }
                                // Handle --longopt
 
-                               for (lo = longopts; lo; lo = lo->next) {
+                               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)
@@ -318,7 +330,8 @@ void get_optflags(void)
 
                // Handle things that don't start with a dash.
                } else {
-                       if (nodash && (nodash>1 || gof.argc == 1)) gof.nodash_now = 1;
+                       if (gof.nodash && (gof.nodash>1 || gof.argc == 1))
+                               gof.nodash_now = 1;
                        else goto notflag;
                }
 
@@ -347,11 +360,11 @@ notflag:
        }
 
        // Sanity check
-       if (toys.optc<minargs) {
-               error_exit("Need%s %d argument%s", letters[!!(minargs-1)], minargs,
-               letters[!(minargs-1)]);
+       if (toys.optc<gof.minargs) {
+               error_exit("Need%s %d argument%s", letters[!!(gof.minargs-1)],
+                               gof.minargs, letters[!(gof.minargs-1)]);
        }
-       if (toys.optc>maxargs)
-               error_exit("Max %d argument%s", maxargs, letters[!(maxargs-1)]);
+       if (toys.optc>gof.maxargs)
+               error_exit("Max %d argument%s", gof.maxargs, letters[!(gof.maxargs-1)]);
        if (CFG_HELP) toys.exithelp = 0;
 }