mirror of https://github.com/CGAL/cgal
1148 lines
33 KiB
C
1148 lines
33 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.
|
|
// ^^**************************************************************************
|
|
|
|
#ifdef USE_STDIO
|
|
# include <stdio.h>
|
|
#else
|
|
# include <iostream>
|
|
#endif
|
|
|
|
// #include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
|
|
#include "options.h"
|
|
|
|
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
|
|
|
|
void OptArgvIter::init(int argc, const char * const argv[])
|
|
{av=argv; ac=argc; ndx=0;}
|
|
|
|
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() ;
|
|
}
|
|
|