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