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