This is cmdparse.c in view mode; [Download] [Up]
//------------------------------------------------------------------------
// ^FILE: cmdparse.c - implementation of the CmdParseCommand
//
// ^DESCRIPTION:
// This file implements the member functions of the CmdParseCommand
// class.
//
// ^HISTORY:
// 04/26/92 Brad Appleton <brad@ssd.csd.harris.com> Created
//
// 03/01/93 Brad Appleton <brad@ssd.csd.harris.com>
// - Added ALLOW_PLUS to list of CmdLine configuration flags
//-^^---------------------------------------------------------------------
#include <stdlib.h>
#include <iostream.h>
#include <fstream.h>
#include <strstream.h>
#include <string.h>
#include <ctype.h>
#include "argtypes.h"
#include "cmdparse.h"
#include "syntax.h"
#include "quoted.h"
enum { SUCCESS = 0, FAILURE = -1 } ;
enum { MAX_IDENT_LEN = 64, MAX_DESCRIPTION_LEN = 1024 } ;
extern "C" {
int isatty(int fd);
}
//------------------------------------------------------------------------ copy
//-------------------
// ^FUNCTION: copy - copy a string
//
// ^SYNOPSIS:
// copy(dest, src)
//
// ^PARAMETERS:
// char * & dest;
// -- where to put the copy we make
//
// const char * src;
// -- the string to copy
//
// ^DESCRIPTION:
// This function duplicates its second parameter to its first parameter.
//
// ^REQUIREMENTS:
// None.
//
// ^SIDE-EFFECTS:
// "dest" is modified to point to the newly allocated and copied result.
//
// ^RETURN-VALUE:
// None.
//
// ^ALGORITHM:
// Trivial.
//-^^----------------
static void
copy(char * & dest, const char * src)
{
if (src == NULL) return ;
dest = new char[::strlen(src) + 1] ;
if (dest) ::strcpy(dest, src);
}
//------------------------------------------------------------------ CmdArgVers
CmdArgVers::CmdArgVers(char opt, const char * kwd, const char * description)
: CmdArg(opt, kwd, description, CmdArg::isOPT)
{
}
CmdArgVers::~CmdArgVers(void)
{
}
int
CmdArgVers::operator()(const char * & , CmdLine & cmd)
{
cerr << cmd.name() << "\trelease " << cmd.release()
<< " at patchlevel " << cmd.patchlevel() << endl ;
cmd.quit(e_VERSION);
return 0; // shutup the compiler about not returning a value
}
//------------------------------------------------------------- CmdParseCommand
//-------------------
// ^FUNCTION: CmdParseCommand::parse_declarations - parse user arguments
//
// ^SYNOPSIS:
// CmdParseCommand::parse_declarations()
//
// ^PARAMETERS:
// None.
//
// ^DESCRIPTION:
// This routine will go through all the input sources that were supplied
// on the command-line. Each of these "input sources" is something that
// contains user argument declarations that need to be parsed. If no
// input sources were given and cin is not connected to a terminal, then
// we try to read the declarations from cin.
//
// If input sources were given, they are parsed in the following order:
// 1) from a string
// 2) from an environment variable
// 3) from a file
//
// If more than one input source is specified then they are processed in
// the above order and each argument is appended to the user's CmdLine
// object in the order that it was seen.
//
// ^REQUIREMENTS:
// This routine should be called by CmdParseCommand::operator() after
// the command-line has been parsed.
//
// ^SIDE-EFFECTS:
// If input comes from a file, then the file is read (until eof or an
// error condition occurs).
//
// Any arguments found are "compiled" and appended to "usr_cmd", the user's
// CmdLine object.
//
// ^RETURN-VALUE:
// 0 upon success, non-zero upon failure.
//
// ^ALGORITHM:
// Follow along - its pretty straightforward.
//-^^----------------
int
CmdParseCommand::parse_declarations(void)
{
const char * str = input_str ;
const char * varname = input_var ;
const char * filename = input_file ;
// If no "input sources" were specified, try cin.
if ((str == NULL) && (varname == NULL) && (filename == NULL)) {
// Make sure cin is NOT interactive!
int fd = ((filebuf *)(cin.rdbuf()))->fd();
if (::isatty(fd)) {
error() << "Can't read argument declarations from a terminal."
<< endl ;
return -1;
}
return parse_declarations(cin);
}
int rc = 0;
// First - parse from the string
if (str) {
rc += parse_declarations(str);
}
// Second - parse from the environment variable
if (varname) {
const char * contents = ::getenv(varname);
if (contents) {
rc += parse_declarations(contents);
} else {
error() << varname << " is empty or is undefined." << endl ;
return -1;
}
}
// Third - parse from the file. If the filename is "-" then it
// means that standard input should be used.
//
if (filename) {
if (::strcmp(filename, "-") == 0) {
rc += parse_declarations(cin);
} else {
ifstream ifs(filename);
if (ifs) {
rc += parse_declarations(ifs);
} else {
error() << "Unable to read from " << filename << '.' << endl ;
return -1;
}
}
}
return rc;
}
//-------------------
// ^FUNCTION: CmdParseCommand::usr_append - add a user argument
//
// ^SYNOPSIS:
// CmdParseCommand::usr_append(type, varname, arg, description)
//
// ^PARAMETERS:
// const char * type;
// -- the name of the desired argument type (which should correspond
// to either CmdArgUsage, CmdArgDummy, or one of the types defined
// in "argtypes.h").
//
// const char * varname;
// -- the name of the shell variable that will be receiving the value
// that was supplied to this argument on the command-line.
//
// ArgSyntax & arg;
// -- the argument syntax corresponding to the "syntax-string" supplied
// by the user.
//
// const char * description;
// -- the user's description of this argument.
//
// ^DESCRIPTION:
// This member function will create a corresponding instance of a derived
// class of CmdArg named by "type" and will append it to the user's CmdLine
// object (named usr_cmd).
//
// "type" may be one of the following (the leading "CmdArg" prefix may be
// omitted):
//
// CmdArgInt, CmdArgFloat, CmdArgChar, CmdArgStr, CmdArgBool,
// CmdArgSet, CmdArgClear, CmdArgToggle, CmdArgUsage, CMdArgDummy
//
// It is NOT necessary to use the name of a "List" CmdArg type in order
// to indicate a list, that will have been inferred from the syntax string
// and the appropriate ShellCmdArg<Type> object that we create will know
// how to handle it.
//
// ^REQUIREMENTS:
// This function should be called after an argument declaration has been
// completely parsed.
//
// ^SIDE-EFFECTS:
// If "type" is invalid - an error is printed on cerr, otherwise
// we create an appopriate CmdArg<Type> object and append it to usr_cmd.
//
// ^RETURN-VALUE:
// 0 for success; non-zero for failure.
//
// ^ALGORITHM:
// Trivial - there's just a lot of type-names to check for.
//-^^----------------
int
CmdParseCommand::usr_append(const char * type,
const char * varname,
ArgSyntax & arg,
const char * description)
{
char * name = NULL ;
char * kwd = NULL ;
char * val = NULL ;
char * desc = NULL ;
unsigned flags = arg.syntax() ;
char opt = arg.optchar() ;
// Need to make copies of some things because we cant assume they
// will be sticking around. We assume that ShellCmdArg::~ShellCmdArg
// will deallocate this storage.
//
::copy(name, varname);
::copy(kwd, arg.keyword());
::copy(val, arg.value());
::copy(desc, description);
// Skip any leading "Cmd", "Arg", or "CmdArg" prefix in the type-name.
if (CmdLine::strmatch("Cmd", type, 3) == CmdLine::str_EXACT) type += 3;
if (CmdLine::strmatch("Arg", type, 3) == CmdLine::str_EXACT) type += 3;
// Create an argument for the appropriate type and append it
// to usr_cmd.
//
if (CmdLine::strmatch("Usage", type) == CmdLine::str_EXACT) {
delete [] name ;
usr_cmd.append(new CmdArgUsage(opt, kwd, desc)) ;
}
else if (CmdLine::strmatch("Dummy", type) == CmdLine::str_EXACT) {
delete [] name ;
usr_cmd.append(new CmdArgDummy(opt, kwd, val, desc, flags));
}
else if (CmdLine::strmatch("Set", type) == CmdLine::str_EXACT) {
usr_cmd.append(new ShellCmdArgBool(name, opt, kwd, desc, flags));
}
else if (CmdLine::strmatch("Clear", type) == CmdLine::str_EXACT) {
usr_cmd.append(new ShellCmdArgClear(name, opt, kwd, desc, flags));
}
else if (CmdLine::strmatch("Toggle", type) == CmdLine::str_EXACT) {
usr_cmd.append(new ShellCmdArgToggle(name, opt, kwd, desc, flags));
}
else if (CmdLine::strmatch("Boolean", type) != CmdLine::str_NONE) {
usr_cmd.append(new ShellCmdArgBool(name, opt, kwd, desc, flags));
}
else if (CmdLine::strmatch("Integer", type) != CmdLine::str_NONE) {
usr_cmd.append(new ShellCmdArgInt(name, opt, kwd, val, desc, flags));
}
else if (CmdLine::strmatch("Float", type) != CmdLine::str_NONE) {
usr_cmd.append(new ShellCmdArgFloat(name, opt, kwd, val, desc, flags));
}
else if (CmdLine::strmatch("Character", type) != CmdLine::str_NONE) {
usr_cmd.append(new ShellCmdArgChar(name, opt, kwd, val, desc, flags));
}
else if (CmdLine::strmatch("String", type) != CmdLine::str_NONE) {
usr_cmd.append(new ShellCmdArgStr(name, opt, kwd, val, desc, flags));
}
else {
cerr << "Unknown argument type \"" << type << "\"." << endl ;
delete [] kwd ;
delete [] val ;
delete [] desc ;
return FAILURE ;
}
return SUCCESS ;
}
//-------------------
// ^FUNCTION: CmdParseCommand::parse_declarations - parse from a string
//
// ^SYNOPSIS:
// CmdParseCommand::parse_declarations(str);
//
// ^PARAMETERS:
// const char * str;
// -- the string containing the argument declarations.
//
// ^DESCRIPTION:
// Parse the user's argument declarations from an input string.
//
// ^REQUIREMENTS:
// This member function should only be called by parse_declarations(void).
//
// ^SIDE-EFFECTS:
// - modifies usr_cmd by appending to it any valid arguments that we parse.
//
// ^RETURN-VALUE:
// 0 for success; non-zero for failure.
//
// ^ALGORITHM:
// Just turn the string into an instream and parse the instream.
//-^^----------------
int
CmdParseCommand::parse_declarations(const char * str)
{
int rc = 0;
char * strbuf = new char[::strlen(str) + 1] ;
(void) ::strcpy(strbuf, str);
istrstream iss(strbuf);
rc = parse_declarations(iss);
delete strbuf ;
return rc ;
}
//-------------------
// ^FUNCTION: CmdParseCommand::parse_declarations - parse from an instream
//
// ^SYNOPSIS:
// CmdParseCommand::parse_declarations(is);
//
// ^PARAMETERS:
// istream & is;
// -- the instream containing the argument declarations.
//
// ^DESCRIPTION:
// Parse the user's argument declarations from an input steam.
//
// ^REQUIREMENTS:
// This member function should only be called by parse_declarations(void).
//
// ^SIDE-EFFECTS:
// - modifies usr_cmd by appending to it any valid arguments that we parse.
//
// ^RETURN-VALUE:
// 0 for success; non-zero for failure.
//
// ^ALGORITHM:
// while not eof do
// - read the type
// - read the name
// - read the syntax
// - read the description
// - convert (type, name, syntax, description) into something we can
// append to usr_cmd.
// done
//-^^----------------
int
CmdParseCommand::parse_declarations(istream & is)
{
// Keep track of the number of declarations that we parse.
unsigned nargs = 0;
if (is.eof()) return SUCCESS;
char arg_type[MAX_IDENT_LEN];
char arg_name[MAX_IDENT_LEN];
QuotedString arg_description(MAX_DESCRIPTION_LEN);
while (is) {
++nargs;
// Skip all non-alpha-numerics
int c = is.peek() ;
while ((c != EOF) && (c != '_') && (! isalnum(c))) {
(void) is.get();
c = is.peek();
}
// First parse the argument type
is.width(sizeof(arg_type) - 1);
is >> arg_type ;
if (! is) {
if (is.eof()) {
return SUCCESS; // end of args
} else {
error() << "Unable to extract type for argument #" << nargs
<< '.' << endl ;
return FAILURE;
}
}
// Now parse the argument name
is.width(sizeof(arg_name) - 1);
is >> arg_name ;
if (! is) {
if (is.eof()) {
error() << "Premature end of input.\n"
<< "\texpecting a name for argument #" << nargs
<< '.' << endl ;
} else {
error() << "Unable to extract name of argument #" << nargs
<< '.' << endl ;
}
return FAILURE;
}
// Now parse the argument syntax
ArgSyntax arg;
is >> arg;
if (! is) {
error() << "Unable to get syntax for \"" << arg_name << "\" argument."
<< endl ;
return FAILURE ;
}
// Now parse the argument description
is >> arg_description ;
if (! is) {
error() << "Unable to get description for \"" << arg_name
<< "\" argument." << endl ;
return FAILURE ;
}
if (usr_append(arg_type, arg_name, arg, arg_description)) {
error() << "Unable to append \"" << arg_name << "\" argument "
<< "to the list." << endl ;
return FAILURE;
}
}
return SUCCESS;
}
//-------------------
// ^FUNCTION: CmdParseCommand::set_args - set the user's arguments.
//
// ^SYNOPSIS:
// CmdParseCommand::set_args(shell)
//
// ^PARAMETERS:
// UnixShell * shell;
// -- the command-interpreter (shell) that we need to output
// variable settings for.
//
// ^DESCRIPTION:
// For each argument that was given on the user's command-line, we need to
// output a variable setting using the sepcified shell's syntax to indicate
// the value that was specified.
//
// ^REQUIREMENTS:
// This member function should only be called by CmdParseCommand::operator()
//
// ^SIDE-EFFECTS:
// All the variable settings or sent to cout (standard output), the invoking
// user is responsible for evaluating what this member function outputs.
//
// ^RETURN-VALUE:
// None.
//
// ^ALGORITHM:
// For each of the user's command-argument objects
// - if the argument is a dummy, then skip it
// - if the argument corresponds to the positional parameters for
// this shell but was not given a value on the command-line then
// unset this shell's positional parameters.
// - if the argument was given then
// - if the argument took a value and no value was given, then
// set the variable <argname>_FLAG to be TRUE.
// - else set the corresponding shell variable.
// endif
// endif
// endfor
//-^^----------------
void
CmdParseCommand::set_args(UnixShell * shell)
{
unsigned flags, syntax;
CmdLineCmdArgIter iter(usr_cmd);
for (CmdArg * cmdarg = iter() ; cmdarg ; cmdarg = iter()) {
flags = cmdarg->flags();
syntax = cmdarg->syntax();
if (cmdarg->is_dummy()) continue;
ShellCmdArg * sh_cmdarg = (ShellCmdArg *)cmdarg;
if ((syntax & CmdArg::isPOS) && (! (flags & CmdArg::VALGIVEN))) {
// if these are the positional-parameters then unset them!
if (shell->is_positionals(sh_cmdarg->name())) {
shell->unset_args(sh_cmdarg->name());
}
}
if (! (flags & CmdArg::GIVEN)) continue;
if ((syntax & CmdArg::isVALTAKEN) && (! (flags & CmdArg::VALGIVEN))) {
// flag was given without its value - we need to record that
char var_name[256];
(void) ::strcpy(var_name, sh_cmdarg->name());
(void) ::strcat(var_name, suffix_str);
ShellVariable sh_var(var_name);
sh_var.set(ShellCmdArgBool::True());
shell->set(sh_var);
} else {
// output the value
if (sh_cmdarg->is_array()) {
shell->set(sh_cmdarg->array(), array_variant);
} else {
shell->set(sh_cmdarg->variable());
}
}
} //for
}
//-------------------------------------------- CmdParseCommand::CmdParseCommand
CmdParseCommand::CmdParseCommand(const char * name)
: CmdLine(name),
anywhere('a', "anywhere",
"Allow options (and keywords) to follow positional parameters."
),
anycase('i', "ignore-case",
"Ignore character case on options."
),
no_abort('n', "noabort",
"Dont exit if bad syntax; try to continue parsing."
),
no_guessing('g', "noguessing",
"Dont \"guess\" for unmatched options/keywords."
),
prompt('p', "prompt",
"Prompt the user interactively for any missing required arguments."
),
plus('+', "plus",
"Allow the string \"+\" to be used as a long-option prefix."
),
opts_only('o', "options-only",
"Dont match keywords (long-options)."
),
kwds_only('k', "keywords-only",
"Dont match options."
),
quiet('q', "quiet",
"Dont print command-line syntax error messages."
),
array_variant('A', "arrays",
"Use alternative syntax for arrays."
),
usage('u', "usage",
"Print command-line usage and exit."
),
version('v', "version",
"Print version information and exit."
),
true_str('T', "true", "string",
"The string to use for boolean arguments that are turned ON \
(default=\"TRUE\")."
),
false_str('F', "false", "string",
"The string to use for boolean arguments that are turned OFF \
(default=\"\")."
),
suffix_str('S', "suffix", "string",
"The suffix to use for missing optional values. (default=\"_FLAG\")."
),
usr_shell('s', "shell", "shellname",
"Set program arguments using the syntax of the given shell \
(default=\"sh\")."
),
input_file('f', "file", "filename",
"The file from which program argument declarations are read."
),
input_var('e', "env", "varname",
"The environment variable containing the program argument declarations."
),
input_str('d', "decls", "string",
"The string that contains the program argument declarations."
),
dummy_arg("--",
"Indicates the end of options/keywords."
),
usr_prog('N', "name", "program-name",
"The name of the program whose arguments are to be parsed.",
(CmdArg::isPOS | CmdArg::isREQ | CmdArg::isVALREQ)
),
usr_args("[arguments ...]",
"The program-arguments to be parsed"
)
{
// Append options.
(*this) << anywhere << anycase << no_abort << no_guessing << prompt << plus
<< opts_only << kwds_only << quiet << array_variant << usage
<< version << true_str << false_str << suffix_str << usr_shell
<< input_file << input_var << input_str << dummy_arg ;
// Append positional parameters.
(*this) << usr_prog << usr_args ;
set(CmdLine::KWDS_ONLY);
// Set up defaults
usr_shell = "sh" ;
true_str = "TRUE" ;
false_str = "" ;
suffix_str = "_FLAG" ;
}
//------------------------------------------- CmdParseCommand::~CmdParseCommand
CmdParseCommand::~CmdParseCommand(void)
{
CmdLineCmdArgIter iter(usr_cmd);
for (CmdArg * cmdarg = iter() ; cmdarg ; cmdarg = iter()) {
delete cmdarg ;
}
}
//-------------------
// ^FUNCTION: CmdParseCommand::operator()
//
// ^SYNOPSIS:
// CmdParseCommand::operator(iter)
//
// ^PARAMETERS:
// CmdLineArgIter & iter;
// -- an object to iterate over the arguments on the command-line.
//
// ^DESCRIPTION:
// This member function is the "guts" of a CmdParseCommand object.
// We perform all the actions necessary to read the user's argument
// declaratins, read the user's command-line, and set the corresponding
// shell variables.
//
// ^REQUIREMENTS:
// None.
//
// ^SIDE-EFFECTS:
// - Modifies all parts of the corresponding CmdParseCommand object.
// - prints variable settings on cout
// - prints usage/error messages on cerr
//
// ^RETURN-VALUE:
// e_SUCCESS -- no errors
// e_USAGE -- no errors - usage printed
// e_VERSION -- no errors - version printed
// e_CMDSYNTAX -- command-line syntax error
// e_BADSHELL -- invalid shell specified
// e_BADDECLS -- invalid declaration(s) given
//
// ^ALGORITHM:
// It gets complicated so follow along.
//-^^----------------
int
CmdParseCommand::operator()(CmdLineArgIter & iter)
{
// Parse arguments
parse(iter);
// Use the specified shell
UnixShell * shell = new UnixShell(usr_shell);
if (! shell->is_valid()) {
error() << "\"" << usr_shell
<< "\" is not a known command interpreter." << endl ;
return e_BADSHELL ;
}
// Handle "-true" and "-false" options
if (true_str.flags() & CmdArg::GIVEN) ShellCmdArgBool::True(true_str);
if (false_str.flags() & CmdArg::GIVEN) ShellCmdArgBool::False(false_str);
// Intitialize user's command-line
usr_cmd.name(usr_prog);
if (parse_declarations()) return e_BADDECLS ;
// Set user parsing preferences
if (anywhere) usr_cmd.clear(CmdLine::OPTS_FIRST);
if (anycase) usr_cmd.set(CmdLine::ANY_CASE_OPTS);
if (no_abort) usr_cmd.set(CmdLine::NO_ABORT);
if (no_guessing) usr_cmd.set(CmdLine::NO_GUESSING);
if (prompt) usr_cmd.set(CmdLine::PROMPT_USER);
if (plus) usr_cmd.set(CmdLine::ALLOW_PLUS);
if (opts_only) usr_cmd.set(CmdLine::OPTS_ONLY);
if (kwds_only) usr_cmd.set(CmdLine::KWDS_ONLY);
if (quiet) usr_cmd.set(CmdLine::QUIET);
// Just print usage if thats all that is desired
if (usage) {
usr_cmd.usage(cout);
return e_USAGE ;
}
// Parse user's command-line
usr_cmd.prologue();
for (int i = 0 ; i < usr_args.count() ; i++) {
usr_cmd.parse_arg(usr_args[i]);
}
usr_cmd.epilogue();
// Set user's variables
set_args(shell);
delete shell ;
return 0;
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.