// ****************************************************************************
// ^FILE: options.c - implement the functions defined in <options.h>
//
// ^HISTORY:
// 01/16/92 Brad Appleton <
[email protected]> Created
//
// 03/23/93 Brad Appleton <
[email protected]>
// - Added OptIstreamIter class
//
// 10/08/93 Brad Appleton <
[email protected]>
// - Added "hidden" options
//
// 02/08/94 Brad Appleton <
[email protected]>
// - Added "OptionSpec" class
// - Permitted use of stdio instead of iostreams via #ifdef USE_STDIO
//
// 03/08/94 Brad Appleton <
[email protected]>
// - completed support for USE_STDIO
// - added #ifdef NO_USAGE for people who always want to print their own
// - Fixed stupid NULL pointer error in OptionsSpec class
//
// 07/31/97 Brad Appleton <
[email protected]>
// - Added PARSE_POS control flag and POSITIONAL return value.
// ^^**************************************************************************
// #include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include "options.h"
using namespace std;
extern "C" {
void exit(int);
}
static const char ident[] = "@(#)Options 1.05" ;
// I need a portable version of "tolower" that does NOT modify
// non-uppercase characters.
//
#define TOLOWER(c) (isupper(c) ? tolower(c) : c)
// Use this to shut the compiler up about NULL strings
#define NULLSTR (char *)NULL
// ******************************************************** insertion operators
// If you are using <stdio.h> then you need this stuff!
// If you are using <iostream.h> then #ifdef this stuff out
//
#ifdef USE_STDIO
// Implement just enough of ostream to get this file to compile
//
static const char endl = '\n' ;
class ostream {
public:
ostream(FILE * fileptr) : fp(fileptr) {}
ostream &
operator<<(char ch);
ostream &
operator<<(const char * str);
ostream &
write(const char * buf, unsigned bufsize);
private:
FILE * fp;
} ;
ostream &
ostream::operator<<(char ch) {
fputc(ch, fp);
return *this;
}
ostream &
ostream::operator<<(const char * str) {
fputs(str, fp);
return *this;
}
ostream &
ostream::write(const char * buf, unsigned ) {
fputs(buf, fp);
return *this;
}
static ostream cerr(stderr);
static ostream cout(stdout);
#endif /* USE_STDIO */
// ************************************************************** OptIter
OptIter::~OptIter(void) {}
const char *
OptIter::operator()(void) {
const char * elt = curr();
(void) next();
return elt;
}
// ************************************************************** OptIterRwd
OptIterRwd::OptIterRwd(void) {}
OptIterRwd::~OptIterRwd(void) {}
// ************************************************************** OptArgvIter
OptArgvIter::~OptArgvIter(void) {}
const char *
OptArgvIter::curr(void) {
return ((ndx == ac) || (av[ndx] == NULL)) ? NULLSTR : av[ndx];
}
void
OptArgvIter::next(void) {
if ((ndx != ac) && av[ndx]) ++ndx;
}
const char *
OptArgvIter::operator()(void) {
return ((ndx == ac) || (av[ndx] == NULL)) ? NULLSTR : av[ndx++];
}
void
OptArgvIter::rewind(void) { ndx = 0; }
// ************************************************************** OptStrTokIter
static const char WHITESPACE[] = " \t\n\r\v\f" ;
const char * OptStrTokIter::default_delims = WHITESPACE ;
OptStrTokIter::OptStrTokIter(const char * tokens, const char * delimiters)
: len(unsigned(strlen(tokens))), str(tokens), seps(delimiters),
cur(NULLSTR), tokstr(NULLSTR)
{
if (seps == NULL) seps = default_delims;
tokstr = new char[len + 1];
(void) ::strcpy(tokstr, str);
cur = ::strtok(tokstr, seps);
}
OptStrTokIter::~OptStrTokIter(void) { delete [] tokstr; }
const char *
OptStrTokIter::curr(void) { return cur; }
void
OptStrTokIter::next(void) { if (cur) cur = ::strtok(NULL, seps); }
const char *
OptStrTokIter::operator()(void) {
const char * elt = cur;
if (cur) cur = ::strtok(NULL, seps);
return elt;
}
void
OptStrTokIter::rewind(void) {
(void) ::strcpy(tokstr, str);
cur = ::strtok(tokstr, seps);
}
// ************************************************************* OptIstreamIter
#ifdef vms
enum { c_COMMENT = '!' } ;
#else
enum { c_COMMENT = '#' } ;
#endif
const unsigned OptIstreamIter::MAX_LINE_LEN = 1024 ;
// Constructor
OptIstreamIter::OptIstreamIter(istream & input) : is(input), tok_iter(NULL)
{
#ifdef USE_STDIO
fprintf(stderr, "%s: Can't use OptIstreamIter class:\n",
"OptIstreamIter::OptIstreamIter");
fprintf(stderr, "\tOptions(3C++) was compiled with USE_STDIO #defined.\n");
exit(-1);
#endif /* USE_STDIO */
}
// Destructor
OptIstreamIter::~OptIstreamIter(void) {
delete tok_iter;
}
const char *
OptIstreamIter::curr(void) {
#ifdef USE_STDIO
return NULLSTR;
#else
const char * result = NULLSTR;
if (tok_iter) result = tok_iter->curr();
if (result) return result;
fill();
return (! is) ? NULLSTR : tok_iter->curr();
#endif /* USE_STDIO */
}
void
OptIstreamIter::next(void) {
#ifdef USE_STDIO
return;
#else
const char * result = NULLSTR;
if (tok_iter) result = tok_iter->operator()();
if (result) return;
fill();
if (! is) tok_iter->next();
#endif /* USE_STDIO */
}
const char *
OptIstreamIter::operator()(void) {
#ifdef USE_STDIO
return NULLSTR;
#else
const char * result = NULLSTR;
if (tok_iter) result = tok_iter->operator()();
if (result) return result;
fill();
return (! is) ? NULLSTR : tok_iter->operator()();
#endif /* USE_STDIO */
}
// What we do is this: for each line of text in the istream, we use
// a OptStrTokIter to iterate over each token on the line.
//
// If the first non-white character on a line is c_COMMENT, then we
// consider the line to be a comment and we ignore it.
//
void
OptIstreamIter::fill(void) {
#ifdef USE_STDIO
return;
#else
char buf[OptIstreamIter::MAX_LINE_LEN];
do {
*buf = '\0';
is.getline(buf, sizeof(buf));
char * ptr = buf;
while (isspace(*ptr)) ++ptr;
if (*ptr && (*ptr != c_COMMENT)) {
delete tok_iter;
tok_iter = new OptStrTokIter(ptr);
return;
}
} while (is);
#endif /* USE_STDIO */
}
// **************************************************** Options class utilities
// Is this option-char null?
inline static int
isNullOpt(char optchar) {
return ((! optchar) || isspace(optchar) || (! isprint(optchar)));
}
// Check for explicit "end-of-options"
inline static int
isEndOpts(const char * token) {
return ((token == NULL) || (! ::strcmp(token, "--"))) ;
}
// See if an argument is an option
inline static int
isOption(unsigned flags, const char * arg) {
return (((*arg != '\0') || (arg[1] != '\0')) &&
((*arg == '-') || ((flags & Options::PLUS) && (*arg == '+')))) ;
}
// See if we should be parsing only options or if we also need to
// parse positional arguments
inline static int
isOptsOnly(unsigned flags) {
return (flags & Options::PARSE_POS) ? 0 : 1;
}
// return values for a keyword matching function
enum kwdmatch_t { NO_MATCH, PARTIAL_MATCH, EXACT_MATCH } ;
// ---------------------------------------------------------------------------
// ^FUNCTION: kwdmatch - match a keyword
//
// ^SYNOPSIS:
// static kwdmatch_t kwdmatch(src, attempt, len)
//
// ^PARAMETERS:
// char * src -- the actual keyword to match
// char * attempt -- the possible keyword to compare against "src"
// int len -- number of character of "attempt" to consider
// (if 0 then we should use all of "attempt")
//
// ^DESCRIPTION:
// See if "attempt" matches some prefix of "src" (case insensitive).
//
// ^REQUIREMENTS:
// - attempt should be non-NULL and non-empty
//
// ^SIDE-EFFECTS:
// None.
//
// ^RETURN-VALUE:
// An enumeration value of type kwdmatch_t corresponding to whether
// We had an exact match, a partial match, or no match.
//
// ^ALGORITHM:
// Trivial
// ^^-------------------------------------------------------------------------
static kwdmatch_t
kwdmatch(const char * src, const char * attempt, int len =0) {
int i;
if (src == attempt) return EXACT_MATCH ;
if ((src == NULL) || (attempt == NULL)) return NO_MATCH ;
if ((! *src) && (! *attempt)) return EXACT_MATCH ;
if ((! *src) || (! *attempt)) return NO_MATCH ;
for (i = 0 ; ((i < len) || (len == 0)) &&
(attempt[i]) && (attempt[i] != ' ') ; i++) {
if (TOLOWER(src[i]) != TOLOWER(attempt[i])) return NO_MATCH ;
}
return (src[i]) ? PARTIAL_MATCH : EXACT_MATCH ;
}
// **************************************************************** OptionSpec
// Class that represents an option-specification
// *NOTE*:: Assumes that the char-ptr given to the constructor points
// to storage that will NOT be modified and whose lifetime will
// be as least as long as the OptionSpec object we construct.
//
class OptionSpec {
public:
OptionSpec(const char * decl =NULLSTR)
: hidden(0), spec(decl)
{
if (spec == NULL) spec = NULL_spec;
CheckHidden();
}
OptionSpec(const OptionSpec & cp) : hidden(cp.hidden), spec(cp.spec) {}
// NOTE: use default destructor!
// Assign to another OptionSpec
OptionSpec &
operator=(const OptionSpec & cp) {
if (this != &cp) {
spec = cp.spec;
hidden = cp.hidden;
}
return *this;
}
// Assign to a string
OptionSpec &
operator=(const char * decl) {
if (spec != decl) {
spec = decl;
hidden = 0;
CheckHidden();
}
return *this;
}
// Convert to char-ptr by returning the original declaration-string
operator const char*() { return isHiddenOpt() ? (spec - 1) : spec; }
// Is this option NULL?
int
isNULL(void) const { return ((spec == NULL) || (spec == NULL_spec)); }
// Is this options incorrectly specified?
int
isSyntaxError(const char * name) const;
// See if this is a Hidden option
int
isHiddenOpt(void) const { return hidden; }
// Get the corresponding option-character
char
OptChar(void) const { return *spec; }
// Get the corresponding long-option string
const char *
LongOpt(void) const {
return (spec[1] && spec[2] && (! isspace(spec[2]))) ? (spec + 2) : NULLSTR;
}
// Does this option require an argument?
int
isValRequired(void) const {
return ((spec[1] == ':') || (spec[1] == '+'));
}
// Does this option take an optional argument?
int
isValOptional(void) const {
return ((spec[1] == '?') || (spec[1] == '*'));
}
// Does this option take no arguments?
int
isNoArg(void) const {
return ((spec[1] == '|') || (! spec[1]));
}
// Can this option take more than one argument?
int
isList(void) const {
return ((spec[1] == '+') || (spec[1] == '*'));
}
// Does this option take any arguments?
int
isValTaken(void) const {
return (isValRequired() || isValOptional()) ;
}
// Format this option in the given buffer
unsigned
Format(char * buf, unsigned optctrls) const;
private:
void
CheckHidden(void) {
if ((! hidden) && (*spec == '-')) {
++hidden;
++spec;
}
}
unsigned hidden : 1; // hidden-flag
const char * spec; // string specification
static const char NULL_spec[];
} ;
const char OptionSpec::NULL_spec[] = "\0\0\0" ;
int
OptionSpec::isSyntaxError(const char * name) const {
int error = 0;
if ((! spec) || (! *spec)) {
cerr << name << ": empty option specifier." << endl;
cerr << "\tmust be at least 1 character long." << endl;
++error;
} else if (spec[1] && (strchr("|?:*+", spec[1]) == NULL)) {
cerr << name << ": bad option specifier \"" << spec << "\"." << endl;
cerr << "\t2nd character must be in the set \"|?:*+\"." << endl;
++error;
}
return error;
}
// ---------------------------------------------------------------------------
// ^FUNCTION: OptionSpec::Format - format an option-spec for a usage message
//
// ^SYNOPSIS:
// unsigned OptionSpec::Format(buf, optctrls) const
//
// ^PARAMETERS:
// char * buf -- where to print the formatted option
// unsigned optctrls -- option-parsing configuration flags
//
// ^DESCRIPTION:
// Self-explanatory.
//
// ^REQUIREMENTS:
// - buf must be large enough to hold the result
//
// ^SIDE-EFFECTS:
// - writes to buf.
//
// ^RETURN-VALUE:
// Number of characters written to buf.
//
// ^ALGORITHM:
// Follow along in the source - it's not hard but it is tedious!
// ^^-------------------------------------------------------------------------
unsigned
OptionSpec::Format(char * buf, unsigned optctrls) const {
#ifdef NO_USAGE
return (*buf = '\0');
#else
static char default_value[] = "<value>";
if (isHiddenOpt()) return (unsigned)(*buf = '\0');
char optchar = OptChar();
const char * longopt = LongOpt();
char * p = buf ;
const char * value = NULLSTR;
int longopt_len = 0;
int value_len = 0;
if (longopt) {
value = ::strchr(longopt, ' ');
longopt_len = (value) ? (value - longopt) : ::strlen(longopt);
} else {
value = ::strchr(spec + 1, ' ');
}
while (value && (*value == ' ')) ++value;
if (value && *value) {
value_len = ::strlen(value);
} else {
value = default_value;
value_len = sizeof(default_value) - 1;
}
if ((optctrls & Options::SHORT_ONLY) &&
((! isNullOpt(optchar)) || (optctrls & Options::NOGUESSING))) {
longopt = NULLSTR;
}
if ((optctrls & Options::LONG_ONLY) &&
(longopt || (optctrls & Options::NOGUESSING))) {
optchar = '\0';
}
if (isNullOpt(optchar) && (longopt == NULL)) {
*buf = '\0';
return 0;
}
*(p++) = '[';
// print the single character option
if (! isNullOpt(optchar)) {
*(p++) = '-';
*(p++) = optchar;
}
if ((! isNullOpt(optchar)) && (longopt)) *(p++) = '|';
// print the long option
if (longopt) {
*(p++) = '-';
if (! (optctrls & (Options::LONG_ONLY | Options::SHORT_ONLY))) {
*(p++) = '-';
}
strncpy(p, longopt, longopt_len);
p += longopt_len;
}
// print any argument the option takes
if (isValTaken()) {
*(p++) = ' ' ;
if (isValOptional()) *(p++) = '[' ;
strcpy(p, value);
p += value_len;
if (isList()) {
strcpy(p, " ...");
p += 4;
}
if (isValOptional()) *(p++) = ']' ;
}
*(p++) = ']';
*p = '\0';
return (unsigned) strlen(buf);
#endif /* USE_STDIO */
}
// ******************************************************************* Options
#if (defined(MSWIN) || defined(OS2) || defined(MSDOS))
# define DIR_SEP_CHAR '\\'
#else
# define DIR_SEP_CHAR '/'
#endif
Options::Options(const char * name, const char * const optv[])
: cmdname(name), optvec(optv), explicit_end(0), optctrls(DEFAULT),
nextchar(NULLSTR), listopt(NULLSTR)
{
const char * basename = ::strrchr(cmdname, DIR_SEP_CHAR);
if (basename) cmdname = basename + 1;
check_syntax();
}
Options::~Options(void) {}
// Make sure each option-specifier has correct syntax.
//
// If there is even one invalid specifier, then exit ungracefully!
//
void
Options::check_syntax(void) const {
int errors = 0;
if ((optvec == NULL) || (! *optvec)) return;
for (const char * const * optv = optvec ; *optv ; optv++) {
OptionSpec optspec = *optv;
errors += optspec.isSyntaxError(cmdname);
}
if (errors) exit(127);
}
// ---------------------------------------------------------------------------
// ^FUNCTION: Options::match_opt - match an option
//
// ^SYNOPSIS:
// const char * match_opt(opt, int ignore_case) const
//
// ^PARAMETERS:
// char opt -- the option-character to match
// int ignore_case -- should we ignore character-case?
//
// ^DESCRIPTION:
// See if "opt" is found in "optvec"
//
// ^REQUIREMENTS:
// - optvec should be non-NULL and terminated by a NULL pointer.
//
// ^SIDE-EFFECTS:
// None.
//
// ^RETURN-VALUE:
// NULL if no match is found,
// otherwise a pointer to the matching option-spec.
//
// ^ALGORITHM:
// foreach option-spec
// - see if "opt" is a match, if so return option-spec
// end-for
// ^^-------------------------------------------------------------------------
const char *
Options::match_opt(char opt, int ignore_case) const {
if ((optvec == NULL) || (! *optvec)) return NULLSTR;
for (const char * const * optv = optvec ; *optv ; optv++) {
OptionSpec optspec = *optv;
char optchar = optspec.OptChar();
if (isNullOpt(optchar)) continue;
if (opt == optchar) {
return optspec;
} else if (ignore_case && (TOLOWER(opt) == TOLOWER(optchar))) {
return optspec;
}
}
return NULLSTR; // not found
}
// ---------------------------------------------------------------------------
// ^FUNCTION: Options::match_longopt - match a long-option
//
// ^SYNOPSIS:
// const char * Options::match_longopt(opt, len, ambiguous)
//
// ^PARAMETERS:
// char * opt -- the long-option to match
// int len -- the number of character of "opt" to match
// int & ambiguous -- set by this routine before returning.
//
// ^DESCRIPTION:
// Try to match "opt" against some unique prefix of a long-option
// (case insensitive).
//
// ^REQUIREMENTS:
// - optvec should be non-NULL and terminated by a NULL pointer.
//
// ^SIDE-EFFECTS:
// - *ambiguous is set to '1' if "opt" matches >1 long-option
// (otherwise it is set to 0).
//
// ^RETURN-VALUE:
// NULL if no match is found,
// otherwise a pointer to the matching option-spec.
//
// ^ALGORITHM:
// ambiguous is FALSE
// foreach option-spec
// if we have an EXACT-MATCH, return the option-spec
// if we have a partial-match then
// if we already had a previous partial match then
// set ambiguous = TRUE and return NULL
// else
// remember this options spec and continue matching
// end-if
// end-if
// end-for
// if we had exactly 1 partial match return it, else return NULL
// ^^-------------------------------------------------------------------------
const char *
Options::match_longopt(const char * opt, int len, int & ambiguous) const {
kwdmatch_t result;
const char * matched = NULLSTR ;
ambiguous = 0;
if ((optvec == NULL) || (! *optvec)) return NULLSTR;
for (const char * const * optv = optvec ; *optv ; optv++) {
OptionSpec optspec = *optv;
const char * longopt = optspec.LongOpt();
if (longopt == NULL) continue;
result = kwdmatch(longopt, opt, len);
if (result == EXACT_MATCH) {
return optspec;
} else if (result == PARTIAL_MATCH) {
if (matched) {
++ambiguous;
return NULLSTR;
} else {
matched = optspec;
}
}
}//for
return matched;
}
// ---------------------------------------------------------------------------
// ^FUNCTION: Options::parse_opt - parse an option
//
// ^SYNOPSIS:
// int Options::parse_opt(iter, optarg)
//
// ^PARAMETERS:
// OptIter & iter -- option iterator
// const char * & optarg -- where to store any option-argument
//
// ^DESCRIPTION:
// Parse the next option in iter (advancing as necessary).
// Make sure we update the nextchar pointer along the way. Any option
// we find should be returned and optarg should point to its argument.
//
// ^REQUIREMENTS:
// - nextchar must point to the prospective option character
//
// ^SIDE-EFFECTS:
// - iter is advanced when an argument completely parsed
// - optarg is modified to point to any option argument
// - if Options::QUIET is not set, error messages are printed on cerr
//
// ^RETURN-VALUE:
// 'c' if the -c option was matched (optarg points to its argument)
// BADCHAR if the option is invalid (optarg points to the bad
// option-character).
//
// ^ALGORITHM:
// It gets complicated -- follow the comments in the source.
// ^^-------------------------------------------------------------------------
int
Options::parse_opt(OptIter & iter, const char * & optarg) {
listopt = NULLSTR; // reset the list pointer
if ((optvec == NULL) || (! *optvec)) return Options::ENDOPTS;
// Try to match a known option
OptionSpec optspec = match_opt(*(nextchar++), (optctrls & Options::ANYCASE));
// Check for an unknown option
if (optspec.isNULL()) {
// See if this was a long-option in disguise
if (! (optctrls & Options::NOGUESSING)) {
unsigned save_ctrls = optctrls;
const char * save_nextchar = nextchar;
nextchar -= 1;
optctrls |= (Options::QUIET | Options::NOGUESSING);
int optchar = parse_longopt(iter, optarg);
optctrls = save_ctrls;
if (optchar > 0) {
return optchar;
} else {
nextchar = save_nextchar;
}
}
if (! (optctrls & Options::QUIET)) {
cerr << cmdname << ": unknown option -"
<< *(nextchar - 1) << "." << endl ;
}
optarg = (nextchar - 1); // record the bad option in optarg
return Options::BADCHAR;
}
// If no argument is taken, then leave now
if (optspec.isNoArg()) {
optarg = NULLSTR;
return optspec.OptChar();
}
// Check for argument in this arg
if (*nextchar) {
optarg = nextchar; // the argument is right here
nextchar = NULLSTR; // we've exhausted this arg
if (optspec.isList()) listopt = optspec ; // save the list-spec
return optspec.OptChar();
}
// Check for argument in next arg
const char * nextarg = iter.curr();
if ((nextarg != NULL) &&
(optspec.isValRequired() || (! isOption(optctrls, nextarg)))) {
optarg = nextarg; // the argument is here
iter.next(); // end of arg - advance
if (optspec.isList()) listopt = optspec ; // save the list-spec
return optspec.OptChar();
}
// No argument given - if its required, thats an error
optarg = NULLSTR;
if (optspec.isValRequired() && !(optctrls & Options::QUIET)) {
cerr << cmdname << ": argument required for -" << optspec.OptChar()
<< " option." << endl ;
}
return optspec.OptChar();
}
// ---------------------------------------------------------------------------
// ^FUNCTION: Options::parse_longopt - parse a long-option
//
// ^SYNOPSIS:
// int Options::parse_longopt(iter, optarg)
//
// ^PARAMETERS:
// OptIter & iter -- option iterator
// const char * & optarg -- where to store any option-argument
//
// ^DESCRIPTION:
// Parse the next long-option in iter (advancing as necessary).
// Make sure we update the nextchar pointer along the way. Any option
// we find should be returned and optarg should point to its argument.
//
// ^REQUIREMENTS:
// - nextchar must point to the prospective option character
//
// ^SIDE-EFFECTS:
// - iter is advanced when an argument completely parsed
// - optarg is modified to point to any option argument
// - if Options::QUIET is not set, error messages are printed on cerr
//
// ^RETURN-VALUE:
// 'c' if the the long-option corresponding to the -c option was matched
// (optarg points to its argument)
// BADKWD if the option is invalid (optarg points to the bad long-option
// name).
//
// ^ALGORITHM:
// It gets complicated -- follow the comments in the source.
// ^^-------------------------------------------------------------------------
int
Options::parse_longopt(OptIter & iter, const char * & optarg) {
int len = 0, ambiguous = 0;
listopt = NULLSTR ; // reset the list-spec
if ((optvec == NULL) || (! *optvec)) return Options::ENDOPTS;
// if a value is supplied in this argv element, get it now
const char * val = strpbrk(nextchar, ":=") ;
if (val) {
len = val - nextchar ;
++val ;
}
// Try to match a known long-option
OptionSpec optspec = match_longopt(nextchar, len, ambiguous);
// Check for an unknown long-option
if (optspec.isNULL()) {
// See if this was a short-option in disguise
if ((! ambiguous) && (! (optctrls & Options::NOGUESSING))) {
unsigned save_ctrls = optctrls;
const char * save_nextchar = nextchar;
optctrls |= (Options::QUIET | Options::NOGUESSING);
int optchar = parse_opt(iter, optarg);
optctrls = save_ctrls;
if (optchar > 0) {
return optchar;
} else {
nextchar = save_nextchar;
}
}
if (! (optctrls & Options::QUIET)) {
cerr << cmdname << ": " << ((ambiguous) ? "ambiguous" : "unknown")
<< " option "
<< ((optctrls & Options::LONG_ONLY) ? "-" : "--")
<< nextchar << "." << endl ;
}
optarg = nextchar; // record the bad option in optarg
nextchar = NULLSTR; // we've exhausted this argument
return (ambiguous) ? Options::AMBIGUOUS : Options::BADKWD;
}
// If no argument is taken, then leave now
if (optspec.isNoArg()) {
if ((val) && ! (optctrls & Options::QUIET)) {
cerr << cmdname << ": option "
<< ((optctrls & Options::LONG_ONLY) ? "-" : "--")
<< optspec.LongOpt() << " does NOT take an argument." << endl ;
}
optarg = val; // record the unexpected argument
nextchar = NULLSTR; // we've exhausted this argument
return optspec.OptChar();
}
// Check for argument in this arg
if (val) {
optarg = val; // the argument is right here
nextchar = NULLSTR; // we exhausted the rest of this arg
if (optspec.isList()) listopt = optspec ; // save the list-spec
return optspec.OptChar();
}
// Check for argument in next arg
const char * nextarg = iter.curr(); // find the next argument to parse
if ((nextarg != NULL) &&
(optspec.isValRequired() || (! isOption(optctrls, nextarg)))) {
optarg = nextarg; // the argument is right here
iter.next(); // end of arg - advance
nextchar = NULLSTR; // we exhausted the rest of this arg
if (optspec.isList()) listopt = optspec ; // save the list-spec
return optspec.OptChar();
}
// No argument given - if its required, thats an error
optarg = NULLSTR;
if (optspec.isValRequired() && !(optctrls & Options::QUIET)) {
const char * longopt = optspec.LongOpt();
const char * spc = ::strchr(longopt, ' ');
int longopt_len;
if (spc) {
longopt_len = spc - longopt;
} else {
longopt_len = ::strlen(longopt);
}
cerr << cmdname << ": argument required for "
<< ((optctrls & Options::LONG_ONLY) ? "-" : "--");
cerr.write(longopt, longopt_len) << " option." << endl ;
}
nextchar = NULLSTR; // we exhausted the rest of this arg
return optspec.OptChar();
}
// ---------------------------------------------------------------------------
// ^FUNCTION: Options::usage - print usage
//
// ^SYNOPSIS:
// void Options::usage(os, positionals)
//
// ^PARAMETERS:
// ostream & os -- where to print the usage
// char * positionals -- command-line syntax for any positional args
//
// ^DESCRIPTION:
// Print command-usage (using either option or long-option syntax) on os.
//
// ^REQUIREMENTS:
// os should correspond to an open output file.
//
// ^SIDE-EFFECTS:
// Prints on os
//
// ^RETURN-VALUE:
// None.
//
// ^ALGORITHM:
// Print usage on os, wrapping long lines where necessary.
// ^^-------------------------------------------------------------------------
void
Options::usage(ostream & os, const char * positionals) const {
#ifdef NO_USAGE
return;
#else
const char * const * optv = optvec;
unsigned cols = 79;
int first, nloop;
char buf[256] ;
if ((optv == NULL) || (! *optv)) return;
// print first portion "usage: progname"
os << "usage: " << cmdname ;
unsigned ll = strlen(cmdname) + 7;
// save the current length so we know how much space to skip for
// subsequent lines.
//
unsigned margin = ll + 1;
// print the options and the positional arguments
for (nloop = 0, first = 1 ; !nloop ; optv++, first = 0) {
unsigned len;
OptionSpec optspec = *optv;
// figure out how wide this parameter is (for printing)
if (! *optv) {
len = strlen(positionals);
++nloop; // terminate this loop
} else {
if (optspec.isHiddenOpt()) continue;
len = optspec.Format(buf, optctrls);
}
// Will this fit?
if ((ll + len + 1) > (cols - first)) {
os << '\n' ; // No - start a new line;
#ifdef USE_STDIO
for (int _i_ = 0; _i_ < margin; ++_i_) os << " ";
#else
os.width(margin); os << "" ;
#endif
ll = margin;
} else {
os << ' ' ; // Yes - just throw in a space
++ll;
}
ll += len;
os << ((nloop) ? positionals : buf) ;
}// for each ad
os << endl ;
#endif /* NO_USAGE */
}
// ---------------------------------------------------------------------------
// ^FUNCTION: Options::operator() - get options from the command-line
//
// ^SYNOPSIS:
// int Options::operator()(iter, optarg)
//
// ^PARAMETERS:
// OptIter & iter -- option iterator
// const char * & optarg -- where to store any option-argument
//
// ^DESCRIPTION:
// Parse the next option in iter (advancing as necessary).
// Make sure we update the nextchar pointer along the way. Any option
// we find should be returned and optarg should point to its argument.
//
// ^REQUIREMENTS:
// None.
//
// ^SIDE-EFFECTS:
// - iter is advanced when an argument is completely parsed
// - optarg is modified to point to any option argument
// - if Options::QUIET is not set, error messages are printed on cerr
//
// ^RETURN-VALUE:
// 0 if all options have been parsed.
// 'c' if the the option or long-option corresponding to the -c option was
// matched (optarg points to its argument).
// BADCHAR if the option is invalid (optarg points to the bad option char).
// BADKWD if the option is invalid (optarg points to the bad long-opt name).
// AMBIGUOUS if an ambiguous keyword name was given (optarg points to the
// ambiguous keyword name).
// POSITIONAL if PARSE_POS was set and the current argument is a positional
// parameter (in which case optarg points to the positional argument).
//
// ^ALGORITHM:
// It gets complicated -- follow the comments in the source.
// ^^-------------------------------------------------------------------------
int
Options::operator()(OptIter & iter, const char * & optarg) {
int parse_opts_only = isOptsOnly(optctrls);
if (parse_opts_only) explicit_end = 0;
// See if we have an option left over from before ...
if ((nextchar) && *nextchar) {
return parse_opt(iter, optarg);
}
// Check for end-of-options ...
const char * arg = NULLSTR;
int get_next_arg = 0;
do {
arg = iter.curr();
get_next_arg = 0;
if (arg == NULL) {
listopt = NULLSTR;
return Options::ENDOPTS;
} else if ((! explicit_end) && isEndOpts(arg)) {
iter.next(); // advance past end-of-options arg
listopt = NULLSTR;
explicit_end = 1;
if (parse_opts_only) return Options::ENDOPTS;
get_next_arg = 1; // make sure we look at the next argument.
}
} while (get_next_arg);
// Do we have a positional arg?
if ( explicit_end || (! isOption(optctrls, arg)) ) {
if (parse_opts_only) {
return Options::ENDOPTS;
} else {
optarg = arg; // set optarg to the positional argument
iter.next(); // advance iterator to the next argument
return Options::POSITIONAL;
}
}
iter.next(); // pass the argument that arg already points to
// See if we have a long option ...
if (! (optctrls & Options::SHORT_ONLY)) {
if ((*arg == '-') && (arg[1] == '-')) {
nextchar = arg + 2;
return parse_longopt(iter, optarg);
} else if ((optctrls & Options::PLUS) && (*arg == '+')) {
nextchar = arg + 1;
return parse_longopt(iter, optarg);
}
}
if (*arg == '-') {
nextchar = arg + 1;
if (optctrls & Options::LONG_ONLY) {
return parse_longopt(iter, optarg);
} else {
return parse_opt(iter, optarg);
}
}
// If we get here - it is because we have a list value
OptionSpec optspec = listopt;
optarg = arg ; // record the list value
return optspec.OptChar() ;
}