Main Page   Class Hierarchy   Compound List   File List   Compound Members   File Members  

ArgParse.cc

Go to the documentation of this file.
00001 /*
00002     File:       ArgParse.cc
00003  
00004     Function:   Argument parsing package
00005  
00006     Author(s):  Paul Heckbert
00007                 Andrew Willmott: converted to C++, 
00008                 modifications to fit in with CL.
00009  
00010     Notes:      
00011 */
00012 
00013 /*
00014  * arg_parse: Command line argument parser.
00015  *
00016  * notable features:
00017  *  arbitrary order of flag arguments
00018  *  automatic argument conversion and type checking
00019  *  multiple-character flag names
00020  *  required, optional, and flag arguments
00021  *  automatic usage message
00022  *  subroutine call for exotic options (variable number of parameters)
00023  *  modularized parsers encourage standardized options
00024  *  expression evaluation
00025  *  works either from argv or in interactive mode,
00026  *      as a primitive language parser and interpreter
00027  *  concise specification
00028  *  easy to use
00029  *
00030  * Paul Heckbert    ph@cs.cmu.edu
00031  *
00032  * 19 April 1988 - written at UC Berkeley
00033  * converted to ANSI C, ajw 1999
00034  *
00035  * simpler version written at Pacific Data Images, Aug 1985.
00036  * Ideas borrowed from Ned Greene's ARGS at New York Inst. of Tech.
00037  * and Alvy Ray Smith's AARG at Pixar.
00038  */
00039 
00040 #include "cl/ArgParse.h"
00041 #include "cl/Expr.h"
00042 
00043 #include <stdarg.h>
00044 #include <ctype.h>
00045 #include <string.h>
00046 #include <stdlib.h>
00047 
00048 
00049 inline bool str_eq(const char *a, const char *b)
00050 {
00051     return (strcmp(a, b) == 0);
00052 };
00053 
00054 
00055 #define CHECKTYPE(form, keyword) \
00056 if (form->type!=0) \
00057 { \
00058 fprintf(stderr, "arg: %s doesn't belong in %s paramlist\n", \
00059 keyword, form->format); \
00060 return 0; \
00061 } \
00062 else
00063 
00064 /* recognize a valid numeric constant or expression by its first char: */
00065 #define NUMERIC(s) (isdigit(*(s)) || \
00066 *(s)=='.' || *(s)=='-' || *(s)=='+' || *(s)=='(')
00067 
00068 ArgForm *arg_to_form1(va_list ap);
00069 ArgForm *arg_find_flag(char *arg, ArgForm *form);
00070 ArgForm *arg_find_reg();
00071 
00072 int arg_debug = 0;              /* debugging level; 0=none */
00073 int arg_doccol = 24;        /* column at which to print doc string*/
00074 int arg_warning = 1;        /* print warnings about repeated flags? */
00075 
00076 static ArgForm *regf;       /* advancing form ptr used by arg_find_reg */
00077 
00078 va_list arg_doc_parse(ArgForm *f, va_list ap);
00079 int arg_format(ArgForm *f);
00080 void arg_init(ArgForm *form);
00081 int arg_done();
00082 int arg_parse_form1(int ac, char **av, ArgForm *form);
00083 int arg_do(int ac, char **av, ArgForm *f);
00084 void av_print(char *str, int ac, char **av);
00085 
00086 typedef enum {SPACE, TOKEN, END} Token_type;
00087 
00088 static void space(FILE *fp, int c, int c1);
00089 static int checkstr(char *s, char *name, char *prev);
00090 static Token_type token_char(FILE *fp, char *p);
00091 static int scan(int narg, char **arg, ArgForm *f);
00092 static int nargs(int ac, char **av, ArgForm *f, int *skip);
00093 
00094 /*
00095  * arg_parse(ac, av, varargs_list)
00096  * Parse the arguments in av according to the varargs list, which contains
00097  * format strings, parameter and subroutine ptrs, and other stuff.
00098  * The varargs list must be terminated by a 0.
00099  * Returns an error code.
00100  */
00101 
00102 int arg_parse(int ac, char **av, ...)
00103 {
00104     int ret;
00105     va_list ap;
00106     ArgForm *form;
00107 
00108     va_start(ap, av);
00109     if (ac < 1 || ac > ARG_NARGMAX)
00110     {
00111         fprintf(stderr,
00112                 "arg_parse: first arg to arg_parse (%d) doesn't look like argc\n",
00113                 ac);
00114         return ARG_BADCALL;
00115     }
00116 
00117     /* convert varargs to formlist */
00118     form = arg_to_form1(ap);
00119     if (!form)
00120         return ARG_BADCALL;
00121 
00122     /* parse args according to form */
00123     if (ac == 2 && str_eq(av[1], "-stdin")) /* no args, run interactive version */
00124         ret = arg_parse_stream(stdin, form);
00125     else                /* args supplied, parse av */
00126         ret = arg_parse_argv(ac, av, form);
00127     return ret;
00128 }
00129 
00130 /*----------------------------------------------------------------------*/
00131 
00132 /*
00133  * arg_to_form: convert varargs to formlist
00134  * not called by arg_parse, but sometimes called from outside to build sublists
00135  */
00136 
00137 ArgForm *arg_to_form(int dummy, ...)
00138 {
00139     va_list ap;
00140 
00141     va_start(ap, dummy);
00142     return arg_to_form1(ap);
00143 }
00144 
00145 /*
00146  * arg_to_form1: convert varargs to formlist recursively.
00147  * assumes va_start has already been called
00148  * calls va_end when done to clean up
00149  * returns 0 on error.
00150  */
00151 
00152 ArgForm *arg_to_form1(va_list ap)
00153 {
00154     char *s, *prevs;
00155     int pi, t;
00156     ArgForm *form, *prevform, *rootform;
00157 
00158     /*
00159      * varargs syntax is:
00160      *     formatstr [KEYWORD val] paramptr* docstr docargptr*
00161      * where there are as many paramptrs as %'s in the format string
00162      * and as many docargptrs as %'s in the doc string
00163      */
00164     rootform = 0;
00165     prevs = "";
00166     for (prevform = 0; (s = va_arg(ap, char *)) != 0; prevform = form)
00167     {
00168 
00169         /* first, read the format string */
00170         if (checkstr(s, "format string", prevs)) return 0;
00171         form = new ArgForm;
00172         form->next = 0;
00173         form->format = s;
00174         form->flag = 0;
00175         form->type = 0;
00176         form->param = 0;
00177         form->parammask = 0;
00178         form->subr = 0;
00179         form->sublist = 0;
00180         if (prevform) prevform->next = form;
00181         else rootform = form;
00182 
00183         /* parse format to create flag and code strings, compute #params */
00184         t = arg_format(form);
00185         if (t) return 0;
00186 
00187         /* next, read the parameters and keywords */
00188         pi = 0;
00189         if (form->nparam > 0)
00190         {
00191             form->type = form->flag[0] == '-' ? ARG_PARAMFLAG : ARG_REGULAR;
00192             form->param = new (int*)[form->nparam];
00193         }
00194         for (; (s = va_arg(ap, char *)) != 0; )
00195         {
00196             /* note that we continue (not break) in all cases except one */
00197             switch ((int)s)
00198             {
00199             case ARG_FLAGNEXT:          /* ptr to flag vbl */
00200                 CHECKTYPE(form, "FLAG");
00201                 form->type = ARG_SIMPFLAG;
00202                 form->param = new (int *);
00203                 *form->param = va_arg(ap, int *);
00204                 continue;
00205             case ARG_SUBRNEXT:          /* ptr to action subr */
00206                 CHECKTYPE(form, "SUBR");
00207                 form->type = ARG_SUBRFLAG;
00208                 form->subr = (int (*)(int argc, char * argv[]))va_arg(ap, int *);
00209                 /* append dots to end of format string */
00210                 s = new char[strlen(form->format) + 5];
00211                 sprintf(s, "%s ...", form->format);
00212                 form->format = s;
00213                 continue;
00214             case ARG_LISTNEXT:          /* ptr to sub-formlist */
00215                 CHECKTYPE(form, "SUBLIST");
00216                 form->type = ARG_SUBLISTFLAG;
00217                 form->sublist = va_arg(ap, ArgForm *);
00218                 continue;
00219             default:            /* ptr to param */
00220                 if (pi >= form->nparam) break;
00221                 form->param[pi++] = (int *)s;
00222                 continue;
00223             }
00224             break;                  /* end of params/keywords */
00225         }
00226 
00227         if (!form->flag[0] && form->type == ARG_SUBLISTFLAG)
00228         {
00229             fprintf(stderr, "arg: sublist must be given a flag name\n");
00230             return 0;
00231         }
00232         if (!form->type)            /* just a doc string */
00233             form->type = ARG_NOP;
00234         /* finally, read the doc string */
00235         if (checkstr(s, "doc string", form->format)) return 0;
00236         form->doc = prevs = s;
00237 
00238         /* skip over doc args */
00239         ap = arg_doc_parse(form, ap);
00240     }
00241     va_end(ap);
00242     return rootform;
00243 }
00244 
00245 /*
00246  * arg_format: parse the format string to create flag string,
00247  * code string, parammask, and count the number of params.
00248  * e.g.: format="-size %d %F"  =>  flag="-size", code="dF", nparam=2
00249  */
00250 
00251 int arg_format(ArgForm *f)
00252 {
00253     char *s, *c;
00254     int n, np;
00255 
00256     if (f->format[0] == '-')
00257     {       /* flag string present */
00258         /* find the end of the flag string, put flag string in f->flag */
00259         for (s = &f->format[1]; *s && *s != ' ' && *s != '%' && *s != '['; s++);
00260         n = s - f->format;
00261         f->flag = new char[n + 1];
00262         bcopy(f->format, f->flag, n);
00263         f->flag[n] = 0;
00264     }
00265     else
00266     {
00267         s = f->format;              /* no flag string: probably a reg arg */
00268         f->flag = "";           /* or maybe a flagless subrflag */
00269     }
00270 
00271     /* extract scanf codes from remainder of format string, put in f->code */
00272     n = (f->format + strlen(f->format) - s) / 2;    /* overestimate # of % codes */
00273     f->code = new char[n + 1];
00274     for (c = f->code, np = 0; ; np++, s++)
00275     {
00276         for (; *s == ' ' || *s == '['; s++)
00277             if (*s == '[') f->parammask |= 1 << np;
00278         if (!*s || *s == ']') break;
00279         if (*s != '%' || !s[1])
00280         {
00281             fprintf(stderr, "arg: bad format string (%s)\n", f->format);
00282             return ARG_BADCALL;
00283         }
00284         *c++ = *++s;
00285     }
00286     for (; *s; s++)
00287         if (*s != ' ' && *s != ']')
00288         {
00289             fprintf(stderr, "bad format (%s), nothing allowed after ']'s\n",
00290                     f->format);
00291             return ARG_BADCALL;
00292         }
00293     f->parammask |= 1 << np;
00294     if (np >= 8*sizeof(int))
00295     {
00296         fprintf(stderr, "out of bits in parammask! too many params to %s\n",
00297                 f->flag);
00298         return ARG_BADCALL;
00299     }
00300 
00301     /* number of parameters to flag = number of '%'s in format string */
00302     f->nparam = np;
00303     *c = 0;
00304     if (c - f->code != f->nparam) fprintf(stderr, "OUCH!\n");
00305     return 0;
00306 }
00307 
00308 /*
00309  * arg_doc_parse: Find the '%' format codes in f->doc and increment varargs
00310  * ptr ap over the doc string parameters.  Updates f->doc to be the formatted
00311  * documentation string and returns the new ap.
00312  */
00313 
00314 va_list arg_doc_parse(ArgForm *f, va_list ap)
00315 {
00316     char *s, buf[256];
00317     int size, gotparam;
00318     va_list ap0;
00319 
00320     ap0 = ap;
00321     gotparam = 0;
00322     for (s = f->doc; *s; s++)
00323     {
00324         for (; *s; s++)         /* search for next format code */
00325             if (s[0] == '%')
00326             {
00327                 if (s[1] == '%')
00328                     s++;    /* skip over %% */
00329                 else
00330                     break;
00331             }
00332         if (!*s) break;
00333         /* skip over numerical parameters */
00334         for (s++; *s && *s == '-' || *s > '0' && *s <= '9' || *s == '.'; s++);
00335         /* now *s points to format code */
00336         switch (*s)
00337         {
00338         case 'h':
00339             size = 0;
00340             s++;
00341             break;      /* half */
00342         case 'l':
00343             size = 2;
00344             s++;
00345             break;      /* long */
00346         default :
00347             size = 1;
00348             break;      /* normal size */
00349         }
00350         if (!*s)
00351         {
00352             fprintf(stderr, "arg: premature end of string in (%s)\n", f->doc);
00353             break;
00354         }
00355         gotparam = 1;
00356         /*
00357          * simulate printf's knowledge of type sizes
00358          * (it's too bad we have to do this)
00359          */
00360         switch (*s)
00361         {
00362         case 'd':
00363         case 'D':
00364         case 'o':
00365         case 'O':
00366         case 'x':
00367         case 'X':
00368         case 'c':
00369             if (size == 2 || *s >= 'A' && *s <= 'Z') va_arg(ap, long);
00370             else va_arg(ap, int);
00371             break;
00372         case 'e':
00373         case 'f':
00374         case 'g':
00375             /* note: float args are converted to doubles by MOST compilers*/
00376             va_arg(ap, double);
00377             break;
00378         case 's':
00379             va_arg(ap, char *);
00380             break;
00381         default:
00382             fprintf(stderr, "arg: unknown format code %%%c in %s\n",
00383                     *s, f->doc);
00384             va_arg(ap, int);
00385             break;
00386         }
00387     }
00388     if (gotparam)
00389     {   /* there are doc parameters, format a new doc string */
00390         vsprintf(buf, f->doc, ap0);
00391         f->doc = new char[sizeof(buf) + 1];
00392         strcpy(f->doc, buf);
00393     }
00394 
00395     return ap;          /* varargs ptr past end of doc params */
00396 }
00397 
00398 /*----------------------------------------------------------------------*/
00399 
00400 #define LINEMAX 256
00401 #define ACMAX 128
00402 
00403 /*
00404  * arg_parse_stream: parse from an input stream (not from an arg vector)
00405  * parse args in stream fp, line by line, according to formlist in form
00406  * Returns 0 on success, negative on failure.
00407  */
00408 
00409 int arg_parse_stream(FILE *fp, ArgForm *form)
00410 {
00411     char c, *av[ACMAX], line[LINEMAX], *p;
00412     Token_type type;
00413     int i, ac, ret, err;
00414     ArgForm *oldregf;
00415 
00416     oldregf = regf;
00417     regf = form;
00418     arg_init(form);
00419 
00420     av[0] = "hi";
00421     ret = 0;
00422     for (; ; )
00423     {               /* read and process line */
00424         p = line;
00425         while ((type = token_char(fp, &c)) == SPACE);
00426         for (ac = 1; type != END && ac < ACMAX; )
00427         {   /* split line into tokens */
00428             av[ac++] = p;       /* save ptr to beginning of token */
00429             do
00430             {
00431                 *p++ = c;
00432                 if (p >= line + LINEMAX)
00433                 {
00434                     fprintf(stderr, "input line too long\n");
00435                     exit(1);
00436                 }
00437             }
00438             while ((type = token_char(fp, &c)) == TOKEN);
00439             *p++ = 0;           /* terminate this token in line[] */
00440             if (type == END) break;
00441             while ((type = token_char(fp, &c)) == SPACE);
00442         }
00443         if (feof(fp)) break;
00444         if (arg_debug)
00445         {
00446             fprintf(stderr, "ac=%d: ", ac);
00447             for (i = 1; i < ac; i++) fprintf(stderr, "(%s) ", av[i]);
00448             fprintf(stderr, "\n");
00449         }
00450 
00451         err = arg_parse_form1(ac, av, form);
00452         if (!ret) ret = err;
00453     }
00454     if (!ret) ret = arg_done();
00455     regf = oldregf;
00456     return ret;
00457 }
00458 
00459 /*
00460  * token_char: is next char in stream fp part of a token?
00461  * returns TOKEN if char in token, SPACE if whitespace, END if end of input line
00462  * *p gets new char.
00463  * handles quoted strings and escaped characters.
00464  */
00465 
00466 static Token_type token_char(FILE *fp, char *p)
00467 {
00468     int c, old_mode;
00469     Token_type type;
00470     static int mode = 0;    /* = '"' or '\'' if inside quoted string */
00471 
00472     type = TOKEN;
00473     do
00474     {
00475         old_mode = mode;
00476         c = getc(fp);
00477         switch (c)
00478         {
00479         case EOF:
00480             type = END;
00481             break;
00482         case '\\':
00483             switch (c = getc(fp))
00484             {
00485             case 'b':
00486                 c = '\b';
00487                 break;
00488             case 'f':
00489                 c = '\f';
00490                 break;
00491             case 'n':
00492                 c = '\n';
00493                 break;
00494             case 'r':
00495                 c = '\r';
00496                 break;
00497             case 't':
00498                 c = '\t';
00499                 break;
00500             case 'v':
00501                 c = '\v';
00502                 break;
00503             case '0':
00504                 c = '\0';
00505                 break;
00506             }
00507             break;
00508         case '"':
00509             switch (mode)
00510             {
00511             case 0:
00512                 mode = '"';
00513                 break;          /* begin " */
00514             case '"':
00515                 mode = 0;
00516                 break;          /* end " */
00517             }
00518             break;
00519         case '\'':
00520             switch (mode)
00521             {
00522             case 0:
00523                 mode = '\'';
00524                 break;          /* begin ' */
00525             case '\'':
00526                 mode = 0;
00527                 break;          /* end ' */
00528             }
00529             break;
00530         case '\n':
00531             switch (mode)
00532             {
00533             case 0:
00534                 type = END;
00535                 break;
00536             }
00537             break;
00538         }
00539         /* loop until we read a literal character */
00540     }
00541     while (old_mode != mode);
00542     *p = c;
00543 
00544     if (type != END && mode == 0 && (c == ' ' || c == '\t' || c == '\n'))
00545         type = SPACE;
00546     return type;
00547 }
00548 
00549 /*
00550  * arg_parse_argv: do the actual parsing!
00551  * parse the arguments in av according to the formlist in form
00552  * Returns 0 on success, negative on failure.
00553  */
00554 
00555 int arg_parse_argv(int ac, char **av, ArgForm *form)
00556 {
00557     int ret;
00558     ArgForm *oldregf;
00559 
00560     oldregf = regf;
00561     regf = form;
00562     arg_init(form);
00563     ret = arg_parse_form1(ac, av, form);
00564     if (!ret) ret = arg_done();
00565     regf = oldregf;
00566     return ret;
00567 }
00568 
00569 int arg_parse_form1(int ac, char **av, ArgForm *form)
00570 {
00571     int i, di;
00572     ArgForm *f;
00573 
00574     for (i = 1; i < ac; i += di)
00575     {
00576         if (arg_debug)
00577             fprintf(stderr, "arg %d: (%s)\n", i, av[i]);
00578         if (av[i][0] == '-' && !NUMERIC(&av[i][1]))
00579         {   /* flag argument */
00580             f = arg_find_flag(av[i], form);
00581             if (!f)
00582             {
00583                 if (av[i][1])
00584                     fprintf(stderr, "unrecognized arg: %s\n", av[i]);
00585                 else        /* arg was "-"; print usage message */
00586                     arg_form_print(form);
00587                 return ARG_EXTRA;
00588             }
00589             di = arg_do(ac - i - 1, &av[i + 1], f);
00590             if (di < 0) return di;
00591             di++;
00592         }
00593         else
00594         {           /* regular argument */
00595             f = arg_find_reg();
00596             if (!f)
00597             {
00598                 /* regular args exhausted, see if any flagless subrflags */
00599                 f = arg_find_flag("", form);
00600                 if (!f)
00601                 {
00602                     fprintf(stderr, "extra arg: %s\n", av[i]);
00603                     return ARG_EXTRA;
00604                 }
00605             }
00606             di = arg_do(ac - i, &av[i], f);
00607             if (di < 0) return di;
00608         }
00609     }
00610 
00611     return 0;
00612 }
00613 
00614 /*
00615  * arg_init: initialize formlist before parsing arguments
00616  * Set simple flags and repeat counts to 0.
00617  */
00618 
00619 void arg_init(ArgForm *form)
00620 {
00621     ArgForm *f;
00622 
00623     for (f = form; f; f = f->next)
00624         if (f->type == ARG_SUBLISTFLAG)
00625             arg_init(f->sublist);   /* recurse */
00626         else
00627         {
00628             f->rep = 0;
00629             if (f->type == ARG_SIMPFLAG) **f->param = 0;
00630         }
00631 }
00632 
00633 int arg_done()
00634 {
00635     for (; regf; regf = regf->next) /* any required reg args remaining? */
00636         if (regf->type == ARG_REGULAR && !(regf->parammask & 1))
00637         {
00638             fprintf(stderr, "regular arg %s (%s) not set\n",
00639                     regf->format, regf->doc);
00640             return ARG_MISSING;
00641         }
00642     return 0;
00643 }
00644 
00645 /*
00646  * arg_find_flag: find the flag matching arg in the form list (tree)
00647  * returns form ptr if found, else 0
00648  */
00649 
00650 ArgForm *arg_find_flag(char *arg, ArgForm *form)
00651 {
00652     ArgForm *f, *t;
00653 
00654     for (f = form; f; f = f->next)
00655     {
00656         if (f->type != ARG_REGULAR && f->type != ARG_NOP && str_eq(f->flag, arg))
00657             return f;
00658         if (f->type == ARG_SUBLISTFLAG)
00659         {
00660             t = arg_find_flag(arg, f->sublist);         /* recurse */
00661             if (t) return t;
00662         }
00663     }
00664     return 0;
00665 }
00666 
00667 /*
00668  * arg_find_reg: find next regular argument
00669  * each call advances the global pointer regf through the formlist
00670  */
00671 
00672 ArgForm *arg_find_reg()
00673 {
00674     ArgForm *f;
00675 
00676     for (; regf; regf = regf->next)
00677     {
00678         if (regf->type == ARG_REGULAR)
00679         {
00680             f = regf;
00681             regf = regf->next;
00682             return f;
00683         }
00684     }
00685     return 0;
00686 }
00687 
00688 /*
00689  * arg_do: process one form by parsing arguments in av according to the
00690  * single form in f
00691  *
00692  * f was found by arg_find_flag or arg_find_reg,
00693  *     so if f is a flag then we know av[-1] matches f->flag
00694  *
00695  * examine av[0]-av[ac-1] to determine number of parameters supplied
00696  *     if simpleflag, set flag parameter and read no args
00697  *     if subrflag, call subroutine on sub-args
00698  *     if sublist, call arg_parse_form on sub-args
00699  *     else it's a paramflag or regular arg, do arg-to-param assignments
00700  * return number of arguments gobbled, or negative error code
00701  */
00702 
00703 int arg_do(int ac, char **av, ArgForm *f)
00704 {
00705     int narg, skip, used, err, i;
00706 
00707     if (arg_debug)
00708         av_print("  arg_do", ac, av);
00709     if (f->type == ARG_SIMPFLAG || f->type == ARG_PARAMFLAG)
00710     {
00711         /* don't complain about repeated subrflags or sublists */
00712         Assert(str_eq(av[ -1], f->flag), "flag wrong");
00713         f->rep++;
00714         if (f->rep > 1 && arg_warning)
00715             fprintf(stderr, "warning: more than one %s flag in arglist\n",
00716                     f->flag);
00717     }
00718 
00719     narg = nargs(ac, av, f, &skip);
00720 
00721     used = 0;
00722     switch (f->type)
00723     {
00724     case ARG_SIMPFLAG:
00725         **f->param = 1;
00726         break;
00727     case ARG_SUBRFLAG:
00728         (*f->subr)(narg, av);
00729         break;
00730     case ARG_SUBLISTFLAG:
00731         arg_parse_argv(narg + 1, &av[ -1], f->sublist);     /* recurse */
00732         used = narg;
00733         break;
00734     default:            /* convert parameters */
00735         err = scan(narg, av, f);
00736         if (err) return err;
00737         used = narg < f->nparam ? narg : f->nparam;
00738         break;
00739     }
00740 
00741     if ((f->type == ARG_REGULAR || f->type == ARG_PARAMFLAG) && used != narg)
00742     {
00743         fprintf(stderr, "warning: %d unused arg%s to %s: ",
00744                 narg - used, narg - used > 1 ? "s" : "", av[ -1]);
00745         for (i = used; i < narg; i++)
00746             fprintf(stderr, "%s ", av[i]);
00747         fprintf(stderr, "\n");
00748     }
00749     return skip;
00750 }
00751 
00752 /*
00753  * nargs: Count number of parameters in arg vector av before the next flag.
00754  * Arguments can be grouped and "escaped" using -{ and -}.
00755  * NOTE: modifies av
00756  * Returns number of valid args in new av.
00757  * Sets *skip to number of args to skip in old av to get to next flag.
00758  *
00759  * This is the most complex code in arg_parse.
00760  * Is there a better way?
00761  *
00762  * examples:
00763  *     input:  ac=3, av=(3 4 -go)
00764  *     output: av unchanged, skip=2, return 2
00765  *
00766  *     input:  ac=4, av=(-{ -ch r -})
00767  *     output: av=(-ch r X X), skip=4, return 2
00768  *
00769  *     input:  ac=4, av=(-{ -foo -} -go)
00770  *     output: av=(-foo X X -go), skip=3, return 1
00771  *
00772  *     input:  ac=6, av=(-{ -ch -{ -rgb -} -})
00773  *     output: av=(-ch -{ -rgb -} X X), skip=6, return 4
00774  *
00775  *     where X stands for junk
00776  */
00777 
00778 static int nargs(int ac, char **av, ArgForm *f, int *skip)
00779 {
00780     char *flag, **au, **av0;
00781     int i, j, level, die, np, mask, voracious;
00782 
00783     np = f->nparam;
00784     mask = f->parammask;
00785     flag = f->type == ARG_REGULAR ? f->format : f->flag;
00786     voracious = f->type == ARG_SUBRFLAG || f->type == ARG_SUBLISTFLAG;
00787     /* subrs&sublists want all the args they can get */
00788 
00789     level = 0;
00790     av0 = au = av;
00791     if (voracious) np = 999;
00792     for (die = 0, i = 0; i < np && i < ac && !die; )
00793     {
00794         if (voracious) j = 999;
00795         else for (j = i + 1; !(mask >> j&1); j++);      /* go until we can stop */
00796         /* try to grab params i through j-1 */
00797         for (; i < j && i < ac || level > 0; i++, au++, av++)
00798         {
00799             if (au != av) *au = *av;
00800             if (str_eq(*av, "-{"))
00801             {
00802                 if (level <= 0) au--;       /* skip "-{" in av if level 0 */
00803                 level++;            /* push a level */
00804             }
00805             else if (str_eq(*av, "-}"))
00806             {
00807                 level--;            /* pop a level */
00808                 if (level <= 0) au--;       /* skip "-}" in av if level 0 */
00809                 if (level < 0)
00810                     fprintf(stderr, "ignoring spurious -}\n");
00811             }
00812             else if (level == 0 && av[0][0] == '-' && !NUMERIC(&av[0][1]))
00813             {
00814                 die = 1;        /* break out of both loops */
00815                 break;              /* encountered flag at level 0 */
00816             }
00817         }
00818     }
00819     if (arg_debug)
00820     {
00821         fprintf(stderr, "    %s: requested %d, got %d args: ",
00822                 flag, np, au - av0);
00823         for (j = 0; j < au - av0; j++)
00824             fprintf(stderr, "%s ", av0[j]);
00825         fprintf(stderr, "\n");
00826     }
00827     *skip = i;
00828     return au - av0;
00829 }
00830 
00831 /*
00832  * scan: call sscanf to read args into param array and do conversion
00833  * returns error code (0 on success)
00834  */
00835 
00836 static int scan(int narg, char **arg, ArgForm *f)
00837 {
00838     static char str[] = "%X";
00839     char *s;
00840     int i, **p;
00841     double x;
00842 
00843     if (f->nparam < narg) narg = f->nparam;
00844     if (!(f->parammask >> narg&1))
00845     {
00846         fprintf(stderr, "you can't give %s just %d params\n",
00847                 f->format, narg);
00848         return ARG_MISSING;
00849     }
00850     for (p = f->param, i = 0; i < narg; i++, p++)
00851     {
00852         str[1] = f->code[i];
00853         switch (str[1])
00854         {
00855         case 'S':
00856             /*
00857              * dynamically allocate memory for string
00858              * for arg_parse_argv: in case argv gets clobbered (rare)
00859              * for arg_parse_stream: since line[] buffer is reused (always)
00860              */
00861             s = new char[strlen(arg[i]) + 1];
00862             strcpy(s, arg[i]);
00863             *(char **)*p = s;
00864             break;
00865         case 's':             /* scanf "%s" strips leading, trailing blanks */
00866             strcpy((char *) *p, arg[i]);
00867             break;
00868         case 'd':
00869             *(int *)*p = expr_eval_int(arg[i]);
00870             if (expr_error == EXPR_BAD)
00871             {   /* expression is garbage */
00872                 fprintf(stderr, "bad %s param\n", f->flag);
00873                 return ARG_BADARG;
00874             }
00875             break;
00876         case 'D':
00877             *(long *)*p = expr_eval_long(arg[i]);
00878             if (expr_error == EXPR_BAD)
00879             {   /* expression is garbage */
00880                 fprintf(stderr, "bad %s param\n", f->flag);
00881                 return ARG_BADARG;
00882             }
00883             break;
00884         case 'f':
00885         case 'F':
00886             x = expr_eval(arg[i]);
00887             if (expr_error == EXPR_BAD)
00888             {   /* expression is garbage */
00889                 fprintf(stderr, "bad %s param\n", f->flag);
00890                 return ARG_BADARG;
00891             }
00892             if (str[1] == 'f') *(float *)*p = x;
00893             else    *(double *)*p = x;
00894             break;
00895         default:
00896             if (sscanf(arg[i], str, *p) != 1)
00897             {
00898                 fprintf(stderr, "bad %s param: \"%s\" doesn't match %s\n",
00899                         f->flag, arg[i], str);
00900                 return ARG_BADARG;
00901             }
00902             break;
00903         }
00904     }
00905     return 0;                     /* return 0 on success */
00906 }
00907 
00908 static char *bar = "==================================================\n";
00909 
00910 /* arg_form_print: print ArgForm as usage message to stderr */
00911 
00912 void arg_form_print(ArgForm *form)
00913 {
00914     ArgForm *f;
00915 
00916     for (f = form; f; f = f->next)
00917     {
00918         if (f->type != ARG_NOP || f->format[0])
00919         {
00920             fprintf(stderr, "%s", f->format);
00921             space(stderr, strlen(f->format), arg_doccol);
00922         }
00923         fprintf(stderr, "%s\n", f->doc);
00924         if (arg_debug)
00925             fprintf(stderr, "   %d (%s) [%s][%s]%x (%s)\n",
00926                     f->type, f->format, f->flag, f->code, f->parammask, f->doc);
00927         if (f->type == ARG_SUBLISTFLAG)
00928         {
00929             fprintf(stderr, bar);
00930             arg_form_print(f->sublist);
00931             fprintf(stderr, bar);
00932         }
00933     }
00934 }
00935 
00936 /* checkstr: check that s is a valid string */
00937 
00938 static int checkstr(char *s, char *name, char *prev)
00939 {
00940     char *delim;
00941 
00942     delim = prev ? "\"" : "";
00943     if (!s || (int)s&ARG_MASKNEXT)
00944     {
00945         fprintf(stderr, "bad arg call: missing %s after %s%s%s\n",
00946                 name, delim, prev, delim);
00947         return 1;
00948     }
00949     return 0;
00950 }
00951 
00952 /*
00953  * space: currently in column c; tab and space over to column c1
00954  * assumes 8-space tabs
00955  */
00956 
00957 static void space(FILE *fp, int c, int c1)
00958 {
00959     if (c >= c1)
00960     {
00961         putc('\n', fp);
00962         c = 0;
00963     }
00964     for (; c < c1&~7; c = (c + 7) & ~7) putc('\t', fp);
00965     for (; c < c1; c++) putc(' ', fp);
00966 }
00967 
00968 void av_print(char *str, int ac, char **av)
00969 {
00970     int i;
00971 
00972     fprintf(stderr, "%s: ", str);
00973     for (i = 0; i < ac; i++)
00974         fprintf(stderr, "%s ", av[i]);
00975     fprintf(stderr, "\n");
00976 }
00977 
00978 void arg_form_append(ArgForm *form, ArgForm *additionalForm)
00979 {
00980     ArgForm *af;
00981 
00982     for (af = form; af->next; af = af->next)
00983         ;
00984     af->next = additionalForm;
00985 }

Generated at Sat Aug 5 00:16:31 2000 for Class Library by doxygen 1.1.0 written by Dimitri van Heesch, © 1997-2000