0% found this document useful (0 votes)
14 views

Options A C++ Option-Parser

The document describes Options, a C++ library for parsing Unix-style command-line options. It allows specifying options with short names like -c or long names like --count. Options understands option arguments and can check for required, optional, or no arguments. The summary shows how to declare allowed options, parse them from command lines, and handle different argument types and option values.

Uploaded by

pika lord
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
14 views

Options A C++ Option-Parser

The document describes Options, a C++ library for parsing Unix-style command-line options. It allows specifying options with short names like -c or long names like --count. Options understands option arguments and can check for required, optional, or no arguments. The summary shows how to declare allowed options, parse them from command lines, and handle different argument types and option values.

Uploaded by

pika lord
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 1

Brad Appleton

Software Tools Developer


E-mail:brad@bradapp.net
WWW: http://www.bradapp.net

Options: a C++ option-parser

Introduction to 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).
Using 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"
}

Option Specification Syntax


Note the character (one of ':', '?', '|', '*', or '+') between the short and long name of the option in
"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]

Noteworthy Exceptions:

If the 1st character of the string is '-', then the rest of the string must correspond to the above
format, and the option is considered to be a hidden-option. This means it will be parsed when
actually matching options from the command-line, but will not show-up if a usage message is
printed using the 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).

Examples:
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> ...]

Intermixing options with positional parameters

If you want 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.
An Extended Example
The following is the C++ code for a simple program that uses 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 ;
}
}

back to Brad Appleton's Home Page

You might also like