home *** CD-ROM | disk | FTP | other *** search
Text File | 1992-07-26 | 55.6 KB | 1,797 lines |
- Newsgroups: comp.sources.misc
- From: brad@hcx1.ssd.csd.harris.com (Brad Appleton)
- Subject: v31i045: options - C++ library for parsing Unix-style command-lines, Part01/02
- Message-ID: <csm-v31i045=options.205059@sparky.IMD.Sterling.COM>
- X-Md4-Signature: ea98ddf612be7937155e18e49d25efd5
- Date: Mon, 27 Jul 1992 01:52:21 GMT
- Approved: kent@sparky.imd.sterling.com
-
- Submitted-by: brad@hcx1.ssd.csd.harris.com (Brad Appleton)
- Posting-number: Volume 31, Issue 45
- Archive-name: options/part01
- Environment: C++
-
- What follows are two different C++ packages for parsing command-line
- arguments: "Options" and "CmdLine". Options is a "bare-bones" package
- that does just the basics with very few bells and whistles thrown in.
- CmdLine on the other hand is a fully object-oriented approach to
- command-line parsing that does more than most of you could ever want.
- CmdLine also comes with a program named "cmdparse" (which is similar
- to Parseargs) to parse arguments for your shell-scripts (including
- perl and tcl).
-
- If you are the least bit interested (or in need of) C++ command-line
- parsing, then please take a look at one (or both) of these packages.
- Although I have gone through extensive alpha and beta testing, I am
- very anxious to get feedback and comments (constructive of course)
- from the C++ programming community at large.
-
- ============================================================================
-
- This is "Options", a C++ library for parsing Unix-style command-line options.
- Options understands options and gnu-long-options and the parsing behavior is
- somewhat configurable. See the documentation (or the file <options.h>) for a
- complete description.
-
- You "declare" your options by declaring an array of strings like so:
-
- const char * optv[] = {
- "c:count <number>",
- "s?str <string>",
- "x|xmode",
- NULL
- } ;
-
- Note the character (one of ':', '?', '|', '*', or '+') between the short
- and long name of the option. 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 0 or more arguments;
- '+' -- indicates that the option takes 1 or more arguments;
-
- Using the above example, optv[] now corresponds to the following:
-
- 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]
-
- Now you can iterate over your options like so:
-
- #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"
- }
-
-
-
- Please look at the README file for further information!
-
- ______________________ "And miles to go before I sleep." ______________________
- Brad Appleton Harris Corp., Computer Systems Division
- Senior Software Engineer 2101 West Cypress Creek Road, M/S 161
- brad@ssd.csd.harris.com Fort Lauderdale, FL 33309-1892 USA
- ...!uunet!travis!brad Phone: (305) 973-5190
- ~~~~~~~~~~~~~~~~~~~~ Disclaimer: I said it, not my company! ~~~~~~~~~~~~~~~~~~~
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then feed it
- # into a shell via "sh file" or similar. To overwrite existing files,
- # type "sh file -c".
- # The tool that generated this appeared in the comp.sources.unix newsgroup;
- # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
- # Contents: README options.3 options.C testopts.C
- # Wrapped by kent@sparky on Sun Jul 26 20:47:43 1992
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- echo If this archive is complete, you will see the following message:
- echo ' "shar: End of archive 1 (of 2)."'
- if test -f 'README' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'README'\"
- else
- echo shar: Extracting \"'README'\" \(5133 characters\)
- sed "s/^X//" >'README' <<'END_OF_FILE'
- X
- X WHAT IS THIS?
- X =============
- X This is "Options", a C++ library for parsing Unix-style command-line options.
- X Options understands options and gnu-long-options and the parsing behavior is
- X somewhat configurable. See the documentation (or the file <options.h>) for a
- X complete description.
- X
- X You "declare" your options by declaring an array of strings like so:
- X
- X const char * optv[] = {
- X "c:count <number>",
- X "s?str <string>",
- X "x|xmode",
- X NULL
- X } ;
- X
- X Note the character (one of ':', '?', '|', '*', or '+') between the short
- X and long name of the option. It specifies the option type:
- X
- X '|' -- indicates that the option takes NO argument;
- X '?' -- indicates that the option takes an OPTIONAL argument;
- X ':' -- indicates that the option takes a REQUIRED argument;
- X '*' -- indicates that the option takes 0 or more arguments;
- X '+' -- indicates that the option takes 1 or more arguments;
- X
- X Using the above example, optv[] now corresponds to the following:
- X
- X progname [-c <number>] [-s [<string>]] [-x]
- X
- X Using long-options, optv corresponds to the following ("-" or "+" may
- X be used instead of "--" as the prefix):
- X
- X progname [--count <number>] [--str [<string>]] [--xmode]
- X
- X Now you can iterate over your options like so:
- X
- X #include <stdlib.h>
- X #include <options.h>
- X
- X main(int argc, char *argv[]) {
- X Options opts(*argv, optv);
- X OptArgvIter iter(--argc, ++argv);
- X const char *optarg, *str = NULL;
- X int errors = 0, xflag = 0, count = 1;
- X
- X while( char optchar = opts(iter, optarg) ) {
- X switch (optchar) {
- X case 's' :
- X str = optarg; break;
- X case 'x' :
- X ++xflag; break;
- X case 'c' :
- X if (optarg == NULL) ++errors;
- X else count = (int) atol(optarg);
- X break;
- X default : ++errors; break;
- X } //switch
- X }
- X ... // process the rest of the arguments in "iter"
- X }
- X
- X
- X AUTHOR
- X ======
- X Brad Appleton Harris Corp., Computer Systems Division
- X Senior Software Engineer 2101 West Cypress Creek Road, M/S 161
- X brad@ssd.csd.harris.com Fort Lauderdale, FL 33309-1892 USA
- X ...!uunet!travis!brad Phone: (305) 973-5190
- X
- X
- X COPY/REUSE POLICY
- X =================
- X Permission is hereby granted to freely copy and redistribute this
- X software, provided that the author is clearly credited in all copies
- X and derivations. Neither the names of the authors nor that of their
- X employers may be used to endorse or promote products derived from this
- X software without specific written permission.
- X
- X
- X DISCLAIMER
- X ==========
- X This software is provided ``As Is'' and without any express or implied
- X warranties. Neither the authors nor any of their employers (including
- X any of their subsidiaries and subdivisions) are responsible for maintaining
- X or supporting this software or for any consequences resulting from the
- X use of this software, no matter how awful, even if they arise from flaws
- X in the software.
- X
- X
- X CONTENTS
- X ========
- X This release should contain the following files:
- X Makefile -- the makefile
- X README -- this file
- X options.3 -- Unix manual page(s) for the Options class
- X options.h -- include file and documentation for the Options class
- X options.C -- the C++ source for the Options class
- X testopts.C -- a test program for the Options class
- X
- X
- X REQUIREMENTS
- X ============
- X This software should compile on most Unix platforms with a C++ compiler
- X with little or no difficulty.
- X
- X
- X PORTING
- X =======
- X Options uses the AT&T C++ iostream library. Beyond that, all the
- X #include files it uses are assumed to have the contents specified by
- X the ANSI-C standard and are assumed to have #ifdef __cplusplus statements
- X for when they are being included by C++ files. Options assumes the
- X existence of the following system header files:
- X
- X <stdarg.h>
- X <stdlib.h>
- X <string.h>
- X <ctype.h>
- X <iostream.h>
- X
- X Other porting problems you may experience are as follows:
- X
- X - you may need to use <varargs.h> instead of <stdarg.h>
- X
- X You will need to tweak the Makefile a tad in order to make it work
- X for your C++ compiler.
- X
- X
- X BUGS
- X ====
- X Please send all bug reports to Brad Appleton <brad@ssd.csd.harris.com>.
- X Dont forget to mention which version of Options you have and which
- X operating system and C++ compiler you are using.
- X
- X
- X ACKNOWLEDGEMENTS
- X ================
- X Options is a complete C++ re-write of an ANSI-C package named "getopts"
- X that I wrote for the FSF that was posted on one of the "gnu" newsgroups
- X (I think it was the group gnu.utils.bug) in February of 1992. Many thanks
- X to David J. MacKenzie for his input. Options provides may more features
- X than "getopts" did.
- X
- X
- X PATCHLEVEL
- X ==========
- X The is release 1 of Options at patchlevel 0.
- X
- X
- X HISTORY
- X =======
- X
- X 07/21/92 Brad Appleton <brad@ssd.csd.harris.com>
- X -----------------------------------------------------------------------------
- X First release.
- X
- END_OF_FILE
- if test 5133 -ne `wc -c <'README'`; then
- echo shar: \"'README'\" unpacked with wrong size!
- fi
- # end of 'README'
- fi
- if test -f 'options.3' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'options.3'\"
- else
- echo shar: Extracting \"'options.3'\" \(14308 characters\)
- sed "s/^X//" >'options.3' <<'END_OF_FILE'
- X.\"---------- TO PRINT, USE: {n,t}roff -man file ----------
- X.if n .po 1
- X.if n .ll 78
- X.\"-----------------------------------
- X.ds NM \f4Options\fP
- X.de XS
- X. RS
- X. nf
- X. ft 4
- X..
- X.de XE
- X. ft R
- X. fi
- X. RE
- X..
- X.ds -- \-\|\-
- X.ds C+ C\s-2\v'-1.5p'\(pl\(pl\v'+1.5p'\s+2
- X.ds .. .\^.\^.
- X.\"-------------------------------------------
- X.\" The following "strings" should correspond to where the CmdLine files
- X.\" have been installed. "i" is where the include files where installed
- X.\" and "l" is where the object-library is installed. You should change
- X.\" the definition of these macros if they are not installed in the
- X.\" places indicated below.
- X.\"-------------------------------------------
- X.ds i /usr/local/include
- X.ds l /usr/local/lib
- X.\"-----------------------------------
- X.TH Options 3\*(C+
- X.\"-----------------------------------
- X.SH NAME
- XOptions \- A \*(C+ class for parsing command-line options
- X.\"-----------------------------------
- X.SH SYNOPSIS
- XThe following are all defined in the include file \f4<options.h>\fP:
- X
- X.nf
- X.ft 4
- X.ps -2
- X// Abstract class to iterate through options/arguments
- X//
- Xclass OptIter {
- Xpublic:
- X OptIter(void);
- X virtual ~OptIter(void);
- X
- X.vs -2
- X // curr() returns the current item in the iterator without
- X // advancing on to the next item. If we are at the end of items
- X // then NULL is returned.
- X.vs +2
- X virtual const char * curr(void) = 0;
- X
- X // next() advances to the next item.
- X virtual void next(void) = 0;
- X
- X.vs -2
- X // operator() returns the current item in the iterator and then
- X // advances on to the next item. If we are at the end of items
- X // then NULL is returned.
- X.vs +2
- X virtual const char * operator()(void);
- X} ;
- X
- X// Abstract class for a rewindable OptIter
- X//
- Xclass OptIterRwd : public OptIter {
- Xpublic:
- X OptIterRwd(void);
- X virtual ~OptIterRwd(void);
- X
- X virtual const char * curr(void) = 0;
- X virtual void next(void) = 0;
- X virtual const char * operator()(void) = 0;
- X
- X // rewind() resets the "current-element" to the first one in the "list"
- X virtual void rewind(void) = 0;
- X} ;
- X
- X.vs -2
- X// Class to iterate through an array of tokens. The array may be terminated
- X// by NULL or a count containing the number of tokens may be given.
- X//
- X.vs +2
- Xclass OptArgvIter : public OptIterRwd {
- Xpublic:
- X OptArgvIter(const char * const argv[]);
- X OptArgvIter(int argc, const char * const argv[]);
- X virtual ~OptArgvIter(void);
- X virtual const char * curr(void);
- X virtual void next(void);
- X virtual const char * operator()(void);
- X virtual void rewind(void);
- X
- X // index returns the current index to use for argv[]
- X int index(void);
- X} ;
- X
- X
- X// Class to iterate through a string containing delimiter-separated tokens
- X//
- Xclass OptStrTokIter : public OptIterRwd {
- Xpublic:
- X OptStrTokIter(const char * tokens, const char * delimiters =0);
- X virtual ~OptStrTokIter(void);
- X virtual const char * curr(void);
- X virtual void next(void);
- X virtual const char * operator()(void);
- X virtual void rewind(void);
- X
- X.vs -2
- X // delimiters() with NO arguments returns the current set of delimiters,
- X // If an argument is given then it is used as the new set of delimiters.
- X.vs +2
- X const char * delimiters(void);
- X void delimiters(const char * delims);
- X} ;
- X
- X
- X.vs -2
- X// Class to hold option-specifications and parsing-specifications
- X//
- X.vs +2
- Xclass Options {
- Xpublic:
- X // Flags to control parsing behavior
- X enum OptCtrl {
- X DEFAULT = 0x00, // Default setting
- X ANYCASE = 0x01, // Ignore case when matching short-options
- X QUIET = 0x02, // Dont print error messages
- X PLUS = 0x04, // Allow "+" as a long-option prefix
- X SHORT_ONLY = 0x08, // Dont accept long-options
- X LONG_ONLY = 0x10, // Dont accept short-options
- X.vs -2
- X // (also allows "-" as a long-option prefix).
- X.vs +2
- X NOGUESSING = 0x20, // Normally, when we see a short (long) option
- X.vs -2
- X // on the command line that doesnt match any
- X // known short (long) options, then we try to
- X // "guess" by seeing if it will match any known
- X // long (short) option. Setting this mask prevents
- X // this "guessing" from occurring.
- X.vs +2
- X } ;
- X
- X // Error return values for operator()
- X enum OptRC { ENDOPTS = 0, BADCHAR = -1, BADKWD = -2, AMBIGUOUS = -3 } ;
- X
- X Options(const char * name, const char * const optv[]);
- X
- X virtual ~Options(void);
- X
- X // name() returns the command name
- X const char * name(void) const;
- X
- X // ctrls() (with 1 argument) sets new control settings
- X unsigned ctrls(void) const;
- X
- X // ctrls() (with no arguments) returns the existing control settings.
- X void ctrls(unsigned newctrls);
- X
- X.vs -2
- X // usage() prints options usage (followed by any positional arguments
- X // given) on the given outstream
- X.vs +2
- X void usage(ostream & os, const char * positionals) const ;
- X
- X.vs -2
- X // operator() iterates through the arguments as necessary (using the
- X // given iterator) and returns the character value of the option
- X // (or long-option) that it matched. If the option has a value
- X // then the value given may be found in optarg (otherwise optarg
- X // will be NULL).
- X //
- X // 0 is returned upon end-of-options. At this point, "iter" may
- X // be used to process any remaining positional parameters.
- X //
- X // If an invalid option is found then BADCHAR is returned and *optarg
- X // is the unrecognized option character.
- X //
- X // If an invalid long-option is found then BADKWD is returned and optarg
- X // points to the bad long-option.
- X //
- X // If an ambiguous long-option is found then AMBIGUOUS is returned and
- X // optarg points to the ambiguous long-option.
- X //
- X // Unless Options::QUIET is used, missing option-arguments and
- X // invalid options (and the like) will automatically cause error
- X // messages to be issued to cerr.
- X.vs +2
- X int operator()(OptIter & iter, const char * & optarg) ;
- X
- X.vs -2
- X // Call this member function after operator() has returned 0
- X // if you want to know whether or not options were explicitly
- X // terminated because "--" appeared on the command-line.
- X //
- X.vs +2
- X int
- X explicit_endopts() const ;
- X} ;
- X
- X.ps +2
- X.ft R
- X.fi
- X.\"-----------------------------------
- X.SH DESCRIPTION
- X.PP
- XThe Options constructor expects a command-name (usually \f4argv[0]\fP) and
- Xa pointer to an array of strings. The last element in this array \fImust\fP
- Xbe NULL. Each non-NULL string in the array must have the following format:
- X
- X.in +5
- XThe 1st character must be the option-name (`c' for a \fB\-c\fP option).
- X
- XThe 2nd character must be one of `\f4|\fP', `?', `:', `*', or `+'.
- X
- X.in +3
- X`\f4|\fP' \tindicates that the option takes \fIno\fP argument;
- X.br
- X`?' \tindicates that the option takes an \fIoptional\fP argument;
- X.br
- X`:' \tindicates that the option takes a \fIrequired\fP argument;
- X.br
- X`*' \tindicates that the option takes \fIzero or more\fP arguments;
- X.br
- X`+' \tindicates that the option takes \fIone or more\fP arguments;
- X.br
- X.in -3
- X
- XThe remainder of the string must be the long-option name.
- X
- X.PP
- XIf desired, the long-option name may be followed by one or more
- Xspaces and then by the name of the option value. This name will
- Xbe used when printing usage messages. If the option-value-name
- Xis not given then the string "<value>" will be used in usage
- Xmessages.
- X
- X.PP
- XOne may use a space to indicate that a particular option does not
- Xhave a corresponding long-option. For example, \f4"c: "\fP (or \f4"c:"\fP)
- Xmeans the \fB\-c\fP option takes a value and has \fIno\fP corresponding
- Xlong-option.
- X
- X.PP
- XTo specify a long-option that has no corresponding single-character
- Xoption is a bit trickier: \f4Options::operator()\fP still needs an
- X``\fIoption character\fP'' to return when that option is matched. One may
- Xuse a whitespace character or a non-printable character as the
- Xsingle-character option in such a case. (hence \f4" |hello"\fP would
- Xonly match \fB\-\|\-hello\fP).
- X.in -5
- X
- X.SS Exceptions to the Above:
- X.PP
- XIf the second character of the string is \f4'\\0'\fP then it is assumed
- Xthat there is no corresponding long-option and that the option
- Xtakes no argument (hence \f4"f"\fP, and \f4"f| "\fP are equivalent).
- X
- X.SS Examples:
- X.br
- X.nf
- X.ft 4
- X.in +5
- Xconst char * optv[] = {
- X "c:count <number>",
- X "s?str <string>",
- X "x",
- X " |hello",
- X "g+groups <newsgroup>",
- X NULL
- X} ;
- X.in -5
- X.ft R
- X.fi
- X
- X\f4optv[]\fP now corresponds to the following:
- X
- X.na
- X.TP 18
- XUsage: \fBcmdname\fP
- X[\fB\-c\fP\f4|\fP\fB\-\|\-count\fP\ \fInumber\fP]
- X[\fB\-s\fP\f4|\fP\fB\-\|\-str\fP\ [\fIstring\fP]]
- X[\fB\-x\fP]
- X[\fB\-\|\-hello\fP]
- X[\fB\-g\fP\f4|\fP\fB\-\|\-groups\fP\ \fInewsgroup\fP\ ...]
- X.ad
- X
- X.PP
- XLong-option names are matched case-insensitive and only a unique prefix
- Xof the name needs to be specified.
- X
- XOption-name characters are case-sensitive!
- X
- X.\"-----------------------------------
- X.SH EXAMPLE
- X.nf
- X.ft 4
- X#include <stdlib.h>
- X#include <iostream.h>
- X#include <options.h>
- X
- Xstatic const char * optv[] = {
- X "H|help",
- X "c:count <number>",
- X "s?str <string>",
- X "x",
- X " |hello",
- X "g+groups <newsgroup>",
- X NULL
- X} ;
- X
- Xmain(int argc, char * argv[]) {
- X int optchar;
- X const char * optarg;
- X const char * str = "default_string";
- X int count = 0, xflag = 0, hello = 0;
- X int errors = 0, ngroups = 0;
- X
- X Options opts(*argv, optv);
- X OptArgvIter iter(--argc, ++argv);
- X
- X while( optchar = opts(iter, optarg) ) {
- X switch (optchar) {
- X case 'H' :
- X opts.usage("files ...", cout);
- X exit(0);
- X break;
- X case 'g' :
- X ++ngroups; break; // the groupname is in "optarg"
- X case 's' :
- X str = optarg; break;
- X case 'x' :
- X ++xflag; break;
- X case ' ' :
- X ++hello; break;
- X case 'c' :
- X if (optarg == NULL) ++errors;
- X else count = (int) atol(optarg);
- X break;
- X default : ++errors; break;
- X } //switch
- X }
- X
- X if (errors || (iter.index() == argc)) {
- X if (! errors) {
- X cerr << opts.name() << ": no filenames given." << endl ;
- X }
- X opts.usage("files ...", cerr);
- X exit(1);
- X }
- X
- X cout << "xflag=" << ((xflag) ? "ON" : "OFF") << endl
- X << "hello=" << ((hello) ? "YES" : "NO") << endl
- X << "count=" << count << endl
- X << "str=\"" << ((str) ? str : "No value given!") << "\"" << endl
- X << "ngroups=" << ngroups << endl ;
- X
- X if (iter.index() < argc) {
- X cout << "files=" ;
- X for (int i = iter.index() ; i < argc ; i++) {
- X cout << "\"" << argv[i] << "\" " ;
- X }
- X cout << endl ;
- X }
- X}
- X
- X.ft R
- X.fi
- X
- X.\"-----------------------------------
- X.SH PARSING
- XAlthough, much of the parsing behavior of \*(NM can be configured at run-time
- Xthere are some \fIcommon rules\fP that are used when parsing command-line
- Xarguments. Many of these so called \fIrules\fP are just a formalization
- Xof things that have become an informal standard over the years.
- X
- X.SS "LONG AND SHORT OPTIONS"
- X.RS
- XBy default, \*(NM will allow both single-character options \fIand\fP
- Xkeywords (long-options) to be matched on the command-line.
- XUnder Unix, a single character option is prefixed by the string ``\-''.
- Xand a long-option is prefixed by the string ``\*(--'' (although the prefix
- X``+'' may also be used if desired). If a token on the command-line exactly
- Xmatches the string ``\*(--'', then all further matching of options (both
- Xlong and short) are disabled and any remaining arguments are considered to
- Xbe positional parameters (even if they look like options).
- X
- XIf short-option processing is disabled, then the prefix ``\-'' may be used
- Xto indicate a long-option (the ``\*(--'' prefix will still be accepted).
- X
- X.SS "OPTION MATCHING"
- X.RS
- XShort-option matching is case-sensitive. Long-options are always matched
- Xcase-insensitive and only a unique prefix of the long-option name needs
- Xto be given in order for it to be matched successfully.
- X.RE
- X
- X.SS "SPECIFYING VALUES TO OPTIONS"
- X.RS
- XBy default, \*(NM will allow the value for an option to be in the
- Xsame command-line token as the option itself, or in a separate
- Xcommand-line token. For short-options, specifying the value in
- Xthe same token simply means immediately following the option
- Xcharacter with the intended value as in "\fB\-c\fIvalue\fR".
- XFor long-options, specifying the value in the same token requires
- Xthat the value be separated from the keyword name by an equal-sign
- X(`=') or by a colon (`:') as in ``\fB\*(--keyword\fP=\fIvalue\fP''
- Xor ``\fB\*(--keyword\fP:\fIvalue\fP''.
- X.RE
- X
- X.\"-----------------------------------
- X.SH CAVEAT
- XBecause of the way in which multi-valued options and options with optional
- Xvalues are handled, it is \fInot\fP possible to supply a value to an
- Xoption in a separate argument (different \f4argv[]\fP element) if the
- Xvalue is \fIoptional\fP and begins with a `\-'. What this means is that
- Xif an option \fB\-s\fP takes an optional value value and you wish to supply
- Xa value of ``\f4-foo\fP'' then you must specify this on the command-line as
- X``\f4\-s\-foo\fP'' instead of ``\f4\-s \-foo\fP'' because the latter
- Xwill be considered to be two separate sets of options.
- X
- XA multi-valued option is terminated by another option or by the end-of
- Xoptions. The following are all equivalent (if \fB\-l\fP is a multi-valued
- Xoption and \fB\-x\fP is an option that takes no value):
- X
- X.nf
- X.in +5
- X.ft 4
- Xcmdname -x -l item1 item2 item3 -- arg1 arg2 arg3
- Xcmdname -x -litem1 -litem2 -litem3 -- arg1 arg2 arg3
- Xcmdname -l item1 item2 item3 -x arg1 arg2 arg3
- X.ft R
- X.in -5
- X.fi
- X
- X.\"-----------------------------------
- X.SH FILES
- X.PP
- X.IP \f4\*i/options.h\fP
- X\*(C+ include file which contains the definition of an option-iterator class,
- Xand argument-iterator classes.
- X
- X.IP \f4\*l/liboptions.a\fP
- XThe object library for \*(NM(3\*(C+).
- X
- X.PP
- XWhen compiling your source (on Unix systems), you may need to use
- Xthe \fB\-I\f4\*i\fR option. When linking your objects,
- Xyou may need to use the \fB\-L\f4\*l\fR option in conjunction
- Xwith the \fB\-l\f4options\fR option.
- X
- X.\"-----------------------------------
- X.SH SEE ALSO
- X\f4<options.h>\fP
- X
- X.\"-----------------------------------
- X.SH AUTHOR
- XBrad Appleton, Harris Computer Systems, <\f4brad@ssd.csd.harris.com\fP>.
- END_OF_FILE
- if test 14308 -ne `wc -c <'options.3'`; then
- echo shar: \"'options.3'\" unpacked with wrong size!
- fi
- # end of 'options.3'
- fi
- if test -f 'options.C' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'options.C'\"
- else
- echo shar: Extracting \"'options.C'\" \(26208 characters\)
- sed "s/^X//" >'options.C' <<'END_OF_FILE'
- X// ****************************************************************************
- X// ^FILE: options.c - implement the functions defined in <options.h>
- X//
- X// ^HISTORY:
- X// 01/16/92 Brad Appleton <brad@ssd.csd.harris.com> Created
- X// ^^**************************************************************************
- X
- X#include <stdlib.h>
- X#include <iostream.h>
- X#include <ctype.h>
- X#include <string.h>
- X
- X#include "options.h"
- X
- Xextern "C" {
- X void exit(int);
- X}
- X
- Xstatic const char ident[] = "@(#)Options 1.00" ;
- X
- X // I need a portable version of "tolower" that does NOT modify
- X // non-uppercase characters.
- X //
- X#define TOLOWER(c) (isupper(c) ? tolower(c) : c)
- X
- X// ************************************************************** OptIter
- X
- XOptIter::~OptIter(void) {}
- X
- Xconst char *
- XOptIter::operator()(void) {
- X const char * elt = curr();
- X (void) next();
- X return elt;
- X}
- X
- X// ************************************************************** OptIterRwd
- X
- XOptIterRwd::OptIterRwd(void) {}
- X
- XOptIterRwd::~OptIterRwd(void) {}
- X
- X// ************************************************************** OptArgvIter
- X
- XOptArgvIter::~OptArgvIter(void) {}
- X
- Xconst char *
- XOptArgvIter::curr(void) {
- X return ((ndx == ac) || (av[ndx] == NULL)) ? NULL : av[ndx];
- X}
- X
- Xvoid
- XOptArgvIter::next(void) {
- X if ((ndx != ac) && av[ndx]) ++ndx;
- X}
- X
- Xconst char *
- XOptArgvIter::operator()(void) {
- X return ((ndx == ac) || (av[ndx] == NULL)) ? NULL : av[ndx++];
- X}
- X
- Xvoid
- XOptArgvIter::rewind(void) { ndx = 0; }
- X
- X// ************************************************************** OptStrTokIter
- X
- Xstatic const char WHITESPACE[] = " \t\n\r\v\f" ;
- Xconst char * OptStrTokIter::default_delims = WHITESPACE ;
- X
- XOptStrTokIter::OptStrTokIter(const char * tokens, const char * delimiters)
- X : len(unsigned(strlen(tokens))), str(tokens), seps(delimiters),
- X cur(NULL), tokstr(NULL)
- X{
- X if (seps == NULL) seps = default_delims;
- X tokstr = new char[len + 1];
- X (void) ::strcpy(tokstr, str);
- X cur = ::strtok(tokstr, seps);
- X}
- X
- X
- XOptStrTokIter::~OptStrTokIter(void) { delete [] tokstr; }
- X
- Xconst char *
- XOptStrTokIter::curr(void) { return cur; }
- X
- Xvoid
- XOptStrTokIter::next(void) { if (cur) cur = ::strtok(NULL, seps); }
- X
- Xconst char *
- XOptStrTokIter::operator()(void) {
- X const char * elt = cur;
- X if (cur) cur = ::strtok(NULL, seps);
- X return elt;
- X}
- X
- Xvoid
- XOptStrTokIter::rewind(void) {
- X (void) ::strcpy(tokstr, str);
- X cur = ::strtok(tokstr, seps);
- X}
- X
- X// ******************************************************************* Options
- X
- XOptions::Options(const char * name, const char * const optv[])
- X : cmdname(name), optvec(optv), explicit_end(0), optctrls(DEFAULT),
- X nextchar(NULL), listopt(NULL)
- X{
- X const char * basename = ::strrchr(cmdname, '/');
- X if (basename) cmdname = basename + 1;
- X}
- X
- XOptions::~Options(void) {}
- X
- X // return values for a keyword matching function
- Xenum kwdmatch_t { NO_MATCH, PARTIAL_MATCH, EXACT_MATCH } ;
- X
- X // Get the option-char of an option-spec.
- Xinline static char
- XOptChar(const char * optspec) { return *optspec; }
- X
- X // Get the long-option of an option-spec.
- Xinline static const char *
- XLongOpt(const char * optspec) {
- X return ((optspec)[1] && (optspec)[2] &&
- X (! isspace((optspec)[2]))) ? ((optspec) + 2) : NULL ;
- X}
- X
- X // Is the option-char null?
- Xinline static int
- XisNullOpt(char optchar) {
- X return ((! optchar) || isspace(optchar) || (! isprint(optchar))) ;
- X}
- X
- X // Does this option require an argument?
- Xinline static int
- XisRequired(const char * optspec) {
- X return (((optspec)[1] == ':') || ((optspec)[1] == '+')) ;
- X}
- X
- X // Does this option take an optional argument?
- Xinline static int
- XisOptional(const char * optspec) {
- X return (((optspec)[1] == '?') || ((optspec)[1] == '*')) ;
- X}
- X
- X // Does this option take no arguments?
- Xinline static int
- XisNoArg(const char * optspec) {
- X return (((optspec)[1] == '|') || (! (optspec)[1])) ;
- X}
- X
- X // Can this option take more than one argument?
- Xinline static int
- XisList(const char * optspec) {
- X return (((optspec)[1] == '+') || ((optspec)[1] == '*')) ;
- X}
- X
- X // Does this option take any arguments?
- Xinline static int
- XisValTaken(const char * optspec) {
- X return (isRequired(optspec) || isOptional(optspec)) ;
- X}
- X
- X // Check for explicit "end-of-options"
- Xinline static int
- XisEndOpts(const char * token)
- X{
- X return ((token == NULL) || (! strcmp(token, "--"))) ;
- X}
- X
- X // See if an argument is an option
- Xinline static int
- XisOption(unsigned flags, const char * arg)
- X{
- X return (((*arg != '\0') || (arg[1] != '\0')) &&
- X ((*arg == '-') || ((flags & Options::PLUS) && (*arg == '+')))) ;
- X}
- X
- X// ---------------------------------------------------------------------------
- X// ^FUNCTION: verify - verify the syntax of each option-spec
- X//
- X// ^SYNOPSIS:
- X// static void verify(optv[])
- X//
- X// ^PARAMETERS:
- X// const char * const optv[] - the vector of option-specs to inspect.
- X//
- X// ^DESCRIPTION:
- X// All we have to do is iterate through the option vector and make sure
- X// That each option-spec is of the proper format.
- X//
- X// ^REQUIREMENTS:
- X// - optv[] should be non-NULL and terminated by a NULL pointer.
- X//
- X// ^SIDE-EFFECTS:
- X// If an invalid option-spec is found, prints a message on cerr and
- X// exits with a status of 127.
- X//
- X// ^RETURN-VALUE:
- X// None.
- X//
- X// ^ALGORITHM:
- X// For each option-spec
- X// - ensure (length > 0)
- X// - verify the the second character is one of "|?:*+"
- X// end-for
- X// ^^-------------------------------------------------------------------------
- Xstatic void
- Xverify(const char * const optv[])
- X{
- X int errors = 0;
- X if ((optv == NULL) || (! *optv)) return;
- X
- X for (; *optv ; optv++) {
- X char *p;
- X if (! **optv) {
- X cerr << "invalid option spec \"" << *optv << "\"." << endl ;
- X cerr << "\tmust be at least 1 character long." << endl ;
- X ++errors;
- X }
- X if ((**optv) && ((*optv)[1]) &&
- X ((p = strchr("|?:*+", (*optv)[1])) == NULL)) {
- X cerr << "invalid option spec \"" << *optv << "\"." << endl ;
- X cerr << "\t2nd character must be in the set \"|?:*+\"." << endl ;
- X ++errors;
- X }
- X }/*for*/
- X
- X if (errors) exit(127);
- X}
- X
- X// ---------------------------------------------------------------------------
- X// ^FUNCTION: kwdmatch - match a keyword
- X//
- X// ^SYNOPSIS:
- X// static kwdmatch_t kwdmatch(src, attempt, len)
- X//
- X// ^PARAMETERS:
- X// char * src -- the actual keyword to match
- X// char * attempt -- the possible keyword to compare against "src"
- X// int len -- number of character of "attempt" to consider
- X// (if 0 then we should use all of "attempt")
- X//
- X// ^DESCRIPTION:
- X// See if "attempt" matches some prefix of "src" (case insensitive).
- X//
- X// ^REQUIREMENTS:
- X// - attempt should be non-NULL and non-empty
- X//
- X// ^SIDE-EFFECTS:
- X// None.
- X//
- X// ^RETURN-VALUE:
- X// An enumeration value of type kwdmatch_t corresponding to whether
- X// We had an exact match, a partial match, or no match.
- X//
- X// ^ALGORITHM:
- X// Trivial
- X// ^^-------------------------------------------------------------------------
- Xstatic kwdmatch_t
- Xkwdmatch(const char * src, const char * attempt, int len =0)
- X{
- X unsigned i;
- X
- X if (src == attempt) return EXACT_MATCH ;
- X if ((src == NULL) || (attempt == NULL)) return NO_MATCH ;
- X if ((! *src) && (! *attempt)) return EXACT_MATCH ;
- X if ((! *src) || (! *attempt)) return NO_MATCH ;
- X
- X for (i = 0 ; ((i < len) || (len == 0)) &&
- X (attempt[i]) && (attempt[i] != ' ') ; i++) {
- X if (TOLOWER(src[i]) != TOLOWER(attempt[i])) return NO_MATCH ;
- X }
- X
- X return (src[i]) ? PARTIAL_MATCH : EXACT_MATCH ;
- X}
- X
- X// ---------------------------------------------------------------------------
- X// ^FUNCTION: match_opt - match an option
- X//
- X// ^SYNOPSIS:
- X// static const char * match_opt(optv, opt)
- X//
- X// ^PARAMETERS:
- X// char * optv[] -- vector of option-specifications
- X// char opt -- the option-character to match
- X// int ignore_case -- should we ignore character-case?
- X//
- X// ^DESCRIPTION:
- X// See if "opt" is found in "optv"
- X//
- X// ^REQUIREMENTS:
- X// - optv should be non-NULL and terminated by a NULL pointer.
- X//
- X// ^SIDE-EFFECTS:
- X// None.
- X//
- X// ^RETURN-VALUE:
- X// NULL if no match is found,
- X// otherwise a pointer to the matching option-spec.
- X//
- X// ^ALGORITHM:
- X// foreach option-spec
- X// - see if "opt" is a match, if so return option-spec
- X// end-for
- X// ^^-------------------------------------------------------------------------
- Xstatic const char *
- Xmatch_opt(const char * const optv[], char opt, int ignore_case =0)
- X{
- X if ((optv == NULL) || (! *optv)) return NULL;
- X
- X for (; *optv ; ++optv) {
- X char optchar = OptChar(*optv);
- X if (isNullOpt(optchar)) continue;
- X if (opt == optchar) {
- X return *optv;
- X } else if (ignore_case && (TOLOWER(opt) == TOLOWER(optchar))) {
- X return *optv;
- X }
- X }
- X
- X return NULL; // not found
- X}
- X
- X// ---------------------------------------------------------------------------
- X// ^FUNCTION: match_longopt - match a long-option
- X//
- X// ^SYNOPSIS:
- X// static const char * match_longopt(optv, opt, len, ambiguous)
- X//
- X// ^PARAMETERS:
- X// char * optv[] -- the vector of option-specs
- X// char * opt -- the long-option to match
- X// int len -- the number of character of "opt" to match
- X// int & ambiguous -- set by this routine before returning.
- X//
- X// ^DESCRIPTION:
- X// Try to match "opt" against some unique prefix of a long-option
- X// (case insensitive).
- X//
- X// ^REQUIREMENTS:
- X// - optvec should be non-NULL and terminated by a NULL pointer.
- X//
- X// ^SIDE-EFFECTS:
- X// - *ambiguous is set to '1' if "opt" matches >1 long-option
- X// (otherwise it is set to 0).
- X//
- X// ^RETURN-VALUE:
- X// NULL if no match is found,
- X// otherwise a pointer to the matching option-spec.
- X//
- X// ^ALGORITHM:
- X// ambiguous is FALSE
- X// foreach option-spec
- X// if we have an EXACT-MATCH, return the option-spec
- X// if we have a partial-match then
- X// if we already had a previous partial match then
- X// set ambiguous = TRUE and retrun NULL
- X// else
- X// remember this options spec and continue matching
- X// end-if
- X// end-if
- X// end-for
- X// if we had exactly 1 partial match return it, else return NULL
- X// ^^-------------------------------------------------------------------------
- Xstatic const char *
- Xmatch_longopt(const char * const optv[],
- X const char * opt,
- X int len,
- X int & ambiguous)
- X{
- X kwdmatch_t result;
- X const char * matched = NULL ;
- X
- X ambiguous = 0;
- X if ((optv == NULL) || (! *optv)) return NULL;
- X
- X for (; *optv ; ++optv) {
- X const char * longopt = LongOpt(*optv) ;
- X if (longopt == NULL) continue;
- X result = kwdmatch(longopt, opt, len);
- X if (result == EXACT_MATCH) {
- X return *optv;
- X } else if (result == PARTIAL_MATCH) {
- X if (matched) {
- X ++ambiguous;
- X return NULL;
- X } else {
- X matched = *optv;
- X }
- X }
- X }/*for*/
- X
- X return matched;
- X}
- X
- X// ---------------------------------------------------------------------------
- X// ^FUNCTION: Options::parse_opt - parse an option
- X//
- X// ^SYNOPSIS:
- X// int Options::parse_opt(iter, optarg)
- X//
- X// ^PARAMETERS:
- X// OptIter & iter -- option iterator
- X// const char * & optarg -- where to store any option-argument
- X//
- X// ^DESCRIPTION:
- X// Parse the next option in iter (advancing as necessary).
- X// Make sure we update the nextchar pointer along the way. Any option
- X// we find should be returned and optarg should point to its argument.
- X//
- X// ^REQUIREMENTS:
- X// - nextchar must point to the prospective option character
- X//
- X// ^SIDE-EFFECTS:
- X// - iter is advanced when an argument completely parsed
- X// - optarg is modified to point to any option argument
- X// - if Options::QUIET is not set, error messages are printed on cerr
- X//
- X// ^RETURN-VALUE:
- X// 'c' if the -c option was matched (optarg points to its argument)
- X// -1 if the option is invalid (optarg points to the bad option-character).
- X//
- X// ^ALGORITHM:
- X// It gets complicated -- follow the comments in the source.
- X// ^^-------------------------------------------------------------------------
- Xint
- XOptions::parse_opt(OptIter & iter, const char * & optarg)
- X{
- X listopt = NULL; // reset the list pointer
- X
- X if ((optvec == NULL) || (! *optvec)) return NULL;
- X
- X // Try to match a known option
- X const char * optspec = match_opt(optvec, *(nextchar++));
- X
- X // Check for an unknown option
- X if (optspec == NULL) {
- X // See if this was a long-option in disguise
- X if (! (optctrls & NOGUESSING)) {
- X unsigned save_ctrls = optctrls;
- X const char * save_nextchar = nextchar;
- X nextchar -= 1;
- X optctrls |= (QUIET | NOGUESSING);
- X int optchar = parse_longopt(iter, optarg);
- X optctrls = save_ctrls;
- X if (optchar > 0) {
- X return optchar;
- X } else {
- X nextchar = save_nextchar;
- X }
- X }
- X if (! (optctrls & QUIET)) {
- X cerr << cmdname << ": unknown option -"
- X << *(nextchar - 1) << "." << endl ;
- X }
- X optarg = (nextchar - 1); // record the bad option in optarg
- X return BADCHAR;
- X }
- X
- X // If no argument is taken, then leave now
- X if (isNoArg(optspec)) {
- X optarg = NULL;
- X return OptChar(optspec);
- X }
- X
- X // Check for argument in this arg
- X if (*nextchar) {
- X optarg = nextchar; // the argument is right here
- X nextchar = NULL; // we've exhausted this arg
- X if (isList(optspec)) listopt = optspec ; // save the list-spec
- X return OptChar(optspec);
- X }
- X
- X // Check for argument in next arg
- X const char * nextarg = iter.curr();
- X if ((nextarg != NULL) &&
- X (isRequired(optspec) || (! isOption(optctrls, nextarg)))) {
- X optarg = nextarg; // the argument is here
- X iter.next(); // end of arg - advance
- X if (isList(optspec)) listopt = optspec ; // save the list-spec
- X return OptChar(optspec);
- X }
- X
- X // No argument given - if its required, thats an error
- X optarg = NULL;
- X if (isRequired(optspec) && !(optctrls & QUIET)) {
- X cerr << cmdname << ": argument required for -" << OptChar(optspec)
- X << " option." << endl ;
- X }
- X return OptChar(optspec);
- X}
- X
- X// ---------------------------------------------------------------------------
- X// ^FUNCTION: Options::parse_longopt - parse a long-option
- X//
- X// ^SYNOPSIS:
- X// int Options::parse_longopt(iter, optarg)
- X//
- X// ^PARAMETERS:
- X// OptIter & iter -- option iterator
- X// const char * & optarg -- where to store any option-argument
- X//
- X// ^DESCRIPTION:
- X// Parse the next long-option in iter (advancing as necessary).
- X// Make sure we update the nextchar pointer along the way. Any option
- X// we find should be returned and optarg should point to its argument.
- X//
- X// ^REQUIREMENTS:
- X// - nextchar must point to the prospective option character
- X//
- X// ^SIDE-EFFECTS:
- X// - iter is advanced when an argument completely parsed
- X// - optarg is modified to point to any option argument
- X// - if Options::QUIET is not set, error messages are printed on cerr
- X//
- X// ^RETURN-VALUE:
- X// 'c' if the the long-option corresponding to the -c option was matched
- X// (optarg points to its argument)
- X// -2 if the option is invalid (optarg points to the bad long-option name).
- X//
- X// ^ALGORITHM:
- X// It gets complicated -- follow the comments in the source.
- X// ^^-------------------------------------------------------------------------
- Xint
- XOptions::parse_longopt(OptIter & iter, const char * & optarg)
- X{
- X int len = 0, ambiguous = 0;
- X
- X listopt = NULL ; // reset the list-spec
- X
- X if ((optvec == NULL) || (! *optvec)) return NULL;
- X
- X // if a value is supplied in this argv element, get it now
- X const char * val = strpbrk(nextchar, ":=") ;
- X if (val) {
- X len = val - nextchar ;
- X ++val ;
- X }
- X
- X // Try to match a known long-option
- X const char * optspec = match_longopt(optvec, nextchar, len, ambiguous);
- X
- X // Check for an unknown long-option
- X if (optspec == NULL) {
- X // See if this was a short-option in disguise
- X if ((! ambiguous) && (! (optctrls & NOGUESSING))) {
- X unsigned save_ctrls = optctrls;
- X const char * save_nextchar = nextchar;
- X optctrls |= (QUIET | NOGUESSING);
- X int optchar = parse_opt(iter, optarg);
- X optctrls = save_ctrls;
- X if (optchar > 0) {
- X return optchar;
- X } else {
- X nextchar = save_nextchar;
- X }
- X }
- X if (! (optctrls & QUIET)) {
- X cerr << cmdname << ": " << ((ambiguous) ? "ambiguous" : "unknown")
- X << " option "
- X << ((optctrls & LONG_ONLY) ? "-" : "--")
- X << nextchar << "." << endl ;
- X }
- X optarg = nextchar; // record the bad option in optarg
- X nextchar = NULL; // we've exhausted this argument
- X return (ambiguous) ? AMBIGUOUS : BADKWD;
- X }
- X
- X // If no argument is taken, then leave now
- X if (isNoArg(optspec)) {
- X if ((val) && ! (optctrls & QUIET)) {
- X cerr << cmdname << ": option "
- X << ((optctrls & LONG_ONLY) ? "-" : "--")
- X << LongOpt(optspec) << " does NOT take an argument." << endl ;
- X }
- X optarg = val; // record the unexpected argument
- X nextchar = NULL; // we've exhausted this argument
- X return OptChar(optspec);
- X }
- X
- X // Check for argument in this arg
- X if (val) {
- X optarg = val; // the argument is right here
- X nextchar = NULL; // we exhausted the rest of this arg
- X if (isList(optspec)) listopt = optspec ; // save the list-spec
- X return OptChar(optspec);
- X }
- X
- X // Check for argument in next arg
- X const char * nextarg = iter.curr(); // find the next argument to parse
- X if ((nextarg != NULL) &&
- X (isRequired(optspec) || (! isOption(optctrls, nextarg)))) {
- X optarg = nextarg; // the argument is right here
- X iter.next(); // end of arg - advance
- X nextchar = NULL; // we exhausted the rest of this arg
- X if (isList(optspec)) listopt = optspec ; // save the list-spec
- X return OptChar(optspec);
- X }
- X
- X // No argument given - if its required, thats an error
- X optarg = NULL;
- X if (isRequired(optspec) && !(optctrls & QUIET)) {
- X const char * longopt = LongOpt(optspec);
- X const char * spc = ::strchr(longopt, ' ');
- X int longopt_len;
- X if (spc) {
- X longopt_len = spc - longopt;
- X } else {
- X longopt_len = ::strlen(longopt);
- X }
- X cerr << cmdname << ": argument required for "
- X << ((optctrls & LONG_ONLY) ? "-" : "--");
- X cerr.write(longopt, longopt_len) << " option." << endl ;
- X }
- X nextchar = NULL; // we exhausted the rest of this arg
- X return OptChar(optspec);
- X}
- X
- X// ---------------------------------------------------------------------------
- X// ^FUNCTION: Options::fmt_opt - format an option-spec for a usage message
- X//
- X// ^SYNOPSIS:
- X// unsigned Options::fmt_opt(optspec, buf)
- X//
- X// ^PARAMETERS:
- X// char * optspec -- the option-specification
- X// char * buf -- where to print the formatted option
- X//
- X// ^DESCRIPTION:
- X// Self-explanatory.
- X//
- X// ^REQUIREMENTS:
- X// - optspec must be a valid option-spec.
- X// - buf must be large enough to hold the result
- X//
- X// ^SIDE-EFFECTS:
- X// - writes to buf.
- X//
- X// ^RETURN-VALUE:
- X// Number of characters written to buf.
- X//
- X// ^ALGORITHM:
- X// Trivial.
- X// ^^-------------------------------------------------------------------------
- Xunsigned
- XOptions::fmt_opt(const char * optspec, char * buf) const
- X{
- X static char default_value[] = "<value>";
- X char optchar = OptChar(optspec);
- X const char * longopt = LongOpt(optspec);
- X char * p = buf ;
- X
- X const char * value = NULL;
- X int longopt_len = 0;
- X int value_len = 0;
- X
- X if (longopt) {
- X value = ::strchr(longopt, ' ');
- X longopt_len = (value) ? (value - longopt) : ::strlen(longopt);
- X } else {
- X value = ::strchr(optspec + 1, ' ');
- X }
- X while (value && (*value == ' ')) ++value;
- X if (value && *value) {
- X value_len = ::strlen(value);
- X } else {
- X value = default_value;
- X value_len = sizeof(default_value) - 1;
- X }
- X
- X if ((optctrls & SHORT_ONLY) &&
- X ((! isNullOpt(optchar)) || (optctrls & NOGUESSING))) {
- X longopt = NULL;
- X }
- X if ((optctrls & LONG_ONLY) && (longopt || (optctrls & NOGUESSING))) {
- X optchar = '\0';
- X }
- X if (isNullOpt(optchar) && (longopt == NULL)) {
- X *buf = '\0';
- X return 0;
- X }
- X
- X *(p++) = '[';
- X
- X // print the single character option
- X if (! isNullOpt(optchar)) {
- X *(p++) = '-';
- X *(p++) = optchar;
- X }
- X
- X if ((! isNullOpt(optchar)) && (longopt)) *(p++) = '|';
- X
- X // print the long option
- X if (longopt) {
- X *(p++) = '-';
- X if (! (optctrls & (LONG_ONLY | SHORT_ONLY))) {
- X *(p++) = '-';
- X }
- X strncpy(p, longopt, longopt_len);
- X p += longopt_len;
- X }
- X
- X // print any argument the option takes
- X if (isValTaken(optspec)) {
- X *(p++) = ' ' ;
- X if (isOptional(optspec)) *(p++) = '[' ;
- X strcpy(p, value);
- X p += value_len;
- X if (isList(optspec)) {
- X strcpy(p, " ...");
- X p += 4;
- X }
- X if (isOptional(optspec)) *(p++) = ']' ;
- X }
- X
- X *(p++) = ']';
- X *p = '\0';
- X
- X return (unsigned) strlen(buf);
- X}
- X
- X// ---------------------------------------------------------------------------
- X// ^FUNCTION: Options::usage - print usage
- X//
- X// ^SYNOPSIS:
- X// void Options::usage(os, positionals)
- X//
- X// ^PARAMETERS:
- X// ostream & os -- where to print the usage
- X// char * positionals -- command-line syntax for any positional args
- X//
- X// ^DESCRIPTION:
- X// Print command-usage (using either option or long-option syntax) on os.
- X//
- X// ^REQUIREMENTS:
- X// os should correspond to an open output file.
- X//
- X// ^SIDE-EFFECTS:
- X// Prints on os
- X//
- X// ^RETURN-VALUE:
- X// None.
- X//
- X// ^ALGORITHM:
- X// Print usage on os, wrapping long lines where necessary.
- X// ^^-------------------------------------------------------------------------
- Xvoid
- XOptions::usage(ostream & os, const char * positionals) const
- X{
- X const char ** optv = optvec;
- X unsigned cols = 79;
- X int first, nloop;
- X char buf[256] ;
- X
- X if ((optv == NULL) || (! *optv)) return;
- X
- X // print first portion "usage: progname"
- X os << "usage: " << cmdname ;
- X unsigned ll = strlen(cmdname) + 7;
- X
- X // save the current length so we know how much space to skip for
- X // subsequent lines.
- X //
- X unsigned margin = ll + 1;
- X
- X // print the options and the positional arguments
- X for (nloop = 0, first = 1 ; !nloop ; optv++, first = 0) {
- X unsigned len;
- X
- X // figure out how wide this parameter is (for printing)
- X if (! *optv) {
- X len = strlen(positionals);
- X ++nloop; // terminate this loop
- X } else {
- X len = fmt_opt(*optv, buf);
- X }
- X
- X // Will this fit?
- X if ((ll + len + 1) > (cols - first)) {
- X os << '\n' ; // No - start a new line;
- X os.width(margin);
- X os << "" ;
- X ll = margin;
- X } else {
- X os << ' ' ; // Yes - just throw in a space
- X ++ll;
- X }
- X ll += len;
- X os << ((nloop) ? positionals : buf) ;
- X }// for each ad
- X
- X os << endl ;
- X}
- X
- X
- X// ---------------------------------------------------------------------------
- X// ^FUNCTION: Options::operator() - get options from the command-line
- X//
- X// ^SYNOPSIS:
- X// int Options::operator()(iter, optarg)
- X//
- X// ^PARAMETERS:
- X// OptIter & iter -- option iterator
- X// const char * & optarg -- where to store any option-argument
- X//
- X// ^DESCRIPTION:
- X// Parse the next option in iter (advancing as necessary).
- X// Make sure we update the nextchar pointer along the way. Any option
- X// we find should be returned and optarg should point to its argument.
- X//
- X// ^REQUIREMENTS:
- X// None.
- X//
- X// ^SIDE-EFFECTS:
- X// - iter is advanced when an argument is completely parsed
- X// - optarg is modified to point to any option argument
- X// - if Options::QUIET is not set, error messages are printed on cerr
- X//
- X// ^RETURN-VALUE:
- X// 0 if all options have been parsed.
- X// 'c' if the the option or long-option corresponding to the -c option was
- X// matched (optarg points to its argument).
- X// -1 if the option is invalid (optarg points to the bad option character).
- X// -2 if the option is invalid (optarg points to the bad long-option name).
- X//
- X// ^ALGORITHM:
- X// It gets complicated -- follow the comments in the source.
- X// ^^-------------------------------------------------------------------------
- Xint
- XOptions::operator()(OptIter & iter, const char * & optarg)
- X{
- X explicit_end = 0;
- X
- X // See if we have an option left over from before ...
- X if ((nextchar) && *nextchar) {
- X return parse_opt(iter, optarg);
- X }
- X
- X // Check for end-of-options ...
- X const char * arg = iter.curr();
- X if (arg == NULL) {
- X listopt = NULL;
- X return ENDOPTS;
- X } else if (isEndOpts(arg)) {
- X iter.next(); // advance past end-of-options arg
- X listopt = NULL;
- X explicit_end = 1;
- X return ENDOPTS;
- X }
- X
- X // Do we have a positional arg?
- X if (! listopt) {
- X if ((! *arg) || (! arg[1])) {
- X return ENDOPTS;
- X } else if ((*arg != '-') &&
- X ((! (optctrls & PLUS)) || (*arg != '+'))) {
- X return ENDOPTS;
- X }
- X }
- X
- X iter.next(); // pass the argument that arg already points to
- X
- X // See if we have a long option ...
- X if (! (optctrls & SHORT_ONLY)) {
- X if ((*arg == '-') && (arg[1] == '-')) {
- X nextchar = arg + 2;
- X return parse_longopt(iter, optarg);
- X } else if ((optctrls & PLUS) && (*arg == '+')) {
- X nextchar = arg + 1;
- X return parse_longopt(iter, optarg);
- X }
- X }
- X if (*arg == '-') {
- X nextchar = arg + 1;
- X if (optctrls & LONG_ONLY) {
- X return parse_longopt(iter, optarg);
- X } else {
- X return parse_opt(iter, optarg);
- X }
- X }
- X
- X // If we get here - it is because we have a list value
- X optarg = arg ; // record the list value
- X return OptChar(listopt) ;
- X}
- X
- END_OF_FILE
- if test 26208 -ne `wc -c <'options.C'`; then
- echo shar: \"'options.C'\" unpacked with wrong size!
- fi
- # end of 'options.C'
- fi
- if test -f 'testopts.C' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'testopts.C'\"
- else
- echo shar: Extracting \"'testopts.C'\" \(3205 characters\)
- sed "s/^X//" >'testopts.C' <<'END_OF_FILE'
- X#include <stdlib.h>
- X#include <iostream.h>
- X#include <string.h>
- X#include <ctype.h>
- X
- X#include <options.h>
- X
- Xextern "C" {
- X void exit(int);
- X long atol(const char *);
- X}
- X
- Xstatic const char *optv[] = {
- X "?|?",
- X "H|help",
- X "f?flags",
- X "g+groups <name>",
- X "c:count <int>",
- X "s?str <string>",
- X "x",
- X " |hello",
- X NULL
- X} ;
- X
- X// I use the following routine when I see the -f option.
- X// It changes the opt_ctrls used by getopts() to take effect
- X// for the next option parsed. The -f option is used to test
- X// the Options::XXXXX flags and should be first on the command-line.
- X//
- Xvoid
- Xsetflags(const char * flags_str, Options & opts) {
- X if (flags_str && *flags_str) {
- X unsigned flags = opts.ctrls();
- X if (::strchr(flags_str, '+')) flags |= Options::PLUS;
- X if (::strchr(flags_str, 'A')) flags |= Options::ANYCASE;
- X if (::strchr(flags_str, 'a')) flags |= Options::ANYCASE;
- X if (::strchr(flags_str, 'L')) flags |= Options::LONG_ONLY;
- X if (::strchr(flags_str, 'l')) flags |= Options::LONG_ONLY;
- X if (::strchr(flags_str, 'S')) flags |= Options::SHORT_ONLY;
- X if (::strchr(flags_str, 's')) flags |= Options::SHORT_ONLY;
- X if (::strchr(flags_str, 'Q')) flags |= Options::QUIET;
- X if (::strchr(flags_str, 'q')) flags |= Options::QUIET;
- X if (::strchr(flags_str, 'n')) flags |= Options::NOGUESSING;
- X if (::strchr(flags_str, 'N')) flags |= Options::NOGUESSING;
- X opts.ctrls(flags);
- X }
- X}
- X
- Xmain(int argc, char * argv[])
- X{
- X int optchar;
- X const char * optarg;
- X const char * str = "default_string";
- X int count = 0, xflag = 0, hello = 0;
- X int errors = 0;
- X int ngroups = 0;
- X
- X Options opts(*argv, optv);
- X OptArgvIter iter(--argc, ++argv);
- X
- X while( optchar = opts(iter, optarg) ) {
- X switch (optchar) {
- X case '?' :
- X case 'H' :
- X opts.usage(cout, "files ...");
- X ::exit(0);
- X break;
- X
- X case 'f' : setflags(optarg, opts); break;
- X
- X case 'g' : ++ngroups; break;
- X
- X case 's' : str = optarg; break;
- X
- X case 'x' : ++xflag; break;
- X
- X case ' ' : ++hello; break;
- X
- X case 'c' :
- X if (optarg == NULL) {
- X ++errors;
- X } else {
- X count = (int) ::atol(optarg);
- X }
- X break;
- X
- X case Options::BADCHAR : // bad option ("-%c", *optarg)
- X case Options::BADKWD : // bad long-option ("--%s", optarg)
- X case Options::AMBIGUOUS : // ambiguous long-option ("--%s", optarg)
- X default :
- X ++errors; break;
- X } /*switch*/
- X }
- X
- X int index = iter.index();
- X
- X if (errors || (index == argc)) {
- X if (! errors) {
- X cerr << opts.name() << ": no filenames given." << endl ;
- X }
- X opts.usage(cerr, "files ...");
- X ::exit(1);
- X }
- X
- X cout << "xflag=" << ((xflag) ? "ON" : "OFF") << '\n'
- X << "hello=" << ((hello) ? "YES" : "NO") << '\n'
- X << "count=" << count << '\n'
- X << "string=\"" << ((str) ? str : "No value given!") << "\"" << '\n'
- X << "ngroups=" << ngroups << endl ;
- X
- X if (index < argc) {
- X cout << "files=" ;
- X for (int i = index ; i < argc ; i++) {
- X cout << "\"" << argv[i] << "\" " ;
- X }
- X cout << endl ;
- X }
- X
- X return 0;
- X}
- END_OF_FILE
- if test 3205 -ne `wc -c <'testopts.C'`; then
- echo shar: \"'testopts.C'\" unpacked with wrong size!
- fi
- # end of 'testopts.C'
- fi
- echo shar: End of archive 1 \(of 2\).
- cp /dev/null ark1isdone
- MISSING=""
- for I in 1 2 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have unpacked both archives.
- rm -f ark[1-9]isdone
- else
- echo You still must unpack the following archives:
- echo " " ${MISSING}
- fi
- exit 0
- exit 0 # Just in case...
-