home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume25 / hostcvt / egetopt.c next >
C/C++ Source or Header  |  1992-01-03  |  7KB  |  281 lines

  1. /*
  2.  * egetopt.c -- Extended 'getopt'.
  3.  *
  4.  * A while back, a public-domain version of getopt() was posted to the
  5.  * net.  A bit later, a gentleman by the name of Keith Bostic made some
  6.  * enhancements and reposted it.
  7.  *
  8.  * In recent weeks (i.e., early-to-mid 1988) there's been some
  9.  * heated discussion in comp.lang.c about the merits and drawbacks
  10.  * of getopt(), especially with regard to its handling of '?'.
  11.  *
  12.  * In light of this, I have taken Mr. Bostic's public-domain getopt()
  13.  * and have made some changes that I hope will be considered to be
  14.  * improvements.  I call this routine 'egetopt' ("Extended getopt").
  15.  * The default behavior of this routine is the same as that of getopt(),
  16.  * but it has some optional features that make it more useful.  These
  17.  * options are controlled by the settings of some global variables.
  18.  * By not setting any of these extra global variables, you will have
  19.  * the same functionality as getopt(), which should satisfy those
  20.  * purists who believe getopt() is perfect and can never be improved.
  21.  * If, on the other hand, you are someone who isn't satisfied with the
  22.  * status quo, egetopt() may very well give you the added capabilities
  23.  * you want.
  24.  *
  25.  * Look at the enclosed README file for a description of egetopt()'s
  26.  * new features.
  27.  *
  28.  * The code was originally posted to the net as getopt.c by ...
  29.  *
  30.  *    Keith Bostic
  31.  *    ARPA: keith@seismo 
  32.  *    UUCP: seismo!keith
  33.  *
  34.  * Current version: added enhancements and comments, reformatted code.
  35.  *
  36.  *    Lloyd Zusman
  37.  *    Master Byte Software
  38.  *    Los Gatos, California
  39.  *    Internet:    ljz@fx.com
  40.  *    UUCP:        ...!ames!fxgrp!ljz
  41.  *
  42.  *        May, 1988
  43.  */
  44.  
  45. #ifndef lint
  46. static char *RCSid = "$Id: egetopt.c,v 1.1 91/11/25 17:45:07 rogers Release $";
  47. #endif
  48.  
  49. /*
  50.  * If you want, include stdio.h or something where EOF and NULL are defined.
  51.  * However, egetopt() is written so as not to need stdio.h, which should
  52.  * make it significantly smaller on some systems.
  53.  */
  54.  
  55. #ifndef EOF
  56. # define EOF        (-1)
  57. #endif /* ! EOF */
  58.  
  59. #ifndef NULL
  60. # define NULL        (char *)0
  61. #endif /* ! NULL */
  62.  
  63. /*
  64.  * None of these constants are referenced in the executable portion of
  65.  * the code ... their sole purpose is to initialize global variables.
  66.  */
  67. #define BADCH        (int)'?'
  68. #define NEEDSEP        (int)':'
  69. #define MAYBESEP    (int)'\0'
  70. #define ERRFD        2
  71. #define EMSG        ""
  72. #define START        "-"
  73.  
  74. /*
  75.  * Here are all the pertinent global variables.
  76.  */
  77. int opterr = 1;        /* if true, output error message */
  78. int optind = 1;        /* index into parent argv vector */
  79. int optopt;        /* character checked for validity */
  80. int optbad = BADCH;    /* character returned on error */
  81. int optchar = 0;    /* character that begins returned option */
  82. int optneed = NEEDSEP;    /* flag for mandatory argument */
  83. int optmaybe = MAYBESEP;/* flag for optional argument */
  84. int opterrfd = ERRFD;    /* file descriptor for error text */
  85. char *optarg;        /* argument associated with option */
  86. char *optstart = START;    /* list of characters that start options */
  87.  
  88.  
  89. /*
  90.  * Macros.
  91.  */
  92.  
  93. /*
  94.  * Conditionally print out an error message and return (depends on the
  95.  * setting of 'opterr' and 'opterrfd').  Note that this version of
  96.  * TELL() doesn't require the existence of stdio.h.
  97.  */
  98. #define TELL(S)    { \
  99.     if (opterr && opterrfd >= 0) { \
  100.         char option = optopt; \
  101.         (void)write(opterrfd, *nargv, strlen(*nargv)); \
  102.         (void)write(opterrfd, (S), strlen(S)); \
  103.         (void)write(opterrfd, &option, 1); \
  104.         (void)write(opterrfd, "\n", 1); \
  105.     } \
  106.     return (optbad); \
  107. }
  108.  
  109. /*
  110.  * This works similarly to index() and strchr().  I include it so that you
  111.  * don't need to be concerned as to which one your system has.
  112.  */
  113. static char *
  114. _sindex(string, ch)
  115. char *string;
  116. int ch;
  117. {
  118.     if (string != NULL) {
  119.         for (; *string != '\0'; ++string) {
  120.             if (*string == (char)ch) {
  121.                 return (string);
  122.             }
  123.         }
  124.     }
  125.  
  126.     return (NULL);
  127. }
  128.  
  129. /*
  130.  * Here it is:
  131.  */
  132. int
  133. egetopt(nargc, nargv, ostr)
  134. int nargc;
  135. char **nargv;
  136. char *ostr;
  137. {
  138.     static char *place = EMSG;    /* option letter processing */
  139.     register char *oli;        /* option letter list index */
  140.     register char *osi = NULL;    /* option start list index */
  141.  
  142.     if (nargv == (char **)NULL) {
  143.         return (EOF);
  144.     }
  145.  
  146.     if (nargc <= optind || nargv[optind] == NULL) {
  147.         return (EOF);
  148.     }
  149.  
  150.     if (place == NULL) {
  151.         place = EMSG;
  152.     }
  153.  
  154.     /*
  155.      * Update scanning pointer.
  156.      */
  157.     if (*place == '\0') {
  158.         place = nargv[optind];
  159.         if (place == NULL) {
  160.             return (EOF);
  161.         }
  162.         osi = _sindex(optstart, *place);
  163.         if (osi != NULL) {
  164.             optchar = (int)*osi;
  165.         }
  166.         if (optind >= nargc || osi == NULL || *++place == '\0') {
  167.                 return (EOF);
  168.         }
  169.  
  170.         /*
  171.          * Two adjacent, identical flag characters were found.
  172.          * This takes care of "--", for example.
  173.          */
  174.         if (*place == place[-1]) {
  175.             ++optind;
  176.             return (EOF);
  177.         }
  178.     }
  179.  
  180.     /*
  181.      * If the option is a separator or the option isn't in the list,
  182.      * we've got an error.
  183.      */
  184.     optopt = (int)*place++;
  185.     oli = _sindex(ostr, optopt);
  186.     if (optopt == optneed || optopt == optmaybe || oli == NULL) {
  187.         /*
  188.          * If we're at the end of the current argument, bump the
  189.          * argument index.
  190.          */
  191.         if (*place == '\0') {
  192.             ++optind;
  193.         }
  194.         TELL(": illegal option -- ");    /* byebye */
  195.     }
  196.  
  197.     /*
  198.      * If there is no argument indicator, then we don't even try to
  199.      * return an argument.
  200.      */
  201.     ++oli;
  202.     if (*oli == '\0' || (*oli != optneed && *oli != optmaybe)) {
  203.         /*
  204.          * If we're at the end of the current argument, bump the
  205.          * argument index.
  206.          */
  207.         if (*place == '\0') {
  208.             ++optind;
  209.         }
  210.         optarg = NULL;
  211.     }
  212.     /*
  213.      * If we're here, there's an argument indicator.  It's handled
  214.      * differently depending on whether it's a mandatory or an
  215.      * optional argument.
  216.      */
  217.     else {
  218.         /*
  219.          * If there's no white space, use the rest of the
  220.          * string as the argument.  In this case, it doesn't
  221.          * matter if the argument is mandatory or optional.
  222.          */
  223.         if (*place != '\0') {
  224.             optarg = place;
  225.         }
  226.         /*
  227.          * If we're here, there's whitespace after the option.
  228.          *
  229.          * Is it a mandatory argument?  If so, return the
  230.          * next command-line argument if there is one.
  231.          */
  232.         else if (*oli == optneed) {
  233.             /*
  234.              * If we're at the end of the argument list, there
  235.              * isn't an argument and hence we have an error.
  236.              * Otherwise, make 'optarg' point to the argument.
  237.              */
  238.             if (nargc <= ++optind) {
  239.                 place = EMSG;
  240.                 TELL(": option requires an argument -- ");
  241.             }
  242.             else {
  243.                 optarg = nargv[optind];
  244.             }
  245.         }
  246.         /*
  247.          * If we're here it must have been an optional argument.
  248.          */
  249.         else {
  250.             if (nargc <= ++optind) {
  251.                 place = EMSG;
  252.                 optarg = NULL;
  253.             }
  254.             else {
  255.                 optarg = nargv[optind];
  256.                 if (optarg == NULL) {
  257.                     place = EMSG;
  258.                 }
  259.                 /*
  260.                  * If the next item begins with a flag
  261.                  * character, we treat it like a new
  262.                  * argument.  This is accomplished by
  263.                  * decrementing 'optind' and returning
  264.                  * a null argument.
  265.                  */
  266.                 else if (_sindex(optstart, *optarg) != NULL) {
  267.                     --optind;
  268.                     optarg = NULL;
  269.                 }
  270.             }
  271.         }
  272.         place = EMSG;
  273.         ++optind;
  274.     }
  275.  
  276.     /*
  277.      * Return option letter.
  278.      */
  279.     return (optopt);
  280. }
  281.