home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / OSK / EFFO / forum7.lzh / RICO / C / LIBSOURCE / ARGPROC / argproc.c next >
Text File  |  2009-11-06  |  17KB  |  589 lines

  1. /*--------------------------------------------------------------------------
  2.  This module defines argproc(), a simple-to-use command line option grabber.
  3.  It was written by Steve Colwell @ srs.uucp.
  4. --------------------------------------------------------------------------*/
  5. #include <stdio.h>
  6. #include <ctype.h>
  7.  
  8. #ifndef os9
  9. #include <string.h>
  10. #else
  11. #include <strings.h>
  12.  
  13. #define    void        int
  14. #define    strchr(x,y)    index(x,y)
  15.  
  16. /*    strspn(s,w) - return number of leading white chars (in w) of s    */
  17. int strspn(s,w)
  18. char *s,*w;
  19. {
  20.     register int i,j;
  21.  
  22.     for(i=0;;i++)
  23.     {
  24.         if (s[i] == '\0')
  25.             return (i);
  26.         else
  27.             for(j=0;;j++)
  28.             {
  29.                 if (w[j] == '\0')
  30.                     return (i);
  31.                 else if (s[i] == w[j])
  32.                     break;
  33.             }
  34.     }
  35. }
  36. #endif
  37.  
  38. #include <varargs.h>
  39. #include <boolean.h>
  40. #include <lose.h>
  41.  
  42. #define ERROR -1
  43.  
  44. #ifndef os9
  45. /*LINTLIBRARY*/
  46. #ifndef lint
  47. static char Sccs_id[] = "@(#)argproc.c    from 1.19 4/15/87 (C) SRS";
  48. #endif
  49. #endif
  50.  
  51. /* Dummy function declaration; this is lint's idea of a function prototype.
  52.  * If you don't use lint, you can safely omit this section.
  53.  */
  54. #ifdef lint
  55.     /*VARARGS3*/
  56.     long argproc(argc, argv, format) 
  57.     int argc;
  58.     char **argv, *format; 
  59.     {
  60.     /* Dummy function body; make lint think all the args were used. */
  61.     *argv[0]= *format; *format = (char) argc; return 0L; 
  62.     }
  63. #else
  64.  
  65. #define MAX_ARG_NAME_LEN 20
  66. #define MAX_ARG_FORM_LEN 50
  67. #define MAX_ARGS     200
  68.  
  69. #define SWITCH 8
  70. #define NORM_ARG 4
  71. #define BOOL_FLAG (SWITCH|1) 
  72. #define BOOL_VAR_FLAG (SWITCH|2)
  73. #define VALUE_FLAG (SWITCH|NORM_ARG)
  74.  
  75. /*----------------------------------------------------------------------
  76.  The format string entry structure.  Holds different information, depending
  77.  on whether the entry is a boolean "-x", and boolean with variable "=x",
  78.  a normal argument "%s", a switched argument "-x%s", or a combination "=x%s".
  79. ----------------------------------------------------------------------*/
  80. typedef
  81.     struct argpType {
  82.     /* one of the above defines */
  83.     int type; 
  84.     /* the number of the bit in the returned long which this entry
  85.      * corresponds to */
  86.     int bitnum;
  87.  
  88.     /* name of the switch for SWITCH types */
  89.     char name[MAX_ARG_NAME_LEN];
  90.  
  91.     /* format reading parameters for NORM_ARG types */
  92.     /* the scanf format string */
  93.     char format[MAX_ARG_FORM_LEN];
  94.     /* the number of format commands in format, e.g. %d,%d gives 2 */
  95.     int fmt_count;
  96.     /* Are the values for the flag optional, or must they be present? */
  97.     boolean optional;
  98.  
  99.     /* An index into the pointer array for the processed args recipient;
  100.      * used for NORM_ARG and BOOL_VAR_FLAG */
  101.     int pnt_index;
  102.     }
  103.     argp_t;
  104.  
  105.  
  106. /*--------------------------------------------------------------------------
  107.  Return pointer to first char in s which matches a char from untilstr.
  108. --------------------------------------------------------------------------*/
  109. char * 
  110. sindex(s, untilstr)
  111.     register char *s, *untilstr;
  112. {
  113.     register char c1, c2, *up;
  114.  
  115.     while ((c1= *s++) != '\0') {
  116.     up = untilstr;
  117.     while ((c2= *up++) != '\0')
  118.         if (c1 == c2)
  119.         return(s-1);
  120.     }
  121.  
  122.     return(s-1);
  123. }
  124.  
  125. /*--------------------------------------------------------------------------
  126.  Copy chars from beg to end-1 into res, add null char.
  127. --------------------------------------------------------------------------*/
  128. static void
  129. be_strcpy(res, beg, end)
  130.     register char *res, *beg, *end;
  131. {
  132.     while (beg != end)
  133.     *res++ = *beg++;
  134.     *res = '\0';
  135. }
  136.  
  137. /*--------------------------------------------------------------------------
  138.  Copy s2 to s1 until a char from untilstr occurs, then null 
  139.  terminate s1 and return a pointer to the terminal char in s2.
  140. --------------------------------------------------------------------------*/
  141. static char *
  142. copy_until(s1, s2, untilstr)
  143.     char *s1, *s2, *untilstr;
  144. {
  145.     char *olds2;
  146.  
  147.     olds2 = s2;
  148.     s2 = sindex(s2, untilstr);
  149.     be_strcpy(s1, olds2, s2);
  150.  
  151.     return(s2);
  152. }
  153.  
  154.  
  155. /*----------------------------------------------------------------------
  156.  Count the number of format specifications.
  157.  Ignore literal "%" and dummy field spec "*".
  158. ----------------------------------------------------------------------*/
  159. static int 
  160. format_count(str)
  161.     char *str;
  162. {
  163.     int count=0;
  164.  
  165.     str = strchr(str, '%');
  166.     while (str++ != NULL) {
  167.     if (*str == '%') 
  168.         str++;
  169.     else if (*str != '*')
  170.         count++;
  171.  
  172.     str = strchr(str, '%');
  173.     }
  174.  
  175.     return count;
  176. }
  177.  
  178.  
  179. /*----------------------------------------------------------------------
  180.  Process the format word which started with a '%'.  The whole word should
  181.  look like '%s' or '%*d' or some other scanf format word starting with %.
  182.  It may have multiple entries, for instance '%d%s'.
  183. -----------------------------------------------------------------------*/
  184. static char *
  185. normArgParse(form, argp, argpcountp, bitnump, pntcountp)
  186.     char *form;
  187.     argp_t *argp;
  188.     int *argpcountp, *bitnump, *pntcountp;
  189. {
  190.     int pos = (*argpcountp)++;
  191.     int fmt_count;
  192.  
  193. /* copy everything, including the first '%' */
  194.     form = copy_until(argp[pos].format, form-1, " \t\n");
  195.  
  196.     argp[pos].type = NORM_ARG;
  197.     argp[pos].optional = FALSE;
  198.     argp[pos].bitnum = *bitnump;
  199.     argp[pos].pnt_index = *pntcountp;
  200.     argp[pos].fmt_count = fmt_count = format_count(argp[pos].format);
  201.  
  202.     *pntcountp += fmt_count;
  203.     *bitnump += fmt_count;
  204.  
  205.     return form;
  206. }
  207.  
  208.  
  209.  
  210. /*----------------------------------------------------------------------
  211.  Make a new entry in the form entry table 'form' for a boolean switch.  It
  212.  may be a boolean variable (according to isBoolVar), and should use the
  213.  specified bit number and name.  If it is boolVar, a pointer to the user
  214.  variables is used up too (pntcountp).
  215. -----------------------------------------------------------------------*/
  216. static void
  217. newBoolEntry(form, bitnum, name, nameLen, isBoolVar, pntcountp)
  218.     argp_t *form;
  219.     char *name;
  220.     int bitnum, nameLen, *pntcountp;
  221.     boolean isBoolVar;
  222. {
  223.     (void) strncpy(form->name, name, nameLen);
  224.     form->name[nameLen] = '\0';
  225.  
  226.     form->bitnum = bitnum;
  227.  
  228.     if (isBoolVar) {
  229.     form->type = BOOL_VAR_FLAG;
  230.     form->pnt_index = (*pntcountp)++;
  231.     } else
  232.     form->type = BOOL_FLAG;
  233. }
  234.  
  235.  
  236. /*----------------------------------------------------------------------
  237.  Process the format word which started with a dash.  The whole word should
  238.  look like '-xy%s' or '=xy%s' where x is a boolean switch, y is a value switch.
  239.  Also handles the case where there are braces around the switch '{-long%d}'
  240.  which means that the switch has a long name, and the case of optional values
  241.  '-x[%d]', or both '{-longname[%d]}'.  The 'form' always points to the char
  242.  after the '-' or '=', and 'wasBrace' indicates whether there was a left
  243.  brace before that.
  244. -----------------------------------------------------------------------*/
  245. static char *
  246. switchParse(form, argp, argpcountp, bitnump, pntcountp, wasBrace)
  247.     char *form;
  248.     argp_t *argp;
  249.     int *argpcountp, *bitnump, *pntcountp;
  250.     boolean wasBrace;
  251. {
  252.     char *oldform = form;
  253.     int pos = *argpcountp;
  254.     boolean leftBracket, isBoolVar;
  255.  
  256. /* if switch started with '=', will return result in a boolean variable */
  257.     isBoolVar = (form[-1] == '=');
  258.  
  259.     form = sindex(form, "}%[ \t\n");
  260.     leftBracket = (*form == '[');
  261.  
  262.     if (oldform == form) 
  263.     Epitaph("argproc: switch must include 1 char flag name(s)");
  264.  
  265.     if (wasBrace) {
  266.     /* Use the entire chunk as one long name since this is in braces.
  267.      * It may have its type changed, to VALUE for instance if % is the
  268.      * next char */
  269.     newBoolEntry(&argp[pos++], (*bitnump)++, oldform, form - oldform,
  270.                     isBoolVar, pntcountp);
  271.     } else {
  272.     /* Assign the one character switch names to individual array places.  
  273.      * The type of the last one may be changed, to VALUE for instance 
  274.      * if the next char is a %. */
  275.     while (oldform != form)
  276.         newBoolEntry(&argp[pos++], (*bitnump)++, oldform++, 1,
  277.                         isBoolVar, pntcountp);
  278.     }
  279.  
  280. /* skip the left bracket if there is one */
  281.     if (leftBracket)
  282.     ++form;
  283.  
  284. /* if there is a %, set up the last switch as a VALUE, not a BOOL. */
  285.     if (*form == '%') {
  286.     int fmt_count;
  287.  
  288.     --pos;
  289.     argp[pos].optional = leftBracket;
  290.     argp[pos].type |= VALUE_FLAG;
  291.     form = copy_until(argp[pos].format, form, 
  292.                 leftBracket ? "]" : "} \t\n");
  293.  
  294.     /* figure out how many variables-to-be-filled this will require */
  295.     argp[pos].fmt_count = fmt_count = format_count(argp[pos].format);
  296.  
  297.     /* show where the first variable-to-be-filled is unless this switch
  298.      * also had a boolean variable-to-fill, in which case the position
  299.      * of the variables is already set. */
  300.     if (!isBoolVar)
  301.         argp[pos].pnt_index = *pntcountp;
  302.     *pntcountp += fmt_count;
  303.  
  304.     *bitnump += fmt_count;
  305.  
  306.     ++pos;
  307.     }
  308.  
  309.     if (leftBracket)
  310.     if (*form++ != ']')
  311.         Epitaph("argproc: missing closing ']' in format string");
  312.  
  313.     if (wasBrace)
  314.     if (*form++ != '}')
  315.         Epitaph("argproc: missing closing '}' in format string");
  316.  
  317.     *argpcountp = pos;
  318.     return form;
  319. }
  320.  
  321.  
  322. /*----------------------------------------------------------------------
  323.  Analyse the contents of the argproc format string.  The result is an
  324.  array, one element per switch in the format string.  The number of
  325.  elements in the array is returned in *argpcountp.  The number of variables
  326.  needed to receive the values from the string is the return value.  That is,
  327.  if the string were "%f -x%d -q =y" the return value would be 3: 1 each for
  328.  the %f, the %d, and the =y variables.
  329. -----------------------------------------------------------------------*/
  330. static int
  331. format_parse(form, argp, argpcountp)
  332.     char *form;
  333.     argp_t *argp;
  334.     int *argpcountp;
  335. {
  336.     int pntcount, bitnum;
  337.  
  338.     *argpcountp = 0;
  339.  
  340.     /* The position in the argument list of variables-to-be-filled. */
  341.     pntcount = 0;
  342.  
  343.     /* The output bit number to be affected by the current format entries */
  344.     bitnum = 0;
  345.  
  346. /* skip white space, process the next word of the format string */
  347.     for (form += strspn(form, " \t\n"); *form; form += strspn(form, " \t\n")) {
  348.     char c;
  349.  
  350.     switch(*form++) {
  351.     case '{': /* A multi-character name switch */
  352.         if ((c = *form++) != '-' && c != '=')
  353.         Epitaph("argproc: brace must start with a switch");
  354.  
  355.         form = switchParse(form, argp, argpcountp, &bitnum, &pntcount, 
  356.                                     TRUE);
  357.         break;
  358.     case '-': /* One or several concatenated switches */
  359.     case '=': /* same as '-' but returns boolean result variable */
  360.         form = switchParse(form, argp, argpcountp, &bitnum, &pntcount,
  361.                                     FALSE);
  362.         break;
  363.     case '%': /* A normal scanf type format string argument */
  364.         form = normArgParse(form, argp, argpcountp, &bitnum, &pntcount);
  365.         break;
  366.     default:
  367.         Epitaph("argproc: invalid char (%c) in format string", *--form);
  368.     }
  369.     }
  370.  
  371.     return pntcount;
  372. }
  373.  
  374.  
  375. /*----------------------------------------------------------------------
  376.  Set the variable corresponding to any BOOL_VAR types in the format string
  377.  to an initial value of FALSE; they will be reset to TRUE when the use
  378.  of that switch is discovered in the user argument string.
  379. ----------------------------------------------------------------------*/
  380. static void
  381. initBoolVar(vars, form, formCnt)
  382.     char *vars[];
  383.     argp_t *form;
  384.     int formCnt;
  385. {
  386.     int i;
  387.  
  388.     for (i=0; i<formCnt; ++i)
  389.     if ((form[i].type & BOOL_VAR_FLAG) == BOOL_VAR_FLAG)
  390.         *((boolean *)vars[ form[i].pnt_index ]) = FALSE;
  391. }
  392.  
  393.  
  394. /*----------------------------------------------------------------------
  395.  Read in up to argp->fmt_count values from indata using sscanf,
  396.  return with bits argp->bitnum+x set if the xth parameter was there.
  397. ----------------------------------------------------------------------*/
  398. static long 
  399. argscanf(indata, argp, ps, errMode)
  400.     char *indata;
  401.     argp_t *argp;
  402.     char **ps;
  403.     int errMode;
  404. {
  405.     long bits;
  406.     int i, howmany, pos;
  407.     char *p1, *p2, *p3, *p4;
  408.  
  409. /* look up the position of the user variable to put the data into; if the
  410.  * format entry has a boolean variable too, skip that to get the position
  411.  * for the scanf. */
  412.     pos = argp->pnt_index;
  413.     if ((argp->type & BOOL_VAR_FLAG) == BOOL_VAR_FLAG)
  414.     ++pos;
  415.  
  416. /* set up the parameters that are needed for the sscanf. */
  417.     switch (argp->fmt_count) {
  418.     case 4: p4 = ps[pos + 3];
  419.     case 3: p3 = ps[pos + 2];
  420.     case 2: p2 = ps[pos + 1];
  421.     case 1: p1 = ps[pos + 0];
  422.     case 0: break;
  423.     default:
  424.     Epitaph("argproc: can only have 4 variables per argument");
  425.     }
  426.     howmany = sscanf(indata, argp->format, p1, p2, p3, p4);
  427.  
  428. /* set the bit in the result for each parameter that was there */
  429.     bits = 0;
  430.     for (i=0; i<howmany; i++)
  431.     bits |= 1 << (argp->bitnum+i);
  432.     
  433.     if (!argp->optional && howmany < 1 && argp->fmt_count > 0) {
  434.     /* This error is caused by the user, not by the programmer,
  435.      * so let the programmer say whether to abort or not
  436.      */
  437.     PrintErr(errMode, "argproc: bad or missing value for flag %s",
  438.         argp->name);
  439.     return ERROR;
  440.     }
  441.     
  442.     return bits;
  443. }
  444.  
  445.  
  446. /*----------------------------------------------------------------------
  447.  Assign values from the user's switch to the appropriate variables.
  448.  'str' is the contents of the switch, starting just after the '-'.
  449. ----------------------------------------------------------------------*/
  450. static long
  451. processSwitch(str, form, formCnt, vars, errMode)
  452.     char *str, *vars[];
  453.     argp_t *form;
  454.     int formCnt, errMode;
  455. {
  456.     int offset, j, ty;
  457.     long found = 0;
  458.  
  459. /* go through each character of the string looking for multiple switches */
  460.     for (offset=0; str[offset] != '\0';) {
  461.     /* check each of the format string entries to see if any match */
  462.     for (j=0; j<formCnt; j++) {
  463.         if ( (form[j].type & SWITCH) && strncmp(str+offset, form[j].name, 
  464.                             strlen(form[j].name))==0) {
  465.         /* skip over the name of the switch */
  466.         offset += strlen(form[j].name);
  467.  
  468.         /* mark that this switch was found */
  469.         found |= 1 << form[j].bitnum;
  470.  
  471.         ty = form[j].type;
  472.  
  473.         if ((ty & BOOL_VAR_FLAG) == BOOL_VAR_FLAG)
  474.             /* set the boolean variable to show the line had this
  475.             switch */
  476.             *((boolean *)vars[ form[j].pnt_index ]) = TRUE;
  477.  
  478.         if ((ty & VALUE_FLAG) == VALUE_FLAG)
  479.             /* if VALUE, no more string to examine after argscanf,
  480.             so return. */
  481.             return found | 
  482.             (argscanf(str+offset, &form[j], vars, errMode) << 1);
  483.  
  484.         /* don't have to do anything for BOOL_FLAG, since the 'found' 
  485.             bit was already set by the code before this switch. */
  486.  
  487.         /* go on to any other switches in the string */
  488.         break;
  489.         }
  490.     }
  491.  
  492.     /* if didn't find switch in format list, it's an error */
  493.     if (j == formCnt) {
  494.         PrintErr(errMode,"argproc: invalid flag -%s", str+offset);
  495.         return ERROR;
  496.     }
  497.     }
  498.  
  499.     return found;
  500. }
  501.  
  502.  
  503. /*----------------------------------------------------------------------
  504.  Go through the argument list, assigning values to the user's variables
  505.  as indicated by the format string breakdown.
  506. ----------------------------------------------------------------------*/
  507. static long
  508. processArgs(form, formCnt, vars, argv, argCnt, errMode)
  509.     argp_t *form;
  510.     char *vars[], *argv[];
  511.     int formCnt, argCnt, errMode;
  512. {
  513.     long found;
  514.     int i, normArgPos;
  515.  
  516.     found = 0;
  517.  
  518.     /* go through the normal arguments in the format string in order as they
  519.      * come off the command line. */
  520.     normArgPos = 0;
  521.  
  522.     for (i=1; i<argCnt; i++)
  523.     /* if argument is a switch... */
  524.     if (argv[i][0] == '-' && argv[i][1] != '\0')
  525.         found |= processSwitch(argv[i] + 1, form, formCnt, vars, errMode);
  526.         else
  527.         /* argument is not a switch, must be a NORM_ARG */
  528.         /* look for the next NORM_ARG from the format string */
  529.         for(; normArgPos < formCnt; ++normArgPos)
  530.         if (form[normArgPos].type == NORM_ARG) {
  531.             found |= argscanf(argv[i], &form[normArgPos++], 
  532.                             vars, errMode);
  533.             break;
  534.         }
  535.  
  536.     return found;
  537. }
  538.  
  539.  
  540. /*----------------------------------------------------------------------
  541.  The actual argument list is argproc(argc, argv, format, vars . . .).  The
  542.  argc and argv are from the user's main(), the format is a switch describing
  543.  string, and the vars are pointers to variables like those passed to scanf
  544.  for receiving the values extracted from argv and arranged as indicated in
  545.  the format string.
  546. ----------------------------------------------------------------------*/
  547. int argproc(va_alist)
  548.     va_dcl
  549. {
  550.     va_list ap;
  551.     char **argv, *form;
  552.     char *vars[MAX_ARGS];
  553.     int argpcount, varCnt, i, argc, errMode;
  554.     argp_t argp[MAX_ARGS];
  555.  
  556.     va_start(&ap);
  557.  
  558.     argc = va_arg(&ap, int);
  559.     argv = va_arg(&ap, char **);
  560.     form = va_arg(&ap, char *);
  561.     if (form == NULL)
  562.     return 0;
  563.  
  564.     switch (*form++) {
  565.     case 'N': case 'n': errMode = ERR_NOTE;   break;
  566.     case 'W': case 'w': errMode = ERR_WARN;   break;
  567.     case 'F': case 'f': errMode = ERR_FATAL;  break;
  568.     default : --form;   errMode = ERR_FATAL;  break;
  569.     }
  570.  
  571. /* setup argp with contents of format string, get how many arguments should
  572.  * be following the format string */
  573.     if ((varCnt = format_parse(form, argp, &argpcount)) > MAX_ARGS)
  574.     Epitaph("too many args. (limit %d)", MAX_ARGS);
  575.     
  576. /* load in the pointers for the rest of the args */
  577.     for (i=0; i<varCnt; i++)
  578.     vars[i] = va_arg(&ap, char *);
  579.  
  580.     va_end(ap);
  581.  
  582. /* initialize the boolean variables to FALSE */
  583.     initBoolVar(vars, argp, argpcount); 
  584.  
  585.     return processArgs(argp, argpcount, vars, argv, argc, errMode);
  586. }
  587.  
  588. #endif        /* "ifndef lint" around real guts of module */
  589.