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 // 04/30/06 Chris Reed 22 // - Updated to modern C++ and STL 23 // - Converted comments to doxygen style 24 // ^^************************************************************************** 25 26 #ifndef _options_h 27 #define _options_h 28 29 #ifdef USE_STDIO 30 #include <stdio.h> 31 #else 32 #include <iostream> 33 #endif 34 35 36 //! Abstract class to iterate through options/arguments 37 //! 38 class OptIter { 39 public: OptIter(void)40 OptIter(void) {} 41 virtual ~OptIter(void); 42 43 //! curr() returns the current item in the iterator without 44 //! advancing on to the next item. If we are at the end of items 45 //! then NULL is returned. 46 virtual const char * 47 curr(void) = 0; 48 49 //! next() advances to the next item. 50 virtual void 51 next(void) = 0; 52 53 //! operator() returns the current item in the iterator and then 54 //! advances on to the next item. If we are at the end of items 55 //! then NULL is returned. 56 virtual const char * 57 operator()(void); 58 } ; 59 60 //! Abstract class for a rewindable OptIter 61 //! 62 class OptIterRwd : public OptIter { 63 public: 64 OptIterRwd(void); 65 66 virtual ~OptIterRwd(void); 67 68 virtual const char * 69 curr(void) = 0; 70 71 virtual void 72 next(void) = 0; 73 74 virtual const char * 75 operator()(void) = 0; 76 77 //! rewind() resets the "current-element" to the first one in the "list" 78 virtual void 79 rewind(void) = 0; 80 } ; 81 82 //! Class to iterate through an array of tokens. The array may be terminated 83 //! by NULL or a count containing the number of tokens may be given. 84 //! 85 class OptArgvIter : public OptIterRwd { 86 private: 87 int ndx; // index of current arg 88 int ac; // arg count 89 const char * const * av; // arg vector 90 91 public: OptArgvIter(const char * const argv[])92 OptArgvIter(const char * const argv[]) 93 : av(argv), ac(-1), ndx(0) {} 94 OptArgvIter(int argc,const char * const argv[])95 OptArgvIter(int argc, const char * const argv[]) 96 : av(argv), ac(argc), ndx(0) {} 97 98 virtual 99 ~OptArgvIter(void); 100 101 virtual const char * 102 curr(void); 103 104 virtual void 105 next(void); 106 107 virtual const char * 108 operator()(void); 109 110 virtual void 111 rewind(void); 112 113 //! index returns the current index to use for argv[] index(void)114 int index(void) { return ndx; } 115 } ; 116 117 118 //! Class to iterate through a string containing delimiter-separated tokens 119 //! 120 class OptStrTokIter : public OptIterRwd { 121 private: 122 unsigned len; // length of token-string 123 const char * str; // the token-string 124 const char * seps; // delimiter-set (separator-characters) 125 const char * cur; // current token 126 char * tokstr; // our copy of the token-string 127 128 static const char * default_delims; // default delimiters = whitespace 129 130 public: 131 OptStrTokIter(const char * tokens, const char * delimiters =0); 132 133 virtual 134 ~OptStrTokIter(void); 135 136 virtual const char * 137 curr(void); 138 139 virtual void 140 next(void); 141 142 virtual const char * 143 operator()(void); 144 145 virtual void 146 rewind(void); 147 148 //! delimiters() with NO arguments returns the current set of delimiters, 149 //! If an argument is given then it is used as the new set of delimiters. 150 const char * delimiters(void)151 delimiters(void) { return seps; } 152 153 void delimiters(const char * delims)154 delimiters(const char * delims) { 155 seps = (delims) ? delims : default_delims ; 156 } 157 } ; 158 159 160 //! OptIstreamIter is a class for iterating over arguments that come 161 //! from an input stream. Each line of the input stream is considered 162 //! to be a set of white-space separated tokens. If the the first 163 //! non-white character on a line is '#' ('!' for VMS systems) then 164 //! the line is considered a comment and is ignored. 165 //! 166 //! \note If a line is more than 1022 characters in length then we 167 //! treat it as if it were several lines of length 1022 or less. 168 //! 169 //! \note The string tokens returned by this iterator are pointers 170 //! to temporary buffers which may not necessarily stick around 171 //! for too long after the call to curr() or operator(), hence 172 //! if you need the string value to persist - you will need to 173 //! make a copy. 174 //! 175 class OptIstreamIter : public OptIter { 176 private: 177 std::istream & is ; 178 OptStrTokIter * tok_iter ; 179 180 void 181 fill(void); 182 183 public: 184 static const unsigned MAX_LINE_LEN ; 185 186 OptIstreamIter(std::istream & input); 187 188 virtual 189 ~OptIstreamIter(void); 190 191 virtual const char * 192 curr(void); 193 194 virtual void 195 next(void); 196 197 virtual const char * 198 operator()(void); 199 } ; 200 201 202 //! \brief parse command-line options 203 //! 204 //! \section Synopsis 205 //! \code 206 //! #include <options.h> 207 //! 208 //! Options opts(cmdname, optv); 209 //! char cmdname[], *optv[]; 210 //! \endcode 211 //! \section Description 212 //! The Options constructor expects a command-name (usually argv[0]) and 213 //! a pointer to an array of strings. The last element in this array MUST 214 //! be NULL. Each non-NULL string in the array must have the following format: 215 //! 216 //! The 1st character must be the option-name ('c' for a -c option). 217 //! 218 //! The 2nd character must be one of '|', '?', ':', '*', or '+'. 219 //! '|' -- indicates that the option takes NO argument; 220 //! '?' -- indicates that the option takes an OPTIONAL argument; 221 //! ':' -- indicates that the option takes a REQUIRED argument; 222 //! '*' -- indicates that the option takes 0 or more arguments; 223 //! '+' -- indicates that the option takes 1 or more arguments; 224 //! 225 //! The remainder of the string must be the long-option name. 226 //! 227 //! If desired, the long-option name may be followed by one or more 228 //! spaces and then by the name of the option value. This name will 229 //! be used when printing usage messages. If the option-value-name 230 //! is not given then the string "<value>" will be used in usage 231 //! messages. 232 //! 233 //! One may use a space to indicate that a particular option does not 234 //! have a corresponding long-option. For example, "c: " (or "c:") 235 //! means the -c option takes a value & has NO corresponding long-option. 236 //! 237 //! To specify a long-option that has no corresponding single-character 238 //! option is a bit trickier: Options::operator() still needs an "option- 239 //! character" to return when that option is matched. One may use a whitespace 240 //! character or a non-printable character as the single-character option 241 //! in such a case. (hence " |hello" would only match "--hello"). 242 //! 243 //! \section Exceptions Exceptions to the above 244 //! If the 1st character of the string is '-', then the rest of the 245 //! string must correspond to the above format, and the option is 246 //! considered to be a hidden-option. This means it will be parsed 247 //! when actually matching options from the command-line, but will 248 //! NOT show-up if a usage message is printed using the usage() member 249 //! function. Such an example might be "-h|hidden". If you want to 250 //! use any "dummy" options (options that are not parsed, but that 251 //! to show up in the usage message), you can specify them along with 252 //! any positional parameters to the usage() member function. 253 //! 254 //! If the 2nd character of the string is '\0' then it is assumed 255 //! that there is no corresponding long-option and that the option 256 //! takes no argument (hence "f", and "f| " are equivalent). 257 //! 258 //! \code 259 //! const char * optv[] = { 260 //! "c:count <number>", 261 //! "s?str <string>", 262 //! "x", 263 //! " |hello", 264 //! "g+groups <newsgroup>", 265 //! NULL 266 //! } ; 267 //! \endcode 268 //! optv[] now corresponds to the following: 269 //! 270 //! usage: cmdname [-c|--count <number>] [-s|--str [<string>]] 271 //! [-x] [--hello] [-g|--groups <newsgroup> ...] 272 //! 273 //! Long-option names are matched case-insensitive and only a unique prefix 274 //! of the name needs to be specified. 275 //! 276 //! Option-name characters are case-sensitive! 277 //! 278 //! \section Caveat 279 //! Because of the way in which multi-valued options and options with optional 280 //! values are handled, it is NOT possible to supply a value to an option in 281 //! a separate argument (different argv[] element) if the value is OPTIONAL 282 //! and begins with a '-'. What this means is that if an option "-s" takes an 283 //! optional value value and you wish to supply a value of "-foo" then you must 284 //! specify this on the command-line as "-s-foo" instead of "-s -foo" because 285 //! "-s -foo" will be considered to be two separate sets of options. 286 //! 287 //! A multi-valued option is terminated by another option or by the end-of 288 //! options. The following are all equivalent (if "-l" is a multi-valued 289 //! option and "-x" is an option that takes no value): 290 //! 291 //! cmdname -x -l item1 item2 item3 -- arg1 arg2 arg3 292 //! cmdname -x -litem1 -litem2 -litem3 -- arg1 arg2 arg3 293 //! cmdname -l item1 item2 item3 -x arg1 arg2 arg3 294 //! 295 //! 296 //! \code 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 //! \endcode 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 * name(void)430 name(void) const { return cmdname; } 431 432 //! ctrls() (with no arguments) returns the existing control settings 433 unsigned ctrls(void)434 ctrls(void) const { return optctrls; } 435 436 //! ctrls() (with 1 argument) sets new control settings 437 void ctrls(unsigned newctrls)438 ctrls(unsigned newctrls) { optctrls = newctrls; } 439 440 //! reset for another pass to parse for options 441 void reset(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 explicit_endopts()485 explicit_endopts() const { return explicit_end; } 486 } ; 487 488 #endif /* _options_h */ 489