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