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 32using namespace std; 33 34extern "C" { 35 void exit(int); 36} 37 38static 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 60static const char endl = '\n' ; 61 62class ostream { 63public: 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 75private: 76 FILE * fp; 77} ; 78 79ostream & 80ostream::operator<<(char ch) { 81 fputc(ch, fp); 82 return *this; 83} 84 85ostream & 86ostream::operator<<(const char * str) { 87 fputs(str, fp); 88 return *this; 89} 90 91ostream & 92ostream::write(const char * buf, unsigned ) { 93 fputs(buf, fp); 94 return *this; 95} 96 97static ostream cerr(stderr); 98static ostream cout(stdout); 99 100#endif /* USE_STDIO */ 101 102// ************************************************************** OptIter 103 104OptIter::~OptIter(void) {} 105 106const char * 107OptIter::operator()(void) { 108 const char * elt = curr(); 109 (void) next(); 110 return elt; 111} 112 113// ************************************************************** OptIterRwd 114 115OptIterRwd::OptIterRwd(void) {} 116 117OptIterRwd::~OptIterRwd(void) {} 118 119// ************************************************************** OptArgvIter 120 121OptArgvIter::~OptArgvIter(void) {} 122 123const char * 124OptArgvIter::curr(void) { 125 return ((ndx == ac) || (av[ndx] == NULL)) ? NULLSTR : av[ndx]; 126} 127 128void 129OptArgvIter::next(void) { 130 if ((ndx != ac) && av[ndx]) ++ndx; 131} 132 133const char * 134OptArgvIter::operator()(void) { 135 return ((ndx == ac) || (av[ndx] == NULL)) ? NULLSTR : av[ndx++]; 136} 137 138void 139OptArgvIter::rewind(void) { ndx = 0; } 140 141// ************************************************************** OptStrTokIter 142 143static const char WHITESPACE[] = " \t\n\r\v\f" ; 144const char * OptStrTokIter::default_delims = WHITESPACE ; 145 146OptStrTokIter::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 157OptStrTokIter::~OptStrTokIter(void) { delete [] tokstr; } 158 159const char * 160OptStrTokIter::curr(void) { return cur; } 161 162void 163OptStrTokIter::next(void) { if (cur) cur = ::strtok(NULL, seps); } 164 165const char * 166OptStrTokIter::operator()(void) { 167 const char * elt = cur; 168 if (cur) cur = ::strtok(NULL, seps); 169 return elt; 170} 171 172void 173OptStrTokIter::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 186const unsigned OptIstreamIter::MAX_LINE_LEN = 1024 ; 187 188 // Constructor 189OptIstreamIter::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 200OptIstreamIter::~OptIstreamIter(void) { 201 delete tok_iter; 202} 203 204const char * 205OptIstreamIter::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 217void 218OptIstreamIter::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 230const char * 231OptIstreamIter::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 // 249void 250OptIstreamIter::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? 272inline static int 273isNullOpt(char optchar) { 274 return ((! optchar) || isspace(optchar) || (! isprint(optchar))); 275} 276 277 // Check for explicit "end-of-options" 278inline static int 279isEndOpts(const char * token) { 280 return ((token == NULL) || (! ::strcmp(token, "--"))) ; 281} 282 283 // See if an argument is an option 284inline static int 285isOption(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 292inline static int 293isOptsOnly(unsigned flags) { 294 return (flags & Options::PARSE_POS) ? 0 : 1; 295} 296 297 // return values for a keyword matching function 298enum 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// ^^------------------------------------------------------------------------- 328static kwdmatch_t 329kwdmatch(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 // 352class OptionSpec { 353public: 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 445private: 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 460const char OptionSpec::NULL_spec[] = "\0\0\0" ; 461 462int 463OptionSpec::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// ^^------------------------------------------------------------------------- 502unsigned 503OptionSpec::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 592Options::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 601Options::~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 // 607void 608Options::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// ^^------------------------------------------------------------------------- 647const char * 648Options::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// ^^------------------------------------------------------------------------- 705const char * 706Options::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// ^^------------------------------------------------------------------------- 764int 765Options::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// ^^------------------------------------------------------------------------- 862int 863Options::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// ^^------------------------------------------------------------------------- 981void 982Options::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// ^^------------------------------------------------------------------------- 1075int 1076Options::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