/* * 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); }