/* 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 * * This version is slightly adapted for use by smenu. */ #include #include #include #include #include "getopt.h" /* Here are all the pertinent global variables. */ /* """""""""""""""""""""""""""""""""""""""""""" */ int eopterr = 1; /* if true, output error message */ int eoptind = 1; /* index into parent argv vector */ int eoptopt; /* character checked for validity */ int eoptbad = BADCH; /* character returned on error */ int eoptchar = 0; /* character that begins returned option */ int eoptneed = NEEDSEP; /* flag for mandatory argument */ int eoptmaybe = MAYBESEP; /* flag for optional argument */ int eopterrfd = ERRFD; /* file descriptor for error text */ char * eoptarg = NULL; /* argument associated with option */ char * eoptstart = START; /* list of characters that start options */ /* Here it is: */ /* """"""""""" */ int egetopt(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 <= eoptind || nargv[eoptind] == NULL) return (EOF); if (place == NULL) place = EMSG; /* Update scanning pointer. */ /* """""""""""""""""""""""" */ if (*place == '\0') { place = nargv[eoptind]; if (place == NULL) return (EOF); else if (*place == '\0') return (EOF); osi = strchr(eoptstart, *place); if (osi != NULL) eoptchar = (int)*osi; if (eoptind >= nargc || osi == NULL || *++place == '\0') return (EOF); /* Two adjacent, identical flag characters were found. */ /* This takes care of "--", for example. */ /* """"""""""""""""""""""""""""""""""""""""""""""""""" */ if (*place == place[-1]) { ++eoptind; return (EOF); } } /* If the option is a separator or the option isn't in the list, */ /* we've got an error. */ /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */ eoptopt = (int)*place++; oli = strchr(ostr, eoptopt); if (eoptopt == eoptneed || eoptopt == (int)eoptmaybe || oli == NULL) { /* If we're at the end of the current argument, bump the */ /* argument index. */ /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */ if (*place == '\0') ++eoptind; TELL("Illegal option -- "); /* bye bye */ return (eoptbad); } /* If there is no argument indicator, then we don't even try to */ /* return an argument. */ /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */ ++oli; if (*oli == '\0' || (*oli != (char)eoptneed && *oli != (char)eoptmaybe)) { /* If we're at the end of the current argument, bump the */ /* argument index. */ /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */ if (*place == '\0') ++eoptind; eoptarg = 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') eoptarg = 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 == (char)eoptneed) { /* If we're at the end of the argument list, there */ /* isn't an argument and hence we have an error. */ /* Otherwise, make 'eoptarg' point to the argument. */ /* """""""""""""""""""""""""""""""""""""""""""""""" */ if (nargc <= ++eoptind) { place = EMSG; TELL("Option requires an argument -- "); return (eoptbad); } else eoptarg = nargv[eoptind]; } /* If we're here it must have been an optional argument. */ /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */ else { if (nargc <= ++eoptind) { place = EMSG; eoptarg = NULL; } else { eoptarg = nargv[eoptind]; if (eoptarg == NULL) place = EMSG; /* If the next item begins with a flag */ /* character, we treat it like a new */ /* argument. This is accomplished by */ /* decrementing 'eoptind' and returning */ /* a null argument. */ /* """""""""""""""""""""""""""""""""""" */ else if (strchr(eoptstart, *eoptarg) != NULL) { --eoptind; eoptarg = NULL; } } } place = EMSG; ++eoptind; } /* Return option letter. */ /* """"""""""""""""""""" */ return (eoptopt); }