|
Options
Options
is a C++ library for parsing Unix-style
command-line options. The full source code distribution for
Options
may be found in Options.tar.gz
(22.5KB, gzipped tar
file).
Options
understands options and gnu-long-options and the
parsing behavior is somewhat configurable: You may specify options to be
be case insensitive, matched by long (keyword) name or short (single
character) name; and a number of other features (see the file
<options.h
> for a complete description).
Options
Options
defines a C++ class of the same name which
represents the allowable set of command-line options and how to parse
them:
#include <options.h> Options opts(cmdname, optv); char cmdname[], *optv[];You "specify" your options by declaring an array of strings like so:
const char * optv[] = {
"c:count <number>",
"s?str <string>",
"x|xmode",
NULL
} ;
Now you can iterate over your options from the command-line as follows:
#include <stdlib.h>
#include <options.h>
main(int argc, char *argv[]) {
Options opts(*argv, optv);
OptArgvIter iter(--argc, ++argv);
const char *optarg, *str = NULL;
int errors = 0, xflag = 0, count = 1;
while( char optchar = opts(iter, optarg) ) {
switch (optchar) {
case 's' :
str = optarg; break;
case 'x' :
++xflag; break;
case 'c' :
if (optarg == NULL) ++errors;
else count = (int) atol(optarg);
break;
default : ++errors; break;
} //switch
}
... // process the rest of the arguments in "iter"
}
"c:count <number>"
.
It specifies the option type:
'|' | -- indicates that the option takes no argument; |
---|---|
'?' | -- indicates that the option takes an optional argument; |
':' | -- indicates that the option takes a required argument; |
'*' | -- indicates that the option takes zero or more arguments; |
'+' | -- indicates that the option takes one or more arguments; |
The remainder of the string must be the long-option name. Please note that long-option names are matched case-insensitive and only a unique prefix of the name needs to be specified. By default, option-name characters are case-sensitive (but this may be changed by turning on a run-time flag).
If desired, the long-option name may be followed by one or more
spaces and then by the name of the option value. This name will
be used when printing usage messages. If the option-value-name
is not given then the string "<value>"
will be used
in usage messages.
One may use a space to indicate that a particular option does not
have a corresponding long-option. For example, "c: "
(or "c:"
) means the -c
option takes a value
and has no corresponding long-option.
To specify a long-option that has no corresponding single-character
option is a bit trickier: Options::operator()
still needs
an "option-character" to return when that option is matched. One may use
a whitespace character or a non-printable character as the single-character
option in such a case. (hence " |hello"
would only match
"--hello
").
Using the previous example, optv[]
now corresponds to the
following command-line syntax:
progname [-c <number>] [-s [<string>]] [-x]
Using long-options, optv[]
corresponds to the following
("-" or "+" may be used instead of "--" as the prefix):
progname [--count <number>] [--str [<string>]] [--xmode]
usage()
member
function. Such an example might be "-h|hidden"
. If you want
to use any "dummy" options (options that are not parsed, but that to
show up in the usage message), you can specify them along with any
positional parameters to the usage()
member function.
If the 2nd character of the string is '\0'
then it is assumed
that there is no corresponding long-option and that the option takes no
argument (hence "f"
, and "f| "
are equivalent).
const char * optv[] = {
"c:count <number>",
"s?str <string>",
"x",
" |hello",
"g+groups <newsgroup>",
NULL
} ;
Now optv[]
corresponds to the following:
usage: cmdname [-c|--count <number>] [-s|--str [<string>]]
[-x] [--hello] [-g|--groups <newsgroup> ...]
Options
to parse your positional arguments too
(in case they are intermingled in with your options) then you can do
that by turning on a special control-flag:
#include <options.h>
main(int argc, char *argv[]) {
Options opts(*argv, optv);
OptArgvIter iter(--argc, ++argv);
char *optarg, *str = NULL;
int errors = 0, xflag = 0, count = 1;
opts.ctrls(Options::PARSE_POS);
while( char optchar = opts(iter, optarg) ) {
switch (optchar) {
case 's' :
str = optarg; break;
case 'x' :
++xflag; break;
case 'c' :
if (optarg == NULL) ++errors;
else count = (int) atol(optarg);
break;
case Options::POSITIONAL :
// handle positional arguments here ...
// (optarg points to the positional argument)
break;
default : ++errors; break;
} //switch
}
}
Options
makes use of an abstract argument-iterator object
to access your command-line arguments. Pre-defined iterator types are
provided for parsing options from an array (typically
argv[]
), an input stream, and a field-delimited string
(using strtok(3C)
). This is convenient for parsing
configurations options from a start-up file or an environment variable.
A side-effect of this however is that Options
cannot assume
the argument source is necessarily argv[]
(which is the
normal case). This means that it cannot permute the argument list (like
GNU getoptlong
) to process all options ahead of positional
arguments when the two are intermixed on the command-line. This can
easily be accomodated however by rearranging the argv[]
yourself as follows:
#include <stdlib.h>
#include <options.h>
main(int argc, char *argv[]) {
Options opts(*argv, optv);
OptArgvIter iter(--argc, ++argv);
char *optarg, *str = NULL;
int errors = 0, xflag = 0, count = 1;
int npos = 0;
opts.ctrls(Options::PARSE_POS);
while( char optchar = opts(iter, optarg) ) {
switch (optchar) {
case 's' :
str = optarg; break;
case 'x' :
++xflag; break;
case 'c' :
if (optarg == NULL) ++errors;
else count = (int) atol(optarg);
break;
case Options::POSITIONAL :
// Push all positional arguments to the front. Note that
// we could swap argv[npos] with argv[iter.index() - 1]
// (assuming we have made sure they arent in fact one and
// the same) if we dont want to lose its previous value.
argv[npos++] = optarg;
break;
default : ++errors; break;
} //switch
}
// Now argv[0] .. argv[npos - 1] contains the positional arguments
for (int i = 0; i < npos; ++i) {
// handle positional argument in argv[i] ...
}
}
The above simply replaces the beginning elements in argv[]
with an argument that have already been parsed, thus moving all the
positional parameters to the front. If you prefer not to lose the
already parsed options, you could do a number of different things. You
could simply perform a swap instead of a replacement (as is mentioned in
the comment above) or you could allocate a new array to hold the
positional arguments, or you could go to the effort of truly permuting
argv[]
yourself.
Options
to parse the command-line and then print
out each command-line option and argument that was specified:
#include <stdlib.h>
#include <options.h>
// Specify options and their syntax
static const char * optv[] = {
"H|help",
"c:count <number>",
"s?str <string>",
"x",
" |hello",
"g+groups <newsgroup>",
NULL
} ;
main(int argc, char * argv[]) {
// Declare storage for the option-letter and its argument (if any).
int optchar;
const char * optarg;
// Set default option values.
const char * str = "default_string";
int count = 0, xflag = 0, hello = 0;
int errors = 0, ngroups = 0;
// Declare Options object and its iterator
Options opts(*argv, optv);
OptArgvIter iter(--argc, ++argv);
// Iterate over options and handle each one as appropriate
while( optchar = opts(iter, optarg) ) {
switch (optchar) {
case 'H' :
opts.usage(cout, "files ...");
exit(0);
break;
case 'g' :
++ngroups; break; // the groupname is in "optarg"
case 's' :
str = optarg; break;
case 'x' :
++xflag; break;
case ' ' :
++hello; break;
case 'c' :
if (optarg == NULL) ++errors;
else count = (int) atol(optarg);
break;
default : ++errors; break; // unknown option
} //switch
}
// Print an error message if bad syntax was used, or if no
// filenames were specified on the command-line.
if (errors || (iter.index() == argc)) {
if (! errors) {
cerr << opts.name() << ": no filenames given." << endl ;
}
opts.usage(cerr, "files ...");
exit(1);
}
// Print out the option values that were specified.
cout << "xflag=" << ((xflag) ? "ON" : "OFF") << endl
<< "hello=" << ((hello) ? "YES" : "NO") << endl
<< "count=" << count << endl
<< "str=\"" << ((str) ? str : "No value given!") << "\"" << endl
<< "ngroups=" << ngroups << endl ;
// Print out the remaining positional arguments on the command-line
if (iter.index() < argc) {
cout << "files=" ;
for (int i = iter.index() ; i < argc ; i++) {
cout << "\"" << argv[i] << "\" " ;
}
cout << endl ;
}
}