Files
bootloader/apps/elftosb/common/options.cpp
László Monda e6c1fce5b4 Add KBOOT.
2016-08-10 01:45:15 +02:00

1264 lines
35 KiB
C++

// ****************************************************************************
// ^FILE: options.c - implement the functions defined in <options.h>
//
// ^HISTORY:
// 01/16/92 Brad Appleton <bradapp@enteract.com> Created
//
// 03/23/93 Brad Appleton <bradapp@enteract.com>
// - Added OptIstreamIter class
//
// 10/08/93 Brad Appleton <bradapp@enteract.com>
// - Added "hidden" options
//
// 02/08/94 Brad Appleton <bradapp@enteract.com>
// - Added "OptionSpec" class
// - Permitted use of stdio instead of iostreams via #ifdef USE_STDIO
//
// 03/08/94 Brad Appleton <bradapp@enteract.com>
// - 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 <bradapp@enteract.com>
// - 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();
}