cfad47cfa3/src/options.cc

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
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