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