[161] | 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 */ |
---|