// **************************************************************************** // ^FILE: options.c - implement the functions defined in // // ^HISTORY: // 01/16/92 Brad Appleton Created // // 03/23/93 Brad Appleton // - Added OptIstreamIter class // // 10/08/93 Brad Appleton // - Added "hidden" options // // 02/08/94 Brad Appleton // - Added "OptionSpec" class // - Permitted use of stdio instead of iostreams via #ifdef USE_STDIO // // 03/08/94 Brad Appleton // - completed support for USE_STDIO // - added #ifdef NO_USAGE for people who always want to print their own // - Fixed stupid NULL pointer error in OptionsSpec class // // 07/31/97 Brad Appleton // - Added PARSE_POS control flag and POSITIONAL return value. // ^^************************************************************************** // #include #include #include #include "options.h" using namespace std; extern "C" { void exit(int); } static const char ident[] = "@(#)Options 1.05"; // I need a portable version of "tolower" that does NOT modify // non-uppercase characters. // #define TOLOWER(c) (isupper(c) ? tolower(c) : c) // Use this to shut the compiler up about NULL strings #define NULLSTR (char *) NULL // ******************************************************** insertion operators // If you are using then you need this stuff! // If you are using then #ifdef this stuff out // #ifdef USE_STDIO // Implement just enough of ostream to get this file to compile // static const char endl = '\n'; class ostream { public: ostream(FILE *fileptr) : fp(fileptr) { } ostream &operator<<(char ch); ostream &operator<<(const char *str); ostream &write(const char *buf, unsigned bufsize); private: FILE *fp; }; ostream &ostream::operator<<(char ch) { fputc(ch, fp); return *this; } ostream &ostream::operator<<(const char *str) { fputs(str, fp); return *this; } ostream &ostream::write(const char *buf, unsigned) { fputs(buf, fp); return *this; } static ostream cerr(stderr); static ostream cout(stdout); #endif /* USE_STDIO */ // ************************************************************** OptIter OptIter::~OptIter(void) { } const char *OptIter::operator()(void) { const char *elt = curr(); (void)next(); return elt; } // ************************************************************** OptIterRwd OptIterRwd::OptIterRwd(void) { } OptIterRwd::~OptIterRwd(void) { } // ************************************************************** OptArgvIter OptArgvIter::~OptArgvIter(void) { } const char *OptArgvIter::curr(void) { return ((ndx == ac) || (av[ndx] == NULL)) ? NULLSTR : av[ndx]; } void OptArgvIter::next(void) { if ((ndx != ac) && av[ndx]) ++ndx; } const char *OptArgvIter::operator()(void) { return ((ndx == ac) || (av[ndx] == NULL)) ? NULLSTR : av[ndx++]; } void OptArgvIter::rewind(void) { ndx = 0; } // ************************************************************** OptStrTokIter static const char WHITESPACE[] = " \t\n\r\v\f"; const char *OptStrTokIter::default_delims = WHITESPACE; OptStrTokIter::OptStrTokIter(const char *tokens, const char *delimiters) : len(unsigned(strlen(tokens))) , str(tokens) , seps(delimiters) , cur(NULLSTR) , tokstr(NULLSTR) { if (seps == NULL) seps = default_delims; tokstr = new char[len + 1]; (void)::strcpy(tokstr, str); cur = ::strtok(tokstr, seps); } OptStrTokIter::~OptStrTokIter(void) { delete[] tokstr; } const char *OptStrTokIter::curr(void) { return cur; } void OptStrTokIter::next(void) { if (cur) cur = ::strtok(NULL, seps); } const char *OptStrTokIter::operator()(void) { const char *elt = cur; if (cur) cur = ::strtok(NULL, seps); return elt; } void OptStrTokIter::rewind(void) { (void)::strcpy(tokstr, str); cur = ::strtok(tokstr, seps); } // ************************************************************* OptIstreamIter #ifdef vms enum { c_COMMENT = '!' }; #else enum { c_COMMENT = '#' }; #endif const unsigned OptIstreamIter::MAX_LINE_LEN = 1024; // Constructor OptIstreamIter::OptIstreamIter(istream &input) : is(input) , tok_iter(NULL) { #ifdef USE_STDIO fprintf(stderr, "%s: Can't use OptIstreamIter class:\n", "OptIstreamIter::OptIstreamIter"); fprintf(stderr, "\tOptions(3C++) was compiled with USE_STDIO #defined.\n"); exit(-1); #endif /* USE_STDIO */ } // Destructor OptIstreamIter::~OptIstreamIter(void) { delete tok_iter; } const char *OptIstreamIter::curr(void) { #ifdef USE_STDIO return NULLSTR; #else const char *result = NULLSTR; if (tok_iter) result = tok_iter->curr(); if (result) return result; fill(); return (!is) ? NULLSTR : tok_iter->curr(); #endif /* USE_STDIO */ } void OptIstreamIter::next(void) { #ifdef USE_STDIO return; #else const char *result = NULLSTR; if (tok_iter) result = tok_iter->operator()(); if (result) return; fill(); if (!is) tok_iter->next(); #endif /* USE_STDIO */ } const char *OptIstreamIter::operator()(void) { #ifdef USE_STDIO return NULLSTR; #else const char *result = NULLSTR; if (tok_iter) result = tok_iter->operator()(); if (result) return result; fill(); return (!is) ? NULLSTR : tok_iter->operator()(); #endif /* USE_STDIO */ } // What we do is this: for each line of text in the istream, we use // a OptStrTokIter to iterate over each token on the line. // // If the first non-white character on a line is c_COMMENT, then we // consider the line to be a comment and we ignore it. // void OptIstreamIter::fill(void) { #ifdef USE_STDIO return; #else char buf[OptIstreamIter::MAX_LINE_LEN]; do { *buf = '\0'; is.getline(buf, sizeof(buf)); char *ptr = buf; while (isspace(*ptr)) ++ptr; if (*ptr && (*ptr != c_COMMENT)) { delete tok_iter; tok_iter = new OptStrTokIter(ptr); return; } } while (is); #endif /* USE_STDIO */ } // **************************************************** Options class utilities // Is this option-char null? inline static int isNullOpt(char optchar) { return ((!optchar) || isspace(optchar) || (!isprint(optchar))); } // Check for explicit "end-of-options" inline static int isEndOpts(const char *token) { return ((token == NULL) || (!::strcmp(token, "--"))); } // See if an argument is an option inline static int isOption(unsigned flags, const char *arg) { return (((*arg != '\0') || (arg[1] != '\0')) && ((*arg == '-') || ((flags & Options::PLUS) && (*arg == '+')))); } // See if we should be parsing only options or if we also need to // parse positional arguments inline static int isOptsOnly(unsigned flags) { return (flags & Options::PARSE_POS) ? 0 : 1; } // return values for a keyword matching function enum kwdmatch_t { NO_MATCH, PARTIAL_MATCH, EXACT_MATCH }; // --------------------------------------------------------------------------- // ^FUNCTION: kwdmatch - match a keyword // // ^SYNOPSIS: // static kwdmatch_t kwdmatch(src, attempt, len) // // ^PARAMETERS: // char * src -- the actual keyword to match // char * attempt -- the possible keyword to compare against "src" // int len -- number of character of "attempt" to consider // (if 0 then we should use all of "attempt") // // ^DESCRIPTION: // See if "attempt" matches some prefix of "src" (case insensitive). // // ^REQUIREMENTS: // - attempt should be non-NULL and non-empty // // ^SIDE-EFFECTS: // None. // // ^RETURN-VALUE: // An enumeration value of type kwdmatch_t corresponding to whether // We had an exact match, a partial match, or no match. // // ^ALGORITHM: // Trivial // ^^------------------------------------------------------------------------- static kwdmatch_t kwdmatch(const char *src, const char *attempt, int len = 0) { int i; if (src == attempt) return EXACT_MATCH; if ((src == NULL) || (attempt == NULL)) return NO_MATCH; if ((!*src) && (!*attempt)) return EXACT_MATCH; if ((!*src) || (!*attempt)) return NO_MATCH; for (i = 0; ((i < len) || (len == 0)) && (attempt[i]) && (attempt[i] != ' '); i++) { if (TOLOWER(src[i]) != TOLOWER(attempt[i])) return NO_MATCH; } return (src[i]) ? PARTIAL_MATCH : EXACT_MATCH; } // **************************************************************** OptionSpec // Class that represents an option-specification // *NOTE*:: Assumes that the char-ptr given to the constructor points // to storage that will NOT be modified and whose lifetime will // be as least as long as the OptionSpec object we construct. // class OptionSpec { public: OptionSpec(const char *decl = NULLSTR) : hidden(0) , spec(decl) { if (spec == NULL) spec = NULL_spec; CheckHidden(); } OptionSpec(const OptionSpec &cp) : hidden(cp.hidden) , spec(cp.spec) { } // NOTE: use default destructor! // Assign to another OptionSpec OptionSpec &operator=(const OptionSpec &cp) { if (this != &cp) { spec = cp.spec; hidden = cp.hidden; } return *this; } // Assign to a string OptionSpec &operator=(const char *decl) { if (spec != decl) { spec = decl; hidden = 0; CheckHidden(); } return *this; } // Convert to char-ptr by returning the original declaration-string operator const char *() { return isHiddenOpt() ? (spec - 1) : spec; } // Is this option NULL? int isNULL(void) const { return ((spec == NULL) || (spec == NULL_spec)); } // Is this options incorrectly specified? int isSyntaxError(const char *name) const; // See if this is a Hidden option int isHiddenOpt(void) const { return hidden; } // Get the corresponding option-character char OptChar(void) const { return *spec; } // Get the corresponding long-option string const char *LongOpt(void) const { return (spec[1] && spec[2] && (!isspace(spec[2]))) ? (spec + 2) : NULLSTR; } // Does this option require an argument? int isValRequired(void) const { return ((spec[1] == ':') || (spec[1] == '+')); } // Does this option take an optional argument? int isValOptional(void) const { return ((spec[1] == '?') || (spec[1] == '*')); } // Does this option take no arguments? int isNoArg(void) const { return ((spec[1] == '|') || (!spec[1])); } // Can this option take more than one argument? int isList(void) const { return ((spec[1] == '+') || (spec[1] == '*')); } // Does this option take any arguments? int isValTaken(void) const { return (isValRequired() || isValOptional()); } // Format this option in the given buffer unsigned Format(char *buf, unsigned optctrls) const; private: void CheckHidden(void) { if ((!hidden) && (*spec == '-')) { ++hidden; ++spec; } } unsigned hidden : 1; // hidden-flag const char *spec; // string specification static const char NULL_spec[]; }; const char OptionSpec::NULL_spec[] = "\0\0\0"; int OptionSpec::isSyntaxError(const char *name) const { int error = 0; if ((!spec) || (!*spec)) { cerr << name << ": empty option specifier." << endl; cerr << "\tmust be at least 1 character long." << endl; ++error; } else if (spec[1] && (strchr("|?:*+", spec[1]) == NULL)) { cerr << name << ": bad option specifier \"" << spec << "\"." << endl; cerr << "\t2nd character must be in the set \"|?:*+\"." << endl; ++error; } return error; } // --------------------------------------------------------------------------- // ^FUNCTION: OptionSpec::Format - format an option-spec for a usage message // // ^SYNOPSIS: // unsigned OptionSpec::Format(buf, optctrls) const // // ^PARAMETERS: // char * buf -- where to print the formatted option // unsigned optctrls -- option-parsing configuration flags // // ^DESCRIPTION: // Self-explanatory. // // ^REQUIREMENTS: // - buf must be large enough to hold the result // // ^SIDE-EFFECTS: // - writes to buf. // // ^RETURN-VALUE: // Number of characters written to buf. // // ^ALGORITHM: // Follow along in the source - it's not hard but it is tedious! // ^^------------------------------------------------------------------------- unsigned OptionSpec::Format(char *buf, unsigned optctrls) const { #ifdef NO_USAGE return (*buf = '\0'); #else static char default_value[] = ""; if (isHiddenOpt()) return (unsigned)(*buf = '\0'); char optchar = OptChar(); const char *longopt = LongOpt(); char *p = buf; const char *value = NULLSTR; int longopt_len = 0; int value_len = 0; if (longopt) { value = ::strchr(longopt, ' '); longopt_len = (value) ? (value - longopt) : ::strlen(longopt); } else { value = ::strchr(spec + 1, ' '); } while (value && (*value == ' ')) ++value; if (value && *value) { value_len = ::strlen(value); } else { value = default_value; value_len = sizeof(default_value) - 1; } if ((optctrls & Options::SHORT_ONLY) && ((!isNullOpt(optchar)) || (optctrls & Options::NOGUESSING))) { longopt = NULLSTR; } if ((optctrls & Options::LONG_ONLY) && (longopt || (optctrls & Options::NOGUESSING))) { optchar = '\0'; } if (isNullOpt(optchar) && (longopt == NULL)) { *buf = '\0'; return 0; } *(p++) = '['; // print the single character option if (!isNullOpt(optchar)) { *(p++) = '-'; *(p++) = optchar; } if ((!isNullOpt(optchar)) && (longopt)) *(p++) = '|'; // print the long option if (longopt) { *(p++) = '-'; if (!(optctrls & (Options::LONG_ONLY | Options::SHORT_ONLY))) { *(p++) = '-'; } strncpy(p, longopt, longopt_len); p += longopt_len; } // print any argument the option takes if (isValTaken()) { *(p++) = ' '; if (isValOptional()) *(p++) = '['; strcpy(p, value); p += value_len; if (isList()) { strcpy(p, " ..."); p += 4; } if (isValOptional()) *(p++) = ']'; } *(p++) = ']'; *p = '\0'; return (unsigned)strlen(buf); #endif /* USE_STDIO */ } // ******************************************************************* Options #if (defined(MSWIN) || defined(OS2) || defined(MSDOS)) #define DIR_SEP_CHAR '\\' #else #define DIR_SEP_CHAR '/' #endif Options::Options(const char *name, const char *const optv[]) : cmdname(name) , optvec(optv) , explicit_end(0) , optctrls(DEFAULT) , nextchar(NULLSTR) , listopt(NULLSTR) { const char *basename = ::strrchr(cmdname, DIR_SEP_CHAR); if (basename) cmdname = basename + 1; check_syntax(); } Options::~Options(void) { } // Make sure each option-specifier has correct syntax. // // If there is even one invalid specifier, then exit ungracefully! // void Options::check_syntax(void) const { int errors = 0; if ((optvec == NULL) || (!*optvec)) return; for (const char *const *optv = optvec; *optv; optv++) { OptionSpec optspec = *optv; errors += optspec.isSyntaxError(cmdname); } if (errors) exit(127); } // --------------------------------------------------------------------------- // ^FUNCTION: Options::match_opt - match an option // // ^SYNOPSIS: // const char * match_opt(opt, int ignore_case) const // // ^PARAMETERS: // char opt -- the option-character to match // int ignore_case -- should we ignore character-case? // // ^DESCRIPTION: // See if "opt" is found in "optvec" // // ^REQUIREMENTS: // - optvec should be non-NULL and terminated by a NULL pointer. // // ^SIDE-EFFECTS: // None. // // ^RETURN-VALUE: // NULL if no match is found, // otherwise a pointer to the matching option-spec. // // ^ALGORITHM: // foreach option-spec // - see if "opt" is a match, if so return option-spec // end-for // ^^------------------------------------------------------------------------- const char *Options::match_opt(char opt, int ignore_case) const { if ((optvec == NULL) || (!*optvec)) return NULLSTR; for (const char *const *optv = optvec; *optv; optv++) { OptionSpec optspec = *optv; char optchar = optspec.OptChar(); if (isNullOpt(optchar)) continue; if (opt == optchar) { return optspec; } else if (ignore_case && (TOLOWER(opt) == TOLOWER(optchar))) { return optspec; } } return NULLSTR; // not found } // --------------------------------------------------------------------------- // ^FUNCTION: Options::match_longopt - match a long-option // // ^SYNOPSIS: // const char * Options::match_longopt(opt, len, ambiguous) // // ^PARAMETERS: // char * opt -- the long-option to match // int len -- the number of character of "opt" to match // int & ambiguous -- set by this routine before returning. // // ^DESCRIPTION: // Try to match "opt" against some unique prefix of a long-option // (case insensitive). // // ^REQUIREMENTS: // - optvec should be non-NULL and terminated by a NULL pointer. // // ^SIDE-EFFECTS: // - *ambiguous is set to '1' if "opt" matches >1 long-option // (otherwise it is set to 0). // // ^RETURN-VALUE: // NULL if no match is found, // otherwise a pointer to the matching option-spec. // // ^ALGORITHM: // ambiguous is FALSE // foreach option-spec // if we have an EXACT-MATCH, return the option-spec // if we have a partial-match then // if we already had a previous partial match then // set ambiguous = TRUE and return NULL // else // remember this options spec and continue matching // end-if // end-if // end-for // if we had exactly 1 partial match return it, else return NULL // ^^------------------------------------------------------------------------- const char *Options::match_longopt(const char *opt, int len, int &ambiguous) const { kwdmatch_t result; const char *matched = NULLSTR; ambiguous = 0; if ((optvec == NULL) || (!*optvec)) return NULLSTR; for (const char *const *optv = optvec; *optv; optv++) { OptionSpec optspec = *optv; const char *longopt = optspec.LongOpt(); if (longopt == NULL) continue; result = kwdmatch(longopt, opt, len); if (result == EXACT_MATCH) { return optspec; } else if (result == PARTIAL_MATCH) { if (matched) { ++ambiguous; return NULLSTR; } else { matched = optspec; } } } // for return matched; } // --------------------------------------------------------------------------- // ^FUNCTION: Options::parse_opt - parse an option // // ^SYNOPSIS: // int Options::parse_opt(iter, optarg) // // ^PARAMETERS: // OptIter & iter -- option iterator // const char * & optarg -- where to store any option-argument // // ^DESCRIPTION: // Parse the next option in iter (advancing as necessary). // Make sure we update the nextchar pointer along the way. Any option // we find should be returned and optarg should point to its argument. // // ^REQUIREMENTS: // - nextchar must point to the prospective option character // // ^SIDE-EFFECTS: // - iter is advanced when an argument completely parsed // - optarg is modified to point to any option argument // - if Options::QUIET is not set, error messages are printed on cerr // // ^RETURN-VALUE: // 'c' if the -c option was matched (optarg points to its argument) // BADCHAR if the option is invalid (optarg points to the bad // option-character). // // ^ALGORITHM: // It gets complicated -- follow the comments in the source. // ^^------------------------------------------------------------------------- int Options::parse_opt(OptIter &iter, const char *&optarg) { listopt = NULLSTR; // reset the list pointer if ((optvec == NULL) || (!*optvec)) return Options::ENDOPTS; // Try to match a known option OptionSpec optspec = match_opt(*(nextchar++), (optctrls & Options::ANYCASE)); // Check for an unknown option if (optspec.isNULL()) { // See if this was a long-option in disguise if (!(optctrls & Options::NOGUESSING)) { unsigned save_ctrls = optctrls; const char *save_nextchar = nextchar; nextchar -= 1; optctrls |= (Options::QUIET | Options::NOGUESSING); int optchar = parse_longopt(iter, optarg); optctrls = save_ctrls; if (optchar > 0) { return optchar; } else { nextchar = save_nextchar; } } if (!(optctrls & Options::QUIET)) { cerr << cmdname << ": unknown option -" << *(nextchar - 1) << "." << endl; } optarg = (nextchar - 1); // record the bad option in optarg return Options::BADCHAR; } // If no argument is taken, then leave now if (optspec.isNoArg()) { optarg = NULLSTR; return optspec.OptChar(); } // Check for argument in this arg if (*nextchar) { optarg = nextchar; // the argument is right here nextchar = NULLSTR; // we've exhausted this arg if (optspec.isList()) listopt = optspec; // save the list-spec return optspec.OptChar(); } // Check for argument in next arg const char *nextarg = iter.curr(); if ((nextarg != NULL) && (optspec.isValRequired() || (!isOption(optctrls, nextarg)))) { optarg = nextarg; // the argument is here iter.next(); // end of arg - advance if (optspec.isList()) listopt = optspec; // save the list-spec return optspec.OptChar(); } // No argument given - if its required, thats an error optarg = NULLSTR; if (optspec.isValRequired() && !(optctrls & Options::QUIET)) { cerr << cmdname << ": argument required for -" << optspec.OptChar() << " option." << endl; } return optspec.OptChar(); } // --------------------------------------------------------------------------- // ^FUNCTION: Options::parse_longopt - parse a long-option // // ^SYNOPSIS: // int Options::parse_longopt(iter, optarg) // // ^PARAMETERS: // OptIter & iter -- option iterator // const char * & optarg -- where to store any option-argument // // ^DESCRIPTION: // Parse the next long-option in iter (advancing as necessary). // Make sure we update the nextchar pointer along the way. Any option // we find should be returned and optarg should point to its argument. // // ^REQUIREMENTS: // - nextchar must point to the prospective option character // // ^SIDE-EFFECTS: // - iter is advanced when an argument completely parsed // - optarg is modified to point to any option argument // - if Options::QUIET is not set, error messages are printed on cerr // // ^RETURN-VALUE: // 'c' if the the long-option corresponding to the -c option was matched // (optarg points to its argument) // BADKWD if the option is invalid (optarg points to the bad long-option // name). // // ^ALGORITHM: // It gets complicated -- follow the comments in the source. // ^^------------------------------------------------------------------------- int Options::parse_longopt(OptIter &iter, const char *&optarg) { int len = 0, ambiguous = 0; listopt = NULLSTR; // reset the list-spec if ((optvec == NULL) || (!*optvec)) return Options::ENDOPTS; // if a value is supplied in this argv element, get it now const char *val = strpbrk(nextchar, ":="); if (val) { len = val - nextchar; ++val; } // Try to match a known long-option OptionSpec optspec = match_longopt(nextchar, len, ambiguous); // Check for an unknown long-option if (optspec.isNULL()) { // See if this was a short-option in disguise if ((!ambiguous) && (!(optctrls & Options::NOGUESSING))) { unsigned save_ctrls = optctrls; const char *save_nextchar = nextchar; optctrls |= (Options::QUIET | Options::NOGUESSING); int optchar = parse_opt(iter, optarg); optctrls = save_ctrls; if (optchar > 0) { return optchar; } else { nextchar = save_nextchar; } } if (!(optctrls & Options::QUIET)) { cerr << cmdname << ": " << ((ambiguous) ? "ambiguous" : "unknown") << " option " << ((optctrls & Options::LONG_ONLY) ? "-" : "--") << nextchar << "." << endl; } optarg = nextchar; // record the bad option in optarg nextchar = NULLSTR; // we've exhausted this argument return (ambiguous) ? Options::AMBIGUOUS : Options::BADKWD; } // If no argument is taken, then leave now if (optspec.isNoArg()) { if ((val) && !(optctrls & Options::QUIET)) { cerr << cmdname << ": option " << ((optctrls & Options::LONG_ONLY) ? "-" : "--") << optspec.LongOpt() << " does NOT take an argument." << endl; } optarg = val; // record the unexpected argument nextchar = NULLSTR; // we've exhausted this argument return optspec.OptChar(); } // Check for argument in this arg if (val) { optarg = val; // the argument is right here nextchar = NULLSTR; // we exhausted the rest of this arg if (optspec.isList()) listopt = optspec; // save the list-spec return optspec.OptChar(); } // Check for argument in next arg const char *nextarg = iter.curr(); // find the next argument to parse if ((nextarg != NULL) && (optspec.isValRequired() || (!isOption(optctrls, nextarg)))) { optarg = nextarg; // the argument is right here iter.next(); // end of arg - advance nextchar = NULLSTR; // we exhausted the rest of this arg if (optspec.isList()) listopt = optspec; // save the list-spec return optspec.OptChar(); } // No argument given - if its required, thats an error optarg = NULLSTR; if (optspec.isValRequired() && !(optctrls & Options::QUIET)) { const char *longopt = optspec.LongOpt(); const char *spc = ::strchr(longopt, ' '); int longopt_len; if (spc) { longopt_len = spc - longopt; } else { longopt_len = ::strlen(longopt); } cerr << cmdname << ": argument required for " << ((optctrls & Options::LONG_ONLY) ? "-" : "--"); cerr.write(longopt, longopt_len) << " option." << endl; } nextchar = NULLSTR; // we exhausted the rest of this arg return optspec.OptChar(); } // --------------------------------------------------------------------------- // ^FUNCTION: Options::usage - print usage // // ^SYNOPSIS: // void Options::usage(os, positionals) // // ^PARAMETERS: // ostream & os -- where to print the usage // char * positionals -- command-line syntax for any positional args // // ^DESCRIPTION: // Print command-usage (using either option or long-option syntax) on os. // // ^REQUIREMENTS: // os should correspond to an open output file. // // ^SIDE-EFFECTS: // Prints on os // // ^RETURN-VALUE: // None. // // ^ALGORITHM: // Print usage on os, wrapping long lines where necessary. // ^^------------------------------------------------------------------------- void Options::usage(ostream &os, const char *positionals) const { #ifdef NO_USAGE return; #else const char *const *optv = optvec; unsigned cols = 79; int first, nloop; char buf[256]; if ((optv == NULL) || (!*optv)) return; // print first portion "usage: progname" os << "usage: " << cmdname; unsigned ll = strlen(cmdname) + 7; // save the current length so we know how much space to skip for // subsequent lines. // unsigned margin = ll + 1; // print the options and the positional arguments for (nloop = 0, first = 1; !nloop; optv++, first = 0) { unsigned len; OptionSpec optspec = *optv; // figure out how wide this parameter is (for printing) if (!*optv) { len = strlen(positionals); ++nloop; // terminate this loop } else { if (optspec.isHiddenOpt()) continue; len = optspec.Format(buf, optctrls); } // Will this fit? if ((ll + len + 1) > (cols - first)) { os << '\n'; // No - start a new line; #ifdef USE_STDIO for (int _i_ = 0; _i_ < margin; ++_i_) os << " "; #else os.width(margin); os << ""; #endif ll = margin; } else { os << ' '; // Yes - just throw in a space ++ll; } ll += len; os << ((nloop) ? positionals : buf); } // for each ad os << endl; #endif /* NO_USAGE */ } // --------------------------------------------------------------------------- // ^FUNCTION: Options::operator() - get options from the command-line // // ^SYNOPSIS: // int Options::operator()(iter, optarg) // // ^PARAMETERS: // OptIter & iter -- option iterator // const char * & optarg -- where to store any option-argument // // ^DESCRIPTION: // Parse the next option in iter (advancing as necessary). // Make sure we update the nextchar pointer along the way. Any option // we find should be returned and optarg should point to its argument. // // ^REQUIREMENTS: // None. // // ^SIDE-EFFECTS: // - iter is advanced when an argument is completely parsed // - optarg is modified to point to any option argument // - if Options::QUIET is not set, error messages are printed on cerr // // ^RETURN-VALUE: // 0 if all options have been parsed. // 'c' if the the option or long-option corresponding to the -c option was // matched (optarg points to its argument). // BADCHAR if the option is invalid (optarg points to the bad option char). // BADKWD if the option is invalid (optarg points to the bad long-opt name). // AMBIGUOUS if an ambiguous keyword name was given (optarg points to the // ambiguous keyword name). // POSITIONAL if PARSE_POS was set and the current argument is a positional // parameter (in which case optarg points to the positional argument). // // ^ALGORITHM: // It gets complicated -- follow the comments in the source. // ^^------------------------------------------------------------------------- int Options::operator()(OptIter &iter, const char *&optarg) { int parse_opts_only = isOptsOnly(optctrls); if (parse_opts_only) explicit_end = 0; // See if we have an option left over from before ... if ((nextchar) && *nextchar) { return parse_opt(iter, optarg); } // Check for end-of-options ... const char *arg = NULLSTR; int get_next_arg = 0; do { arg = iter.curr(); get_next_arg = 0; if (arg == NULL) { listopt = NULLSTR; return Options::ENDOPTS; } else if ((!explicit_end) && isEndOpts(arg)) { iter.next(); // advance past end-of-options arg listopt = NULLSTR; explicit_end = 1; if (parse_opts_only) return Options::ENDOPTS; get_next_arg = 1; // make sure we look at the next argument. } } while (get_next_arg); // Do we have a positional arg? if (explicit_end || (!isOption(optctrls, arg))) { if (parse_opts_only) { return Options::ENDOPTS; } else { optarg = arg; // set optarg to the positional argument iter.next(); // advance iterator to the next argument return Options::POSITIONAL; } } iter.next(); // pass the argument that arg already points to // See if we have a long option ... if (!(optctrls & Options::SHORT_ONLY)) { if ((*arg == '-') && (arg[1] == '-')) { nextchar = arg + 2; return parse_longopt(iter, optarg); } else if ((optctrls & Options::PLUS) && (*arg == '+')) { nextchar = arg + 1; return parse_longopt(iter, optarg); } } if (*arg == '-') { nextchar = arg + 1; if (optctrls & Options::LONG_ONLY) { return parse_longopt(iter, optarg); } else { return parse_opt(iter, optarg); } } // If we get here - it is because we have a list value OptionSpec optspec = listopt; optarg = arg; // record the list value return optspec.OptChar(); }