summaryrefslogtreecommitdiff
path: root/IvyCursor/options.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'IvyCursor/options.cxx')
-rw-r--r--IvyCursor/options.cxx1146
1 files changed, 1146 insertions, 0 deletions
diff --git a/IvyCursor/options.cxx b/IvyCursor/options.cxx
new file mode 100644
index 0000000..50c14d3
--- /dev/null
+++ b/IvyCursor/options.cxx
@@ -0,0 +1,1146 @@
+// ****************************************************************************
+// ^FILE: options.c - implement the functions defined in <options.h>
+//
+// ^HISTORY:
+// 01/16/92 Brad Appleton <bradapp@enteract.com> Created
+//
+// 03/23/93 Brad Appleton <bradapp@enteract.com>
+// - Added OptIstreamIter class
+//
+// 10/08/93 Brad Appleton <bradapp@enteract.com>
+// - Added "hidden" options
+//
+// 02/08/94 Brad Appleton <bradapp@enteract.com>
+// - Added "OptionSpec" class
+// - Permitted use of stdio instead of iostreams via #ifdef USE_STDIO
+//
+// 03/08/94 Brad Appleton <bradapp@enteract.com>
+// - 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 <bradapp@enteract.com>
+// - Added PARSE_POS control flag and POSITIONAL return value.
+// ^^**************************************************************************
+
+#include <stdafx.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "options.h"
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+
+using std::cerr;
+using std::endl;
+
+
+
+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 <stdio.h> then you need this stuff!
+ // If you are using <iostream.h> 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, size_t len =0) {
+ size_t 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[] = "<value>";
+ if (isHiddenOpt()) return (unsigned)(*buf = '\0');
+ char optchar = OptChar();
+ const char * longopt = LongOpt();
+ char * p = buf ;
+
+ const char * value = NULLSTR;
+ size_t longopt_len = 0;
+ size_t 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[])
+ : explicit_end(0), optctrls(DEFAULT), optvec(optv),
+ nextchar(NULLSTR), listopt(NULLSTR), cmdname(_name)
+{
+ 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, size_t 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) {
+ size_t len = 0;
+ int 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, ' ');
+ size_t 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 ;
+ size_t ll = strlen(cmdname) + 7;
+
+ // save the current length so we know how much space to skip for
+ // subsequent lines.
+ //
+ size_t margin = ll + 1;
+
+ // print the options and the positional arguments
+ for (nloop = 0, first = 1 ; !nloop ; optv++, first = 0) {
+ size_t 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() ;
+}
+
+
+