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