This is unix.c in view mode; [Download] [Up]
//------------------------------------------------------------------------ // ^FILE: unix.c - implement the unix-specific portions of CmdLine // // ^DESCRIPTION: // This file implements the public and private CmdLine library functions // that are specific to the native command-line syntax of Unix. // // The following functions are implemented: // // CmdLine::parse_option() -- parse an option // CmdLine::parse_keyword() -- parse a keyword // CmdLine::parse_value() -- parse a value // CmdLine::parse_arg() -- parse a single argument // CmdLine::arg_error() -- format an argument for error messages // CmdLine::fmt_arg() -- format an argument for usage messages // // ^HISTORY: // 01/09/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 <iostream.h> #include <strstream.h> #include <stdlib.h> #include <string.h> #include "exits.h" #include "cmdline.h" #include "states.h" // // Some Helper function for getting and recognizing prefixes // // Function to tell us if an argument looks like an option inline static int isOPTION(const char * s) { return ((*(s) == '-') && ((*((s)+1) != '-')) && ((*((s)+1) != '\0'))) ; } // Function to return the option-prefix inline static const char * OptionPrefix(void) { return "-" ; } // Function to tell us if an argument looks like a long-option. // // NOTE: allowing "+" does not preclude the use of "--" // inline static int isKEYWORD(const char *s, int allow_plus) { return (((*(s) == '-') && (*((s)+1) == '-') && (*((s)+2) != '\0')) || (allow_plus && (*(s) == '+') && ((*((s)+1) != '\0')))) ; } // Function to return the long-option prefix inline static const char * KeywordPrefix(int allow_plus) { return (allow_plus) ? "+" : "--" ; } // Need to know when an argument means "end-of-options" inline static int isENDOPTIONS(const char *s) { return ((*(s) == '-') && (*((s)+1) == '-') && (*((s)+2) == '\0')) ; } // Function to return the "end-of-options" string inline static const char * EndOptions(void) { return "--" ; } //------- // ^FUNCTION: CmdLine::parse_option - parse a Unix option // // ^SYNOPSIS: // unsigned CmdLine::parse_option(arg); // // ^PARAMETERS: // const char * arg; // -- the command-line argument containing the prospective option // // ^DESCRIPTION: // This routine will attempt to "handle" all options specified in // the string "arg". For each option found, its compile-function // is called and the corresponding state of both the command // and of the matched option(s) is (are) updated. // // ^REQUIREMENTS: // "arg" should point past any leading option prefix (such as "-"). // // ^SIDE-EFFECTS: // "cmd" is modified accordingly as each option is parsed (as are its // constituent arguments). If there are syntax errors then error messages // are printed if QUIET is NOT set. // // ^RETURN-VALUE: // NO_ERROR is returned if no errors were encountered. Otherwise the // return value is a combination of bitmasks of type CmdLine::CmdStatus // (defined in <cmdline.h>) indicating all the problems that occurred. // // ^ALGORITHM: // see if we left an option dangling without parsing its value. // for each option bundled in "arg" // try to match the option // if no match issue a message (unless QUIET is set) // else // if the option takes NO argument than handle that // else if the option takes an argument // if the rest or arg is not empty then // call the option's compile-function using the rest of // arg as the value. // skip past whatever portion of value was used // else // update the state of the command to show that we are expecting // to see the value for this option as the next argument. // endif // endif // update the state of the argument. // endif // endfor //-^^---- unsigned CmdLine::parse_option(const char * arg) { const char * save_arg = arg; unsigned save_flags = 0, rc = 0 ; CmdArg * cmdarg = NULL; int bad_val; // see if we left an argument dangling without a value ck_need_val() ; do { // loop over bundled options cmdarg = opt_match(*arg); if (cmdarg == NULL) { // If we were in the middle of a guess - sorry no cigar, otherwise // guess that maybe this is a keyword and not an keyword. // if (cmd_state & cmd_GUESSING) { if (arg == save_arg) return BAD_OPTION; } else { if (! (cmd_flags & NO_GUESSING)) { cmd_state |= cmd_GUESSING; rc = parse_keyword(arg); cmd_state &= ~cmd_GUESSING; if (rc != BAD_KEYWORD) return rc; } } if (! (cmd_flags & QUIET)) { error() << "unknown option \"" << OptionPrefix() << char(*arg) << "\"." << endl ; } rc |= BAD_OPTION ; ++arg ; // skip bad option continue ; } ++arg ; // skip past option character save_flags = cmdarg->flags() ; cmdarg->clear(); cmdarg->set(CmdArg::OPTION) ; if ((! *arg) && (cmdarg->syntax() & CmdArg::isVALTAKEN)) { // End of string -- value must be in next arg // Save this cmdarg-pointer for later and set the parse_state to // indicate that we are expecting a value. // if (cmdarg->syntax() & CmdArg::isVALSTICKY) { // If this argument is sticky we already missed our chance // at seeing a value. // if (cmdarg->syntax() & CmdArg::isVALREQ) { if (! (cmd_flags & QUIET)) { error() << "value required in same argument for " << OptionPrefix() << char(cmdarg->char_name()) << " option." << endl; } rc |= (VAL_MISSING | VAL_NOTSTICKY) ; cmdarg->flags(save_flags); } else { // The value is optional - set the GIVEN flag and call // handle_arg with NULL (and check the result). // const char * null_str = NULL; cmdarg->set(CmdArg::GIVEN) ; cmd_parse_state = cmd_START_STATE ; bad_val = handle_arg(cmdarg, null_str); if (bad_val) { if (! (cmd_flags & QUIET)) { arg_error("bad value for", cmdarg) << "." << endl ; } rc |= BAD_VALUE ; cmdarg->flags(save_flags); } } } else { // Wait for the value to show up next time around cmdarg->set(CmdArg::GIVEN) ; cmd_matched_arg = cmdarg ; cmd_parse_state = cmd_WANT_VAL ; if (cmdarg->syntax() & CmdArg::isVALREQ) { cmd_parse_state += cmd_TOK_REQUIRED ; } } return rc ; } // If this option is an isVALSEP and "arg" is not-empty then we // have an error. // if ((cmdarg->syntax() & CmdArg::isVALTAKEN) && (cmdarg->syntax() & CmdArg::isVALSEP)) { if (! (cmd_flags & QUIET)) { error() << "value required in separate argument for " << OptionPrefix() << char(cmdarg->char_name()) << " option." << endl; } rc |= (VAL_MISSING | VAL_NOTSEP) ; cmdarg->flags(save_flags); return rc; } else { // handle the option const char * save_arg = arg; bad_val = handle_arg(cmdarg, arg); if (bad_val) { if (! (cmd_flags & QUIET)) { arg_error("bad value for", cmdarg) << "." << endl ; } rc |= BAD_VALUE ; cmdarg->flags(save_flags); } cmdarg->set(CmdArg::GIVEN); if (arg != save_arg) cmdarg->set(CmdArg::VALGIVEN); } } while (arg && *arg) ; return rc ; } //------- // ^FUNCTION: CmdLine::parse_keyword - parse a Unix keyword // // ^SYNOPSIS: // unsigned CmdLine::parse_keyword(arg); // // ^PARAMETERS: // const char * arg; // -- the command-line argument containing the prospective keyword // // ^DESCRIPTION: // This routine will attempt to "handle" the keyword specified in // the string "arg". For any keyword found, its compile-function // is called and the corresponding state of both the command // and of the matched keyword(s) is (are) updated. // // ^REQUIREMENTS: // "arg" should point past any leading keyword prefix (such as "--"). // // ^SIDE-EFFECTS: // "cmd" is modified accordingly as the keyword is parsed (as are its // constituent arguments). If there are syntax errors then error messages // are printed if QUIET is NOT set. // // ^RETURN-VALUE: // NO_ERROR is returned if no errors were encountered. Otherwise the // return value is a combination of bitmasks of type CmdLine::CmdStatus // (defined in <cmdline.h>) indicating all the problems that occurred. // // ^ALGORITHM: // see if we left an option dangling without parsing its value. // look for a possible value for this keyword denoted by ':' or '=' // try to match "arg" as a keyword // if no match issue a message (unless QUIET is set) // else // if the keyword takes NO argument than handle that // else if the keyword takes an argument // if a value was found "arg" // call the keyword's compile-function with the value found. // else // update the state of the command to show that we are expecting // to see the value for this option as the next argument. // endif // endif // update the state of the argument. // endif //-^^---- unsigned CmdLine::parse_keyword(const char * arg) { unsigned save_flags = 0, rc = 0 ; CmdArg * cmdarg = NULL ; int ambiguous = 0, len = -1, bad_val; const char * val = NULL ; int plus = (cmd_flags & ALLOW_PLUS) ; // Can we use "+"? // see if we left an argument dangling without a value ck_need_val() ; // If there is a value with this argument, get it now! val = ::strpbrk(arg, ":=") ; if (val) { len = val - arg ; ++val ; } cmdarg = kwd_match(arg, len, ambiguous); if (cmdarg == NULL) { // If we were in the middle of a guess - sorry no cigar, otherwise // guess that maybe this is an option and not a keyword. // if (cmd_state & cmd_GUESSING) { return BAD_KEYWORD; } else if ((! ambiguous) || (len == 1)) { if (! (cmd_flags & NO_GUESSING)) { cmd_state |= cmd_GUESSING; rc = parse_option(arg); cmd_state &= ~cmd_GUESSING; if (rc != BAD_OPTION) return rc; } } if (! (cmd_flags & QUIET)) { error() << ((ambiguous) ? "ambiguous" : "unknown") << " option " << "\"" << ((cmd_flags & KWDS_ONLY) ? OptionPrefix() : KeywordPrefix(plus)) << arg << "\"." << endl ; } rc |= ((ambiguous) ? KWD_AMBIGUOUS : BAD_KEYWORD) ; return rc ; } save_flags = cmdarg->flags() ; cmdarg->clear(); cmdarg->set(CmdArg::KEYWORD) ; if ((cmdarg->syntax() & CmdArg::isVALTAKEN) && (val == NULL)) { // Value must be in the next argument. // Save this cmdarg for later and indicate that we are // expecting a value. // if (cmdarg->syntax() & CmdArg::isVALSTICKY) { // If this argument is sticky we already missed our chance // at seeing a value. // if (cmdarg->syntax() & CmdArg::isVALREQ) { if (! (cmd_flags & QUIET)) { error() << "value required in same argument for " << ((cmd_flags & KWDS_ONLY) ? OptionPrefix() : KeywordPrefix(plus)) << cmdarg->keyword_name() << " option." << endl; } rc |= (VAL_MISSING | VAL_NOTSTICKY) ; cmdarg->flags(save_flags); } else { // The value is optional - set the GIVEN flag and call // handle_arg with NULL (and check the result). // const char * null_str = NULL; cmdarg->set(CmdArg::GIVEN) ; cmd_parse_state = cmd_START_STATE ; bad_val = handle_arg(cmdarg, null_str); if (bad_val) { if (! (cmd_flags & QUIET)) { arg_error("bad value for", cmdarg) << "." << endl ; } rc |= BAD_VALUE ; cmdarg->flags(save_flags); } } } else { // Wait for the value to show up next time around cmdarg->set(CmdArg::GIVEN) ; cmd_matched_arg = cmdarg ; cmd_parse_state = cmd_WANT_VAL ; if (cmdarg->syntax() & CmdArg::isVALREQ) { cmd_parse_state += cmd_TOK_REQUIRED ; } } return rc ; } // If this option is an isVALSEP and "val" is not-NULL then we // have an error. // if (val && (cmdarg->syntax() & CmdArg::isVALTAKEN) && (cmdarg->syntax() & CmdArg::isVALSEP)) { if (! (cmd_flags & QUIET)) { error() << "value required in separate argument for " << ((cmd_flags & KWDS_ONLY) ? OptionPrefix() : KeywordPrefix(plus)) << cmdarg->keyword_name() << " option." << endl; } rc |= (VAL_MISSING | VAL_NOTSEP) ; cmdarg->flags(save_flags); return rc; } // handle the keyword bad_val = handle_arg(cmdarg, val); if (bad_val) { if (! (cmd_flags & QUIET)) { arg_error("bad value for", cmdarg) << "." << endl ; } rc |= BAD_VALUE ; cmdarg->flags(save_flags); } return rc ; } //------- // ^FUNCTION: CmdLine::parse_value - parse a Unix value // // ^SYNOPSIS: // unsigned CmdLine::parse_value(arg); // // ^PARAMETERS: // const char * arg; // -- the command-line argument containing the prospective value // // ^DESCRIPTION: // This routine will attempt to "handle" the value specified in // the string "arg". The compile-function of the corresponding // argument-value is called and the corresponding state of both // the command and of the matched option(s) is (are) updated. // If the value corresponds to a multi-valued argument, then that // is handled here. // // ^REQUIREMENTS: // // ^SIDE-EFFECTS: // "cmd" is modified accordingly for the value that is parsed (as are its // constituent arguments). If there are syntax errors then error messages // are printed if QUIET is NOT set. // // ^RETURN-VALUE: // NO_ERROR is returned if no errors were encountered. Otherwise the // return value is a combination of bitmasks of type CmdLine::CmdStatus // (defined in <cmdline.h>) indicating all the problems that occurred. // // ^ALGORITHM: // If the command-state says we are waiting for the value of an option // find the option that we matched last // else // match the next positional parameter in "cmd" // if there isnt one issue a "too many args" message // (unless cmd_QUIETs is set) and return. // endif // handle the given value and update the argument and command states. //-^^---- unsigned CmdLine::parse_value(const char * arg) { unsigned save_flags = 0, rc = 0 ; int bad_val; CmdArg * cmdarg = NULL; if (cmd_parse_state & cmd_WANT_VAL) { if (cmd_matched_arg == NULL) { cerr << "*** Internal error in class CmdLine.\n" << "\tparse-state is inconsistent with last-matched-arg." << endl ; ::exit(e_INTERNAL); } // get back the cmdarg that we saved for later // - here is the value it was expecting // cmdarg = cmd_matched_arg ; save_flags = cmdarg->flags() ; } else { // argument is positional - find out which one it is cmdarg = pos_match() ; if (cmdarg == NULL) { if (! (cmd_flags & QUIET)) { error() << "too many arguments given." << endl ; } rc |= TOO_MANY_ARGS ; return rc ; } save_flags = cmdarg->flags() ; cmdarg->clear(); cmdarg->set(CmdArg::POSITIONAL) ; if (cmd_flags & OPTS_FIRST) { cmd_state |= cmd_END_OF_OPTIONS ; } } // handle this value cmdarg->set(CmdArg::VALSEP) ; bad_val = handle_arg(cmdarg, arg); if (bad_val) { if (! (cmd_flags & QUIET)) { arg_error("bad value for", cmdarg) << "." << endl ; } rc |= BAD_VALUE ; cmdarg->flags(save_flags); if (! (cmdarg->syntax() & CmdArg::isLIST)) { cmd_parse_state = cmd_START_STATE; } } // If the value was okay and we were requiring a value, then // a value is no longer required. // if ((! bad_val) && (cmdarg->syntax() & CmdArg::isLIST)) { cmd_parse_state &= ~cmd_TOK_REQUIRED ; } return rc ; } //------- // ^FUNCTION: CmdLine::parse_arg - parse an argv[] element unix-style // // ^SYNOPSIS: // unsigned CmdLine::parse_arg(arg) // // ^PARAMETERS: // const char * arg; // -- an argument string (argv[] element) from the command-line // // ^DESCRIPTION: // This routine will determine whether "arg" is an option, a long-option, // or a value and call the appropriate parse_xxxx function defined above. // // ^REQUIREMENTS: // // ^SIDE-EFFECTS: // "cmd" is modified accordingly for the string that is parsed (as are its // constituent arguments). If there are syntax errors then error messages // are printed if QUIET is NOT set. // // ^RETURN-VALUE: // NO_ERROR is returned if no errors were encountered. Otherwise the // return value is a combination of bitmasks of type CmdLine::CmdStatus // (defined in <cmdline.h>) indicating all the problems that occurred. // // ^ALGORITHM: // if we are expecting a required value // call parse_value() // else if "arg" is an option // skip past the option prefix // call parse_option() // else if "arg" is a keyword // skip past the kewyord prefix // call parse_keyword() // else if "arg" is "--" (meaning end of options) // see that we didnt leave an option dangling without a value // indicate end-of-options in the command-state // else // call parse_value() // endif //-^^---- unsigned CmdLine::parse_arg(const char * arg) { if (arg == NULL) return cmd_status ; int plus = (cmd_flags & ALLOW_PLUS) ; // Can we use "+"? if (cmd_parse_state & cmd_TOK_REQUIRED) { // If a required value is expected, then this argument MUST be // the value (even if it looks like an option // cmd_status |= parse_value(arg) ; } else if (isOPTION(arg) && (! (cmd_state & cmd_END_OF_OPTIONS))) { ++arg ; // skip '-' option character if (cmd_flags & KWDS_ONLY) { cmd_state |= cmd_KEYWORDS_USED ; cmd_status |= parse_keyword(arg) ; } else { cmd_state |= cmd_OPTIONS_USED ; cmd_status |= parse_option(arg) ; } } else if ((! (cmd_flags & OPTS_ONLY)) && isKEYWORD(arg, plus) && (! (cmd_state & cmd_END_OF_OPTIONS))) { cmd_state |= cmd_KEYWORDS_USED ; if (*arg == '+') { ++arg ; // skip over '+' keyword prefix } else { arg += 2 ; // skip over '--' keyword prefix } cmd_status |= parse_keyword(arg) ; } else if (isENDOPTIONS(arg) && (! (cmd_state & cmd_END_OF_OPTIONS))) { cmd_state |= cmd_END_OF_OPTIONS ; // see if we left an argument dangling without a value ck_need_val() ; } else { cmd_status |= parse_value(arg) ; } return cmd_status ; } //------- // ^FUNCTION: CmdLine::arg_error - format an argument for error messages // // ^SYNOPSIS: // ostream & arg_error(error_str, cmdarg); // // ^PARAMETERS: // const char * error_str; // -- the problem with the argument // // const CmdArg * cmdarg; // -- the argument to be formatted // // ^DESCRIPTION: // This function will write to "os" the argument corresponding to // "cmdarg" as we would like it to appear in error messages that pertain // to this argument. // // ^REQUIREMENTS: // None. // // ^SIDE-EFFECTS: // writes to "os" // // ^RETURN-VALUE: // A reference to os. // // ^ALGORITHM: // Pretty straightforward, just print to os the way we // want the argument to appear in usage messages. //-^^---- ostream & CmdLine::arg_error(const char * error_str, const CmdArg * cmdarg) const { int plus = (cmd_flags & ALLOW_PLUS) ; // Can we use "+"? ostream & os = error() << error_str << char(' ') ; if (cmdarg->flags() & CmdArg::GIVEN) { if (cmdarg->flags() & CmdArg::KEYWORD) { os << ((cmd_flags & KWDS_ONLY) ? OptionPrefix() : KeywordPrefix(plus)) << cmdarg->keyword_name() << " option" ; } else if (cmdarg->flags() & CmdArg::OPTION) { os << OptionPrefix() << (char)cmdarg->char_name() << " option" ; } else { os << cmdarg->value_name() << " argument" ; } } else { if (cmdarg->syntax() & CmdArg::isPOS) { os << cmdarg->value_name() << " argument" ; } else { if (cmd_flags & KWDS_ONLY) { os << OptionPrefix() << cmdarg->keyword_name() << " option" ; } else { os << OptionPrefix() << (char)cmdarg->char_name() << " option" ; } } } return os; } //------- // ^FUNCTION: CmdLine::fmt_arg - format an argument for usage messages // // ^SYNOPSIS: // unsigned CmdLine::fmt_arg(cmdarg, buf, bufsize, syntax, level); // // ^PARAMETERS: // const CmdArg * cmdarg; // -- the argument to be formatted // // char * buf; // -- where to print the formatted result // // unsigned bufsize; // -- number of bytes allocated for buf. // // CmdLine::CmdLineSyntax syntax; // -- the syntax to use (option, long-option, or both). // // CmdLine::CmdUsageLevel; // -- the usage-level corresponding to this portion of the // usage message. // // ^DESCRIPTION: // This function will write into "buf" the argument corresponding to // "cmdarg" as we would like it to appear in usage messages. // // ^REQUIREMENTS: // "buf" must be large enough to hold the result. // // ^SIDE-EFFECTS: // writes to "buf" // // ^RETURN-VALUE: // the length of the formatted result. // // ^ALGORITHM: // Its kind of tedious so follow along. //-^^---- unsigned CmdLine::fmt_arg(const CmdArg * cmdarg, char * buf, unsigned bufsize, CmdLine::CmdLineSyntax syntax, CmdLine::CmdUsageLevel level) const { ostrstream oss(buf, bufsize); *buf = '\0'; int plus = (cmd_flags & ALLOW_PLUS) ; // Can we use "+"? char optchar = cmdarg->char_name(); const char * keyword = cmdarg->keyword_name(); // Need to adjust the syntax if optchar or keyword is empty if ((! (cmdarg->syntax() & CmdArg::isPOS)) && ((! optchar) || (keyword == NULL))) { if (keyword == NULL) { if ((cmd_flags & KWDS_ONLY) && (cmd_flags & NO_GUESSING)) { return 0; } else { syntax = cmd_OPTS_ONLY; } } if (! optchar) { if ((cmd_flags & OPTS_ONLY) && (cmd_flags & NO_GUESSING)) { return 0; } else { syntax = cmd_KWDS_ONLY; } } } // If the argument is optional - print the leading '[' if ((level == VERBOSE_USAGE) && (! (cmdarg->syntax() & CmdArg::isREQ))) { oss << char('[') ; } // If we have a sticky-argument and usage is cmd_BOTH then it gets // really hairy so we just treat this as a special case right here // and now. // if ((syntax == cmd_BOTH) && (! (cmdarg->syntax() & CmdArg::isPOS)) && (cmdarg->syntax() & CmdArg::isVALTAKEN) && (cmdarg->syntax() & CmdArg::isVALSTICKY)) { if (cmdarg->syntax() & CmdArg::isVALOPT) { oss << OptionPrefix() << char(optchar) << char('[') << cmdarg->value_name() << "]|" << KeywordPrefix(plus) << keyword << "[=" << cmdarg->value_name() << char(']') ; } else { oss << OptionPrefix() << optchar << cmdarg->value_name() << char('|') << KeywordPrefix(plus) << keyword << char('=') << cmdarg->value_name() ; } if ((level == VERBOSE_USAGE) && (cmdarg->syntax() & CmdArg::isLIST)) { oss << " ..." ; } if ((level == VERBOSE_USAGE) && (! (cmdarg->syntax() & CmdArg::isREQ))) { oss << char(']') ; } oss << ends ; return (oss.pcount() - 1); } if (! (cmdarg->syntax() & CmdArg::isPOS)) { switch(syntax) { case cmd_OPTS_ONLY : oss << OptionPrefix() << char(optchar) ; break ; case cmd_KWDS_ONLY : oss << ((cmd_flags & KWDS_ONLY) ? OptionPrefix() : KeywordPrefix(plus)) << keyword ; break ; case cmd_BOTH : oss << OptionPrefix() << char(optchar) << char('|') << KeywordPrefix(plus) << keyword ; break ; default : cerr << "*** Internal error in class CmdLine.\n" << "\tunknown CmdLineSyntax value (" << int(syntax) << ")." << endl ; ::exit(e_INTERNAL); } //switch if (cmdarg->syntax() & CmdArg::isVALTAKEN) { if (! (cmdarg->syntax() & CmdArg::isVALSTICKY)) { oss << char(' ') ; } } } // If the argument takes a value then print the value if (cmdarg->syntax() & CmdArg::isVALTAKEN) { if ((! (cmdarg->syntax() & CmdArg::isPOS)) && (cmdarg->syntax() & CmdArg::isVALOPT)) { oss << char('[') ; } if (cmdarg->syntax() & CmdArg::isVALSTICKY) { if (syntax == cmd_KWDS_ONLY) oss << char('=') ; } oss << cmdarg->value_name() ; if ((level == VERBOSE_USAGE) && (cmdarg->syntax() & CmdArg::isLIST)) { oss << " ..." ; } if ((! (cmdarg->syntax() & CmdArg::isPOS)) && (cmdarg->syntax() & CmdArg::isVALOPT)) { oss << char(']') ; } } if ((level == VERBOSE_USAGE) && (! (cmdarg->syntax() & CmdArg::isREQ))) { oss << char(']') ; } oss << ends ; return (oss.pcount() - 1) ; }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.