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.