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