home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 1 / 1502 < prev    next >
Internet Message Format  |  1990-12-28  |  13KB

  1. From: darcy@druid.uucp (D'Arcy J.M. Cain)
  2. Newsgroups: alt.sources
  3. Subject: getarg - a replacement for getopt
  4. Message-ID: <1990Jun22.172759.27249@druid.uucp>
  5. Date: 22 Jun 90 17:27:59 GMT
  6.  
  7. This is not being shar'ed or anything since it is only one file and it
  8. can be used on systems that cannot unshar by running the shell.  Just
  9. remove everything up to the first opening comment and the signature.
  10.  
  11. /*
  12. getarg.c
  13. Written by D'Arcy J.M. Cain
  14. D'Arcy Cain Consulting
  15. 275 Manse Road, Unit # 24
  16. West Hill, Ontario
  17. M1E 4X8
  18. 416 281 6094
  19.  
  20. UUCP: darcy@druid
  21.  
  22. This routine may be freely distributed as long as credit is given to D'Arcy
  23. J.M. Cain, the source is included and this notice remains intact.  There is
  24. specifically no restrictions on use of the program including personal or
  25. commercial.  You may even charge others for this routine as long as the above
  26. conditions are met.
  27.  
  28. This is not shareware and no registration fee is expected.  If you like the
  29. program and want to support this method of distribution, write a program or
  30. routine and distribute it the same way and I will feel I have been paid.
  31.  
  32. Of course gifts of money, drinks and extravagant jewels are always welcome.
  33.  
  34. As for documentation, you're looking at it.
  35.  
  36. This set of routines is a replacement for getopt.  To the user it should
  37. look the same except that options and files may be intermingled on the
  38. command line instead of forcing options to come first.  This allows
  39. things like the following where option 'a' takes a word argument:
  40.     command -vx -a on file1 -a off file2
  41. allowing the user to process file1 with the a flag on and file 2 with the a
  42. flag off.
  43.  
  44. In addition, the caller may set up the argument list from more than one
  45. place.  The first place, of course, would be from the command line as
  46. getopt does.  Other possibilities are to read the an environment variable
  47. or a file for arguments.  You may even have one of the options cause a
  48. file to be read to insert arguments.  I am suggesting that "-@ filename"
  49. be used for consistency unless someone can suggest why this would not
  50. be suitable.
  51.  
  52. To implement this, getarg splits the function into two main parts plus
  53. a third part which is added for programmer convenience.  The first part
  54. is initarg which initialises the argument list.  The prototype is:
  55.  
  56.     int        initarg(int argc, char **argv);
  57.  
  58. and would normally be called as follows:
  59.  
  60.     initarg(argc - 1, argv + 1);
  61.  
  62. This function can be called as often as you wish.  Each time you call
  63. initarg, the argument list is stuffed into the current list at the point
  64. which is currently being processed.  Thus, after making the above call you
  65. might look for an environment variable and if it exists then parse it into
  66. an argument list and call initarg again with that list.  This effectively
  67. allows users to set up defaults in their .profile and to override them when
  68. calling the program.  For example, say that there was program 'foo' which
  69. takes an option 'a' which took as an argument the word 'on' or 'off' and a
  70. user wanted to set it up so that it was normally off.  The user could add
  71. the line:
  72.     foo=-aoff
  73. to the .profile.  If one time the user wants the option on then a command
  74. line such as
  75.     foo -a on
  76. is effectively
  77.     foo -aoff -a on
  78. Which, if the code is written to allow flags to change back and forth, will
  79. change the default action.
  80.  
  81. In addition you can use arguments from the command line to open files
  82. and read in more arguments which can be stuffed into the argument
  83. stream.
  84.  
  85.     if (c == '@')
  86.         load_args_from_file(optarg);
  87.  
  88. Note that there is a possibility of a problem if initarg is called while
  89. an argument is being parsed.  Consider the following code:
  90.  
  91.     while ((c = getarg("abcz")) != 0)
  92.     {
  93.         case 'a':
  94.             something();
  95.             break;
  96.  
  97.         case 'b':
  98.             something();
  99.             break;
  100.  
  101.         case 'c':
  102.             something();
  103.             break;
  104.  
  105.         case 'z':
  106.             foo("standard.arg");
  107.             break;
  108.     }
  109.  
  110. where foo is designed to read a file as a series of arguments and call
  111. initarg.  This can cause serious problems if you have a command such as
  112. "bar -abzc" since it will replace the pointer to "-abzc" with the first
  113. argument in the file.  Of course this will probably never be a problem
  114. since you would most likely include the file name to be read with the
  115. option rather than hard coding it so the current argument will be consumed
  116. before reading in the file but before pointing to the next argument.
  117.  
  118.  
  119. For programmer convenience there is a routine called initarge() which
  120. is prototyped as:
  121.     int        initarge(int argc, char **argv);
  122. and is normally called as
  123.     initarge(argc, argv);
  124.  
  125. Notice that this is similar to the initarg example above except that all
  126. the arguments are sent including the program name.  This function will in
  127. turn call initarg with argc - 1 and argv +1.  In addition it will take the
  128. program's base name and look for an environment variable with that name.
  129. If found, it parses the string and stuffs that in as well.  Note that the
  130. environment string may include arguments with spaces if the argument is
  131. delimited by quotes.  This could get a little tricky since the quotes must
  132. be escaped in the shell.  for example the following string
  133.     -a "hello, world"
  134. would have to be created with the following command
  135.     foo="-a \"hello, world\""
  136. and of course strings that have quotes in them get even more complicated.
  137.     foo="-a \"This is a quote - \\\"\""
  138. which becomes
  139.     -a "This is a quote - \""
  140. And that becomes the strings
  141.     -a
  142.     This is a quote - "
  143.  
  144.  
  145. Both initarg and initarge return -1 on error.  The only error condition
  146. is not enough memory to run the routines.  Otherwise they return the
  147. number of arguments added to the argument list.
  148.  
  149.  
  150. The other module is the function getarg which returns options and
  151. arguments to the calling program.  It is prototyped as follows:
  152.     int            getarg(char *optstr);
  153.  
  154. The parameter optstr is similar to the third parameter to getopt(3).
  155. The other arguments are not needed since initarg gets the argument list.
  156.  
  157. There are five possible returns from getarg.  If there are no more options
  158. or arguments to read then it returns a zero.  If an option is read but
  159. is not matched in optstr or a needed argument is missing then a question
  160. mark is returned.  If a non option argument is read then -1 is returned
  161. and optarg points to the argument.  If a '-' appears as a separate argument
  162. then a special return of '-' is returned indicating standard input (only by
  163. convention of course.)  Otherwise it must be a valid option and the letter
  164. is returned.  If it is an option expecting an argument then optarg points
  165. to the argument.
  166.  
  167. One extra feature I have added in is a semi-colon operator similiar to the
  168. colon operator.  If an option letter in opstring is followed by a semi-colon,
  169. the option may *optionally* take an argument.  The argument must follow the
  170. option letter as part of the same argument.  This normally means no spaces
  171. between the option letter and its argument.  I am not to sure about this
  172. one since it has to be treated a little different than other arguments by
  173. the user.  Comments on this feature hereby solicited.
  174.  
  175. The global variable opterr is not implemented.  With Windows and other
  176. screen based applications getting to be so popular it is assumed that the
  177. calling routine will want to handle its own error message handling.  I am
  178. also thinking about dropping optind as a global since I haven't figured
  179. out any use for it now.  If someone can think of one, please let me know.
  180. In the meantime you shouldn't declare optind as external in your program
  181. unless you have to.
  182.  
  183. Sample usage assuming two mutually exclusive options 'a' and 'b', option
  184. 'o' to specify output and option 'v' which must be set before any files
  185. are processed and not allowed after processing begins.
  186.  
  187.     main(int argc, char **argv)
  188.     {
  189.         int c, proc_started = 0;
  190.         FILE    *in_fp, *out_fp = stdout;
  191.         extern char *optarg;
  192.         static char *opt_str[] = { "abo;v", "abo;" };
  193.         .
  194.         .
  195.         .
  196.         initarg(argc - 1, argv + 1);
  197.             --- OR ---
  198.         initarge(argc, argv);
  199.  
  200.         while ((c = getarg(opt_str[proc_started])) != 0)
  201.         {
  202.             switch (c)
  203.             {
  204.                 case 'a':
  205.                     if (bflag)
  206.                         errflag++;
  207.                     else
  208.                         aflag++;
  209.                     break;
  210.  
  211.                 case 'b':
  212.                     if (aflag)
  213.                         errflag++;
  214.                     else
  215.                         bflag++;
  216.                     break;
  217.  
  218.                 case 'v':
  219.                     vflag++;
  220.                     break;
  221.  
  222.                 case 'o':
  223.                     if ((out_fp != stdout) && (out_fp != NULL))
  224.                         fclose(out_fp);
  225.  
  226.                     if (optarg == NULL)    ** no argument means stdout **
  227.                         out_fp = stdout;
  228.                     else if ((out_fp = fopen(optarg, "w")) == NULL)
  229.                         err_exit("Can't open output file");
  230.  
  231.                     break;
  232.  
  233.                 case -1:
  234.                     if ((fp = fopen(optarg, "r")) != NULL)
  235.                         do_stuff(in_fp, out_fp);
  236.                     else
  237.                         err_exit("Can't open input file\n");
  238.  
  239.                     proc_started = 1;
  240.                     break;
  241.  
  242.                 case '-':
  243.                     do_stuff(stdin, out_fp);
  244.                     proc_started = 1;
  245.                     break;
  246.  
  247.                 case '?':
  248.                     usage();
  249.                     errflag++;
  250.                     break;
  251.             }
  252.  
  253.             if (errflag)
  254.                 do_error_stuff_and_exit();
  255.         }
  256.     }
  257.  
  258. */
  259.  
  260. #ifdef BSD
  261. #include <strings.h>
  262. #else
  263. #include <string.h>
  264. #define    index    strchr
  265. #endif
  266.  
  267. #include    <stdlib.h>
  268. #include    <malloc.h>
  269. #include    <ctype.h>
  270.  
  271. #ifndef        NULL
  272. #define        NULL    (void *)(0)
  273. #endif
  274.  
  275. int        optind = 0;
  276. char    *optarg;
  277.  
  278. /* Note that the above declarations can cause problems with programs
  279. that use getopt(3) if this module is scanned first in the link phase.
  280. This means that if you use getopt sometimes then you should keep this
  281. module separate and link it in specifically when needed.  Alternatively
  282. you can change the names of the above externs (perhaps declare optind as
  283. static as programs don't really need it anyway) and have a #define so
  284. that the program still uses the above name(s).  I considered using a
  285. different name for optarg but was afraid that anything I picked would
  286. conflict with user's names.
  287. */
  288.  
  289. static char        **pargv = NULL;
  290. static int        pargc = 0;
  291.  
  292. int        initarg(int argc, char **argv)
  293. {
  294.     int        k = argc * sizeof(char *);
  295.  
  296.     /* check for trivial case */
  297.     if (!argc)
  298.         return(0);
  299.  
  300.     /* get or expand space */
  301.     if (pargc == 0)
  302.         pargv = malloc(k);
  303.     else
  304.         pargv = realloc(pargv, pargc + k);
  305.  
  306.     if (pargv == NULL)
  307.         return(-1);                /* not enough memory for argument pointers */
  308.  
  309.     /* if adding arguments insert them at current argument */
  310.     if (pargc)
  311.         for (k = pargc - 1; k >= optind; k--)
  312.             pargv[k + argc] = pargv[k];
  313.  
  314.     for (k = 0; k < argc; k++)
  315.         pargv[optind + k] = argv[k];
  316.  
  317.     pargc += argc;
  318.     return(pargc);
  319. }
  320.  
  321.  
  322. int        initarge(int argc, char **argv)
  323. {
  324.     char    *env_str, *env_args[64];
  325.     int        k, j = 0;
  326. #ifdef    __MSDOS__
  327.     char    prog_name[64];
  328. #endif
  329.  
  330.     if ((k = initarg(argc - 1, argv + 1)) == -1)
  331.         return(-1);                /* not enough memory for argument pointers */
  332.  
  333. #ifdef    __MSDOS__
  334.     if ((env_str = strrchr(argv[0], '\\')) == NULL)
  335.     {
  336.         strcpy(prog_name, argv[0]);
  337.         if ((env_str = strchr(prog_name, ':')) != NULL)
  338.             strcpy(prog_name, env_str + 1);
  339.     }
  340.     else
  341.         strcpy(prog_name, env_str + 1);
  342.  
  343.     if ((env_str = strchr(prog_name, '.')) != NULL)
  344.         *env_str = 0;
  345.  
  346.     if ((env_str = getenv(prog_name)) == NULL)
  347. #else
  348.     if ((env_str = strrchr(argv[0], '/')) != NULL)
  349.         env_str++;
  350.     else
  351.         env_str = argv[0];
  352.  
  353.     if ((env_str = getenv(env_str)) == NULL)
  354. #endif
  355.         return(k);
  356.  
  357.     if ((env_args[0] = malloc(strlen(env_str) + 1)) == NULL)
  358.         return(-1);                /* not enough memory for argument pointers */
  359.  
  360.     env_str = strcpy(env_args[0], env_str);
  361.  
  362.     while (isspace(*env_str))
  363.         env_str++;
  364.  
  365.     while (*env_str)
  366.     {
  367.         if (*env_str == '"')
  368.         {
  369.             env_args[j++] = ++env_str;
  370.  
  371.             while (*env_str && *env_str != '"')
  372.             {
  373.                 if (*env_str == '\\')
  374.                 {
  375.                     strcpy(env_str, env_str + 1);
  376.                     env_str++;
  377.                 }
  378.                 env_str++;
  379.             }
  380.         }
  381.         else
  382.         {
  383.             env_args[j++] = env_str;
  384.  
  385.             while (*env_str && !isspace(*env_str))
  386.                 env_str++;
  387.         }
  388.  
  389.         if (*env_str)
  390.             *env_str++ = 0;
  391.  
  392.         while (*env_str && isspace(*env_str))
  393.             env_str++;
  394.     }
  395.  
  396.     if ((j = initarg(k, env_args)) == 0)
  397.         return(-1);                /* not enough memory for argument pointers */
  398.  
  399.     return(j + k);
  400. }
  401.  
  402. /*
  403. The meat of the module.  This returns options and arguments similar to
  404. getopt() as described above.
  405. */
  406.  
  407. int        getarg(const char *opts)
  408. {
  409.     static int sp = 0;
  410.     int c;
  411.     char *cp;
  412.  
  413.     optarg = NULL;
  414.  
  415.     /* return 0 if we have read all the arguments */
  416.     if(optind >= pargc)
  417.     {
  418.         if (pargv != NULL)
  419.             free(pargv);
  420.  
  421.         pargc = 0;
  422.         return(0);
  423.     }
  424.  
  425.     /* Are we starting to look at a new argument? */
  426.     if(sp == 0)
  427.     {
  428.         /* return it if it is a file name */
  429.         if (*pargv[optind] != '-')
  430.         {
  431.             optarg = pargv[optind++];
  432.             return(-1);
  433.         }
  434.  
  435.         /* special return for standard input */
  436.         if (strcmp(pargv[optind], "-") == 0)
  437.             return('-');
  438.  
  439.         /* otherwise point to option letter */
  440.         sp = 1;
  441.     }
  442.     else if (pargv[optind][++sp] == 0)
  443.     {
  444.         /* recursive call if end of this argument */
  445.         sp = 0;
  446.         optind++;
  447.         return(getarg(opts));
  448.     }
  449.  
  450.     c = pargv[optind][sp];
  451.  
  452.     if(c == ':' || (cp = index(opts, c)) == NULL)
  453.         return('?');
  454.  
  455.     if(*++cp == ':')
  456.     {
  457.         /* Note the following code does not allow leading
  458.            spaces or all spaces in an argument */
  459.  
  460.         while (isspace(pargv[optind][++sp]))
  461.             ;
  462.  
  463.         if(pargv[optind][sp])
  464.             optarg = pargv[optind++] + sp;
  465.         else if(++optind >= pargc)
  466.             c = '?';
  467.         else
  468.             optarg = pargv[optind++];
  469.  
  470.         sp = 0;
  471.     }
  472.     else if (*cp == ';')
  473.     {
  474.         while (isspace(pargv[optind][++sp]))
  475.             ;
  476.  
  477.         if (pargv[optind][sp])
  478.             optarg = pargv[optind] + sp;
  479.  
  480.         optind++;
  481.         sp = 0;
  482.     }
  483.  
  484.     return(c);
  485. }
  486.  
  487. -- 
  488. D'Arcy J.M. Cain (darcy@druid)     |   Government:
  489. D'Arcy Cain Consulting             |   Organized crime with an attitude
  490. West Hill, Ontario, Canada         |
  491. (416) 281-6094                     |
  492.