// -*- mode: c++; c-indent-level: 4; c++-member-init-indent: 8; comment-column: 35; -*- //----------------------------------------------------------------------------- /* eoParser.h some classes to parser either the command line or a parameter file (c) Marc Schoenauer and Geneura team, 1999 This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Contact: todos@geneura.ugr.es, http://geneura.ugr.es */ //----------------------------------------------------------------------------- #ifndef _PARSER_H #define _PARSER_H #include // for strcasecmp ... maybe there's a c++ way of doing it? // Yep there is, but needs either a simple functor for the equal function // or a hand-rolled std::string template class (this isn't that horrible as // it sounds, it just means a new std::string_traits class with two changed // function definitions. (Maarten) #ifdef _MSC_VER #define strcasecmp(a,b) _strnicmp(a,b,strlen(a)) #endif // STL includes #include #include #include #include #include #include // include for std::exceptions #include // std::logic_error //----------------------------------------------------------------------------- // Class Param //----------------------------------------------------------------------------- /** * A param repesents an argument that can be passed to a program in the command line */ class Param { public: /** * Type of params */ enum valueType { INT, UL, FLOAT, STRING, BOOL, ARRAY, TITLE }; /** * Construct an Param. * @param _shortName Short name of the argument * @param _longName Long name of the argument * @param _default The default value * @param _valueType Type of the parameter ("integer","unsigned long", "float","char", "bool" and so on) * @param _description Description of the parameter. What is useful for. * @param _required If it is a necessary parameter or not */ Param (std::string _shortName="-h", std::string _longName="--help", std::string _default = "", valueType _valType= STRING, std::string _description="Shows this help", bool _required=false ) : repShortName(_shortName), repLongName(_longName), repDescription(_description ), repEnv(""), repDefault(_default), repValue(_default), repValType( _valType), repRequired( _required), repChanged(false) { const char *c = repLongName.c_str(); for(unsigned i=0; i getArray (const std::string& _shortName, const std::string& _longName, const std::string& _default = "", const std::string& _description="", bool _required=false) { Param param ( _shortName, _longName, _default, Param::ARRAY, _description, _required ); parse( param ); params.push_back( param ); istrstream is(param.value().c_str()); std::vector retValue; std::string tmpStr; is >> tmpStr; while(is){ retValue.push_back(tmpStr); is >> tmpStr; } return retValue; }; /** * Gets the int value of a param given the full description of the parameter * @param see above * @std::exception BadType if the param's value isn't a correct int */ int getInt (const std::string& _shortName, const std::string& _longName, const std::string& _default = "", const std::string& _description="", bool _required=false) { Param param ( _shortName, _longName, _default, Param::INT, _description, _required ); parse( param ); params.push_back( param ); // now gets the value istrstream is( param.value().c_str()); int retValue; is >> retValue; if (!is) { throw Parser::BadType(param.longName().c_str(), param.value().c_str(), "float"); return 0; } else { return retValue; } }; /** * Gets the unsigned lon value of a param given ... * @param see above * @std::exception BadType if the param's value isn't a correct unsigned long */ int getUnsignedLong (const std::string& _shortName, const std::string& _longName, const std::string& _default = "", const std::string& _description="", bool _required=false) { Param param ( _shortName, _longName, _default, Param::UL, _description, _required ); parse( param ); params.push_back( param ); // now gets the value istrstream is( param.value().c_str()); unsigned long retValue; is >> retValue; if (!is) { throw Parser::BadType(param.longName().c_str(), param.value().c_str(), "float"); return 0; } else { return retValue; } }; /** * Gets the float value of a param given the description of the parameter * @param see above * @std::exception BadType if the param's value isn't a correct int */ float getFloat (const std::string& _shortName, const std::string& _longName, const std::string& _default = "", const std::string& _description="", bool _required=false) { Param param ( _shortName, _longName, _default, Param::FLOAT, _description, _required ); parse( param ); params.push_back( param ); // now gets the value istrstream is( param.value().c_str()); float retValue; is >> retValue; if (!is) { throw Parser::BadType(param.longName().c_str(), param.value().c_str(), "float"); return 0; } else { return retValue; } }; std::string parse_std::string (std::istream & _is) { std::string paramValue; _is >> paramValue; //if the first character of the std::string or array is not a " => just one word or array-element. if( paramValue[0] != '\"' ) return paramValue; if( paramValue[1] == '\"' ) // the empty std::string return "" ; //else => read until the next " (the end of the std::string). const char *c = paramValue.c_str(); std::string tmpStr = c+1;// skip the " if (tmpStr[tmpStr.length()-1] == '\"') { // one word only //tmpStr[tmpStr.length()-1] = '\0'; tmpStr.erase( &tmpStr[tmpStr.length()-1] ); return tmpStr; } bool stop = false; while (_is && !stop) { _is >> paramValue; // test last character of paramValue for " if (paramValue[paramValue.length()-1] == '\"') { paramValue.erase( ¶mValue[paramValue.length()-1] ); //paramValue[paramValue.length()-1] = '\0'; stop = true; } tmpStr = tmpStr + " " + paramValue ; } return tmpStr; }; void parse (Param & param) { int i; std::string tmpStr, ReadStr, FirstWord; // FIRST: look if the associated environment variables have any value, to use them. if( getenv( param.environment().c_str() ) ) { //std::cout <<"\t\t ENV param: ,"<shortName()<<", ,"<environment().c_str())<> tmpStr; if ( ( !strcmp(tmpStr.c_str(), param.shortName().c_str()) ) || ( !strcasecmp(tmpStr.c_str(), param.longName().c_str()) ) ) { // found the keyword Param::valueType tmp = param.valType(); switch ( tmp ) { case Param::TITLE: std::cerr << "Error, we should not be there" << std::endl; exit(1); break; case Param::BOOL : param.value("true" ); break; case Param::INT: case Param::UL: case Param::FLOAT: is >> tmpStr; param.value(tmpStr); break; case Param::STRING: tmpStr = parse_std::string(is); param.value(tmpStr); break; case Param::ARRAY: ReadStr = parse_std::string(is); if ( ReadStr != "<" ) { // no "<" ">" --> a single std::string in the array param.value(ReadStr); break; } // read next word - and keep it in case of <> mismatch FirstWord = parse_std::string(is); // test for empty array if (FirstWord == ">") { param.value(""); break; } // else, read all words until ">" tmpStr = FirstWord; ReadStr = parse_std::string(is); while ( is && (ReadStr != ">") ) { tmpStr = tmpStr + " " + ReadStr; ReadStr = parse_std::string(is); } if (!is) { // there was a "<" without the corresponding ">" throw Parser::BadArrayParam( param.longName(), FirstWord ); param.value(FirstWord); // assume unique std::string } else param.value(tmpStr); break; } } } } // LAST (highest priority) parse the command line arguments for (i=1 ; i" --> a single std::string in the array param.value(ReadStr); }else{ // read next word - and keep it in case of <> mismatch FirstWord = parse_argv[i++]; // test for empty array if (FirstWord == ">") { param.value(""); }else{ // else, read all words until ">" tmpStr = FirstWord; ReadStr = parse_argv[i++]; while ( (i=parse_argc) && (ReadStr != ">") ) { // there was a "<" without the corresponding ">" throw Parser::BadArrayParam( param.longName(), FirstWord ); param.value(FirstWord); // assume unique std::string }else{ param.value(tmpStr); } } } } } break; } //MS after trying all possibilities, and if the value has not changed // though the parameter was required, protest! if (param.required() && !param.changed()) throw Parser::MissingReqParam(param.shortName()); }; /** * Sets a new value for a param given its short name or its long name. * @param _name One of the names of the param. * @param _value Value to be assigned. * @std::exception UnknownArg if the param doesn't exist * @std::exception MissingVal if the param hasn't got a value */ Param::valueType setParamValue (const std::string& _name, const char* _value){ std::vector::iterator pos; for (pos=params.begin() ; pos!=params.end() ; pos++) if (pos->shortName()==_name || pos->longName()==_name) break; // if found ... if (pos!=params.end()) { switch ( pos->valType() ) { case Param::TITLE: std::cerr << "Error, we should not be there" << std::endl; exit(1); break; case Param::BOOL : pos->value("true"); break; case Param::ARRAY : case Param::INT: case Param::UL: case Param::FLOAT: case Param::STRING: if (_value != NULL){ pos->value(_value); }else{ throw Parser::MissingVal(_name); return Param::BOOL; } break; } // switch return pos->valType(); }else{ throw Parser::UnknownArg(_name); return Param::BOOL; } }; /// the output method - generate the .status file (unless other name is given) friend std::ostream & operator<< ( std::ostream & os, Parser & _parser ) { std::vector::iterator p; //print every param with its value for ( p=_parser.params.begin(); p!=_parser.params.end(); p++ ) { switch ( p->valType() ) { case Param::BOOL : if( p->value() == (std::string) "true") os << p->longName(); else os << "#" << p->longName() ; // so the name of the bool is commented out break; case Param::INT: case Param::UL: case Param::FLOAT: os << p->longName()<<" "<value(); break; case Param::ARRAY : os << p->longName() << " < " << p->value().c_str() << " >" ; break; case Param::STRING: os << p->longName()<<" \""<value().c_str()<<"\" "; break; case Param::TITLE: os << std::endl; // Title is in the description below break; } // switch os << "\t #" << p->shortName() << " : " << p->description(); if (p->valType() != Param::TITLE) os << " [" << p->defValue() << "]" ; os << std::endl; } return os; }; /** * Prints out the std::list of parameters in the output file (if specified) */ void outputParam(std::string _OutputFile="") { if (_OutputFile == "") { _OutputFile = parse_argv[0]; _OutputFile += ".status"; } std::ofstream os(_OutputFile.c_str()); os << "Parameters used by \"" << programName << "\" (" << programDescription << ")" << std::endl << std::endl; os << *this; }; /** * Prints an automatic help in the standard output using the information * provided by parameters */ void printHelp() { std::vector::iterator p; // unsigned i; // print program name and description std::cout << this->programName <<": "<valType() != Param::TITLE ) { // if( p->valType() != Param::BOOL ){ // std::cout << ( (!p->required())?"[":""); // std::cout <shortName()<<" value"<required())?"]":"")<<" "; // }else{ // std::cout << "["<shortName()<<"] "; // } // } // for p std::cout << "Where:"<valType() != Param::TITLE ) { // Victor: 04-Jan-2000 // Modified because the - and -- prefixes are not needed. /* std::cout << "-" << p->shortName() <<", --"<longName()<<":\t" <description()<shortName() <<", " << p->longName()<<":\t" <description()<valType() ) { case Param::INT: std::cout <<"Integer"; break; case Param::UL: std::cout <<"Unsigned Long Integer"; break; case Param::FLOAT: std::cout <<"Float"; break; case Param::STRING: std::cout <<"String"; break; case Param::ARRAY: std::cout <<"An array of std::strings, enclosed within < >"; break; case Param::BOOL: std::cout << "Flag"; break; case Param::TITLE: break; } // switch if(p->valType() == Param::BOOL) std::cout << ") True if present" << std::endl; else std::cout<<") "<<( (p->required())?"Required":"Optional" )<<". By default: "<defValue()<description() << std::endl; } } // for p std::cout << std::endl; }; /** * This class managges unknown argument std::exceptions. */ class UnknownArg : public std::logic_error { public: /** * Constructor * @param _arg std::string to be shown when the std::exception occurs */ UnknownArg( const std::string& _arg): std::logic_error( "Invalid argument: "+_arg ) { }; }; /** * This class managges bad param types. */ class BadType : public std::logic_error { public: /** * Constructor * @param _param The param * @param _value The value of the param */ BadType(const std::string& _param, const std::string& _value, const std::string& _correctType) : std::logic_error("The value '" + _value + "' assigned to the argument " + _param + " isn't a correct "+_correctType) { }; }; /** * This class managges std::exceptions produced when there isn't a value for a parameter. */ class MissingVal : public std::logic_error { public: /** * Constructor * @param _param The param */ MissingVal(const std::string& _param) : std::logic_error("Missing value for parameter " + _param) {}; }; /** * This class managges std::exceptions produced when the user forgot a required parameter. */ class MissingReqParam : public std::logic_error { public: /** * Constructor * @param _shortName The param's short name */ MissingReqParam(const std::string& _shortName) : std::logic_error("Missing required parameter " + _shortName) {}; }; /** * This class managges std::exceptions du to < without a > in array value */ class BadArrayParam : public std::logic_error { public: /** * Constructor * @param _param The param * @param _first_word The first word read after the "<" */ BadArrayParam(const std::string& _param, const std::string &_first_word) : std::logic_error("Array parameter " + _param + ": No matching > (" + _first_word + "... )") {}; }; void createParamFile( std::ostream& _os ) { std::vector::iterator p; for ( p=params.begin(); p!=params.end(); p++ ) { switch( p->valType() ) { case Param::TITLE: _os << std::endl << "# -- "; break; case Param::BOOL: _os << ((p->value()=="true" )?"":"#") << p->longName(); break; case Param::STRING: _os << p->longName()<<"\t\""<value()<<"\""; break; case Param::ARRAY: _os << p->longName()<<"\t< "<value()<<" >"; break; default: _os << p->longName()<<"\t"<value(); break; } // switch _os << "\t #" << p->description() << std::endl; } } private: std::vector params; std::string programName; std::string programDescription; int parse_argc; char **parse_argv; std::string InputFileName; }; #endif