cfad47cfa3/src/options.h

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
1
/* This file defines classes used to parse command-line options.
2
 * Options may be parsed from an array of strings, or from any structure
3
 * for which a corresponding option-iterator exists.
4
 *
5
 * This file is part of Brad Appleton's "Options" library, slightly
6
 * modified to be more portable.  Everything in this file is subject to
7
 * the following legal blurb:
8
 *
9
 *	Copyright (C) Brad Appleton.
10
 *
11
 *	AUTHOR
12
 *	======
13
 *	Brad Appleton, Software Tools Developer
14
 *	mailto:bradapp@enteract.com
15
 *	http://www.enteract.com/~bradapp
16
 *
17
 *	COPY/REUSE POLICY
18
 *	=================
19
 *	Permission is hereby granted to freely copy and redistribute
20
 *	this software, provided that the author is clearly credited in
21
 *	all copies and derivations. Neither the names of the authors nor
22
 *	that of their employers may be used to endorse or promote
23
 *	products derived from this software without specific written
24
 *	permission.
25
 *
26
 *	DISCLAIMER
27
 *	==========
28
 *	This software is provided "As Is" and without any express or
29
 *	implied warranties.  Neither the authors nor any of their
30
 *	employers (including any of their subsidiaries and subdivisions)
31
 *	are responsible for maintaining or supporting this software or
32
 *	for any consequences resulting from the use of this software, no
33
 *	matter how awful, even if they arise from flaws in the software.
34
 */
35
#ifndef OPTIONS_H
36
#define OPTIONS_H
37
38
class  istream;
39
40
class  ostream {
41
public:
42
   ostream(FILE * fileptr) : fp(fileptr) {}
43
44
   ostream &
45
   operator<<(char ch);
46
47
   ostream &
48
   operator<<(const char * str);
49
50
   ostream &
51
   write(const char * buf, unsigned bufsize);
52
53
private:
54
   FILE * fp;
55
} ;
56
57
// Abstract class to iterate through options/arguments
58
//
59
class OptIter {
60
public:
61
   OptIter(void) {}
62
   virtual ~OptIter(void);
63
64
      // curr() returns the current item in the iterator without
65
      // advancing on to the next item. If we are at the end of items
66
      // then NULL is returned.
67
   virtual const char *
68
   curr(void) = 0;
69
70
      // next() advances to the next item.
71
   virtual void
72
   next(void) = 0;
73
74
      // operator() returns the current item in the iterator and then
75
      // advances on to the next item. If we are at the end of items
76
      // then NULL is returned.
77
   virtual const char *
78
   operator()(void);
79
} ;
80
81
// Abstract class for a rewindable OptIter
82
//
83
class OptIterRwd : public OptIter {
84
public:
85
   OptIterRwd(void);
86
87
   virtual ~OptIterRwd(void);
88
89
   virtual const char *
90
   curr(void) = 0;
91
92
   virtual void
93
   next(void) = 0;
94
95
   virtual const char *
96
   operator()(void) = 0;
97
98
      // rewind() resets the "current-element" to the first one in the "list"
99
   virtual void
100
   rewind(void) = 0;
101
} ;
102
103
// Class to iterate through an array of tokens. The array may be terminated
104
// by NULL or a count containing the number of tokens may be given.
105
//
106
class OptArgvIter : public OptIterRwd {
107
private:
108
   int            ndx;   // index of current arg
109
   int            ac;    // arg count
110
   const char * const * av;  // arg vector
111
112
public:
113
   OptArgvIter(const char * const argv[])
114
      : ndx(0), ac(-1), av(argv) {}
115
116
   OptArgvIter(int argc, const char * const argv[])
117
      : ndx(0), ac(argc), av(argv) {}
118
119
   virtual
120
   ~OptArgvIter(void);
121
122
   virtual const char *
123
   curr(void);
124
125
   virtual void
126
   next(void);
127
128
   virtual const char *
129
   operator()(void);
130
131
   virtual void
132
   rewind(void);
133
134
      // index returns the current index to use for argv[]
135
   int index(void)  { return  ndx; }
136
} ;
137
138
139
// Class to iterate through a string containing delimiter-separated tokens
140
//
141
class OptStrTokIter : public OptIterRwd {
142
private:
143
   unsigned     len;        // length of token-string
144
   const char * str;        // the token-string
145
   const char * seps;       // delimiter-set (separator-characters)
146
   const char * cur;        // current token
147
   char       * tokstr;     // our copy of the token-string
148
149
   static const char * default_delims;  // default delimiters = whitespace
150
151
public:
152
   OptStrTokIter(const char * tokens, const char * delimiters =0);
153
154
   virtual
155
   ~OptStrTokIter(void);
156
157
   virtual const char *
158
   curr(void);
159
160
   virtual void
161
   next(void);
162
163
   virtual const char *
164
   operator()(void);
165
166
   virtual void
167
   rewind(void);
168
169
      // delimiters() with NO arguments returns the current set of delimiters,
170
      // If an argument is given then it is used as the new set of delimiters.
171
   const char *
172
   delimiters(void)  { return  seps; }
173
174
   void
175
   delimiters(const char * delims) {
176
      seps = (delims) ? delims : default_delims ;
177
   }
178
} ;
179
180
181
   // OptIstreamIter is a class for iterating over arguments that come
182
   // from an input stream. Each line of the input stream is considered
183
   // to be a set of white-space separated tokens. If the the first
184
   // non-white character on a line is '#' ('!' for VMS systems) then
185
   // the line is considered a comment and is ignored.
186
   //
187
   // *Note:: If a line is more than 1022 characters in length then we
188
   // treat it as if it were several lines of length 1022 or less.
189
   //
190
   // *Note:: The string tokens returned by this iterator are pointers
191
   //         to temporary buffers which may not necessarily stick around
192
   //         for too long after the call to curr() or operator(), hence
193
   //         if you need the string value to persist - you will need to
194
   //         make a copy.
195
   //
196
class OptIstreamIter : public OptIter {
197
private:
198
   istream & is ;
199
   OptStrTokIter * tok_iter ;
200
201
   void
202
   fill(void);
203
204
public:
205
   static const unsigned  MAX_LINE_LEN ;
206
207
   OptIstreamIter(istream & input);
208
209
   virtual
210
   ~OptIstreamIter(void);
211
212
   virtual const char *
213
   curr(void);
214
215
   virtual void
216
   next(void);
217
218
   virtual const char *
219
   operator()(void);
220
} ;
221
222
223
// Now we are ready to define a class to declare and parse command-options
224
//
225
//
226
// CLASS
227
// =====
228
// Options  -- parse command-line options
229
//
230
// SYNOPSIS
231
// ========
232
//   #include <options.h>
233
//
234
//   Options opts(cmdname, optv);
235
//   char cmdname[], *optv[];
236
//
237
// DESCRIPTION
238
// ===========
239
// The Options constructor expects a command-name (usually argv[0]) and
240
// a pointer to an array of strings.  The last element in this array MUST
241
// be NULL. Each non-NULL string in the array must have the following format:
242
//
243
//   The 1st character must be the option-name ('c' for a -c option).
244
//
245
//   The 2nd character must be one of '|', '?', ':', '*', or '+'.
246
//      '|' -- indicates that the option takes NO argument;
247
//      '?' -- indicates that the option takes an OPTIONAL argument;
248
//      ':' -- indicates that the option takes a REQUIRED argument;
249
//      '*' -- indicates that the option takes 0 or more arguments;
250
//      '+' -- indicates that the option takes 1 or more arguments;
251
//
252
//   The remainder of the string must be the long-option name.
253
//
254
//   If desired, the long-option name may be followed by one or more
255
//   spaces and then by the name of the option value. This name will
256
//   be used when printing usage messages. If the option-value-name
257
//   is not given then the string "<value>" will be used in usage
258
//   messages.
259
//
260
//   One may use a space to indicate that a particular option does not
261
//   have a corresponding long-option.  For example, "c: " (or "c:")
262
//   means the -c option takes a value & has NO corresponding long-option.
263
//
264
//   To specify a long-option that has no corresponding single-character
265
//   option is a bit trickier: Options::operator() still needs an "option-
266
//   character" to return when that option is matched. One may use a whitespace
267
//   character or a non-printable character as the single-character option
268
//   in such a case. (hence " |hello" would only match "--hello").
269
//
270
//   EXCEPTIONS TO THE ABOVE:
271
//   ------------------------
272
//   If the 1st character of the string is '-', then the rest of the
273
//   string must correspond to the above format, and the option is
274
//   considered to be a hidden-option. This means it will be parsed
275
//   when actually matching options from the command-line, but will
276
//   NOT show-up if a usage message is printed using the usage() member
277
//   function. Such an example might be "-h|hidden". If you want to
278
//   use any "dummy" options (options that are not parsed, but that
279
//   to show up in the usage message), you can specify them along with
280
//   any positional parameters to the usage() member function.
281
//
282
//   If the 2nd character of the string is '\0' then it is assumed
283
//   that there is no corresponding long-option and that the option
284
//   takes no argument (hence "f", and "f| " are equivalent).
285
//
286
//   Examples:
287
//      const char * optv[] = {
288
//          "c:count   <number>",
289
//          "s?str     <string>",
290
//          "x",
291
//          " |hello",
292
//          "g+groups  <newsgroup>",
293
//          NULL
294
//      } ;
295
//
296
//      optv[] now corresponds to the following:
297
//
298
//            usage: cmdname [-c|--count <number>] [-s|--str [<string>]]
299
//                           [-x] [--hello] [-g|--groups <newsgroup> ...]
300
//
301
// Long-option names are matched case-insensitive and only a unique prefix
302
// of the name needs to be specified.
303
//
304
// Option-name characters are case-sensitive!
305
//
306
// CAVEAT
307
// ======
308
// Because of the way in which multi-valued options and options with optional
309
// values are handled, it is NOT possible to supply a value to an option in
310
// a separate argument (different argv[] element) if the value is OPTIONAL
311
// and begins with a '-'. What this means is that if an option "-s" takes an
312
// optional value value and you wish to supply a value of "-foo" then you must
313
// specify this on the command-line as "-s-foo" instead of "-s -foo" because
314
// "-s -foo" will be considered to be two separate sets of options.
315
//
316
// A multi-valued option is terminated by another option or by the end-of
317
// options. The following are all equivalent (if "-l" is a multi-valued
318
// option and "-x" is an option that takes no value):
319
//
320
//    cmdname -x -l item1 item2 item3 -- arg1 arg2 arg3
321
//    cmdname -x -litem1 -litem2 -litem3 -- arg1 arg2 arg3
322
//    cmdname -l item1 item2 item3 -x arg1 arg2 arg3
323
//
324
//
325
// EXAMPLE
326
// =======
327
//    #include <options.h>
328
//
329
//    static const char * optv[] = {
330
//       "H|help",
331
//       "c:count   <number>",
332
//       "s?str     <string>",
333
//       "x",
334
//       " |hello",
335
//       "g+groups  <newsgroup>",
336
//       NULL
337
//    } ;
338
//
339
//    main(int argc, char * argv[]) {
340
//       int  optchar;
341
//       const char * optarg;
342
//       const char * str = "default_string";
343
//       int  count = 0, xflag = 0, hello = 0;
344
//       int  errors = 0, ngroups = 0;
345
//
346
//       Options  opts(*argv, optv);
347
//       OptArgvIter  iter(--argc, ++argv);
348
//
349
//       while( optchar = opts(iter, optarg) ) {
350
//          switch (optchar) {
351
//          case 'H' :
352
//             opts.usage(cout, "files ...");
353
//             exit(0);
354
//             break;
355
//          case 'g' :
356
//             ++ngroups; break;  // the groupname is in "optarg"
357
//          case 's' :
358
//             str = optarg; break;
359
//          case 'x' :
360
//             ++xflag; break;
361
//          case ' ' :
362
//             ++hello; break;
363
//          case 'c' :
364
//             if (optarg == NULL)  ++errors;
365
//             else  count = (int) atol(optarg);
366
//             break;
367
//          default :  ++errors; break;
368
//          } //switch
369
//       }
370
//
371
//       if (errors || (iter.index() == argc)) {
372
//          if (! errors) {
373
//             cerr << opts.name() << ": no filenames given." << endl ;
374
//          }
375
//          opts.usage(cerr, "files ...");
376
//          exit(1);
377
//       }
378
//
379
//       cout << "xflag=" << ((xflag) ? "ON"  : "OFF") << endl
380
//            << "hello=" << ((hello) ? "YES" : "NO") << endl
381
//            << "count=" << count << endl
382
//            << "str=\"" << ((str) ? str : "No value given!") << "\"" << endl
383
//            << "ngroups=" << ngroups << endl ;
384
//
385
//       if (iter.index() < argc) {
386
//          cout << "files=" ;
387
//          for (int i = iter.index() ; i < argc ; i++) {
388
//             cout << "\"" << argv[i] << "\" " ;
389
//          }
390
//          cout << endl ;
391
//       }
392
//    }
393
//
394
class Options {
395
private:
396
   unsigned       explicit_end : 1;  // were we terminated because of "--"?
397
   unsigned       optctrls : 7;  // control settings (a set of OptCtrl masks)
398
   const char  * const * optvec; // vector of option-specifications (last=NULL)
399
   const char   * nextchar;      // next option-character to process
400
   const char   * listopt;       // last list-option we matched
401
   const char   * cmdname;       // name of the command
402
403
   void
404
   check_syntax(void) const;
405
406
   const char *
407
   match_opt(char opt, int ignore_case =0) const;
408
409
   const char *
410
   match_longopt(const char * opt, int  len, int & ambiguous) const;
411
412
   int
413
   parse_opt(OptIter & iter, const char * & optarg);
414
415
   int
416
   parse_longopt(OptIter & iter, const char * & optarg);
417
418
public:
419
   enum OptCtrl {
420
      DEFAULT    = 0x00,  // Default setting
421
      ANYCASE    = 0x01,  // Ignore case when matching short-options
422
      QUIET      = 0x02,  // Dont print error messages
423
      PLUS       = 0x04,  // Allow "+" as a long-option prefix
424
      SHORT_ONLY = 0x08,  // Dont accept long-options
425
      LONG_ONLY  = 0x10,  // Dont accept short-options
426
                            // (also allows "-" as a long-option prefix).
427
      NOGUESSING = 0x20,  // Normally, when we see a short (long) option
428
                            // on the command line that doesnt match any
429
                            // known short (long) options, then we try to
430
                            // "guess" by seeing if it will match any known
431
                            // long (short) option. Setting this mask prevents
432
                            // this "guessing" from occurring.
433
      PARSE_POS = 0x40    // By default, Options will not present positional
434
                            // command-line arguments to the user and will
435
                            // instead stop parsing when the first positonal
436
                            // argument has been encountered. If this flag
437
                            // is given, Options will present positional
438
                            // arguments to the user with a return code of
439
                            // POSITIONAL; ENDOPTS will be returned only
440
                            // when the end of the argument list is reached.
441
   } ;
442
443
      // Error return values for operator()
444
      //
445
   enum OptRC {
446
      ENDOPTS    =  0,
447
      BADCHAR    = -1,
448
      BADKWD     = -2,
449
      AMBIGUOUS  = -3,
450
      POSITIONAL = -4
451
   } ;
452
453
   Options(const char * name, const char * const optv[]);
454
455
   virtual
456
   ~Options(void);
457
458
      // name() returns the command name
459
   const char *
460
   name(void) const { return  cmdname; }
461
462
      // ctrls() (with no arguments) returns the existing control settings
463
   unsigned
464
   ctrls(void) const { return  optctrls; }
465
466
      // ctrls() (with 1 argument) sets new control settings
467
   void
468
   ctrls(unsigned newctrls) { optctrls = newctrls; }
469
470
      // reset for another pass to parse for options
471
   void
472
   reset(void) { nextchar = listopt = NULL; }
473
474
      // usage() prints options usage (followed by any positional arguments
475
      // listed in the parameter "positionals") on the given outstream
476
   void
477
   usage(ostream & os, const char * positionals) const ;
478
479
      // operator() iterates through the arguments as necessary (using the
480
      // given iterator) and returns the character value of the option
481
      // (or long-option) that it matched. If the option has a value
482
      // then the value given may be found in optarg (otherwise optarg
483
      // will be NULL).
484
      //
485
      // 0 is returned upon end-of-options. At this point, "iter" may
486
      // be used to process any remaining positional parameters. If the
487
      // PARSE_POS control-flag is set then 0 is returned only when all
488
      // arguments in "iter" have been exhausted.
489
      //
490
      // If an invalid option is found then BADCHAR is returned and *optarg
491
      // is the unrecognized option character.
492
      //
493
      // If an invalid long-option is found then BADKWD is returned and optarg
494
      // points to the bad long-option.
495
      //
496
      // If an ambiguous long-option is found then AMBIGUOUS is returned and
497
      // optarg points to the ambiguous long-option.
498
      //
499
      // If the PARSE_POS control-flag is set then POSITIONAL is returned
500
      // when a positional argument is encountered and optarg points to
501
      // the positonal argument (and "iter" is advanced to the next argument
502
      // in the iterator).
503
      //
504
      // Unless Options::QUIET is used, missing option-arguments and
505
      // invalid options (and the like) will automatically cause error
506
      // messages to be issued to cerr.
507
   int
508
   operator()(OptIter & iter, const char * & optarg) ;
509
510
      // Call this member function after operator() has returned 0
511
      // if you want to know whether or not options were explicitly
512
      // terminated because "--" appeared on the command-line.
513
      //
514
   int
515
   explicit_endopts() const { return  explicit_end; }
516
} ;
517
518
#endif /* OPTIONS_H */