This is cmdline.h in view mode; [Download] [Up]
//------------------------------------------------------------------------ // ^FILE: cmdline.h - declare the basic classes used in the CmdLine library // // ^DESCRIPTION: // This file declares the three basic classes used in the CmdLine library. // These three classes are "CmdArg" (a command-argument object), // "CmdLineArgIter" (an object to iterate over a set of arguments), // and "CmdLine" (the command-line object itself). // // ^HISTORY: // 03/19/92 Brad Appleton <brad@ssd.csd.harris.com> Created // // 03/01/93 Brad Appleton <brad@ssd.csd.harris.com> // - Added arg_sequence field to CmdArg // - Added cmd_nargs_parsed field to CmdLine // - Added cmd_description field to CmdLine // - Added exit_handler() and quit() member-functions to CmdLine // - Added ALLOW_PLUS to list of CmdLine configuration flags //-^^--------------------------------------------------------------------- #ifndef _usr_include_cmdline_h #define _usr_include_cmdline_h class ostream ; class istream ; class CmdLine ; class CmdArgListIter ; class CmdArgListList ; //----------------------------------------------------------------------------- // A CmdArg is an abstract command-line argument. // At this level (being the base class), all a command argument // contains is the "interface" (on the command-line) of the // argument, and some information (after the command-line has // been parsed) that says "how" the argument appeared (if it did). // // The interface of a CmdArg consists of 6 things: // 1) a character name // 2) a keyword name // 3) a value name (if the argument takes a value) // 4) an argument description // 5) a set of flags describing the syntax of the argument. // 6) a set of flags to record how (and if) the argument // appeared on the command-line. // // When constructing a CmdArg, the most common syntax-flags can be // inferred from the syntax used in the argument description, // and the argument value name. If the first non-white character // of the argument description is ';', then the argument is considered // to be "secret" and is NOT printed in usage messages. // // When specifiying a value, one may enclose the value name between // '[' and ']' to indicate the value is optional. Also, one may follow // the actual value name with "..." to indicate that the value corresponds // to a LIST of values. // // Hence, the only time you really need to explicitly specify any syntax // flags is when you want a value to be "strictly sticky" or "strictly // separate" and/or you want an argument to be able to be matched both // positionally AND by (long or short) keyword. // class CmdArg { public: friend class CmdLine ; // Flags that define the argument syntax enum CmdArgSyntax { isOPT = 0x00, // argument is optional isREQ = 0x01, // argument is required isVALOPT = 0x02, // argument value is optional isVALREQ = 0x04, // argument value is required isVALSEP = 0x08, // argument value must be in a separate token isVALSTICKY = 0x10, // argument value must be in the same token isLIST = 0x20, // argument is a list isPOS = 0x40, // argument is positional isHIDDEN = 0x80, // argument is not to be printed in usage isVALTAKEN = (isVALREQ | isVALOPT), // argument takes a value isOPTVALOPT = (isOPT | isVALOPT), isOPTVALREQ = (isOPT | isVALREQ), isPOSVALOPT = (isPOS | isVALOPT), isPOSVALREQ = (isPOS | isVALREQ), } ; // Flags that say how the argument was specied on the command-line enum CmdArgFlags { GIVEN = 0x01, // argument was given VALGIVEN = 0x02, // argument value was given OPTION = 0x04, // item was matched as an option KEYWORD = 0x08, // item was matched as a keyword POSITIONAL = 0x10, // item was matched as a positional argument VALSEP = 0x20, // value was in a separate token } ; // Create an option that takes a value. // // The default flags are to assume that the argument is optional // and that the value is required. // // Some examples: // // CmdArg('c', "count", "number", "specify the # of copies to use"); // // CmdArg('d', "debug", "[level]", "turn on debugging and optionally " // "specify the debug level"); // // CmdArg('l', "list", "items ...", "specify a list of items."); // CmdArg(char optchar, const char * keyword, const char * value, const char * description, unsigned syntax_flags =isOPTVALREQ); // Create an option that takes no value. // // The default flags are to assume that the argument is optional. // // Some examples: // // CmdArg('m', "mode", "turn on this mode"); // CmdArg(char optchar, const char * keyword, const char * description, unsigned syntax_flags =isOPT); // Create a positional argument. // // The default flags are to assume that the argument is positional // and that the argument value is required. // // Some examples: // // CmdArg("file", "file to read"); // // CmdArg("[file]", "optional file to read"); // // CmdArg("file ...", "list of files to read"); // // CmdArg("[file ...]", "optional list of files to read"); // CmdArg(const char * value, const char * description, unsigned syntax_flags =isPOSVALREQ); CmdArg(const CmdArg & cp); virtual ~CmdArg(void); // over-ride this function to return a non-zero value if you // wish the argument to be ignored (except for usage messages). // virtual int is_dummy(void); // Here is the "primary" function that makes everything work ... // // Whenever we actually "match" an argument on the command-line, // we need to tell the argument it was matched (and how), and // give it the string value (if there is one) to associate with it. // // At this point, the argument object is then responsible for // performing whatever "magic" is to be done. This might be going // off and reading a file, performing some other actions, or just // "compiling" the argument into some internal value (specified // by the derived class) to be queried at a later time. // // The parameters to this function are as follows: // // PARAMETER 1: arg // The string value on the command-line to associate with this // argument. If this argument does not take a value, or the // value is optional and was NOT supplied, then NULL is passed. // This parameter is a reference parameter and before returning, // "arg" should either be set to NULL (to indicate that the entire // argument was used) or should point to the first unused character // of "arg". // // PARAMETER 2: cmd // A reference to the command-line object that is currently being // parsed. There are parts of this object that may be helpful in // determining what to do. In particular, we may want to look at // the "flags" of the command. If the QUIET flag is set, then // this routine should suppress the printing of any error messages. // If the TEMP flag is set, then "arg" (the first parameter) points // to storage that may not be around for much longer and we may // want to make a copy of it. // // Before, this function is called, the CmdLine object that is parsing // the arguments has already set the "flags()" of this argument to tell // us how we appeared on the command-line this time around (were we // specified as an option or positionally? Is "arg" in a separate argv[] // element?). // // After we have done our "magic" and set the reference parameter // "arg", this function should return a value of 0 if everything // is A-OK and "arg" was a correctly specified value for this type of // argument. If something went wrong (like a syntax error in "arg"), // then we should return a non-zero value. // virtual int operator()(const char * & arg, CmdLine & cmd) = 0; // Retrieve the syntax flags for this argument. unsigned syntax(void) const { return arg_syntax; } // Get the flags that say how this argument was specified. unsigned flags(void) const { return arg_flags; } // Get the sequence number corresponding to the last // time this argument was matched on the command-line. // // If this argument was not matched, the sequence number // will be zero, otherwise it will be 'N' where the last // time this argument was matched, it was the 'N'th argument // encountered. unsigned sequence(void) const { return arg_sequence; } // Get the character (short-option) name of this argument. // Returns '\0' if there isnt one. char char_name(void) const { return arg_char_name; } // Get the keyword (long-option) name of this argument // Returns NULL if there isnt one. const char * keyword_name(void) const { return arg_keyword_name; } // Get the value name of this argument. // Returns NULL if this argument takes no value. const char * value_name(void) const { return arg_value_name; } // Get the description (help-message) of this argument. const char * description(void) const { return arg_description; } // If we were compiled for dubugging, then dump this argument virtual void dump(ostream & os, unsigned level =0) const; private: // Member functions for internal use void adjust_syntax(void); void parse_description(void); void parse_value(void); void flags(unsigned newflags) { arg_flags = newflags; } void set(unsigned flags) { arg_flags |= flags; } void clear(unsigned flags =~0) { arg_flags &= ~flags; } // set sequence number void sequence(unsigned num) { arg_sequence = num; } // Private data members unsigned alloc_value_name : 1 ; unsigned arg_flags : 8 ; unsigned arg_syntax : 8 ; unsigned arg_sequence; char arg_char_name; const char * arg_keyword_name; const char * arg_value_name; const char * arg_description; } ; //----------------------------------------------------------------------------- // In order to parse arguments, we need to have an input source where // the arguments are coming from. CmdLineArgIter is an abstract // iterator class for cycling through all the command-line arguments // from an arbitrary input source. // class CmdLineArgIter { public: CmdLineArgIter(void); virtual ~CmdLineArgIter(void); // Return the current argument and advance to the next one. // Returns NULL if we are already at the end of the arguments // virtual const char * operator()(void) = 0; // Are the args returned by operator() pointing to temporary storage? virtual int is_temporary(void) const = 0; private: CmdLineArgIter(const CmdLineArgIter &) ; CmdLineArgIter & operator=(const CmdLineArgIter &) ; } ; // CmdArgvIter is a class used to iterate through command arguments // that come from an array of strings (like argv[] from main()). // class CmdArgvIter : public CmdLineArgIter { public: CmdArgvIter(int argc, const char * const argv[]) : count(argc), array(argv), index(0) {} CmdArgvIter(const char * const argv[]) : array(argv), index(0), count(-1) {} virtual ~CmdArgvIter(void); virtual const char * operator()(void); // is_temporary returns 0 for CmdArgvIter virtual int is_temporary(void) const; // Restart using a different string array. void reset(int argc, const char * const argv[]) { count = argc; array = argv; index = 0; } void reset(const char * const argv[]) { array = argv; index = 0; count = -1; } private: CmdArgvIter(const CmdArgvIter &) ; CmdArgvIter & operator=(const CmdArgvIter &) ; int count; int index; const char * const * array; } ; // CmdStrTok iterator is a class for iterating over arguments that // are specified in a string of tokens that are delimited by a // particular set of characters. The strtok(3C) library function // is used to extract tokens from the string. // // If NULL is given as the delimiter-set, then whitespace is assumed. // class CmdStrTokIter : public CmdLineArgIter { public: CmdStrTokIter(const char * tokens, const char * delimiters =0); virtual ~CmdStrTokIter(void); virtual const char * operator()(void); // Reset using a new token-string and delimiter set. void reset(const char * tokens, const char * delimiters =0); // Get the current delimiter set const char * delimiters(void) const { return seps; } // Change the current delimiter set void delimiters(const char * new_delimiters) { seps = new_delimiters; } // is_temporary returns 1 for CmdStrTokIter virtual int is_temporary(void) const; private: CmdStrTokIter(const CmdStrTokIter &) ; CmdStrTokIter & operator=(const CmdStrTokIter &) ; char * tokstr; const char * seps; const char * token; } ; // CmdIstreamIter is a class for iterating over arguments that come // from an input stream. Each line of the input stream is considered // to be a set of white-space separated tokens. If the the first // non-white character on a line is '#' ('!' for VMS systems) then // the line is considered a comment and is ignored. // // *Note:: If a line is more than 1022 characters in length then we // treat it as if it were several lines of length 1022 or less. // class CmdIstreamIter : public CmdLineArgIter { public: static const unsigned MAX_LINE_LEN ; CmdIstreamIter(istream & input); virtual ~CmdIstreamIter(void); virtual const char * operator()(void); // is_temporary returns 1 for CmdIstreamIter virtual int is_temporary(void) const; private: istream & is ; CmdStrTokIter * tok_iter ; } ; //----------------------------------------------------------------------------- // Here is the class that represents a command-line object. A command // line object is a parsing machine (with machine states), whose parsing // behavior may be configured at run-time by specifying various CmdFlags // (defined below). A command-line object also contains a command-name // and a list of CmdArg objects that correspond to the various arguments // that are allowed to occur on the command line. // class CmdLine { public: friend class CmdLineCmdArgIter; // Flags that define parsing behavior // The default flags (for Unix) are OPTS_FIRST. enum CmdFlags { ANY_CASE_OPTS = 0x001, // Ignore character-case for short-options PROMPT_USER = 0x002, // Prompt the user for missing required args NO_ABORT = 0x004, // Dont quit upon syntax error OPTS_FIRST = 0x008, // No options after positional parameters OPTS_ONLY = 0x010, // Dont accept long-options KWDS_ONLY = 0x020, // Dont accept short-options TEMP = 0x040, // Assume all arg-strings are temporary QUIET = 0x080, // Dont print syntax error messages NO_GUESSING = 0x100, // Dont guess if cant match an option. // Unless this flag is given, then // when we see an unmatched option, // we will try to see if it matches // a keyword (and vice-versa). ALLOW_PLUS = 0x200, // Allow "+" (as well as "--") as a prefix // indicating long-options. } ; // Flags to convey parsing-status enum CmdStatus { NO_ERROR = 0x000, // No problems ARG_MISSING = 0x001, // A required argument was not specified VAL_MISSING = 0x002, // A required argument value was not specified VAL_NOTSTICKY = 0x004, // Value needs to be in same token VAL_NOTSEP = 0x008, // Value needs to be in separate token KWD_AMBIGUOUS = 0x010, // An ambiguous keyword prefix was specified BAD_OPTION = 0x020, // An invalid option was specified BAD_KEYWORD = 0x040, // An invalid keyword was specified BAD_VALUE = 0x080, // An invalid value was specified for an arg TOO_MANY_ARGS = 0x100, // Too many positional args were specified } ; // Contructors and Destructors ... // // It is not necessary to supply a command-name at construction // time, but one SHOULD be specified before parsing a command-line // or printing a usage message. // // Similarly, CmdArgs are not required at construction time and may // even be added on the fly. All desired arguments should be added // before any parsing happens and before printing usage. // // The order in which CmdArgs are added to a CmdLine is important // because for positional parameters, this specifies the order in // which they are expected to appear on the command-line. // CmdLine(const char * name =0); CmdLine(const char * name, CmdArg * cmdarg1 ...); // last arg should be NULL CmdLine(CmdArg * cmdarg1 ...); // last arg should be NULL virtual ~CmdLine(void); // Get the command name. const char * name(void) const { return cmd_name; } // Specify a command name. void name(const char * progname); // Get the command description. const char * description(void) const { return cmd_description; } // Specify a command description. void description(const char * the_description) { cmd_description = the_description; } // Append an argument CmdLine & append(CmdArg * cmdarg); CmdLine & append(CmdArg & cmdarg) { return append(& cmdarg); } // The insertion operator (operator<<) is merely a convenient // shorthand for the append() member function. // CmdLine & operator<<(CmdArg * cmdarg) { return append(cmdarg) ; } CmdLine & operator<<(CmdArg & cmdarg) { return append(& cmdarg) ; } // // Messages and Status // // Specify the verboseness of usage messages enum CmdUsageLevel { NO_USAGE = 0, // Dont print usage at all. TERSE_USAGE = 1, // Just print command-line syntax. VERBOSE_USAGE = 2, // Print command-line syntax & argument-descriptions. DEFAULT_USAGE = 3, // Read the $USAGE_LEVEL environment variable for // the usage-level: 0=none, 1=terse, 2=verbose. // if $USAGE_LEVEL is empty or is not 0, 1, or 2 // then VERBOSE_USAGE is used. } ; // Print usage on the given output stream using the given verboseness ostream & usage(ostream & os, CmdUsageLevel level =DEFAULT_USAGE) const ; // Print usage on the CmdLine's error-outstream ostream & usage(CmdUsageLevel level =DEFAULT_USAGE) const ; // Obtain the current status of the command. The status will be // zero if everything is alright; otherwise it will correspond // to a combination of CmdStatus bitmasks telling us precisely // what went wrong. // unsigned status(void) const { return cmd_status; } // Print an error message prefix on the error output stream // associated with this command. The prefix printed is the // basename of the command followed by a ':'. // // Hence error messages associated with this command may be // printed as follows: // // my_cmd.error() << "This is what went wrong!" << endl; // // If QUIET (or for that matter, any non-zero value) is given // as a parameter, then nothing is printed on the ostream // (which may be useful if you wish only to obtain a reference // this CmdLine's error-outstream). // ostream & error(unsigned quiet =0) const; // If the QUIET-flag is not set, then we need to know where // to print any error messages (the default is cerr). // // Use this member function to specify the desired output stream // for error messages. // void error(ostream & os) { cmd_err = &os; } // // Get & set the command-parsing-flags // // Get the current set of command-flags unsigned flags(void) const { return cmd_flags; } // Specify a new set of command-flags void flags(unsigned newflags) { cmd_flags = newflags; } // Set only the given command-flags void set(unsigned flags) { cmd_flags |= flags; } // Clear only the given command-flags void clear(unsigned flags =~0) { cmd_flags &= ~flags; } // // We are somewhat flexible in the way we parse arguments. // Before any parsing can occur, some preprocessing needs to // be done. After we have parsed all the arguments, some // post-processing needs to be done. If you use the "parse()" // member function, then this pre- and post- processing is // automatically performed for you UNLESS you specify a second // parameter of NO_PROCESSING. If you have arguments that are // coming from more then one input source, you will have to // manually activate pre-processing (by calling prologue()), // then parse all your arguments (using parse() with NO_PROCESSING // and/or using arg_parse()), and then manually activate // post-processing (by calling epilogue()). // // Parse a set of arguments (pre- and post- processing is // automatically performed UNLESS "NO_PROCESSING" is given // as the second argument). // // Return the resultant command status. // enum { NO_PROCESSING = 0, AUTO_PROCESSING = 1 } ; unsigned parse(CmdLineArgIter & arg_iter, int processing =AUTO_PROCESSING) ; // Perform the necessary pre-processing. // Return the resultant command status. // unsigned prologue(void) ; // Parse a single argument (pre- and post- processing is // NOT performed). // unsigned parse_arg(const char * arg) ; // Perform the necessary post-processing. // Return the resultant command status. // unsigned epilogue(void) ; // // Find out the number of arguments parsed so far // unsigned nargs_parsed(void) const { return cmd_nargs_parsed; } // // Exception handling (well -- not really) // typedef void (* quit_func_t)(int); // When a fatal error is encountered or when parsing needs to // terminate immediately, the quit() member function is called. // If the programmer has used quit_handler() to setup his own // handler-function, then that function is called; otherwise // exit() is called with the given status. // void quit(int status); // Set the quit-handler. The quit-handler is a pointer to // a function that has no return value and takes a single // integer parameter. // void quit_handler(quit_func_t quit_func) { cmd_quit_handler = quit_func ; } // Get the current quit-handler (returns NULL if there isnt one) // quit_func_t quit_handler(void) const { return cmd_quit_handler; } // // Retrieve a specific argument // // Retrieve an argument based on its character-name. // Returns NULL if no argument matches the given character. // CmdArg * operator[](char optchar) const { return opt_match(optchar); } // Retrieve an argument based on its keyword-name. // (if an argument has no keyword-name then we try to match its // value name instead). // // Returns NULL if no argument matches the given keyword or // the keyword is ambiguous. // CmdArg * operator[](const char * keyword) const { int ambig = 0; return kwd_match(keyword, -1, ambig, 1); } // // Version specific information // // get the release number static unsigned release(void); // get the patchlevel number static unsigned patchlevel(void); // get the SCCS identifier string static const char * ident(void); // // These next few functions are used internally but are general // enough in purpose as to possibly be useful to others. // // return type for an attempted keyword match enum strmatch_t { str_NONE, str_PARTIAL, str_EXACT } ; // Try to match "attempt" against "src", if len is 0 then // only the first "len" characters are compared. // // Returns str_EXACT for an exact-match str_PARTIAL for a // partial-match, and str_NONE otherwise. // static strmatch_t strmatch(const char * src, const char * attempt, unsigned len =0); // Print a hanging indented paragraph on an outstream. Long lines // are broken at word boundaries and are wrapped to line up with // the rest of the paragraph. The format looks like the following // (text starts on a new line is the strlen(title) >= indent): // // <------------------------- maxcols -------------------------------> // <--- margin ---><--- indent ---> // title This is the first sentence. This // is the second sentence. etc ... // static void strindent(ostream & os, unsigned maxcols, unsigned margin, const char * title, unsigned indent, const char * text); // // Debugging stuff ... // // If we were compiled for dubugging, then dump this command virtual void dump(ostream & os, unsigned level =0) const; // If we were compiled for dubugging, then dump the argument list virtual void dump_args(ostream & os, unsigned level =0) const; private: // Private data members unsigned cmd_parse_state : 8 ; unsigned cmd_state : 8 ; unsigned cmd_flags : 16 ; unsigned cmd_status : 16 ; unsigned cmd_nargs_parsed ; const char * cmd_name ; const char * cmd_description ; CmdArg * cmd_matched_arg ; CmdArgListList * cmd_args ; ostream * cmd_err ; quit_func_t cmd_quit_handler ; // Disallow copying and assignment CmdLine(const CmdLine & ); CmdLine & operator=(const CmdLine & ); // // Member functions for internal use // // Specify the command syntax to use for usage messages enum CmdLineSyntax { cmd_OPTS_ONLY = 0, cmd_KWDS_ONLY = 1, cmd_BOTH = 2 } ; int handle_arg(CmdArg * cmdarg, const char * & arg); void ck_need_val(void); CmdLineSyntax syntax(void) const; unsigned prompt_user(CmdArg * cmdarg); unsigned missing_args(void); CmdArg * opt_match(char optchar) const; CmdArg * kwd_match(const char * kwd, int len, int & is_ambiguous, int match_value =0) const; CmdArg * pos_match(void) const; unsigned parse_option(const char * arg); unsigned parse_keyword(const char * arg); unsigned parse_value(const char * arg); ostream & arg_error(const char * error_str, const CmdArg * cmdarg) const; unsigned fmt_arg(const CmdArg * cmdarg, char * buf, unsigned bufsize, CmdLineSyntax syntax, CmdUsageLevel level) const; static CmdUsageLevel get_usage_level(void); unsigned print_synopsis(CmdLineSyntax syntax, ostream & os, int cols) const; void print_descriptions(CmdLineSyntax syntax, ostream & os, int cols, unsigned longest) const; } ; // "os << cmd" is equivalent to "cmd.usage(os)" inline ostream & operator <<(ostream & os, CmdLine & cmd) { return cmd.usage(os); } //----------------------------------------------------------------------------- // We want to provide the user with a means to iterate over all the // arguments in the argument list of a command-line. We will provide // a class named "CmdLineCmdArgIter" to do this. class CmdLineCmdArgIter { public: CmdLineCmdArgIter(CmdLine & cmd); CmdLineCmdArgIter(CmdLine * cmd); virtual ~CmdLineCmdArgIter(void); // Return the current argument and advance to the next one. // Returns NULL if we are already at the end of the list. // CmdArg * operator()(void); private: CmdLineCmdArgIter(const CmdLineCmdArgIter &); CmdLineCmdArgIter & operator=(const CmdLineCmdArgIter &); CmdArgListIter * iter; } ; #endif /* _usr_include_cmdline_h */
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.