home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Monster Media 1994 #1
/
monster.zip
/
monster
/
PROG_C
/
CL187A.ZIP
/
CMDLN.CPP
< prev
next >
Wrap
C/C++ Source or Header
|
1994-03-15
|
9KB
|
335 lines
/*
cmdln.cpp -- command line option parser
(C) Copyright 1994 John Webster Small
All rights reserved
Notes:
1. Call the constructor CmdLn() or the parse()
member function with argc, argv, and an options
string. The options string is a string of
option characters that may appear on your
application's command line after the switch
character. The valid switch characters are
defined in the CmdLn::switches string below.
If an option takes a string argument then a
colon must immediately follow that option
character in the options string to let
getOption() know to look for the string
argument. For example, the options string "a:"
signifies that the "a" option has an optional
string argument. If an option takes a numeric
argument then a pound sign, i.e. #, must
immediately follow that option character in the
options string to let getOption() know to look
for the argument and convert it to a numeric
value. For example, the options string
"Aa:b#B" signifies that the "A" option has no
arguments other than an optional "+" or "-",
the "a" option has an optional string argument,
the "b" option has an option numeric argument
that may be proceeded by an optional ":", and
the "B" has no arguments just like the "A"
option. The syntax for the options string is
as follows:
options string ::= {optch[:|#]}+
You read the syntax as: An options string is
one or more (the "+" indicates this) option
characters any of which may be followed by a
colon or a pound sign to indicate that the
option has an optional string or numeric
argument, respectively.
2. Call getOption() repeatedly to parse command
line arguments. GetOption() returns the
current option character being processed along
with any argument via the variable CmdLn::
optArg. If the argument is numeric as
indicated by the pound sign in the options
string, then the variable CmdLn::optNum
additionally holds this value. Given the
options string "a:b#cd" and the command line
"-ahello -b:10 -cc+d-", the first call to
getOption() would return 'a' with CmdLn::optArg
== "hello" and the second would return 'b' with
CmdLn::optArg == ":10" and CmdLn:optNum == 10.
Note the ":" in the command line parameter is
optional! The third and fourth calls would
return "c" with CmdLn::optOff == 0 while the
fifth call would return "d" with CmdLn::optOff
== 1. As you can see the "+/-" indicators are
optional and when missing "+" is assumed.
Another important point is that options can be
clustered such as '-cd'.
GetOption() can recognize clusters of command
line options. A cluster is the switch
character followed immediately by any number of
option characters, no white space. If an
option takes an argument, i.e. string or
numeric, then the option's character must be
the last option in the cluster with the
argument immediately following, i.e. no white
space. The syntax for an option cluster is as
follows:
option cluster
::= swCh{optCh[\+|\-]}+
::= swCh{optCh[\+|\-]}*
{optch[strArg|[:]numArg]}
Wow! That reads: a command line option cluster
starts with the switch character, e.g. '-',
with either one or more option characters (+
means one or more) or in the second case any
number of option characters (* means zero or
more), only the last of which is allowed to
take an argument.
If getOption() encounters an unknown option it
returns '?' and the unknown option in CmdLn::
optNot. The rest of the cluster is not parsed
in this case.
The Unix convention is for options to come
first on the command line followed by other
parameters. With CmdLn they are allowed in any
order. Each time getOption() encounters a
parameter it returns '%'.
The last value returned from getOption() can
also be found in CmdLn:: optCh. The type of
option can be found in CmdLn::optType
enumerated as NA, FLAG, STR, NUM, UNKNOWN, and
PARAM. When getOption() has finished
processing all arguments it returns 0.
3. For example, if the options string contains
"C:af:zN#" then 'C' and 'f' take string
arguments and N a numeric argument. A valid
command line for the demo below would be:
cmdln C:af:zN# -a:fnew outfile /z-Ccmdfile -N:20
with repeated calls to getOption() returning:
'a' with optOff == 0
optType == FLAG
? with optNot == ':'
optType == UNKNOWN
'f' with optArg == "new"
optType == STR
'%' with param == "outfile"
optType == NA
'z' with optOff != 0
optType == FLAG
'C' with optArg == "cmdfile"
optType == STR
'N' with optArg == ":20"
optNum == 20
optType == NUM
0
4. To test CmdLn, define CMDLN_DEMO below and
then compile and run cmdln.cpp. Be sure you
understand the demo code in main() before using
cmdln.cpp.
*/
//#define CMDLN_DEMO
#include <ctype.h> /* isalpha() */
#include <string.h> /* strchr(), strdup() */
#include <stdlib.h> /* atoi() */
#include <iomanip.h>
#include "cmdln.h"
void argument::reset()
{
optType = NA;
optCh = optNot = '\0';
optArg = param = (char *) 0;
optOff = optNum = 0;
}
argument& argument::operator=(const argument& a)
{
optType = a.optType;
optCh = a.optCh;
optNot = a.optNot;
optArg = a.optArg;
param = a.param;
optOff = a.optOff;
optNum = a.optNum;
return *this;
}
ostream& operator<<(ostream& os, argument& a)
{
switch (a.optType) {
case argument::FLAG:
os << a.optCh
<< (a.optOff? " -" : " +");
break;
case argument::STR:
os << a.optCh;
if (a.optArg)
os << " " << a.optArg;
break;
case argument::NUM:
os << a.optCh;
if (a.optArg) if (a.optNum)
os << " " << a.optNum;
else
os << " " << a.optArg;
break;
case argument::UNKNOWN:
os << "? " << a.optNot;
break;
case argument::PARAM:
os << "% " << a.param;
break;
}
return os;
}
char CmdLn::switches[] = "/-";
/*
Switches is initialized here for DOS switch
characters. Change as necessary for your
host OS shell.
*/
void CmdLn::parse(int argc, char *argv[],
char *options)
{
this->argv = argv;
this->options = (options? options : "");
optCluster = (char *) 0;
this->argc = argc;
argvEnd = 0;
argi = 1;
reset();
}
int CmdLn::getOption(void)
{
char *lookup;
if (argvEnd)
return 0;
reset();
if (!optCluster || (*optCluster == '\0')) {
if (argi >= argc) {
/* no more parameters */
argvEnd = 1;
return 0;
}
/* next possible option cluster */
if ((optCluster = argv[argi++])
== (char *)0) {
argvEnd = 1;
return 1;
}
if (!strchr(switches,*optCluster)) {
/* param */
param = optCluster;
optCluster = (char *) 0;
optType = PARAM;
return (optCh = '%');
}
optCluster++;/* next possible option */
}
if (!isalpha(*optCluster)) {
/* unknown option */
optNot = *optCluster;
optCluster = (char *) 0;
optType = UNKNOWN;
return (optCh = '?');
}
if ((lookup = strchr(options,*optCluster++))
== (char *)0) {
/* unknown option */
optNot = *--optCluster;
optCluster = (char *) 0;
optType = UNKNOWN;
return (optCh = '?');
}
/* validate option character */
switch (lookup[1]) {
case ':' :
if (*optCluster != '\0')
optArg = optCluster;
optCluster = (char *) 0;
optType = STR;
break;
case '#' :
if (*optCluster != '\0') {
optArg = optCluster;
optNum = atoi((*optArg == ':')?
&optArg[1]:optArg);
}
optCluster = (char *) 0;
optType = NUM;
break;
/*
non argument switches are allowed to
have optional +/- flags
*/
default :
switch (*optCluster) {
case '-' :
optOff = 1;
case '+' :
optCluster++;
break;
}
optType = FLAG;
break;
}
return (optCh = *lookup);
/* return option */
}
#ifdef CMDLN_DEMO
main(int argc, char *argv[])
{
cout << endl;
if (argc < 3) {
cerr << "Usage: cmdln optionsStr "
"options+" << endl << endl;
cerr << "e.g. cmdln C:af:zN# -afnew "
"outfile /z-Ccmdfile -N:20"
<< endl;
return 1;
}
// skip over options string
CmdLn args(argc-1,&argv[1],argv[1]);
// display command line
for (unsigned i = 1; i < argc; i++)
cout << argv[i] << " ";
cout << endl << endl;
while (args.getOption())
cout << args << endl;
return 0;
}
#endif // CMDLN_DEMO