Home | History | Annotate | Line # | Download | only in common
options.cpp revision 1.1.2.2
      1 // ****************************************************************************
      2 // ^FILE: options.c - implement the functions defined in <options.h>
      3 //
      4 // ^HISTORY:
      5 //    01/16/92	Brad Appleton	<bradapp (at) enteract.com>	Created
      6 //
      7 //    03/23/93	Brad Appleton	<bradapp (at) enteract.com>
      8 //    - Added OptIstreamIter class
      9 //
     10 //    10/08/93	Brad Appleton	<bradapp (at) enteract.com>
     11 //    - Added "hidden" options
     12 //
     13 //    02/08/94	Brad Appleton	<bradapp (at) enteract.com>
     14 //    - Added "OptionSpec" class
     15 //    - Permitted use of stdio instead of iostreams via #ifdef USE_STDIO
     16 //
     17 //    03/08/94	Brad Appleton	<bradapp (at) enteract.com>
     18 //    - completed support for USE_STDIO
     19 //    - added #ifdef NO_USAGE for people who always want to print their own
     20 //    - Fixed stupid NULL pointer error in OptionsSpec class
     21 //
     22 //    07/31/97	Brad Appleton	<bradapp (at) enteract.com>
     23 //    - Added PARSE_POS control flag and POSITIONAL return value.
     24 // ^^**************************************************************************
     25 
     26 // #include <stdlib.h>
     27 #include <ctype.h>
     28 #include <string.h>
     29 
     30 #include "options.h"
     31 
     32 using namespace std;
     33 
     34 extern "C" {
     35    void  exit(int);
     36 }
     37 
     38 static const char ident[] = "@(#)Options  1.05" ;
     39 
     40    // I need a portable version of "tolower" that does NOT modify
     41    // non-uppercase characters.
     42    //
     43 #define  TOLOWER(c)  (isupper(c) ? tolower(c) : c)
     44 
     45    // Use this to shut the compiler up about NULL strings
     46 #define  NULLSTR  (char *)NULL
     47 
     48 // ******************************************************** insertion operators
     49 
     50   // If you are using <stdio.h> then you need this stuff!
     51   // If you are using <iostream.h> then #ifdef this stuff out
     52   //
     53 
     54 
     55 #ifdef  USE_STDIO
     56 
     57    // Implement just enough of ostream to get this file to compile
     58    //
     59 
     60 static const char endl = '\n' ;
     61 
     62 class  ostream {
     63 public:
     64    ostream(FILE * fileptr) : fp(fileptr) {}
     65 
     66    ostream &
     67    operator<<(char ch);
     68 
     69    ostream &
     70    operator<<(const char * str);
     71 
     72    ostream &
     73    write(const char * buf, unsigned bufsize);
     74 
     75 private:
     76    FILE * fp;
     77 } ;
     78 
     79 ostream &
     80 ostream::operator<<(char ch) {
     81    fputc(ch, fp);
     82    return *this;
     83 }
     84 
     85 ostream &
     86 ostream::operator<<(const char * str) {
     87    fputs(str, fp);
     88    return *this;
     89 }
     90 
     91 ostream &
     92 ostream::write(const char * buf, unsigned ) {
     93    fputs(buf, fp);
     94    return *this;
     95 }
     96 
     97 static  ostream  cerr(stderr);
     98 static  ostream  cout(stdout);
     99 
    100 #endif  /* USE_STDIO */
    101 
    102 // ************************************************************** OptIter
    103 
    104 OptIter::~OptIter(void) {}
    105 
    106 const char *
    107 OptIter::operator()(void)  {
    108    const char * elt = curr();
    109    (void) next();
    110    return  elt;
    111 }
    112 
    113 // ************************************************************** OptIterRwd
    114 
    115 OptIterRwd::OptIterRwd(void) {}
    116 
    117 OptIterRwd::~OptIterRwd(void) {}
    118 
    119 // ************************************************************** OptArgvIter
    120 
    121 OptArgvIter::~OptArgvIter(void) {}
    122 
    123 const char *
    124 OptArgvIter::curr(void) {
    125    return ((ndx == ac) || (av[ndx] == NULL)) ? NULLSTR : av[ndx];
    126 }
    127 
    128 void
    129 OptArgvIter::next(void) {
    130    if ((ndx != ac) && av[ndx]) ++ndx;
    131 }
    132 
    133 const char *
    134 OptArgvIter::operator()(void) {
    135    return ((ndx == ac) || (av[ndx] == NULL)) ? NULLSTR : av[ndx++];
    136 }
    137 
    138 void
    139 OptArgvIter::rewind(void) { ndx = 0; }
    140 
    141 // ************************************************************** OptStrTokIter
    142 
    143 static const char WHITESPACE[] = " \t\n\r\v\f" ;
    144 const char * OptStrTokIter::default_delims = WHITESPACE ;
    145 
    146 OptStrTokIter::OptStrTokIter(const char * tokens, const char * delimiters)
    147    : len(unsigned(strlen(tokens))), str(tokens), seps(delimiters),
    148      cur(NULLSTR), tokstr(NULLSTR)
    149 {
    150    if (seps == NULL)  seps = default_delims;
    151    tokstr = new char[len + 1];
    152    (void) ::strcpy(tokstr, str);
    153    cur = ::strtok(tokstr, seps);
    154 }
    155 
    156 
    157 OptStrTokIter::~OptStrTokIter(void) { delete [] tokstr; }
    158 
    159 const char *
    160 OptStrTokIter::curr(void) { return cur; }
    161 
    162 void
    163 OptStrTokIter::next(void) { if (cur) cur = ::strtok(NULL, seps); }
    164 
    165 const char *
    166 OptStrTokIter::operator()(void) {
    167    const char * elt = cur;
    168    if (cur) cur = ::strtok(NULL, seps);
    169    return  elt;
    170 }
    171 
    172 void
    173 OptStrTokIter::rewind(void) {
    174    (void) ::strcpy(tokstr, str);
    175    cur = ::strtok(tokstr, seps);
    176 }
    177 
    178 // ************************************************************* OptIstreamIter
    179 
    180 #ifdef vms
    181    enum { c_COMMENT = '!' } ;
    182 #else
    183    enum { c_COMMENT = '#' } ;
    184 #endif
    185 
    186 const unsigned  OptIstreamIter::MAX_LINE_LEN = 1024 ;
    187 
    188    // Constructor
    189 OptIstreamIter::OptIstreamIter(istream & input) : is(input), tok_iter(NULL)
    190 {
    191 #ifdef  USE_STDIO
    192    fprintf(stderr, "%s: Can't use OptIstreamIter class:\n",
    193                    "OptIstreamIter::OptIstreamIter");
    194    fprintf(stderr, "\tOptions(3C++) was compiled with USE_STDIO #defined.\n");
    195    exit(-1);
    196 #endif  /* USE_STDIO */
    197 }
    198 
    199    // Destructor
    200 OptIstreamIter::~OptIstreamIter(void) {
    201    delete  tok_iter;
    202 }
    203 
    204 const char *
    205 OptIstreamIter::curr(void) {
    206 #ifdef  USE_STDIO
    207    return  NULLSTR;
    208 #else
    209    const char * result = NULLSTR;
    210    if (tok_iter)  result = tok_iter->curr();
    211    if (result)  return  result;
    212    fill();
    213    return (! is) ? NULLSTR : tok_iter->curr();
    214 #endif  /* USE_STDIO */
    215 }
    216 
    217 void
    218 OptIstreamIter::next(void) {
    219 #ifdef  USE_STDIO
    220    return;
    221 #else
    222    const char * result = NULLSTR;
    223    if (tok_iter)  result = tok_iter->operator()();
    224    if (result)  return;
    225    fill();
    226    if (! is) tok_iter->next();
    227 #endif  /* USE_STDIO */
    228 }
    229 
    230 const char *
    231 OptIstreamIter::operator()(void) {
    232 #ifdef  USE_STDIO
    233    return  NULLSTR;
    234 #else
    235    const char * result = NULLSTR;
    236    if (tok_iter)  result = tok_iter->operator()();
    237    if (result)  return  result;
    238    fill();
    239    return (! is) ? NULLSTR : tok_iter->operator()();
    240 #endif  /* USE_STDIO */
    241 }
    242 
    243    // What we do is this: for each line of text in the istream, we use
    244    // a OptStrTokIter to iterate over each token on the line.
    245    //
    246    // If the first non-white character on a line is c_COMMENT, then we
    247    // consider the line to be a comment and we ignore it.
    248    //
    249 void
    250 OptIstreamIter::fill(void) {
    251 #ifdef USE_STDIO
    252    return;
    253 #else
    254    char buf[OptIstreamIter::MAX_LINE_LEN];
    255    do {
    256       *buf = '\0';
    257       is.getline(buf, sizeof(buf));
    258       char * ptr = buf;
    259       while (isspace(*ptr)) ++ptr;
    260       if (*ptr && (*ptr != c_COMMENT)) {
    261          delete  tok_iter;
    262          tok_iter = new OptStrTokIter(ptr);
    263          return;
    264       }
    265    } while (is);
    266 #endif  /* USE_STDIO */
    267 }
    268 
    269 // **************************************************** Options class utilities
    270 
    271    // Is this option-char null?
    272 inline static int
    273 isNullOpt(char optchar) {
    274    return  ((! optchar) || isspace(optchar) || (! isprint(optchar)));
    275 }
    276 
    277    // Check for explicit "end-of-options"
    278 inline static int
    279 isEndOpts(const char * token) {
    280    return ((token == NULL) || (! ::strcmp(token, "--"))) ;
    281 }
    282 
    283    // See if an argument is an option
    284 inline static int
    285 isOption(unsigned  flags, const char * arg) {
    286    return  (((*arg != '\0') || (arg[1] != '\0')) &&
    287             ((*arg == '-')  || ((flags & Options::PLUS) && (*arg == '+')))) ;
    288 }
    289 
    290    // See if we should be parsing only options or if we also need to
    291    // parse positional arguments
    292 inline static int
    293 isOptsOnly(unsigned  flags) {
    294    return  (flags & Options::PARSE_POS) ? 0 : 1;
    295 }
    296 
    297    // return values for a keyword matching function
    298 enum kwdmatch_t { NO_MATCH, PARTIAL_MATCH, EXACT_MATCH } ;
    299 
    300 // ---------------------------------------------------------------------------
    301 // ^FUNCTION: kwdmatch - match a keyword
    302 //
    303 // ^SYNOPSIS:
    304 //    static kwdmatch_t kwdmatch(src, attempt, len)
    305 //
    306 // ^PARAMETERS:
    307 //    char * src -- the actual keyword to match
    308 //    char * attempt -- the possible keyword to compare against "src"
    309 //    int len -- number of character of "attempt" to consider
    310 //               (if 0 then we should use all of "attempt")
    311 //
    312 // ^DESCRIPTION:
    313 //    See if "attempt" matches some prefix of "src" (case insensitive).
    314 //
    315 // ^REQUIREMENTS:
    316 //    - attempt should be non-NULL and non-empty
    317 //
    318 // ^SIDE-EFFECTS:
    319 //    None.
    320 //
    321 // ^RETURN-VALUE:
    322 //    An enumeration value of type kwdmatch_t corresponding to whether
    323 //    We had an exact match, a partial match, or no match.
    324 //
    325 // ^ALGORITHM:
    326 //    Trivial
    327 // ^^-------------------------------------------------------------------------
    328 static kwdmatch_t
    329 kwdmatch(const char * src, const char * attempt, int len =0) {
    330    int  i;
    331 
    332    if (src == attempt)  return  EXACT_MATCH ;
    333    if ((src == NULL) || (attempt == NULL))  return  NO_MATCH ;
    334    if ((! *src) && (! *attempt))  return  EXACT_MATCH ;
    335    if ((! *src) || (! *attempt))  return  NO_MATCH ;
    336 
    337    for (i = 0 ; ((i < len) || (len == 0)) &&
    338                 (attempt[i]) && (attempt[i] != ' ') ; i++) {
    339       if (TOLOWER(src[i]) != TOLOWER(attempt[i]))  return  NO_MATCH ;
    340    }
    341 
    342    return  (src[i]) ? PARTIAL_MATCH : EXACT_MATCH ;
    343 }
    344 
    345 // **************************************************************** OptionSpec
    346 
    347    // Class that represents an option-specification
    348    //    *NOTE*:: Assumes that the char-ptr given to the constructor points
    349    //             to storage that will NOT be modified and whose lifetime will
    350    //             be as least as long as the OptionSpec object we construct.
    351    //
    352 class OptionSpec {
    353 public:
    354    OptionSpec(const char * decl =NULLSTR)
    355       : hidden(0), spec(decl)
    356    {
    357       if (spec == NULL)  spec = NULL_spec;
    358       CheckHidden();
    359    }
    360 
    361    OptionSpec(const OptionSpec & cp) : hidden(cp.hidden), spec(cp.spec) {}
    362 
    363    // NOTE: use default destructor!
    364 
    365       // Assign to another OptionSpec
    366    OptionSpec &
    367    operator=(const OptionSpec & cp) {
    368       if (this != &cp) {
    369          spec = cp.spec;
    370          hidden = cp.hidden;
    371       }
    372       return *this;
    373    }
    374 
    375       // Assign to a string
    376    OptionSpec &
    377    operator=(const char * decl) {
    378       if (spec != decl) {
    379          spec = decl;
    380          hidden = 0;
    381          CheckHidden();
    382       }
    383       return *this;
    384    }
    385 
    386       // Convert to char-ptr by returning the original declaration-string
    387    operator const char*() { return  isHiddenOpt() ? (spec - 1) : spec; }
    388 
    389       // Is this option NULL?
    390    int
    391    isNULL(void) const { return ((spec == NULL) || (spec == NULL_spec)); }
    392 
    393       // Is this options incorrectly specified?
    394    int
    395    isSyntaxError(const char * name) const;
    396 
    397       // See if this is a Hidden option
    398    int
    399    isHiddenOpt(void) const { return  hidden; }
    400 
    401       // Get the corresponding option-character
    402    char
    403    OptChar(void) const { return  *spec; }
    404 
    405       // Get the corresponding long-option string
    406    const char *
    407    LongOpt(void) const {
    408        return  (spec[1] && spec[2] && (! isspace(spec[2]))) ? (spec + 2) : NULLSTR;
    409    }
    410 
    411       // Does this option require an argument?
    412    int
    413    isValRequired(void) const {
    414       return  ((spec[1] == ':') || (spec[1] == '+'));
    415    }
    416 
    417       // Does this option take an optional argument?
    418    int
    419    isValOptional(void) const {
    420       return  ((spec[1] == '?') || (spec[1] == '*'));
    421    }
    422 
    423       // Does this option take no arguments?
    424    int
    425    isNoArg(void) const {
    426       return  ((spec[1] == '|') || (! spec[1]));
    427    }
    428 
    429       // Can this option take more than one argument?
    430    int
    431    isList(void) const {
    432       return  ((spec[1] == '+') || (spec[1] == '*'));
    433    }
    434 
    435       // Does this option take any arguments?
    436    int
    437    isValTaken(void) const {
    438       return  (isValRequired() || isValOptional()) ;
    439    }
    440 
    441       // Format this option in the given buffer
    442    unsigned
    443    Format(char * buf, unsigned optctrls) const;
    444 
    445 private:
    446    void
    447    CheckHidden(void) {
    448       if ((! hidden) && (*spec == '-')) {
    449          ++hidden;
    450          ++spec;
    451       }
    452    }
    453 
    454    unsigned     hidden : 1;  // hidden-flag
    455    const char * spec;        // string specification
    456 
    457    static const char NULL_spec[];
    458 } ;
    459 
    460 const char OptionSpec::NULL_spec[] = "\0\0\0" ;
    461 
    462 int
    463 OptionSpec::isSyntaxError(const char * name) const {
    464    int  error = 0;
    465    if ((! spec) || (! *spec)) {
    466       cerr << name << ": empty option specifier." << endl;
    467       cerr << "\tmust be at least 1 character long." << endl;
    468       ++error;
    469    } else if (spec[1] && (strchr("|?:*+", spec[1]) == NULL)) {
    470       cerr << name << ": bad option specifier \"" << spec << "\"." << endl;
    471       cerr << "\t2nd character must be in the set \"|?:*+\"." << endl;
    472       ++error;
    473    }
    474    return  error;
    475 }
    476 
    477 // ---------------------------------------------------------------------------
    478 // ^FUNCTION: OptionSpec::Format - format an option-spec for a usage message
    479 //
    480 // ^SYNOPSIS:
    481 //    unsigned OptionSpec::Format(buf, optctrls) const
    482 //
    483 // ^PARAMETERS:
    484 //    char * buf -- where to print the formatted option
    485 //    unsigned  optctrls -- option-parsing configuration flags
    486 //
    487 // ^DESCRIPTION:
    488 //    Self-explanatory.
    489 //
    490 // ^REQUIREMENTS:
    491 //    - buf must be large enough to hold the result
    492 //
    493 // ^SIDE-EFFECTS:
    494 //    - writes to buf.
    495 //
    496 // ^RETURN-VALUE:
    497 //    Number of characters written to buf.
    498 //
    499 // ^ALGORITHM:
    500 //    Follow along in the source - it's not hard but it is tedious!
    501 // ^^-------------------------------------------------------------------------
    502 unsigned
    503 OptionSpec::Format(char * buf, unsigned optctrls) const {
    504 #ifdef NO_USAGE
    505    return  (*buf = '\0');
    506 #else
    507    static  char default_value[] = "<value>";
    508    if (isHiddenOpt())  return (unsigned)(*buf = '\0');
    509    char optchar = OptChar();
    510    const char * longopt = LongOpt();
    511    char * p = buf ;
    512 
    513    const char * value = NULLSTR;
    514    int    longopt_len = 0;
    515    int    value_len = 0;
    516 
    517    if (longopt) {
    518       value = ::strchr(longopt, ' ');
    519       longopt_len = (value) ? (value - longopt) : ::strlen(longopt);
    520    } else {
    521       value = ::strchr(spec + 1, ' ');
    522    }
    523    while (value && (*value == ' '))  ++value;
    524    if (value && *value) {
    525       value_len = ::strlen(value);
    526    } else {
    527       value = default_value;
    528       value_len = sizeof(default_value) - 1;
    529    }
    530 
    531    if ((optctrls & Options::SHORT_ONLY) &&
    532        ((! isNullOpt(optchar)) || (optctrls & Options::NOGUESSING))) {
    533       longopt = NULLSTR;
    534    }
    535    if ((optctrls & Options::LONG_ONLY) &&
    536        (longopt || (optctrls & Options::NOGUESSING))) {
    537       optchar = '\0';
    538    }
    539    if (isNullOpt(optchar) && (longopt == NULL)) {
    540       *buf = '\0';
    541       return  0;
    542    }
    543 
    544    *(p++) = '[';
    545 
    546    // print the single character option
    547    if (! isNullOpt(optchar)) {
    548       *(p++) = '-';
    549       *(p++) = optchar;
    550    }
    551 
    552    if ((! isNullOpt(optchar)) && (longopt))  *(p++) = '|';
    553 
    554    // print the long option
    555    if (longopt) {
    556       *(p++) = '-';
    557       if (! (optctrls & (Options::LONG_ONLY | Options::SHORT_ONLY))) {
    558          *(p++) = '-';
    559       }
    560       strncpy(p, longopt, longopt_len);
    561       p += longopt_len;
    562    }
    563 
    564    // print any argument the option takes
    565    if (isValTaken()) {
    566       *(p++) = ' ' ;
    567       if (isValOptional())  *(p++) = '[' ;
    568       strcpy(p, value);
    569       p += value_len;
    570       if (isList()) {
    571          strcpy(p, " ...");
    572          p += 4;
    573       }
    574       if (isValOptional())  *(p++) = ']' ;
    575    }
    576 
    577    *(p++) = ']';
    578    *p = '\0';
    579 
    580    return  (unsigned) strlen(buf);
    581 #endif  /* USE_STDIO */
    582 }
    583 
    584 // ******************************************************************* Options
    585 
    586 #if (defined(MSWIN) || defined(OS2) || defined(MSDOS))
    587 # define DIR_SEP_CHAR '\\'
    588 #else
    589 # define DIR_SEP_CHAR '/'
    590 #endif
    591 
    592 Options::Options(const char * name, const char * const optv[])
    593    : cmdname(name), optvec(optv), explicit_end(0), optctrls(DEFAULT),
    594      nextchar(NULLSTR), listopt(NULLSTR)
    595 {
    596    const char * basename = ::strrchr(cmdname, DIR_SEP_CHAR);
    597    if (basename)  cmdname = basename + 1;
    598    check_syntax();
    599 }
    600 
    601 Options::~Options(void) {}
    602 
    603    // Make sure each option-specifier has correct syntax.
    604    //
    605    // If there is even one invalid specifier, then exit ungracefully!
    606    //
    607 void
    608 Options::check_syntax(void) const {
    609    int  errors = 0;
    610    if ((optvec == NULL) || (! *optvec))  return;
    611 
    612    for (const char * const * optv = optvec ; *optv ; optv++) {
    613       OptionSpec  optspec = *optv;
    614       errors += optspec.isSyntaxError(cmdname);
    615    }
    616    if (errors)  exit(127);
    617 }
    618 
    619 // ---------------------------------------------------------------------------
    620 // ^FUNCTION: Options::match_opt - match an option
    621 //
    622 // ^SYNOPSIS:
    623 //    const char * match_opt(opt, int  ignore_case) const
    624 //
    625 // ^PARAMETERS:
    626 //    char opt -- the option-character to match
    627 //    int  ignore_case -- should we ignore character-case?
    628 //
    629 // ^DESCRIPTION:
    630 //    See if "opt" is found in "optvec"
    631 //
    632 // ^REQUIREMENTS:
    633 //    - optvec should be non-NULL and terminated by a NULL pointer.
    634 //
    635 // ^SIDE-EFFECTS:
    636 //    None.
    637 //
    638 // ^RETURN-VALUE:
    639 //    NULL if no match is found,
    640 //    otherwise a pointer to the matching option-spec.
    641 //
    642 // ^ALGORITHM:
    643 //    foreach option-spec
    644 //       - see if "opt" is a match, if so return option-spec
    645 //    end-for
    646 // ^^-------------------------------------------------------------------------
    647 const char *
    648 Options::match_opt(char opt, int ignore_case) const {
    649    if ((optvec == NULL) || (! *optvec))  return  NULLSTR;
    650 
    651    for (const char * const * optv = optvec ; *optv ; optv++) {
    652       OptionSpec  optspec = *optv;
    653       char optchar = optspec.OptChar();
    654       if (isNullOpt(optchar))  continue;
    655       if (opt == optchar) {
    656          return  optspec;
    657       } else if (ignore_case && (TOLOWER(opt) == TOLOWER(optchar))) {
    658          return  optspec;
    659       }
    660    }
    661 
    662    return  NULLSTR;  // not found
    663 }
    664 
    665 // ---------------------------------------------------------------------------
    666 // ^FUNCTION: Options::match_longopt - match a long-option
    667 //
    668 // ^SYNOPSIS:
    669 //   const char * Options::match_longopt(opt, len, ambiguous)
    670 //
    671 // ^PARAMETERS:
    672 //    char * opt -- the long-option to match
    673 //    int len -- the number of character of "opt" to match
    674 //    int & ambiguous -- set by this routine before returning.
    675 //
    676 // ^DESCRIPTION:
    677 //    Try to match "opt" against some unique prefix of a long-option
    678 //    (case insensitive).
    679 //
    680 // ^REQUIREMENTS:
    681 //    - optvec should be non-NULL and terminated by a NULL pointer.
    682 //
    683 // ^SIDE-EFFECTS:
    684 //    - *ambiguous is set to '1' if "opt" matches >1 long-option
    685 //      (otherwise it is set to 0).
    686 //
    687 // ^RETURN-VALUE:
    688 //    NULL if no match is found,
    689 //    otherwise a pointer to the matching option-spec.
    690 //
    691 // ^ALGORITHM:
    692 //    ambiguous is FALSE
    693 //    foreach option-spec
    694 //       if we have an EXACT-MATCH, return the option-spec
    695 //       if we have a partial-match then
    696 //          if we already had a previous partial match then
    697 //             set ambiguous = TRUE and return NULL
    698 //          else
    699 //             remember this options spec and continue matching
    700 //          end-if
    701 //       end-if
    702 //    end-for
    703 //    if we had exactly 1 partial match return it, else return NULL
    704 // ^^-------------------------------------------------------------------------
    705 const char *
    706 Options::match_longopt(const char * opt, int  len, int & ambiguous) const {
    707    kwdmatch_t  result;
    708    const char * matched = NULLSTR ;
    709 
    710    ambiguous = 0;
    711    if ((optvec == NULL) || (! *optvec))  return  NULLSTR;
    712 
    713    for (const char * const * optv = optvec ; *optv ; optv++) {
    714       OptionSpec  optspec = *optv;
    715       const char * longopt = optspec.LongOpt();
    716       if (longopt == NULL)  continue;
    717       result = kwdmatch(longopt, opt, len);
    718       if (result == EXACT_MATCH) {
    719          return  optspec;
    720       } else if (result == PARTIAL_MATCH) {
    721          if (matched) {
    722             ++ambiguous;
    723             return  NULLSTR;
    724          } else {
    725             matched = optspec;
    726          }
    727       }
    728    }//for
    729 
    730    return  matched;
    731 }
    732 
    733 // ---------------------------------------------------------------------------
    734 // ^FUNCTION: Options::parse_opt - parse an option
    735 //
    736 // ^SYNOPSIS:
    737 //    int Options::parse_opt(iter, optarg)
    738 //
    739 // ^PARAMETERS:
    740 //    OptIter & iter -- option iterator
    741 //    const char * & optarg -- where to store any option-argument
    742 //
    743 // ^DESCRIPTION:
    744 //    Parse the next option in iter (advancing as necessary).
    745 //    Make sure we update the nextchar pointer along the way. Any option
    746 //    we find should be returned and optarg should point to its argument.
    747 //
    748 // ^REQUIREMENTS:
    749 //    - nextchar must point to the prospective option character
    750 //
    751 // ^SIDE-EFFECTS:
    752 //    - iter is advanced when an argument completely parsed
    753 //    - optarg is modified to point to any option argument
    754 //    - if Options::QUIET is not set, error messages are printed on cerr
    755 //
    756 // ^RETURN-VALUE:
    757 //    'c' if the -c option was matched (optarg points to its argument)
    758 //    BADCHAR if the option is invalid (optarg points to the bad
    759 //                                                   option-character).
    760 //
    761 // ^ALGORITHM:
    762 //    It gets complicated -- follow the comments in the source.
    763 // ^^-------------------------------------------------------------------------
    764 int
    765 Options::parse_opt(OptIter & iter, const char * & optarg) {
    766    listopt = NULLSTR;  // reset the list pointer
    767 
    768    if ((optvec == NULL) || (! *optvec))  return  Options::ENDOPTS;
    769 
    770       // Try to match a known option
    771    OptionSpec optspec = match_opt(*(nextchar++), (optctrls & Options::ANYCASE));
    772 
    773       // Check for an unknown option
    774    if (optspec.isNULL()) {
    775       // See if this was a long-option in disguise
    776       if (! (optctrls & Options::NOGUESSING)) {
    777          unsigned  save_ctrls = optctrls;
    778          const char * save_nextchar = nextchar;
    779          nextchar -= 1;
    780          optctrls |= (Options::QUIET | Options::NOGUESSING);
    781          int  optchar = parse_longopt(iter, optarg);
    782          optctrls = save_ctrls;
    783          if (optchar > 0) {
    784             return  optchar;
    785          } else {
    786             nextchar = save_nextchar;
    787          }
    788       }
    789       if (! (optctrls & Options::QUIET)) {
    790          cerr << cmdname << ": unknown option -"
    791               << *(nextchar - 1) << "." << endl ;
    792       }
    793       optarg = (nextchar - 1);  // record the bad option in optarg
    794       return  Options::BADCHAR;
    795    }
    796 
    797       // If no argument is taken, then leave now
    798    if (optspec.isNoArg()) {
    799       optarg = NULLSTR;
    800       return  optspec.OptChar();
    801    }
    802 
    803       // Check for argument in this arg
    804    if (*nextchar) {
    805       optarg = nextchar; // the argument is right here
    806       nextchar = NULLSTR;   // we've exhausted this arg
    807       if (optspec.isList())  listopt = optspec ;  // save the list-spec
    808       return  optspec.OptChar();
    809    }
    810 
    811       // Check for argument in next arg
    812    const char * nextarg = iter.curr();
    813    if ((nextarg != NULL)  &&
    814        (optspec.isValRequired() || (! isOption(optctrls, nextarg)))) {
    815       optarg = nextarg;    // the argument is here
    816       iter.next();         // end of arg - advance
    817       if (optspec.isList())  listopt = optspec ;  // save the list-spec
    818       return  optspec.OptChar();
    819    }
    820 
    821      // No argument given - if its required, thats an error
    822    optarg = NULLSTR;
    823    if (optspec.isValRequired() &&  !(optctrls & Options::QUIET)) {
    824       cerr << cmdname << ": argument required for -" << optspec.OptChar()
    825            << " option." << endl ;
    826    }
    827    return  optspec.OptChar();
    828 }
    829 
    830 // ---------------------------------------------------------------------------
    831 // ^FUNCTION: Options::parse_longopt - parse a long-option
    832 //
    833 // ^SYNOPSIS:
    834 //    int Options::parse_longopt(iter, optarg)
    835 //
    836 // ^PARAMETERS:
    837 //    OptIter & iter -- option iterator
    838 //    const char * & optarg -- where to store any option-argument
    839 //
    840 // ^DESCRIPTION:
    841 //    Parse the next long-option in iter (advancing as necessary).
    842 //    Make sure we update the nextchar pointer along the way. Any option
    843 //    we find should be returned and optarg should point to its argument.
    844 //
    845 // ^REQUIREMENTS:
    846 //    - nextchar must point to the prospective option character
    847 //
    848 // ^SIDE-EFFECTS:
    849 //    - iter is advanced when an argument completely parsed
    850 //    - optarg is modified to point to any option argument
    851 //    - if Options::QUIET is not set, error messages are printed on cerr
    852 //
    853 // ^RETURN-VALUE:
    854 //    'c' if the the long-option corresponding to the -c option was matched
    855 //         (optarg points to its argument)
    856 //    BADKWD if the option is invalid (optarg points to the bad long-option
    857 //                                                                     name).
    858 //
    859 // ^ALGORITHM:
    860 //    It gets complicated -- follow the comments in the source.
    861 // ^^-------------------------------------------------------------------------
    862 int
    863 Options::parse_longopt(OptIter & iter, const char * & optarg) {
    864    int  len = 0, ambiguous = 0;
    865 
    866    listopt = NULLSTR ;  // reset the list-spec
    867 
    868    if ((optvec == NULL) || (! *optvec))  return  Options::ENDOPTS;
    869 
    870       // if a value is supplied in this argv element, get it now
    871    const char * val = strpbrk(nextchar, ":=") ;
    872    if (val) {
    873       len = val - nextchar ;
    874       ++val ;
    875    }
    876 
    877       // Try to match a known long-option
    878    OptionSpec  optspec = match_longopt(nextchar, len, ambiguous);
    879 
    880       // Check for an unknown long-option
    881    if (optspec.isNULL()) {
    882       // See if this was a short-option in disguise
    883       if ((! ambiguous) && (! (optctrls & Options::NOGUESSING))) {
    884          unsigned  save_ctrls = optctrls;
    885          const char * save_nextchar = nextchar;
    886          optctrls |= (Options::QUIET | Options::NOGUESSING);
    887          int  optchar = parse_opt(iter, optarg);
    888          optctrls = save_ctrls;
    889          if (optchar > 0) {
    890             return  optchar;
    891          } else {
    892             nextchar = save_nextchar;
    893          }
    894       }
    895       if (! (optctrls & Options::QUIET)) {
    896          cerr << cmdname << ": " << ((ambiguous) ? "ambiguous" : "unknown")
    897               << " option "
    898               << ((optctrls & Options::LONG_ONLY) ? "-" : "--")
    899               << nextchar << "." << endl ;
    900       }
    901       optarg = nextchar;  // record the bad option in optarg
    902       nextchar = NULLSTR;    // we've exhausted this argument
    903       return  (ambiguous) ? Options::AMBIGUOUS : Options::BADKWD;
    904    }
    905 
    906       // If no argument is taken, then leave now
    907    if (optspec.isNoArg()) {
    908       if ((val) && ! (optctrls & Options::QUIET)) {
    909          cerr << cmdname << ": option "
    910               << ((optctrls & Options::LONG_ONLY) ? "-" : "--")
    911               << optspec.LongOpt() << " does NOT take an argument." << endl ;
    912       }
    913       optarg = val;     // record the unexpected argument
    914       nextchar = NULLSTR;  // we've exhausted this argument
    915       return  optspec.OptChar();
    916    }
    917 
    918       // Check for argument in this arg
    919    if (val) {
    920       optarg = val;      // the argument is right here
    921       nextchar = NULLSTR;   // we exhausted the rest of this arg
    922       if (optspec.isList())  listopt = optspec ;  // save the list-spec
    923       return  optspec.OptChar();
    924    }
    925 
    926       // Check for argument in next arg
    927    const char * nextarg = iter.curr();  // find the next argument to parse
    928    if ((nextarg != NULL)  &&
    929        (optspec.isValRequired() || (! isOption(optctrls, nextarg)))) {
    930       optarg = nextarg;        // the argument is right here
    931       iter.next();             // end of arg - advance
    932       nextchar = NULLSTR;         // we exhausted the rest of this arg
    933       if (optspec.isList())  listopt = optspec ;  // save the list-spec
    934       return  optspec.OptChar();
    935    }
    936 
    937      // No argument given - if its required, thats an error
    938    optarg = NULLSTR;
    939    if (optspec.isValRequired() &&  !(optctrls & Options::QUIET)) {
    940       const char * longopt = optspec.LongOpt();
    941       const char * spc = ::strchr(longopt, ' ');
    942       int  longopt_len;
    943       if (spc) {
    944          longopt_len = spc - longopt;
    945       } else {
    946          longopt_len = ::strlen(longopt);
    947       }
    948       cerr << cmdname << ": argument required for "
    949            << ((optctrls & Options::LONG_ONLY) ? "-" : "--");
    950       cerr.write(longopt, longopt_len) << " option." << endl ;
    951    }
    952    nextchar = NULLSTR;           // we exhausted the rest of this arg
    953    return  optspec.OptChar();
    954 }
    955 
    956 // ---------------------------------------------------------------------------
    957 // ^FUNCTION: Options::usage - print usage
    958 //
    959 // ^SYNOPSIS:
    960 //    void Options::usage(os, positionals)
    961 //
    962 // ^PARAMETERS:
    963 //    ostream & os -- where to print the usage
    964 //    char * positionals -- command-line syntax for any positional args
    965 //
    966 // ^DESCRIPTION:
    967 //    Print command-usage (using either option or long-option syntax) on os.
    968 //
    969 // ^REQUIREMENTS:
    970 //    os should correspond to an open output file.
    971 //
    972 // ^SIDE-EFFECTS:
    973 //    Prints on os
    974 //
    975 // ^RETURN-VALUE:
    976 //    None.
    977 //
    978 // ^ALGORITHM:
    979 //    Print usage on os, wrapping long lines where necessary.
    980 // ^^-------------------------------------------------------------------------
    981 void
    982 Options::usage(ostream & os, const char * positionals) const {
    983 #ifdef NO_USAGE
    984    return;
    985 #else
    986    const char * const * optv = optvec;
    987    unsigned  cols = 79;
    988    int  first, nloop;
    989    char  buf[256] ;
    990 
    991    if ((optv == NULL) || (! *optv))  return;
    992 
    993       // print first portion "usage: progname"
    994    os << "usage: " << cmdname ;
    995    unsigned  ll = strlen(cmdname) + 7;
    996 
    997       // save the current length so we know how much space to skip for
    998       // subsequent lines.
    999       //
   1000    unsigned  margin = ll + 1;
   1001 
   1002       // print the options and the positional arguments
   1003    for (nloop = 0, first = 1 ; !nloop ; optv++, first = 0) {
   1004       unsigned  len;
   1005       OptionSpec   optspec = *optv;
   1006 
   1007          // figure out how wide this parameter is (for printing)
   1008       if (! *optv) {
   1009          len = strlen(positionals);
   1010          ++nloop;  // terminate this loop
   1011       } else {
   1012          if (optspec.isHiddenOpt())  continue;
   1013          len = optspec.Format(buf, optctrls);
   1014       }
   1015 
   1016       //  Will this fit?
   1017       if ((ll + len + 1) > (cols - first)) {
   1018          os << '\n' ;  // No - start a new line;
   1019 #ifdef USE_STDIO
   1020          for (int _i_ = 0; _i_ < margin; ++_i_)  os << " ";
   1021 #else
   1022          os.width(margin); os << "" ;
   1023 #endif
   1024          ll = margin;
   1025       } else {
   1026          os << ' ' ;  // Yes - just throw in a space
   1027          ++ll;
   1028       }
   1029       ll += len;
   1030       os << ((nloop) ? positionals : buf) ;
   1031    }// for each ad
   1032 
   1033    os << endl ;
   1034 #endif  /* NO_USAGE */
   1035 }
   1036 
   1037 
   1038 // ---------------------------------------------------------------------------
   1039 // ^FUNCTION: Options::operator() - get options from the command-line
   1040 //
   1041 // ^SYNOPSIS:
   1042 //   int Options::operator()(iter, optarg)
   1043 //
   1044 // ^PARAMETERS:
   1045 //    OptIter & iter -- option iterator
   1046 //    const char * & optarg -- where to store any option-argument
   1047 //
   1048 // ^DESCRIPTION:
   1049 //    Parse the next option in iter (advancing as necessary).
   1050 //    Make sure we update the nextchar pointer along the way. Any option
   1051 //    we find should be returned and optarg should point to its argument.
   1052 //
   1053 // ^REQUIREMENTS:
   1054 //    None.
   1055 //
   1056 // ^SIDE-EFFECTS:
   1057 //    - iter is advanced when an argument is completely parsed
   1058 //    - optarg is modified to point to any option argument
   1059 //    - if Options::QUIET is not set, error messages are printed on cerr
   1060 //
   1061 // ^RETURN-VALUE:
   1062 //     0 if all options have been parsed.
   1063 //    'c' if the the option or long-option corresponding to the -c option was
   1064 //         matched (optarg points to its argument).
   1065 //    BADCHAR if the option is invalid (optarg points to the bad option char).
   1066 //    BADKWD if the option is invalid (optarg points to the bad long-opt name).
   1067 //    AMBIGUOUS if an ambiguous keyword name was given (optarg points to the
   1068 //         ambiguous keyword name).
   1069 //    POSITIONAL if PARSE_POS was set and the current argument is a positional
   1070 //         parameter (in which case optarg points to the positional argument).
   1071 //
   1072 // ^ALGORITHM:
   1073 //    It gets complicated -- follow the comments in the source.
   1074 // ^^-------------------------------------------------------------------------
   1075 int
   1076 Options::operator()(OptIter & iter, const char * & optarg) {
   1077    int parse_opts_only = isOptsOnly(optctrls);
   1078    if (parse_opts_only)  explicit_end = 0;
   1079 
   1080       // See if we have an option left over from before ...
   1081    if ((nextchar) && *nextchar) {
   1082       return  parse_opt(iter, optarg);
   1083    }
   1084 
   1085       // Check for end-of-options ...
   1086    const char * arg = NULLSTR;
   1087    int get_next_arg = 0;
   1088    do {
   1089       arg = iter.curr();
   1090       get_next_arg = 0;
   1091       if (arg == NULL) {
   1092          listopt = NULLSTR;
   1093          return  Options::ENDOPTS;
   1094       } else if ((! explicit_end) && isEndOpts(arg)) {
   1095          iter.next();   // advance past end-of-options arg
   1096          listopt = NULLSTR;
   1097          explicit_end = 1;
   1098          if (parse_opts_only)  return  Options::ENDOPTS;
   1099          get_next_arg = 1;  // make sure we look at the next argument.
   1100       }
   1101    } while (get_next_arg);
   1102 
   1103       // Do we have a positional arg?
   1104    if ( explicit_end || (! isOption(optctrls, arg)) ) {
   1105       if (parse_opts_only) {
   1106          return  Options::ENDOPTS;
   1107       } else {
   1108          optarg = arg;  // set optarg to the positional argument
   1109          iter.next();   // advance iterator to the next argument
   1110          return  Options::POSITIONAL;
   1111       }
   1112    }
   1113 
   1114    iter.next();  // pass the argument that arg already points to
   1115 
   1116       // See if we have a long option ...
   1117    if (! (optctrls & Options::SHORT_ONLY)) {
   1118       if ((*arg == '-') && (arg[1] == '-')) {
   1119          nextchar = arg + 2;
   1120          return  parse_longopt(iter, optarg);
   1121       } else if ((optctrls & Options::PLUS) && (*arg == '+')) {
   1122          nextchar = arg + 1;
   1123          return  parse_longopt(iter, optarg);
   1124       }
   1125    }
   1126    if (*arg == '-') {
   1127       nextchar = arg + 1;
   1128       if (optctrls & Options::LONG_ONLY) {
   1129          return  parse_longopt(iter, optarg);
   1130       } else {
   1131          return  parse_opt(iter, optarg);
   1132       }
   1133    }
   1134 
   1135       // If we get here - it is because we have a list value
   1136    OptionSpec  optspec = listopt;
   1137    optarg = arg ;        // record the list value
   1138    return  optspec.OptChar() ;
   1139 }
   1140 
   1141