home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / snip9707.zip / GETOPTS.C < prev    next >
C/C++ Source or Header  |  1997-07-05  |  18KB  |  580 lines

  1. /* +++Date last modified: 05-Jul-1997 */
  2.  
  3. /************************************************************************/
  4. /*                                                                      */
  5. /*  GETOPTS.C - Universal command line options parser                   */
  6. /*                                                                      */
  7. /*  Original Copyright 1990-96 by Robert B. Stout as part of            */
  8. /*  the MicroFirm Function Library (MFL)                                */
  9. /*                                                                      */
  10. /*  The user is granted a free limited license to use this source file  */
  11. /*  to create royalty-free programs, subject to the terms of the        */
  12. /*  license restrictions specified in the LICENSE.MFL file.             */
  13. /*                                                                      */
  14. /************************************************************************/
  15.  
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <string.h>
  19. #include <ctype.h>
  20. #include <limits.h>
  21. #include "sniptype.h"
  22. #include "filnames.h"
  23. #include "errors.h"
  24. #include "dirport.h"
  25. #include "snipmath.h"
  26. #include "minmax.h"
  27. #include "getopts.h"
  28. #include "numcnvrt.h"
  29.  
  30. #define MAX_XARGS 512
  31.  
  32. int         xargc = 0;
  33. char       *xargv[MAX_XARGS]        = {NULL};
  34. Boolean_T   getopts_range_err       = False_;
  35. Boolean_T   xargs_on                = True_;
  36.  
  37. static FILE *rspfile = NULL;
  38. static int   count = 0, argidx = 0;
  39.  
  40. enum proc_stat { PSerror = -1, PSok, PSliteral};
  41.  
  42. static Boolean_T      PASCAL bounds(struct Option_Tag *);
  43. static enum proc_stat PASCAL swProc(char *swStr);
  44. static void           PASCAL argProc(char *argStr);
  45.  
  46. /*
  47. **  getopts()
  48. **
  49. **  Parameters: 1 - argc from main()
  50. **              2 - argv from main()
  51. **              3 - your program's options[] array
  52. **
  53. **  Returns: Number of options specified or Error_
  54. **
  55. **  Notes: 1. Your program should declare the global options[] array which
  56. **            specifies all options recognized by getopts().
  57. **
  58. **         2. Out of range data are coerced into range and getopts_range_err
  59. **            is set True_.
  60. */
  61.  
  62. int getopts(int argc, char *argv[])
  63. {
  64.       int i;
  65.       Boolean_T scanning = True_;
  66.       struct Option_Tag *ptr;
  67.       char rspfname[FILENAME_MAX];
  68.       char newarg[256];
  69.       enum proc_stat ps;
  70.  
  71.       xargc = argc;
  72.       xargv[argidx++] = argv[0];
  73.       for (i = 1, count = 0; i < argc; )
  74.       {
  75.             /*
  76.             ** If necessary, open a response file
  77.             */
  78.  
  79.             if (scanning && !rspfile && '@' == argv[i][0])
  80.             {
  81.                   rspfile = cant(&argv[i][1], "r");
  82.                   --xargc;
  83.                   continue;
  84.             }
  85.  
  86.             /*
  87.             ** Get the next argument
  88.             */
  89.  
  90.             if (rspfile)
  91.             {
  92.                   if (NULL == fgets(newarg, 256, rspfile))
  93.                   {
  94.                         rspfile = NULL;
  95.                         ++i;
  96.                         continue;
  97.                   }
  98.                   else
  99.                   {
  100.                         if ('\n' == LAST_CHAR(newarg))
  101.                               LAST_CHAR(newarg) = NUL;
  102.                   }
  103.             }
  104.             else
  105.             {
  106.                   strcpy(newarg, argv[i++]);
  107.             }
  108.  
  109.             /*
  110.             ** Per Unix tradition, back-to-back switch characters signify
  111.             ** the end of the switches
  112.             */
  113.  
  114.             if ((2 == strlen(newarg)) && strchr("-/", newarg[0]) &&
  115.                   strchr("-/", newarg[1]))
  116.             {
  117.                   scanning = False_;
  118.                   if (!rspfile)
  119.                         --xargc;
  120.                   continue;
  121.             }
  122.  
  123.             if (scanning && (strchr("-/", newarg[0])))
  124.             {
  125.                   ps = swProc(newarg);
  126.  
  127.                   if (PSerror == ps)
  128.                         return Error_;
  129.  
  130.                   if (PSok == ps)
  131.                         continue;
  132.             }
  133.  
  134.             /*
  135.             ** If we got here, newarg must be an argument or filename
  136.             */
  137.  
  138.             argProc(newarg);
  139.       }
  140.       return count;
  141. }
  142.  
  143. /*
  144. **  Static function to process switch statements
  145. **
  146. **  Parameters: 1 - argv[i] containing the switch
  147. **
  148. **  Returns: PSok      if switch successful
  149. **           PSerror   if invalid
  150. **           PSliteral if literal (non-switch) argument
  151. */
  152.  
  153. static enum proc_stat PASCAL swProc(char *swStr)
  154. {
  155.       struct Option_Tag *ptr;
  156.       Boolean_T searching;
  157.       unsigned short byte_var;
  158.       char *arg_ptr;
  159.  
  160.       /*
  161.       ** Found a switch - If the 2nd character is also a switch
  162.       ** character. If so, then it's a literal and is skipped
  163.       */
  164.  
  165.       if (strchr("-/@", swStr[1]))
  166.             return PSliteral;
  167.  
  168.       for (ptr = options, searching = True_; searching; ++ptr)
  169.       {
  170.             if (!ptr->case_sense)
  171.             {
  172.                   ptr->letter = toupper(ptr->letter);
  173.                   swStr[1]    = toupper(swStr[1]);
  174.             }
  175.             if ((int)swStr[1] == ptr->letter) switch (ptr->type)
  176.             {
  177.             case Boolean_Tag:
  178.                   if ('-' == swStr[2])
  179.                         *((Boolean_T *)(ptr->buf)) = False_;
  180.                   else  *((Boolean_T *)(ptr->buf)) = True_;
  181.                   searching = False_;
  182.                   break;
  183.  
  184.             case Byte_Tag:
  185.                   if (!swStr[2])
  186.                   {
  187.                         if (ptr->Default)
  188.                               arg_ptr = ptr->Default;
  189.                         else  return PSerror;
  190.                   }
  191.                   else  arg_ptr = &swStr[2];
  192.  
  193.                   sscanf(arg_ptr, "%hx", &byte_var);
  194.                   *((char *)(ptr->buf)) = (unsigned char)(byte_var & 0xff);
  195.                   bounds(ptr);
  196.                   searching = False_;
  197.                   break;
  198.  
  199.             case Int_Tag:
  200.                   if (!swStr[2])
  201.                   {
  202.                         if (ptr->Default)
  203.                               arg_ptr = ptr->Default;
  204.                         else  return PSerror;
  205.                   }
  206.                   else  arg_ptr = &swStr[2];
  207.  
  208.                   *((int *)(ptr->buf)) = (int)(dround(getopts_eval(arg_ptr)));
  209.                   bounds(ptr);
  210.                   searching = False_;
  211.                   break;
  212.  
  213.             case Short_Tag:
  214.                   if (!swStr[2])
  215.                   {
  216.                         if (ptr->Default)
  217.                               arg_ptr = ptr->Default;
  218.                         else  return PSerror;
  219.                   }
  220.                   else  arg_ptr = &swStr[2];
  221.  
  222.                   *((short *)(ptr->buf)) =
  223.                         (short)(dround(getopts_eval(arg_ptr)));
  224.                   bounds(ptr);
  225.                   searching = False_;
  226.                   break;
  227.  
  228.             case Word_Tag:
  229.                   if (!swStr[2])
  230.                   {
  231.                         if (ptr->Default)
  232.                               arg_ptr = ptr->Default;
  233.                         else  return PSerror;
  234.                   }
  235.                   else  arg_ptr = &swStr[2];
  236.  
  237.                   sscanf(arg_ptr, "%hx", (unsigned short *)(ptr->buf));
  238.                   bounds(ptr);
  239.                   searching = False_;
  240.                   break;
  241.  
  242.             case Long_Tag:
  243.                   if (!swStr[2])
  244.                   {
  245.                         if (ptr->Default)
  246.                               arg_ptr = ptr->Default;
  247.                         else  return PSerror;
  248.                   }
  249.                   else  arg_ptr = &swStr[2];
  250.  
  251.                   *((long *)(ptr->buf)) =
  252.                         (long)(dround(getopts_eval(arg_ptr)));
  253.                   bounds(ptr);
  254.                   searching = False_;
  255.                   break;
  256.  
  257.             case DWord_Tag:
  258.                   if (!swStr[2])
  259.                   {
  260.                         if (ptr->Default)
  261.                               arg_ptr = ptr->Default;
  262.                         else  return PSerror;
  263.                   }
  264.                   else  arg_ptr = &swStr[2];
  265.  
  266.                   sscanf(arg_ptr, "%lx", (unsigned long *)(ptr->buf));
  267.                   bounds(ptr);
  268.                   searching = False_;
  269.                   break;
  270.  
  271.             case Float_Tag:
  272.                   if (!swStr[2])
  273.                   {
  274.                         if (ptr->Default)
  275.                               arg_ptr = ptr->Default;
  276.                         else  return PSerror;
  277.                   }
  278.                   else  arg_ptr = &swStr[2];
  279.  
  280.                   *((double *)(ptr->buf)) = (double)getopts_eval(arg_ptr);
  281.                   bounds(ptr);
  282.                   searching = False_;
  283.                   break;
  284.  
  285.             case DFloat_Tag:
  286.                   if (!swStr[2])
  287.                   {
  288.                         if (ptr->Default)
  289.                               arg_ptr = ptr->Default;
  290.                         else  return PSerror;
  291.                   }
  292.                   else  arg_ptr = &swStr[2];
  293.  
  294.                   *((double *)(ptr->buf)) = (double)getopts_eval(arg_ptr);
  295.                   bounds(ptr);
  296.                   searching = False_;
  297.                   break;
  298.  
  299.             case String_Tag:
  300.                   if (!swStr[2] && ptr->Default)
  301.                         strcpy(ptr->buf, (char *)(ptr->Default));
  302.                   else  strcpy(ptr->buf, &swStr[2]);
  303.  
  304.                   searching = False_;
  305.                   break;
  306.  
  307.             default:
  308.                   return Error_;
  309.             }
  310.       }
  311.       ++count;
  312.       if (!rspfile)
  313.             --xargc;
  314.  
  315.       return PSok;
  316. }
  317.  
  318. /*
  319. **  Static function to process arguments
  320. */
  321.  
  322. static void PASCAL argProc(char *argStr)
  323. {
  324.       DOSFileData ff;
  325.  
  326.       /* If no wildcards or ignoring wildcards, just copy it */
  327.  
  328.       if (!xargs_on || !has_wild(argStr))
  329.       {
  330.             xargv[argidx] = malloc(strlen(argStr) + 1);
  331.             if (NULL == xargv[argidx])
  332.                   ErrExit("Out of memory");
  333.             strcpy(xargv[argidx], argStr);
  334.             ++argidx;
  335.             return;
  336.       }
  337.       else  /* Expand wildcards, if possible                      */
  338.       {
  339.             if (Success_ == FIND_FIRST(argStr, _A_ANY, &ff))
  340.             {
  341.                   char path[FILENAME_MAX];
  342.                   char *p;
  343.  
  344.                   /* Save the path for re-attachment              */
  345.  
  346.                   fnSplit(argStr, NULL, path, NULL, NULL, NULL, NULL);
  347.  
  348.                   --xargc;    /* We add stuff in the loop, so back up   */
  349.                   do
  350.                   {                             
  351.                         xargv[argidx] = malloc(strlen(ff_name(&ff))
  352.                                                + strlen(path) + 2);
  353.                         if (NULL == xargv[argidx])
  354.                               ErrExit("Out of memory");
  355.                         fnMerge(xargv[argidx], NULL, path, NULL, ff_name(&ff),
  356.                                 NULL, NULL);
  357.                         ++argidx;
  358.                         ++xargc;
  359.  
  360.                   } while (Success_ == FIND_NEXT(&ff));
  361.                   FIND_END(&ff);
  362.             }
  363.       }
  364. }
  365.  
  366. /*
  367. **  Assure new data are within specified ranges, return non-zero if coerced
  368. */
  369.  
  370. static Boolean_T PASCAL bounds(struct Option_Tag *option)
  371. {
  372.       Boolean_T coerced = False_;
  373.       union {
  374.             unsigned char     B;
  375.             int               I;
  376.             short             S;
  377.             unsigned short    W;
  378.             long              L;
  379.             unsigned long     DW;
  380.             float             F;
  381.             double            D;
  382.       } tmp, val;
  383.       
  384.       switch(option->type)
  385.       {
  386.       case Byte_Tag:
  387.             tmp.B = *((unsigned char *)(option->buf));
  388.             if (option->max)
  389.             {
  390.                   sscanf(option->max, "%hx", &val.B);
  391.                   tmp.B = min(tmp.B, val.B);
  392.             }
  393.             if (option->min)
  394.             {
  395.                   sscanf(option->min, "%hx", &val.B);
  396.                   tmp.B = max(tmp.B, val.B);
  397.             }
  398.             if (*((unsigned char *)(option->buf)) != tmp.B)
  399.             {
  400.                   getopts_range_err = True_;
  401.                   *((unsigned char *)(option->buf)) = tmp.B;
  402.                   coerced = True_;
  403.             }
  404.             break;
  405.  
  406.       case Int_Tag:
  407.             tmp.I = *((int *)(option->buf));
  408.             if (option->max)
  409.             {
  410.                   val.D = dround(getopts_eval(option->max));
  411.                   if (val.D > (double)INT_MAX)
  412.                         val.I = INT_MAX;
  413.                   else  val.I = (int)val.D;
  414.                   tmp.I = min(tmp.I, val.I);
  415.             }
  416.             if (option->min)
  417.             {
  418.                   val.D = dround(getopts_eval(option->min));
  419.                   if (val.D < (double)INT_MIN)
  420.                         val.I = INT_MIN;
  421.                   else  val.I = (int)val.D;
  422.                   tmp.I = max(tmp.I, val.I);
  423.             }
  424.             if (*((int *)(option->buf)) != tmp.I)
  425.             {
  426.                   getopts_range_err = True_;
  427.                   *((int *)(option->buf)) = tmp.I;
  428.                   coerced = True_;
  429.             }
  430.             break;
  431.  
  432.       case Short_Tag:
  433.             tmp.S = *((short *)(option->buf));
  434.             if (option->max)
  435.             {
  436.                   val.D = dround(getopts_eval(option->max));
  437.                   if (val.D > (double)SHRT_MAX)
  438.                         val.S = SHRT_MAX;
  439.                   else  val.S = (short)val.D;
  440.                   tmp.S = min(tmp.I, val.I);
  441.             }
  442.             if (option->min)
  443.             {
  444.                   val.D = dround(getopts_eval(option->min));
  445.                   if (val.D < (double)SHRT_MIN)
  446.                         val.S = SHRT_MIN;
  447.                   else  val.S = (short)val.D;
  448.                   tmp.S = max(tmp.I, val.I);
  449.             }
  450.             if (*((short *)(option->buf)) != tmp.S)
  451.             {
  452.                   getopts_range_err = True_;
  453.                   *((short *)(option->buf)) = tmp.I;
  454.                   coerced = True_;
  455.             }
  456.             break;
  457.  
  458.       case Word_Tag:
  459.             tmp.W = *((unsigned short *)(option->buf));
  460.             if (option->max)
  461.             {
  462.                   sscanf(option->max, "%hx", &val.W);
  463.                   tmp.W = min(tmp.W, val.W);
  464.             }
  465.             if (option->min)
  466.             {
  467.                   sscanf(option->min, "%hx", &val.W);
  468.                   tmp.W = max(tmp.W, val.W);
  469.             }
  470.             if (*((unsigned short *)(option->buf)) != tmp.W)
  471.             {
  472.                   getopts_range_err = True_;
  473.                   *((unsigned short *)(option->buf)) = tmp.W;
  474.                   coerced = True_;
  475.             }
  476.             break;
  477.  
  478.       case Long_Tag:
  479.             tmp.L = *((long *)(option->buf));
  480.             if (option->max)
  481.             {
  482.                   val.D = dround(getopts_eval(option->max));
  483.                   if (val.D > (double)LONG_MAX)
  484.                         val.L = LONG_MAX;
  485.                   else  val.L = (long)val.D;
  486.                   tmp.L = min(tmp.L, val.L);
  487.             }
  488.             if (option->min)
  489.             {
  490.                   val.D = dround(getopts_eval(option->min));
  491.                   if (val.D < (double)LONG_MIN)
  492.                         val.L = LONG_MIN;
  493.                   else  val.L = (int)val.D;
  494.                   tmp.L = max(tmp.L, val.L);
  495.             }
  496.             if (*((long *)(option->buf)) != tmp.L)
  497.             {
  498.                   getopts_range_err = True_;
  499.                   *((long *)(option->buf)) = tmp.L;
  500.                   coerced = True_;
  501.             }
  502.             break;
  503.  
  504.       case DWord_Tag:
  505.             tmp.DW = *((unsigned long *)(option->buf));
  506.             if (option->max)
  507.             {
  508.                   sscanf(option->max, "%lx", &val.DW);
  509.                   tmp.DW = min(tmp.DW, val.DW);
  510.             }
  511.             if (option->min)
  512.             {
  513.                   sscanf(option->min, "%hx", &val.DW);
  514.                   tmp.DW = max(tmp.DW, val.DW);
  515.             }
  516.             if (*((unsigned long *)(option->buf)) != tmp.DW)
  517.             {
  518.                   getopts_range_err = True_;
  519.                   *((unsigned long *)(option->buf)) = tmp.DW;
  520.                   coerced = True_;
  521.             }
  522.             break;
  523.  
  524.       case Float_Tag:
  525.             tmp.F = *((float *)(option->buf));
  526.             if (option->max)
  527.             {
  528.                   val.F = (float)getopts_eval(option->max);
  529.                   tmp.F = min(tmp.F, val.F);
  530.             }
  531.             if (option->min)
  532.             {
  533.                   val.F = (float)getopts_eval(option->min);
  534.                   tmp.F = max(tmp.F, val.F);
  535.             }
  536.             if (*((float *)(option->buf)) != tmp.F)
  537.             {
  538.                   getopts_range_err = True_;
  539.                   *((float *)(option->buf)) = tmp.F;
  540.                   coerced = True_;
  541.             }
  542.             break;
  543.  
  544.       case DFloat_Tag:
  545.             tmp.D = *((double *)(option->buf));
  546.             if (option->max)
  547.             {
  548.                   val.D = getopts_eval(option->max);
  549.                   tmp.D = min(tmp.D, val.D);
  550.             }
  551.             if (option->min)
  552.             {
  553.                   val.D = getopts_eval(option->min);
  554.                   tmp.D = max(tmp.D, val.D);
  555.             }
  556.             if (*((double *)(option->buf)) != tmp.D)
  557.             {
  558.                   getopts_range_err = True_;
  559.                   *((double *)(option->buf)) = tmp.D;
  560.                   coerced = True_;
  561.             }
  562.             break;
  563.       }
  564.  
  565.       return coerced;
  566. }
  567.  
  568. /*
  569. **  Simplified evaluate() call - returns double or aborts
  570. */
  571.  
  572. double getopts_eval(char *str)
  573. {
  574.       double retval;
  575.  
  576.       if (Success_ == evaluate(str, &retval))
  577.             return retval;
  578.       else  ErrExit("Error evlauating \"%s\" - aborting\n", str);
  579. }
  580.