source: trunk/MFCtooling/replay/options.h @ 357

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