/* -*- c: tabs: 4 -*- ****************************************************** * Module name : getopt * Module type : Core Services * Compiler(s) : ANSI C * Environment(s): n/a * * Description: * Command line option parsing * * Provides user functions * getopt getoptl * * Contants: * n/a * * Version Who Date Description 1.0 APY 02/06/97 Long argument version. 1.1 APY 07/05/98 Core Services * * $Name: $ * $Source: /cvsroot/device/DEVL/UTILS/CMDFILE/getopt.c,v $ * $Revision: 1.2 $ $Date: 2004/05/10 03:47:05 $ $State: Exp $ * $Author: ayoung $ $Locker: $ */ #include #include #include #include "cmdfile.h" #include "getopt.h" #ifndef index #define index strchr #define rindex strrchr #endif /* * get option letter from argument vector */ #ifndef sun int opterr = 1, /* if error message should be printed */ optind = 1; /* index into parent argv vector */ #else extern int opterr; extern int optind; #endif int optopt, /* character checked for validity */ optlidx = -1; /* long index */ char *optarg; /* argument associated with option */ #define BADCH (int)'?' #define EMSG "" /*---------------------------------------------------------------------------- Synopsis: #include int getopt ( int nargc, char *const *nargv, const char *ostr ) extern int optarg; extern int optind; extern int opterr; int CsGetoptl ( int nargc, char *const *nargv, const char *ostr, const struct option *lopt, int long_only ) extern int optlidx; MT-Level: Unsafe Purpose: The CsGetopt function returns the next option letter in 'argv' that matches a letter in 'ostr'. It supports all the rules of the command syntax standard (see below). This function is the functional replacement of the standard UNIX getopt() function. The CsGetoptl functions extends the command syntax including support for long options. 'ostr' must contain the option letters the command using CsGetopt()/CsGetoptl() will recognize; if a letter is followed by a colon, the option is expected to have an argument, or group of arguments, which may be separated from it by white space. 'optarg' is set to point to the start of the option argument on return. 'optind' represents the 'argv' index of the next argument to be processed. 'optind' is external and is initialised to 1 before the first call. When all options have been processed (that is, up to the first non-option argument), EOF is returned. The special option "--" (two hyphens) may be used to delimit the end of the options; when it is encountered, EOF is returned and "--"' is skipped. This is useful in delimiting non-option arguments that begin with ``-'' (hyphen). The additional parameters to CsGetoptl() enable long argument handling. 'lopt' defines the argument names and 'long_only' is used to specify whether long options are only matched when preceded with "--" or or long and short arguments can be mixed. 'optlidx' represents the 'lopt' index of the matched long argument, initialised to -1 upon each call. Parameters: nargc, Number of command line arguments. nargv, The NULL terinated argument list. ostr, Argument syntax specification lopt, Specifies the pointer to the long argument list struct option { char *name; Option name int has_arg; 0=none, 1=required, 2=optional int *flag; Value buffer int val; value return on detection }; The 'name' fields contains the address of the NUL terminated string command label. 'has_arg' speicfies the whether an associated argument isn't excepted (0), is required (1), or is optional (2). The following manifest constants are defined for use within the 'has_arg' field: no_argument No argument expected required_argument An argument must be supplied optional_argument An optional argument 'flag' is the optional address of the buffer assigned with the value of argument. 'val' states the value to be returned upon the given option being encountered. long_only, When specified as non-zero, long arguments are only matched when preceded with "--". Command Syntax Standard: The following command syntax should be obeyed. The CsGetopt function supports Rules 3-10 below. Rules 10b-10c are only active during long argument processing. The enforcement of the other rules must be done by the command itself. 1. Command names (name above) must be between two and nine characters long. 2. Command names must include only lower-case letters and digits. 3. Option names (option above) must be one character long. 4. All options must be preceded by "-". 5. Options with no arguments may be grouped after a single "-". 6. The first option-argument (optarg above) following an option must be preceded by a tab or space character. 7. Option-arguments cannot be optional. 8. Groups of option-arguments following an option must either be separated by commas or separated by tab or space character and quoted (-o xxx,z,yy or -o "xxx z yy"). 9. All options must precede operands (cmdarg above) on the command line. 10. "--" may be used to indicate the end of the options. 10b "--" may precede a long option. 10c If a command element has the form "-f", where f is a valid short option, don't consider it an abbreviated form of a long option that starts with f. Otherwise there would be no way to give the -f short option. On the other hand, if there's a long option "fubar" and the command element is "-fu", do consider that an abbreviation of the long option, just like "--fu", and not "-f" with arg "u". 11. The order of the options relative to one another should not matter. 12. The relative order of the operands (cmdarg above) may affect their significance in ways determined by the command with which they appear. 13. "-" preceded and followed by a space character should only be used to mean standard input. Returns: int - The CsGetopt functions return the character matched, otherwise EOF at the end of input stream, or '?' upon a parer error. The parser prints an error message on the standard error and returns a "?" (question mark) when it encounters an option letter not included in 'ostr' or 'lopt' or no argument is located after an option that expects one. Error messages may be disabled by setting 'opterr' to 0. The value of the character that caused the error is in 'optopt'. In addition CsGetoptl() generates messages when ambiguous arguments between the short and long selections are presented. Examples: The following code fragment shows how one might process the arguments for a command that can take the mutually exclusive options a and b, and the option o, which requires an argument: #include #include #include int main(int argc, char **argv) { int aflg=0, bflg=0, errflg=0, c; char *ofile = NULL; while ((c = CsGetopt(argc, argv, "abo:")) != EOF) switch (c) { case 'a': if (bflg) errflg++; else aflg++; break; case 'b': if (aflg) errflg++; else bflg++; break; case 'o': ofile = optarg; void)printf("ofile = %s\n", ofile); break; case '?': errflg++; } if (errflg) { (void)fprintf(stderr, "usage: cmd [-a|-b] [-o ] files...\n"); exit (2); } while(optind < argc) printf("%s\n", argv[optind++]); } The following code fragment shows how one might process the arguments for a command that can take the options 'a' and 'b', plus 'verbose' and 'debug' which requires an argument: #include #include #include static struct option longopts[] = { {"verbose", 0, NULL, 'v'}, {"debug", 1, NULL, 'd'}, }; int main(int argc, char **argv) { int aflg=0, bflg=0, debug=0, verbose=0, c; while ((c = CsGetoptl(argc, argv, "ab", longopts, 1)) != EOF) switch (c) { case 'a': aflg++; break; case 'b': bflg++; break; case 'd': debug = atoi(optarg); break; case 'v': verbose++; break; case '?': (void)fprintf(stderr, "usage: cmd [-ab] [-debug ] [-verbose] ...\n"); exit (2); } : } ----------------------------------------------------------------------------*/ int getopt( int nargc, char *const *nargv, const char *ostr) { return getoptl(nargc, nargv, ostr, NULL, 0); } int getopt_long( int nargc, char *const *nargv, const char *ostr, const struct option *lopts, int *longind) { int ret = getoptl(nargc, nargv, ostr, lopts, 0); if (longind) *longind = optlidx; return (ret); } int getopt_long_only( int nargc, char *const *nargv, const char *ostr, const struct option *lopts, int *longind) { int ret = getoptl(nargc, nargv, ostr, lopts, 1); if (longind) *longind = optlidx; return (ret); } int getoptl( int nargc, char *const *nargv, const char *ostr, const struct option *lopt, int long_only) { static char *place = EMSG; /* option letter processing */ register char *oli; /* option letter list index */ char *pProg; if ((pProg = rindex(*nargv, '/')) == NULL) pProg = *nargv; else pProg++; optlidx = -1; if (!*place) { /* Update scanning pointer */ if (optind >= nargc || *(place = nargv[optind]) != '-') { place = EMSG; return (EOF); } if (place[1] && *++place == '-') { if (!*++place) { /* Found "--" , meaning premature end of options */ optind++; place = EMSG; return (EOF); } } /* Reference GCC 'getopt': * If ARGV-element has the form "-f", where f is a valid short * option, don't consider it an abbreviated form of a long * option that starts with f. Otherwise there would be no way * to give the -f short option. * * On the other hand, if there's a long option "fubar" and * the ARGV-element is "-fu", do consider that an abbreviation * of the long option, just like "--fu", and not "-f" with * arg "u". * * This distinction seems to be the most useful approach. */ if (lopt != NULL && (nargv[optind][1] == '-' || (!long_only && (nargv[optind][2] || !index(ostr?ostr:"", nargv[optind][1]))))) { const struct option *pOpt; #define EXTRACT 1 #define NONEXTRACT 2 #define AMBIGUOUS 4 int flags = 0; char *pEnd; int i; /* Test all long options for either exact match * or abbreviated matches. */ for (pEnd = place; *pEnd && *pEnd != '='; pEnd++) ; for (pOpt=lopt, i=0; pOpt->name; pOpt++, i++) if (!strncmp(pOpt->name, place, pEnd-place)) { if ((int)(pEnd - place) == (int)strlen(pOpt->name)) { flags &= ~AMBIGUOUS; flags |= EXTRACT; /* Exact match */ optlidx = i; break; } if (flags & NONEXTRACT) flags |= AMBIGUOUS; /* Multiple match(s) */ else { flags |= NONEXTRACT; /* Part match */ optlidx = i; } } if (flags & AMBIGUOUS) { if (opterr) (void) fprintf(stderr, "%s: option `%s' is ambiguous\n", pProg, place); optind++; place = EMSG; return BADCH; } /* Long option match */ if (flags & (EXTRACT|NONEXTRACT)) { pOpt = lopt+optlidx; optind++; /* Parse argument request */ if (*pEnd) { if (pOpt->has_arg) optarg = pEnd+1; else { if (opterr) { if (nargv[optind-1][1] == '-') { /* Long format */ (void) fprintf(stderr, "%s: option `--%s' doesn't allow an argument\n", pProg, pOpt->name); } else { /* Short */ (void) fprintf(stderr, "%s: option `%c%s' doesn't allow an argument\n", pProg, nargv[optind-1][0], pOpt->name); } } place = EMSG; return BADCH; } } else if (pOpt->has_arg) { if (optind < nargc && strcmp(nargv[optind], "--") != 0) { /* Argument available (non-terminator) */ optarg = nargv[optind++]; } else if (pOpt->has_arg != optional_argument) { /* No argument */ if (opterr) (void) fprintf(stderr, "%s: option `%s' requires an argument\n", pProg, nargv[optind - 1]); place = EMSG; return (ostr && ostr[0] == ':' ? ':' : BADCH); } else { optarg = NULL; /* Optional argument */ } } /* Return completion value */ place = EMSG; if (pOpt->flag) { *(pOpt->flag) = pOpt->val; return 0; } return pOpt->val; } /* Can't find it as a long option. If this is not * 'long_only', or the option starts with '--' or * is not a valid short option, then it's an error. * * Otherwise interpret it as a short option. */ if (!long_only || nargv[optind][1] == '-' || index(ostr ? ostr : "", *place) == NULL) { if (opterr) { if (nargv[optind][1] == '-') { /* Long format */ (void) fprintf(stderr, "%s: unrecognized option `--%s'\n", pProg, place); } else { /* Short */ (void) fprintf(stderr, "%s: unrecognized option `%c%s'\n", pProg, nargv[optind][0], place); } } optind++; place = EMSG; return BADCH; } } } /* Option letter okay? */ if ((optopt = (int) *place++) == (int) ':' || (oli = index(ostr, optopt)) == NULL) { /* If the user didn't specify '-' as an option, assume it means EOF */ if (optopt == (int) '-') return (EOF); if (!*place) ++optind; if (opterr) (void) fprintf(stderr, "%s: illegal option -- %c\n", pProg, optopt); return (BADCH); } /*LINTED*/ if (*++oli != ':') { /* Don't need argument */ optarg = NULL; if (!*place) ++optind; } else { /* Need an argument */ if (*place) { /* No white space */ optarg = place; } else if (nargc <= ++optind || strcmp(nargv[optind], "--") == 0) { /* No argument, or terminator encountered */ if (opterr) (void) fprintf(stderr, "%s: option requires an argument -- %c\n", pProg, optopt); place = EMSG; return (BADCH); } else { /* White space -- return next argument */ optarg = nargv[optind]; } place = EMSG; ++optind; } return (optopt); /* Dump back option letter */ }