home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 11 Util / 11-Util.zip / shtdwn16.zip / egetopt.c next >
C/C++ Source or Header  |  1993-12-29  |  7KB  |  283 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. /*
  46.  * If you want, include stdio.h or something where EOF and NULL are defined.
  47.  * However, egetopt() is written so as not to need stdio.h, which should
  48.  * make it significantly smaller on some systems.
  49.  */
  50.  
  51. #include <string.h>
  52. #include <io.h>
  53.  
  54. #ifndef EOF
  55. # define EOF        (-1)
  56. #endif /* ! EOF */
  57.  
  58. #ifndef NULL
  59. # define NULL        (char *)0
  60. #endif /* ! NULL */
  61.  
  62. /*
  63.  * None of these constants are referenced in the executable portion of
  64.  * the code ... their sole purpose is to initialize global variables.
  65.  */
  66. #define BADCH        (int)'?'
  67. #define NEEDSEP        (int)':'
  68. #define MAYBESEP    (int)'\0'
  69. #define ERRFD        2
  70. #define EMSG        ""
  71. #define START        "-"
  72.  
  73. /*
  74.  * Here are all the pertinent global variables.
  75.  */
  76. static char *_sindex(char *, int);
  77. int egetopt(int, char **, char *);
  78.  
  79. int opterr = 1;        /* if true, output error message */
  80. int optind = 1;        /* index into parent argv vector */
  81. int optopt;        /* character checked for validity */
  82. int optbad = BADCH;    /* character returned on error */
  83. int optchar = 0;    /* character that begins returned option */
  84. int optneed = NEEDSEP;    /* flag for mandatory argument */
  85. int optmaybe = MAYBESEP;/* flag for optional argument */
  86. int opterrfd = ERRFD;    /* file descriptor for error text */
  87. char *optarg;        /* argument associated with option */
  88. char *optstart = START;    /* list of characters that start options */
  89.  
  90.  
  91. /*
  92.  * Macros.
  93.  */
  94.  
  95. /*
  96.  * Conditionally print out an error message and return (depends on the
  97.  * setting of 'opterr' and 'opterrfd').  Note that this version of
  98.  * TELL() doesn't require the existence of stdio.h.
  99.  */
  100. #define TELL(S)    { \
  101.     if (opterr && opterrfd >= 0) { \
  102.         char option = optopt; \
  103.         write(opterrfd, *nargv, strlen(*nargv)); \
  104.         write(opterrfd, (S), strlen(S)); \
  105.         write(opterrfd, &option, 1); \
  106.         write(opterrfd, "\n", 1); \
  107.     } \
  108.     return (optbad); \
  109. }
  110.  
  111. /*
  112.  * This works similarly to index() and strchr().  I include it so that you
  113.  * don't need to be concerned as to which one your system has.
  114.  */
  115. static char *
  116. _sindex(string, ch)
  117. char *string;
  118. int ch;
  119. {
  120.     if (string != NULL) {
  121.         for (; *string != '\0'; ++string) {
  122.             if (*string == (char)ch) {
  123.                 return (string);
  124.             }
  125.         }
  126.     }
  127.  
  128.     return (NULL);
  129. }
  130.  
  131. /*
  132.  * Here it is:
  133.  */
  134. int
  135. egetopt(nargc, nargv, ostr)
  136. int nargc;
  137. char **nargv;
  138. char *ostr;
  139. {
  140.     static char *place = EMSG;    /* option letter processing */
  141.     register char *oli;        /* option letter list index */
  142.     register char *osi = NULL;    /* option start list index */
  143.  
  144.     if (nargv == (char **)NULL) {
  145.         return (EOF);
  146.     }
  147.  
  148.     if (nargc <= optind || nargv[optind] == NULL) {
  149.         return (EOF);
  150.     }
  151.  
  152.     if (place == NULL) {
  153.         place = EMSG;
  154.     }
  155.  
  156.     /*
  157.      * Update scanning pointer.
  158.      */
  159.     if (*place == '\0') {
  160.         place = nargv[optind];
  161.         if (place == NULL) {
  162.             return (EOF);
  163.         }
  164.         osi = _sindex(optstart, *place);
  165.         if (osi != NULL) {
  166.             optchar = (int)*osi;
  167.         }
  168.         if (optind >= nargc || osi == NULL || *++place == '\0') {
  169.                 return (EOF);
  170.         }
  171.  
  172.         /*
  173.          * Two adjacent, identical flag characters were found.
  174.          * This takes care of "--", for example.
  175.          */
  176.         if (*place == place[-1]) {
  177.             ++optind;
  178.             return (EOF);
  179.         }
  180.     }
  181.  
  182.     /*
  183.      * If the option is a separator or the option isn't in the list,
  184.      * we've got an error.
  185.      */
  186.     optopt = (int)*place++;
  187.     oli = _sindex(ostr, optopt);
  188.     if (optopt == optneed || optopt == optmaybe || oli == NULL) {
  189.         /*
  190.          * If we're at the end of the current argument, bump the
  191.          * argument index.
  192.          */
  193.         if (*place == '\0') {
  194.             ++optind;
  195.         }
  196.         TELL(": illegal option -- ");    /* byebye */
  197.     }
  198.  
  199.     /*
  200.      * If there is no argument indicator, then we don't even try to
  201.      * return an argument.
  202.      */
  203.     ++oli;
  204.     if (*oli == '\0' || (*oli != optneed && *oli != optmaybe)) {
  205.         /*
  206.          * If we're at the end of the current argument, bump the
  207.          * argument index.
  208.          */
  209.         if (*place == '\0') {
  210.             ++optind;
  211.         }
  212.         optarg = NULL;
  213.     }
  214.     /*
  215.      * If we're here, there's an argument indicator.  It's handled
  216.      * differently depending on whether it's a mandatory or an
  217.      * optional argument.
  218.      */
  219.     else {
  220.         /*
  221.          * If there's no white space, use the rest of the
  222.          * string as the argument.  In this case, it doesn't
  223.          * matter if the argument is mandatory or optional.
  224.          */
  225.         if (*place != '\0') {
  226.             optarg = place;
  227.         }
  228.         /*
  229.          * If we're here, there's whitespace after the option.
  230.          *
  231.          * Is it a mandatory argument?  If so, return the
  232.          * next command-line argument if there is one.
  233.          */
  234.         else if (*oli == optneed) {
  235.             /*
  236.              * If we're at the end of the argument list, there
  237.              * isn't an argument and hence we have an error.
  238.              * Otherwise, make 'optarg' point to the argument.
  239.              */
  240.             if (nargc <= ++optind) {
  241.                 place = EMSG;
  242.                 TELL(": option requires an argument -- ");
  243.             }
  244.             else {
  245.                 optarg = nargv[optind];
  246.             }
  247.         }
  248.         /*
  249.          * If we're here it must have been an optional argument.
  250.          */
  251.         else {
  252.             if (nargc <= ++optind) {
  253.                 place = EMSG;
  254.                 optarg = NULL;
  255.             }
  256.             else {
  257.                 optarg = nargv[optind];
  258.                 if (optarg == NULL) {
  259.                     place = EMSG;
  260.                 }
  261.                 /*
  262.                  * If the next item begins with a flag
  263.                  * character, we treat it like a new
  264.                  * argument.  This is accomplished by
  265.                  * decrementing 'optind' and returning
  266.                  * a null argument.
  267.                  */
  268.                 else if (_sindex(optstart, *optarg) != NULL) {
  269.                     --optind;
  270.                     optarg = NULL;
  271.                 }
  272.             }
  273.         }
  274.         place = EMSG;
  275.         ++optind;
  276.     }
  277.  
  278.     /*
  279.      * Return option letter.
  280.      */
  281.     return (optopt);
  282. }
  283.