//////////////////////////////////////////////////////////////////////// // FILE: argparser.h // AUTHOR: Johannes Winkelmann, jw@tks6.net // COPYRIGHT: (c) 2004 by Johannes Winkelmann // --------------------------------------------------------------------- // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. //////////////////////////////////////////////////////////////////////// #ifndef _ARGPARSER_H_ #define _ARGPARSER_H_ #include #include #include // TODO: // -- important // - allow multiple occurences of arguments: // prt-get --config-append="..." --config-append="..." // - Allow global --help, --usage // - check for duplicate entries // -- nice to have // 2. allow optional values for args, like --with-gtk[=DIR] // 4. improve predefined commands: allow descriptions even for such // 7. Add predefined --version, show in usage; add Contact text // 8. Allow disabling of predefined options // 9. make parseError more convenient (passing cmd->name all over...) class APOpt; class APCmd; /** * \brief argument parser class * * Yet another argument parser, meant to speed up development of new * applications. Its focus is on being object oriented and safe to use */ class ArgParser { public: /** * types of argument number checking */ enum ArgNumberCheck { NONE, MIN, EQ, MAX }; /* * APOpt and APCmd are the classes used in client code, to make * efficient comparison of the selected command. * * In addition, it's currently mainly for programs which use a * command syntax, much like CVS */ class APOpt { public: friend class ArgParser; APOpt() : id(-1), m_initialized(false) {} bool operator ==(const APOpt& other) const { return other.id == id; } void init(const std::string& longName, char shortName, const std::string& description, const bool valueRequired=false, const std::string& valueName="") { m_initialized = true; m_longName = longName; m_shortName = shortName; m_description = description; m_valueRequired = valueRequired; m_valueName = valueName; } private: int id; std::string m_longName; char m_shortName; std::string m_description; bool m_valueRequired; std::string m_valueName; bool m_initialized; }; class APCmd { public: friend class ArgParser; APCmd() : id(-1) {} bool operator ==(const APCmd& other) const { return other.id == id; } private: int id; }; private: // internal representation of options and commands class Option { public: int id; std::string description; char shortName; std::string longName; bool requiresValue; std::string valueName; }; class Command { public: int id; std::string name; std::string description; ArgNumberCheck argNumberCheck; unsigned int argNumber; std::string otherArguments; std::map mandatoryOptions; std::map options; ArgParser::APCmd* apCmd; }; public: ArgParser(); virtual ~ArgParser(); /** * add a command * * \param cmd a reference to the command; use it to compare the actually selected command against this one after parsing * \param name the name of the command to be parsed from the command line * \param description a description, used for the help screens * \param argNumberCheck what kind of argument number checking * \param argNumber optional number of arguments * \param otherOptions value to display in the help screen for following (non option) arguments */ int addCommand(APCmd& cmd, const std::string& name, const std::string& description, ArgNumberCheck argNumberCheck, const int argNumber=-1, const std::string& otherArguments=""); /** * add an option to a command - this will fail with an assertion * of \a key has not been initialized using init() * * \param cmd the command to add an option to * \param key the option reference; use it to check for certain options after parsing * \param required whether this option is required * \param longName the long name of this command (to be used with '--'); leave it empty if you don't want to use a long option name * \param shortName the short name of this command (to be used with '-'); pass 0 if you don't want to use a short option name * \param description the description of this option, to be used in the help screen * \param valueRequired whether this option requires a value * \param valueName the name of the value, to be used in the help screen */ int addOption(const APCmd& cmd, APOpt& key, bool required); /** * the actual parsing. Highly recommended :-) */ void parse(int argc, char** argv); /** * the command which was parsed, to be used to compare against * actual APCmd obtained from addCommand calls */ APCmd command() const; /** * the name of the application, from argv[0] */ std::string appName() const; /** * \return true if \a key is set, false otherwise */ bool isSet(const APOpt& key) const; /** * \return the value attached to the option \key if any */ std::string getOptionValue(const APOpt& key) const; /** * the remaining arguments */ const std::vector& otherArguments() const; /** * \return an application identification to be used in the usage */ virtual std::string getAppIdentification() const { return ""; } private: std::string generateHelpForCommand(const std::string& command) const; std::string generateUsage() const; bool isSet(int key) const; std::string generateOptionString(Option* o) const; void parseError(const std::string& error, const std::string& cmd="") const; std::map m_commands; std::map m_commandIdMap; std::map m_options; std::map m_optionsByShortName; std::map m_optionsByLongName; std::map m_setOptions; std::vector m_otherArguments; APCmd m_command; std::string m_appName; int m_cmdIdCounter; int m_optIdCounter; APOpt PREDEFINED_CMD_HELP; }; #endif /* _ARGPARSER_H_ */