1 // **************************************************************************** 2 // ^FILE: options.c - implement the functions defined in <options.h> 3 // 4 // ^HISTORY: 5 // 01/16/92 Brad Appleton <bradapp@enteract.com> Created 6 // 7 // 03/23/93 Brad Appleton <bradapp@enteract.com> 8 // - Added OptIstreamIter class 9 // 10 // 10/08/93 Brad Appleton <bradapp@enteract.com> 11 // - Added "hidden" options 12 // 13 // 02/08/94 Brad Appleton <bradapp@enteract.com> 14 // - Added "OptionSpec" class 15 // - Permitted use of stdio instead of iostreams via #ifdef USE_STDIO 16 // 17 // 03/08/94 Brad Appleton <bradapp@enteract.com> 18 // - completed support for USE_STDIO 19 // - added #ifdef NO_USAGE for people who always want to print their own 20 // - Fixed stupid NULL pointer error in OptionsSpec class 21 // 22 // 07/31/97 Brad Appleton <bradapp@enteract.com> 23 // - Added PARSE_POS control flag and POSITIONAL return value. 24 // ^^************************************************************************** 25 26 // #include <stdlib.h> 27 #include <ctype.h> 28 #include <string.h> 29 30 #include "options.h" 31 32 using namespace std; 33 34 extern "C" { 35 void exit(int); 36 } 37 38 static const char ident[] = "@(#)Options 1.05" ; 39 40 // I need a portable version of "tolower" that does NOT modify 41 // non-uppercase characters. 42 // 43 #define TOLOWER(c) (isupper(c) ? tolower(c) : c) 44 45 // Use this to shut the compiler up about NULL strings 46 #define NULLSTR (char *)NULL 47 48 // ******************************************************** insertion operators 49 50 // If you are using <stdio.h> then you need this stuff! 51 // If you are using <iostream.h> then #ifdef this stuff out 52 // 53 54 55 #ifdef USE_STDIO 56 57 // Implement just enough of ostream to get this file to compile 58 // 59 60 static const char endl = '\n' ; 61 62 class ostream { 63 public: 64 ostream(FILE * fileptr) : fp(fileptr) {} 65 66 ostream & 67 operator<<(char ch); 68 69 ostream & 70 operator<<(const char * str); 71 72 ostream & 73 write(const char * buf, unsigned bufsize); 74 75 private: 76 FILE * fp; 77 } ; 78 79 ostream & 80 ostream::operator<<(char ch) { 81 fputc(ch, fp); 82 return *this; 83 } 84 85 ostream & 86 ostream::operator<<(const char * str) { 87 fputs(str, fp); 88 return *this; 89 } 90 91 ostream & 92 ostream::write(const char * buf, unsigned ) { 93 fputs(buf, fp); 94 return *this; 95 } 96 97 static ostream cerr(stderr); 98 static ostream cout(stdout); 99 100 #endif /* USE_STDIO */ 101 102 // ************************************************************** OptIter 103 104 OptIter::~OptIter(void) {} 105 106 const char * 107 OptIter::operator()(void) { 108 const char * elt = curr(); 109 (void) next(); 110 return elt; 111 } 112 113 // ************************************************************** OptIterRwd 114 115 OptIterRwd::OptIterRwd(void) {} 116 117 OptIterRwd::~OptIterRwd(void) {} 118 119 // ************************************************************** OptArgvIter 120 121 OptArgvIter::~OptArgvIter(void) {} 122 123 const char * 124 OptArgvIter::curr(void) { 125 return ((ndx == ac) || (av[ndx] == NULL)) ? NULLSTR : av[ndx]; 126 } 127 128 void 129 OptArgvIter::next(void) { 130 if ((ndx != ac) && av[ndx]) ++ndx; 131 } 132 133 const char * 134 OptArgvIter::operator()(void) { 135 return ((ndx == ac) || (av[ndx] == NULL)) ? NULLSTR : av[ndx++]; 136 } 137 138 void 139 OptArgvIter::rewind(void) { ndx = 0; } 140 141 // ************************************************************** OptStrTokIter 142 143 static const char WHITESPACE[] = " \t\n\r\v\f" ; 144 const char * OptStrTokIter::default_delims = WHITESPACE ; 145 146 OptStrTokIter::OptStrTokIter(const char * tokens, const char * delimiters) 147 : len(unsigned(strlen(tokens))), str(tokens), seps(delimiters), 148 cur(NULLSTR), tokstr(NULLSTR) 149 { 150 if (seps == NULL) seps = default_delims; 151 tokstr = new char[len + 1]; 152 (void) ::strcpy(tokstr, str); 153 cur = ::strtok(tokstr, seps); 154 } 155 156 157 OptStrTokIter::~OptStrTokIter(void) { delete [] tokstr; } 158 159 const char * 160 OptStrTokIter::curr(void) { return cur; } 161 162 void 163 OptStrTokIter::next(void) { if (cur) cur = ::strtok(NULL, seps); } 164 165 const char * 166 OptStrTokIter::operator()(void) { 167 const char * elt = cur; 168 if (cur) cur = ::strtok(NULL, seps); 169 return elt; 170 } 171 172 void 173 OptStrTokIter::rewind(void) { 174 (void) ::strcpy(tokstr, str); 175 cur = ::strtok(tokstr, seps); 176 } 177 178 // ************************************************************* OptIstreamIter 179 180 #ifdef vms 181 enum { c_COMMENT = '!' } ; 182 #else 183 enum { c_COMMENT = '#' } ; 184 #endif 185 186 const unsigned OptIstreamIter::MAX_LINE_LEN = 1024 ; 187 188 // Constructor 189 OptIstreamIter::OptIstreamIter(istream & input) : is(input), tok_iter(NULL) 190 { 191 #ifdef USE_STDIO 192 fprintf(stderr, "%s: Can't use OptIstreamIter class:\n", 193 "OptIstreamIter::OptIstreamIter"); 194 fprintf(stderr, "\tOptions(3C++) was compiled with USE_STDIO #defined.\n"); 195 exit(-1); 196 #endif /* USE_STDIO */ 197 } 198 199 // Destructor 200 OptIstreamIter::~OptIstreamIter(void) { 201 delete tok_iter; 202 } 203 204 const char * 205 OptIstreamIter::curr(void) { 206 #ifdef USE_STDIO 207 return NULLSTR; 208 #else 209 const char * result = NULLSTR; 210 if (tok_iter) result = tok_iter->curr(); 211 if (result) return result; 212 fill(); 213 return (! is) ? NULLSTR : tok_iter->curr(); 214 #endif /* USE_STDIO */ 215 } 216 217 void 218 OptIstreamIter::next(void) { 219 #ifdef USE_STDIO 220 return; 221 #else 222 const char * result = NULLSTR; 223 if (tok_iter) result = tok_iter->operator()(); 224 if (result) return; 225 fill(); 226 if (! is) tok_iter->next(); 227 #endif /* USE_STDIO */ 228 } 229 230 const char * 231 OptIstreamIter::operator()(void) { 232 #ifdef USE_STDIO 233 return NULLSTR; 234 #else 235 const char * result = NULLSTR; 236 if (tok_iter) result = tok_iter->operator()(); 237 if (result) return result; 238 fill(); 239 return (! is) ? NULLSTR : tok_iter->operator()(); 240 #endif /* USE_STDIO */ 241 } 242 243 // What we do is this: for each line of text in the istream, we use 244 // a OptStrTokIter to iterate over each token on the line. 245 // 246 // If the first non-white character on a line is c_COMMENT, then we 247 // consider the line to be a comment and we ignore it. 248 // 249 void 250 OptIstreamIter::fill(void) { 251 #ifdef USE_STDIO 252 return; 253 #else 254 char buf[OptIstreamIter::MAX_LINE_LEN]; 255 do { 256 *buf = '\0'; 257 is.getline(buf, sizeof(buf)); 258 char * ptr = buf; 259 while (isspace(*ptr)) ++ptr; 260 if (*ptr && (*ptr != c_COMMENT)) { 261 delete tok_iter; 262 tok_iter = new OptStrTokIter(ptr); 263 return; 264 } 265 } while (is); 266 #endif /* USE_STDIO */ 267 } 268 269 // **************************************************** Options class utilities 270 271 // Is this option-char null? 272 inline static int 273 isNullOpt(char optchar) { 274 return ((! optchar) || isspace(optchar) || (! isprint(optchar))); 275 } 276 277 // Check for explicit "end-of-options" 278 inline static int 279 isEndOpts(const char * token) { 280 return ((token == NULL) || (! ::strcmp(token, "--"))) ; 281 } 282 283 // See if an argument is an option 284 inline static int 285 isOption(unsigned flags, const char * arg) { 286 return (((*arg != '\0') || (arg[1] != '\0')) && 287 ((*arg == '-') || ((flags & Options::PLUS) && (*arg == '+')))) ; 288 } 289 290 // See if we should be parsing only options or if we also need to 291 // parse positional arguments 292 inline static int 293 isOptsOnly(unsigned flags) { 294 return (flags & Options::PARSE_POS) ? 0 : 1; 295 } 296 297 // return values for a keyword matching function 298 enum kwdmatch_t { NO_MATCH, PARTIAL_MATCH, EXACT_MATCH } ; 299 300 // --------------------------------------------------------------------------- 301 // ^FUNCTION: kwdmatch - match a keyword 302 // 303 // ^SYNOPSIS: 304 // static kwdmatch_t kwdmatch(src, attempt, len) 305 // 306 // ^PARAMETERS: 307 // char * src -- the actual keyword to match 308 // char * attempt -- the possible keyword to compare against "src" 309 // int len -- number of character of "attempt" to consider 310 // (if 0 then we should use all of "attempt") 311 // 312 // ^DESCRIPTION: 313 // See if "attempt" matches some prefix of "src" (case insensitive). 314 // 315 // ^REQUIREMENTS: 316 // - attempt should be non-NULL and non-empty 317 // 318 // ^SIDE-EFFECTS: 319 // None. 320 // 321 // ^RETURN-VALUE: 322 // An enumeration value of type kwdmatch_t corresponding to whether 323 // We had an exact match, a partial match, or no match. 324 // 325 // ^ALGORITHM: 326 // Trivial 327 // ^^------------------------------------------------------------------------- 328 static kwdmatch_t 329 kwdmatch(const char * src, const char * attempt, int len =0) { 330 int i; 331 332 if (src == attempt) return EXACT_MATCH ; 333 if ((src == NULL) || (attempt == NULL)) return NO_MATCH ; 334 if ((! *src) && (! *attempt)) return EXACT_MATCH ; 335 if ((! *src) || (! *attempt)) return NO_MATCH ; 336 337 for (i = 0 ; ((i < len) || (len == 0)) && 338 (attempt[i]) && (attempt[i] != ' ') ; i++) { 339 if (TOLOWER(src[i]) != TOLOWER(attempt[i])) return NO_MATCH ; 340 } 341 342 return (src[i]) ? PARTIAL_MATCH : EXACT_MATCH ; 343 } 344 345 // **************************************************************** OptionSpec 346 347 // Class that represents an option-specification 348 // *NOTE*:: Assumes that the char-ptr given to the constructor points 349 // to storage that will NOT be modified and whose lifetime will 350 // be as least as long as the OptionSpec object we construct. 351 // 352 class OptionSpec { 353 public: 354 OptionSpec(const char * decl =NULLSTR) 355 : hidden(0), spec(decl) 356 { 357 if (spec == NULL) spec = NULL_spec; 358 CheckHidden(); 359 } 360 361 OptionSpec(const OptionSpec & cp) : hidden(cp.hidden), spec(cp.spec) {} 362 363 // NOTE: use default destructor! 364 365 // Assign to another OptionSpec 366 OptionSpec & 367 operator=(const OptionSpec & cp) { 368 if (this != &cp) { 369 spec = cp.spec; 370 hidden = cp.hidden; 371 } 372 return *this; 373 } 374 375 // Assign to a string 376 OptionSpec & 377 operator=(const char * decl) { 378 if (spec != decl) { 379 spec = decl; 380 hidden = 0; 381 CheckHidden(); 382 } 383 return *this; 384 } 385 386 // Convert to char-ptr by returning the original declaration-string 387 operator const char*() { return isHiddenOpt() ? (spec - 1) : spec; } 388 389 // Is this option NULL? 390 int 391 isNULL(void) const { return ((spec == NULL) || (spec == NULL_spec)); } 392 393 // Is this options incorrectly specified? 394 int 395 isSyntaxError(const char * name) const; 396 397 // See if this is a Hidden option 398 int 399 isHiddenOpt(void) const { return hidden; } 400 401 // Get the corresponding option-character 402 char 403 OptChar(void) const { return *spec; } 404 405 // Get the corresponding long-option string 406 const char * 407 LongOpt(void) const { 408 return (spec[1] && spec[2] && (! isspace(spec[2]))) ? (spec + 2) : NULLSTR; 409 } 410 411 // Does this option require an argument? 412 int 413 isValRequired(void) const { 414 return ((spec[1] == ':') || (spec[1] == '+')); 415 } 416 417 // Does this option take an optional argument? 418 int 419 isValOptional(void) const { 420 return ((spec[1] == '?') || (spec[1] == '*')); 421 } 422 423 // Does this option take no arguments? 424 int 425 isNoArg(void) const { 426 return ((spec[1] == '|') || (! spec[1])); 427 } 428 429 // Can this option take more than one argument? 430 int 431 isList(void) const { 432 return ((spec[1] == '+') || (spec[1] == '*')); 433 } 434 435 // Does this option take any arguments? 436 int 437 isValTaken(void) const { 438 return (isValRequired() || isValOptional()) ; 439 } 440 441 // Format this option in the given buffer 442 unsigned 443 Format(char * buf, unsigned optctrls) const; 444 445 private: 446 void 447 CheckHidden(void) { 448 if ((! hidden) && (*spec == '-')) { 449 ++hidden; 450 ++spec; 451 } 452 } 453 454 unsigned hidden : 1; // hidden-flag 455 const char * spec; // string specification 456 457 static const char NULL_spec[]; 458 } ; 459 460 const char OptionSpec::NULL_spec[] = "\0\0\0" ; 461 462 int 463 OptionSpec::isSyntaxError(const char * name) const { 464 int error = 0; 465 if ((! spec) || (! *spec)) { 466 cerr << name << ": empty option specifier." << endl; 467 cerr << "\tmust be at least 1 character long." << endl; 468 ++error; 469 } else if (spec[1] && (strchr("|?:*+", spec[1]) == NULL)) { 470 cerr << name << ": bad option specifier \"" << spec << "\"." << endl; 471 cerr << "\t2nd character must be in the set \"|?:*+\"." << endl; 472 ++error; 473 } 474 return error; 475 } 476 477 // --------------------------------------------------------------------------- 478 // ^FUNCTION: OptionSpec::Format - format an option-spec for a usage message 479 // 480 // ^SYNOPSIS: 481 // unsigned OptionSpec::Format(buf, optctrls) const 482 // 483 // ^PARAMETERS: 484 // char * buf -- where to print the formatted option 485 // unsigned optctrls -- option-parsing configuration flags 486 // 487 // ^DESCRIPTION: 488 // Self-explanatory. 489 // 490 // ^REQUIREMENTS: 491 // - buf must be large enough to hold the result 492 // 493 // ^SIDE-EFFECTS: 494 // - writes to buf. 495 // 496 // ^RETURN-VALUE: 497 // Number of characters written to buf. 498 // 499 // ^ALGORITHM: 500 // Follow along in the source - it's not hard but it is tedious! 501 // ^^------------------------------------------------------------------------- 502 unsigned 503 OptionSpec::Format(char * buf, unsigned optctrls) const { 504 #ifdef NO_USAGE 505 return (*buf = '\0'); 506 #else 507 static char default_value[] = "<value>"; 508 if (isHiddenOpt()) return (unsigned)(*buf = '\0'); 509 char optchar = OptChar(); 510 const char * longopt = LongOpt(); 511 char * p = buf ; 512 513 const char * value = NULLSTR; 514 int longopt_len = 0; 515 int value_len = 0; 516 517 if (longopt) { 518 value = ::strchr(longopt, ' '); 519 longopt_len = (value) ? (value - longopt) : ::strlen(longopt); 520 } else { 521 value = ::strchr(spec + 1, ' '); 522 } 523 while (value && (*value == ' ')) ++value; 524 if (value && *value) { 525 value_len = ::strlen(value); 526 } else { 527 value = default_value; 528 value_len = sizeof(default_value) - 1; 529 } 530 531 if ((optctrls & Options::SHORT_ONLY) && 532 ((! isNullOpt(optchar)) || (optctrls & Options::NOGUESSING))) { 533 longopt = NULLSTR; 534 } 535 if ((optctrls & Options::LONG_ONLY) && 536 (longopt || (optctrls & Options::NOGUESSING))) { 537 optchar = '\0'; 538 } 539 if (isNullOpt(optchar) && (longopt == NULL)) { 540 *buf = '\0'; 541 return 0; 542 } 543 544 *(p++) = '['; 545 546 // print the single character option 547 if (! isNullOpt(optchar)) { 548 *(p++) = '-'; 549 *(p++) = optchar; 550 } 551 552 if ((! isNullOpt(optchar)) && (longopt)) *(p++) = '|'; 553 554 // print the long option 555 if (longopt) { 556 *(p++) = '-'; 557 if (! (optctrls & (Options::LONG_ONLY | Options::SHORT_ONLY))) { 558 *(p++) = '-'; 559 } 560 strncpy(p, longopt, longopt_len); 561 p += longopt_len; 562 } 563 564 // print any argument the option takes 565 if (isValTaken()) { 566 *(p++) = ' ' ; 567 if (isValOptional()) *(p++) = '[' ; 568 strcpy(p, value); 569 p += value_len; 570 if (isList()) { 571 strcpy(p, " ..."); 572 p += 4; 573 } 574 if (isValOptional()) *(p++) = ']' ; 575 } 576 577 *(p++) = ']'; 578 *p = '\0'; 579 580 return (unsigned) strlen(buf); 581 #endif /* USE_STDIO */ 582 } 583 584 // ******************************************************************* Options 585 586 #if (defined(MSWIN) || defined(OS2) || defined(MSDOS)) 587 # define DIR_SEP_CHAR '\\' 588 #else 589 # define DIR_SEP_CHAR '/' 590 #endif 591 592 Options::Options(const char * name, const char * const optv[]) 593 : cmdname(name), optvec(optv), explicit_end(0), optctrls(DEFAULT), 594 nextchar(NULLSTR), listopt(NULLSTR) 595 { 596 const char * basename = ::strrchr(cmdname, DIR_SEP_CHAR); 597 if (basename) cmdname = basename + 1; 598 check_syntax(); 599 } 600 601 Options::~Options(void) {} 602 603 // Make sure each option-specifier has correct syntax. 604 // 605 // If there is even one invalid specifier, then exit ungracefully! 606 // 607 void 608 Options::check_syntax(void) const { 609 int errors = 0; 610 if ((optvec == NULL) || (! *optvec)) return; 611 612 for (const char * const * optv = optvec ; *optv ; optv++) { 613 OptionSpec optspec = *optv; 614 errors += optspec.isSyntaxError(cmdname); 615 } 616 if (errors) exit(127); 617 } 618 619 // --------------------------------------------------------------------------- 620 // ^FUNCTION: Options::match_opt - match an option 621 // 622 // ^SYNOPSIS: 623 // const char * match_opt(opt, int ignore_case) const 624 // 625 // ^PARAMETERS: 626 // char opt -- the option-character to match 627 // int ignore_case -- should we ignore character-case? 628 // 629 // ^DESCRIPTION: 630 // See if "opt" is found in "optvec" 631 // 632 // ^REQUIREMENTS: 633 // - optvec should be non-NULL and terminated by a NULL pointer. 634 // 635 // ^SIDE-EFFECTS: 636 // None. 637 // 638 // ^RETURN-VALUE: 639 // NULL if no match is found, 640 // otherwise a pointer to the matching option-spec. 641 // 642 // ^ALGORITHM: 643 // foreach option-spec 644 // - see if "opt" is a match, if so return option-spec 645 // end-for 646 // ^^------------------------------------------------------------------------- 647 const char * 648 Options::match_opt(char opt, int ignore_case) const { 649 if ((optvec == NULL) || (! *optvec)) return NULLSTR; 650 651 for (const char * const * optv = optvec ; *optv ; optv++) { 652 OptionSpec optspec = *optv; 653 char optchar = optspec.OptChar(); 654 if (isNullOpt(optchar)) continue; 655 if (opt == optchar) { 656 return optspec; 657 } else if (ignore_case && (TOLOWER(opt) == TOLOWER(optchar))) { 658 return optspec; 659 } 660 } 661 662 return NULLSTR; // not found 663 } 664 665 // --------------------------------------------------------------------------- 666 // ^FUNCTION: Options::match_longopt - match a long-option 667 // 668 // ^SYNOPSIS: 669 // const char * Options::match_longopt(opt, len, ambiguous) 670 // 671 // ^PARAMETERS: 672 // char * opt -- the long-option to match 673 // int len -- the number of character of "opt" to match 674 // int & ambiguous -- set by this routine before returning. 675 // 676 // ^DESCRIPTION: 677 // Try to match "opt" against some unique prefix of a long-option 678 // (case insensitive). 679 // 680 // ^REQUIREMENTS: 681 // - optvec should be non-NULL and terminated by a NULL pointer. 682 // 683 // ^SIDE-EFFECTS: 684 // - *ambiguous is set to '1' if "opt" matches >1 long-option 685 // (otherwise it is set to 0). 686 // 687 // ^RETURN-VALUE: 688 // NULL if no match is found, 689 // otherwise a pointer to the matching option-spec. 690 // 691 // ^ALGORITHM: 692 // ambiguous is FALSE 693 // foreach option-spec 694 // if we have an EXACT-MATCH, return the option-spec 695 // if we have a partial-match then 696 // if we already had a previous partial match then 697 // set ambiguous = TRUE and return NULL 698 // else 699 // remember this options spec and continue matching 700 // end-if 701 // end-if 702 // end-for 703 // if we had exactly 1 partial match return it, else return NULL 704 // ^^------------------------------------------------------------------------- 705 const char * 706 Options::match_longopt(const char * opt, int len, int & ambiguous) const { 707 kwdmatch_t result; 708 const char * matched = NULLSTR ; 709 710 ambiguous = 0; 711 if ((optvec == NULL) || (! *optvec)) return NULLSTR; 712 713 for (const char * const * optv = optvec ; *optv ; optv++) { 714 OptionSpec optspec = *optv; 715 const char * longopt = optspec.LongOpt(); 716 if (longopt == NULL) continue; 717 result = kwdmatch(longopt, opt, len); 718 if (result == EXACT_MATCH) { 719 return optspec; 720 } else if (result == PARTIAL_MATCH) { 721 if (matched) { 722 ++ambiguous; 723 return NULLSTR; 724 } else { 725 matched = optspec; 726 } 727 } 728 }//for 729 730 return matched; 731 } 732 733 // --------------------------------------------------------------------------- 734 // ^FUNCTION: Options::parse_opt - parse an option 735 // 736 // ^SYNOPSIS: 737 // int Options::parse_opt(iter, optarg) 738 // 739 // ^PARAMETERS: 740 // OptIter & iter -- option iterator 741 // const char * & optarg -- where to store any option-argument 742 // 743 // ^DESCRIPTION: 744 // Parse the next option in iter (advancing as necessary). 745 // Make sure we update the nextchar pointer along the way. Any option 746 // we find should be returned and optarg should point to its argument. 747 // 748 // ^REQUIREMENTS: 749 // - nextchar must point to the prospective option character 750 // 751 // ^SIDE-EFFECTS: 752 // - iter is advanced when an argument completely parsed 753 // - optarg is modified to point to any option argument 754 // - if Options::QUIET is not set, error messages are printed on cerr 755 // 756 // ^RETURN-VALUE: 757 // 'c' if the -c option was matched (optarg points to its argument) 758 // BADCHAR if the option is invalid (optarg points to the bad 759 // option-character). 760 // 761 // ^ALGORITHM: 762 // It gets complicated -- follow the comments in the source. 763 // ^^------------------------------------------------------------------------- 764 int 765 Options::parse_opt(OptIter & iter, const char * & optarg) { 766 listopt = NULLSTR; // reset the list pointer 767 768 if ((optvec == NULL) || (! *optvec)) return Options::ENDOPTS; 769 770 // Try to match a known option 771 OptionSpec optspec = match_opt(*(nextchar++), (optctrls & Options::ANYCASE)); 772 773 // Check for an unknown option 774 if (optspec.isNULL()) { 775 // See if this was a long-option in disguise 776 if (! (optctrls & Options::NOGUESSING)) { 777 unsigned save_ctrls = optctrls; 778 const char * save_nextchar = nextchar; 779 nextchar -= 1; 780 optctrls |= (Options::QUIET | Options::NOGUESSING); 781 int optchar = parse_longopt(iter, optarg); 782 optctrls = save_ctrls; 783 if (optchar > 0) { 784 return optchar; 785 } else { 786 nextchar = save_nextchar; 787 } 788 } 789 if (! (optctrls & Options::QUIET)) { 790 cerr << cmdname << ": unknown option -" 791 << *(nextchar - 1) << "." << endl ; 792 } 793 optarg = (nextchar - 1); // record the bad option in optarg 794 return Options::BADCHAR; 795 } 796 797 // If no argument is taken, then leave now 798 if (optspec.isNoArg()) { 799 optarg = NULLSTR; 800 return optspec.OptChar(); 801 } 802 803 // Check for argument in this arg 804 if (*nextchar) { 805 optarg = nextchar; // the argument is right here 806 nextchar = NULLSTR; // we've exhausted this arg 807 if (optspec.isList()) listopt = optspec ; // save the list-spec 808 return optspec.OptChar(); 809 } 810 811 // Check for argument in next arg 812 const char * nextarg = iter.curr(); 813 if ((nextarg != NULL) && 814 (optspec.isValRequired() || (! isOption(optctrls, nextarg)))) { 815 optarg = nextarg; // the argument is here 816 iter.next(); // end of arg - advance 817 if (optspec.isList()) listopt = optspec ; // save the list-spec 818 return optspec.OptChar(); 819 } 820 821 // No argument given - if its required, thats an error 822 optarg = NULLSTR; 823 if (optspec.isValRequired() && !(optctrls & Options::QUIET)) { 824 cerr << cmdname << ": argument required for -" << optspec.OptChar() 825 << " option." << endl ; 826 } 827 return optspec.OptChar(); 828 } 829 830 // --------------------------------------------------------------------------- 831 // ^FUNCTION: Options::parse_longopt - parse a long-option 832 // 833 // ^SYNOPSIS: 834 // int Options::parse_longopt(iter, optarg) 835 // 836 // ^PARAMETERS: 837 // OptIter & iter -- option iterator 838 // const char * & optarg -- where to store any option-argument 839 // 840 // ^DESCRIPTION: 841 // Parse the next long-option in iter (advancing as necessary). 842 // Make sure we update the nextchar pointer along the way. Any option 843 // we find should be returned and optarg should point to its argument. 844 // 845 // ^REQUIREMENTS: 846 // - nextchar must point to the prospective option character 847 // 848 // ^SIDE-EFFECTS: 849 // - iter is advanced when an argument completely parsed 850 // - optarg is modified to point to any option argument 851 // - if Options::QUIET is not set, error messages are printed on cerr 852 // 853 // ^RETURN-VALUE: 854 // 'c' if the the long-option corresponding to the -c option was matched 855 // (optarg points to its argument) 856 // BADKWD if the option is invalid (optarg points to the bad long-option 857 // name). 858 // 859 // ^ALGORITHM: 860 // It gets complicated -- follow the comments in the source. 861 // ^^------------------------------------------------------------------------- 862 int 863 Options::parse_longopt(OptIter & iter, const char * & optarg) { 864 int len = 0, ambiguous = 0; 865 866 listopt = NULLSTR ; // reset the list-spec 867 868 if ((optvec == NULL) || (! *optvec)) return Options::ENDOPTS; 869 870 // if a value is supplied in this argv element, get it now 871 const char * val = strpbrk(nextchar, ":=") ; 872 if (val) { 873 len = val - nextchar ; 874 ++val ; 875 } 876 877 // Try to match a known long-option 878 OptionSpec optspec = match_longopt(nextchar, len, ambiguous); 879 880 // Check for an unknown long-option 881 if (optspec.isNULL()) { 882 // See if this was a short-option in disguise 883 if ((! ambiguous) && (! (optctrls & Options::NOGUESSING))) { 884 unsigned save_ctrls = optctrls; 885 const char * save_nextchar = nextchar; 886 optctrls |= (Options::QUIET | Options::NOGUESSING); 887 int optchar = parse_opt(iter, optarg); 888 optctrls = save_ctrls; 889 if (optchar > 0) { 890 return optchar; 891 } else { 892 nextchar = save_nextchar; 893 } 894 } 895 if (! (optctrls & Options::QUIET)) { 896 cerr << cmdname << ": " << ((ambiguous) ? "ambiguous" : "unknown") 897 << " option " 898 << ((optctrls & Options::LONG_ONLY) ? "-" : "--") 899 << nextchar << "." << endl ; 900 } 901 optarg = nextchar; // record the bad option in optarg 902 nextchar = NULLSTR; // we've exhausted this argument 903 return (ambiguous) ? Options::AMBIGUOUS : Options::BADKWD; 904 } 905 906 // If no argument is taken, then leave now 907 if (optspec.isNoArg()) { 908 if ((val) && ! (optctrls & Options::QUIET)) { 909 cerr << cmdname << ": option " 910 << ((optctrls & Options::LONG_ONLY) ? "-" : "--") 911 << optspec.LongOpt() << " does NOT take an argument." << endl ; 912 } 913 optarg = val; // record the unexpected argument 914 nextchar = NULLSTR; // we've exhausted this argument 915 return optspec.OptChar(); 916 } 917 918 // Check for argument in this arg 919 if (val) { 920 optarg = val; // the argument is right here 921 nextchar = NULLSTR; // we exhausted the rest of this arg 922 if (optspec.isList()) listopt = optspec ; // save the list-spec 923 return optspec.OptChar(); 924 } 925 926 // Check for argument in next arg 927 const char * nextarg = iter.curr(); // find the next argument to parse 928 if ((nextarg != NULL) && 929 (optspec.isValRequired() || (! isOption(optctrls, nextarg)))) { 930 optarg = nextarg; // the argument is right here 931 iter.next(); // end of arg - advance 932 nextchar = NULLSTR; // we exhausted the rest of this arg 933 if (optspec.isList()) listopt = optspec ; // save the list-spec 934 return optspec.OptChar(); 935 } 936 937 // No argument given - if its required, thats an error 938 optarg = NULLSTR; 939 if (optspec.isValRequired() && !(optctrls & Options::QUIET)) { 940 const char * longopt = optspec.LongOpt(); 941 const char * spc = ::strchr(longopt, ' '); 942 int longopt_len; 943 if (spc) { 944 longopt_len = spc - longopt; 945 } else { 946 longopt_len = ::strlen(longopt); 947 } 948 cerr << cmdname << ": argument required for " 949 << ((optctrls & Options::LONG_ONLY) ? "-" : "--"); 950 cerr.write(longopt, longopt_len) << " option." << endl ; 951 } 952 nextchar = NULLSTR; // we exhausted the rest of this arg 953 return optspec.OptChar(); 954 } 955 956 // --------------------------------------------------------------------------- 957 // ^FUNCTION: Options::usage - print usage 958 // 959 // ^SYNOPSIS: 960 // void Options::usage(os, positionals) 961 // 962 // ^PARAMETERS: 963 // ostream & os -- where to print the usage 964 // char * positionals -- command-line syntax for any positional args 965 // 966 // ^DESCRIPTION: 967 // Print command-usage (using either option or long-option syntax) on os. 968 // 969 // ^REQUIREMENTS: 970 // os should correspond to an open output file. 971 // 972 // ^SIDE-EFFECTS: 973 // Prints on os 974 // 975 // ^RETURN-VALUE: 976 // None. 977 // 978 // ^ALGORITHM: 979 // Print usage on os, wrapping long lines where necessary. 980 // ^^------------------------------------------------------------------------- 981 void 982 Options::usage(ostream & os, const char * positionals) const { 983 #ifdef NO_USAGE 984 return; 985 #else 986 const char * const * optv = optvec; 987 unsigned cols = 79; 988 int first, nloop; 989 char buf[256] ; 990 991 if ((optv == NULL) || (! *optv)) return; 992 993 // print first portion "usage: progname" 994 os << "usage: " << cmdname ; 995 unsigned ll = strlen(cmdname) + 7; 996 997 // save the current length so we know how much space to skip for 998 // subsequent lines. 999 // 1000 unsigned margin = ll + 1; 1001 1002 // print the options and the positional arguments 1003 for (nloop = 0, first = 1 ; !nloop ; optv++, first = 0) { 1004 unsigned len; 1005 OptionSpec optspec = *optv; 1006 1007 // figure out how wide this parameter is (for printing) 1008 if (! *optv) { 1009 len = strlen(positionals); 1010 ++nloop; // terminate this loop 1011 } else { 1012 if (optspec.isHiddenOpt()) continue; 1013 len = optspec.Format(buf, optctrls); 1014 } 1015 1016 // Will this fit? 1017 if ((ll + len + 1) > (cols - first)) { 1018 os << '\n' ; // No - start a new line; 1019 #ifdef USE_STDIO 1020 for (int _i_ = 0; _i_ < margin; ++_i_) os << " "; 1021 #else 1022 os.width(margin); os << "" ; 1023 #endif 1024 ll = margin; 1025 } else { 1026 os << ' ' ; // Yes - just throw in a space 1027 ++ll; 1028 } 1029 ll += len; 1030 os << ((nloop) ? positionals : buf) ; 1031 }// for each ad 1032 1033 os << endl ; 1034 #endif /* NO_USAGE */ 1035 } 1036 1037 1038 // --------------------------------------------------------------------------- 1039 // ^FUNCTION: Options::operator() - get options from the command-line 1040 // 1041 // ^SYNOPSIS: 1042 // int Options::operator()(iter, optarg) 1043 // 1044 // ^PARAMETERS: 1045 // OptIter & iter -- option iterator 1046 // const char * & optarg -- where to store any option-argument 1047 // 1048 // ^DESCRIPTION: 1049 // Parse the next option in iter (advancing as necessary). 1050 // Make sure we update the nextchar pointer along the way. Any option 1051 // we find should be returned and optarg should point to its argument. 1052 // 1053 // ^REQUIREMENTS: 1054 // None. 1055 // 1056 // ^SIDE-EFFECTS: 1057 // - iter is advanced when an argument is completely parsed 1058 // - optarg is modified to point to any option argument 1059 // - if Options::QUIET is not set, error messages are printed on cerr 1060 // 1061 // ^RETURN-VALUE: 1062 // 0 if all options have been parsed. 1063 // 'c' if the the option or long-option corresponding to the -c option was 1064 // matched (optarg points to its argument). 1065 // BADCHAR if the option is invalid (optarg points to the bad option char). 1066 // BADKWD if the option is invalid (optarg points to the bad long-opt name). 1067 // AMBIGUOUS if an ambiguous keyword name was given (optarg points to the 1068 // ambiguous keyword name). 1069 // POSITIONAL if PARSE_POS was set and the current argument is a positional 1070 // parameter (in which case optarg points to the positional argument). 1071 // 1072 // ^ALGORITHM: 1073 // It gets complicated -- follow the comments in the source. 1074 // ^^------------------------------------------------------------------------- 1075 int 1076 Options::operator()(OptIter & iter, const char * & optarg) { 1077 int parse_opts_only = isOptsOnly(optctrls); 1078 if (parse_opts_only) explicit_end = 0; 1079 1080 // See if we have an option left over from before ... 1081 if ((nextchar) && *nextchar) { 1082 return parse_opt(iter, optarg); 1083 } 1084 1085 // Check for end-of-options ... 1086 const char * arg = NULLSTR; 1087 int get_next_arg = 0; 1088 do { 1089 arg = iter.curr(); 1090 get_next_arg = 0; 1091 if (arg == NULL) { 1092 listopt = NULLSTR; 1093 return Options::ENDOPTS; 1094 } else if ((! explicit_end) && isEndOpts(arg)) { 1095 iter.next(); // advance past end-of-options arg 1096 listopt = NULLSTR; 1097 explicit_end = 1; 1098 if (parse_opts_only) return Options::ENDOPTS; 1099 get_next_arg = 1; // make sure we look at the next argument. 1100 } 1101 } while (get_next_arg); 1102 1103 // Do we have a positional arg? 1104 if ( explicit_end || (! isOption(optctrls, arg)) ) { 1105 if (parse_opts_only) { 1106 return Options::ENDOPTS; 1107 } else { 1108 optarg = arg; // set optarg to the positional argument 1109 iter.next(); // advance iterator to the next argument 1110 return Options::POSITIONAL; 1111 } 1112 } 1113 1114 iter.next(); // pass the argument that arg already points to 1115 1116 // See if we have a long option ... 1117 if (! (optctrls & Options::SHORT_ONLY)) { 1118 if ((*arg == '-') && (arg[1] == '-')) { 1119 nextchar = arg + 2; 1120 return parse_longopt(iter, optarg); 1121 } else if ((optctrls & Options::PLUS) && (*arg == '+')) { 1122 nextchar = arg + 1; 1123 return parse_longopt(iter, optarg); 1124 } 1125 } 1126 if (*arg == '-') { 1127 nextchar = arg + 1; 1128 if (optctrls & Options::LONG_ONLY) { 1129 return parse_longopt(iter, optarg); 1130 } else { 1131 return parse_opt(iter, optarg); 1132 } 1133 } 1134 1135 // If we get here - it is because we have a list value 1136 OptionSpec optspec = listopt; 1137 optarg = arg ; // record the list value 1138 return optspec.OptChar() ; 1139 } 1140 1141