source: trunk/MFCtooling/replay/options.cpp @ 161

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