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 | // |
---|
29 | class OptIter { |
---|
30 | public: |
---|
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 | // |
---|
53 | class OptIterRwd : public OptIter { |
---|
54 | public: |
---|
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 | // |
---|
76 | class OptArgvIter : public OptIterRwd { |
---|
77 | private: |
---|
78 | int ndx; // index of current arg |
---|
79 | int ac; // arg count |
---|
80 | const char * const * av; // arg vector |
---|
81 | |
---|
82 | public: |
---|
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 | // |
---|
111 | class OptStrTokIter : public OptIterRwd { |
---|
112 | private: |
---|
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 | |
---|
121 | public: |
---|
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 | // |
---|
166 | class OptIstreamIter : public OptIter { |
---|
167 | private: |
---|
168 | std::istream & is ; |
---|
169 | OptStrTokIter * tok_iter ; |
---|
170 | |
---|
171 | void |
---|
172 | fill(void); |
---|
173 | |
---|
174 | public: |
---|
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 | // |
---|
364 | class Options { |
---|
365 | private: |
---|
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 | |
---|
388 | public: |
---|
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 */ |
---|