diff options
Diffstat (limited to 'egetopt.c')
-rw-r--r-- | egetopt.c | 276 |
1 files changed, 276 insertions, 0 deletions
diff --git a/egetopt.c b/egetopt.c new file mode 100644 index 0000000..8183d35 --- /dev/null +++ b/egetopt.c @@ -0,0 +1,276 @@ +/* + * egetopt.c -- Extended 'getopt'. + * + * A while back, a public-domain version of getopt() was posted to the + * net. A bit later, a gentleman by the name of Keith Bostic made some + * enhancements and reposted it. + * + * In recent weeks (i.e., early-to-mid 1988) there's been some + * heated discussion in comp.lang.c about the merits and drawbacks + * of getopt(), especially with regard to its handling of '?'. + * + * In light of this, I have taken Mr. Bostic's public-domain getopt() + * and have made some changes that I hope will be considered to be + * improvements. I call this routine 'egetopt' ("Extended getopt"). + * The default behavior of this routine is the same as that of getopt(), + * but it has some optional features that make it more useful. These + * options are controlled by the settings of some global variables. + * By not setting any of these extra global variables, you will have + * the same functionality as getopt(), which should satisfy those + * purists who believe getopt() is perfect and can never be improved. + * If, on the other hand, you are someone who isn't satisfied with the + * status quo, egetopt() may very well give you the added capabilities + * you want. + * + * Look at the enclosed README file for a description of egetopt()'s + * new features. + * + * The code was originally posted to the net as getopt.c by ... + * + * Keith Bostic + * ARPA: keith@seismo + * UUCP: seismo!keith + * + * Current version: added enhancements and comments, reformatted code. + * + * Lloyd Zusman + * Master Byte Software + * Los Gatos, California + * Internet: ljz@fx.com + * UUCP: ...!ames!fxgrp!ljz + * + * May, 1988 + */ + +/* + * If you want, include stdio.h or something where EOF and NULL are defined. + * However, egetopt() is written so as not to need stdio.h, which should + * make it significantly smaller on some systems. + */ + +#ifndef EOF +# define EOF (-1) +#endif /* ! EOF */ + +#ifndef NULL +# define NULL (char *)0 +#endif /* ! NULL */ + +/* + * None of these constants are referenced in the executable portion of + * the code ... their sole purpose is to initialize global variables. + */ +#define BADCH (int)'?' +#define NEEDSEP (int)':' +#define MAYBESEP (int)'\0' +#define ERRFD 2 +#define EMSG "" +#define START "-" + +/* + * Here are all the pertinent global variables. + */ +int opterr = 1; /* if true, output error message */ +int optind = 1; /* index into parent argv vector */ +int optopt; /* character checked for validity */ +int optbad = BADCH; /* character returned on error */ +int optchar = 0; /* character that begins returned option */ +int optneed = NEEDSEP; /* flag for mandatory argument */ +int optmaybe = MAYBESEP;/* flag for optional argument */ +int opterrfd = ERRFD; /* file descriptor for error text */ +char *optarg; /* argument associated with option */ +char *optstart = START; /* list of characters that start options */ + + +/* + * Macros. + */ + +/* + * Conditionally print out an error message and return (depends on the + * setting of 'opterr' and 'opterrfd'). Note that this version of + * TELL() doesn't require the existence of stdio.h. + */ +#define TELL(S) { \ + if (opterr && opterrfd >= 0) { \ + char option = optopt; \ + write(opterrfd, *nargv, strlen(*nargv)); \ + write(opterrfd, (S), strlen(S)); \ + write(opterrfd, &option, 1); \ + write(opterrfd, "\n", 1); \ + } \ + return (optbad); \ +} + +/* + * This works similarly to index() and strchr(). I include it so that you + * don't need to be concerned as to which one your system has. + */ +static char * +_sindex(string, ch) +char *string; +int ch; +{ + if (string != NULL) { + for (; *string != '\0'; ++string) { + if (*string == (char)ch) { + return (string); + } + } + } + + return (NULL); +} + +/* + * Here it is: + */ +int +egetopt(nargc, nargv, ostr) +int nargc; +char **nargv; +char *ostr; +{ + static char *place = EMSG; /* option letter processing */ + register char *oli; /* option letter list index */ + register char *osi = NULL; /* option start list index */ + + if (nargv == (char **)NULL) { + return (EOF); + } + + if (nargc <= optind || nargv[optind] == NULL) { + return (EOF); + } + + if (place == NULL) { + place = EMSG; + } + + /* + * Update scanning pointer. + */ + if (*place == '\0') { + place = nargv[optind]; + if (place == NULL) { + return (EOF); + } + osi = _sindex(optstart, *place); + if (osi != NULL) { + optchar = (int)*osi; + } + if (optind >= nargc || osi == NULL || *++place == '\0') { + return (EOF); + } + + /* + * Two adjacent, identical flag characters were found. + * This takes care of "--", for example. + */ + if (*place == place[-1]) { + ++optind; + return (EOF); + } + } + + /* + * If the option is a separator or the option isn't in the list, + * we've got an error. + */ + optopt = (int)*place++; + oli = _sindex(ostr, optopt); + if (optopt == optneed || optopt == optmaybe || oli == NULL) { + /* + * If we're at the end of the current argument, bump the + * argument index. + */ + if (*place == '\0') { + ++optind; + } + TELL(": illegal option -- "); /* byebye */ + } + + /* + * If there is no argument indicator, then we don't even try to + * return an argument. + */ + ++oli; + if (*oli == '\0' || (*oli != optneed && *oli != optmaybe)) { + /* + * If we're at the end of the current argument, bump the + * argument index. + */ + if (*place == '\0') { + ++optind; + } + optarg = NULL; + } + /* + * If we're here, there's an argument indicator. It's handled + * differently depending on whether it's a mandatory or an + * optional argument. + */ + else { + /* + * If there's no white space, use the rest of the + * string as the argument. In this case, it doesn't + * matter if the argument is mandatory or optional. + */ + if (*place != '\0') { + optarg = place; + } + /* + * If we're here, there's whitespace after the option. + * + * Is it a mandatory argument? If so, return the + * next command-line argument if there is one. + */ + else if (*oli == optneed) { + /* + * If we're at the end of the argument list, there + * isn't an argument and hence we have an error. + * Otherwise, make 'optarg' point to the argument. + */ + if (nargc <= ++optind) { + place = EMSG; + TELL(": option requires an argument -- "); + } + else { + optarg = nargv[optind]; + } + } + /* + * If we're here it must have been an optional argument. + */ + else { + if (nargc <= ++optind) { + place = EMSG; + optarg = NULL; + } + else { + optarg = nargv[optind]; + if (optarg == NULL) { + place = EMSG; + } + /* + * If the next item begins with a flag + * character, we treat it like a new + * argument. This is accomplished by + * decrementing 'optind' and returning + * a null argument. + */ + else if (_sindex(optstart, *optarg) != NULL) { + --optind; + optarg = NULL; + } + } + } + place = EMSG; + ++optind; + } + + /* + * Return option letter. + */ + return (optopt); +} |