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

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