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