diff options
Diffstat (limited to 'qpid/cpp/src/qpid/Options.cpp')
-rw-r--r-- | qpid/cpp/src/qpid/Options.cpp | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/qpid/cpp/src/qpid/Options.cpp b/qpid/cpp/src/qpid/Options.cpp new file mode 100644 index 0000000000..0021afc574 --- /dev/null +++ b/qpid/cpp/src/qpid/Options.cpp @@ -0,0 +1,335 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "config.h" +#include "qpid/Options.h" +#include "qpid/OptionsTemplates.h" +#include "qpid/Exception.h" +#include "qpid/log/Logger.h" +#include "qpid/sys/Time.h" + +#include <boost/bind.hpp> + +#include <fstream> +#include <algorithm> +#include <iostream> + +namespace qpid { + +using namespace std; + + +namespace { + +struct EnvOptMapper { + static bool matchChar(char env, char opt) { + return (env==toupper(opt)) || (strchr("-.", opt) && env=='_'); + } + + static bool matchStr(const string& env, boost::shared_ptr<po::option_description> desc) { + return desc->long_name().size() == env.size() && + std::equal(env.begin(), env.end(), desc->long_name().begin(), &matchChar); + } + + static bool matchCase(const string& env, boost::shared_ptr<po::option_description> desc) { + return env == desc->long_name(); + } + + EnvOptMapper(const Options& o) : opts(o) {} + + string operator()(const string& envVar) { + static const std::string prefix("QPID_"); + if (envVar.substr(0, prefix.size()) == prefix) { + string env = envVar.substr(prefix.size()); + typedef const std::vector< boost::shared_ptr<po::option_description> > OptDescs; + OptDescs::const_iterator i = + find_if(opts.options().begin(), opts.options().end(), boost::bind(matchStr, env, _1)); + if (i != opts.options().end()) + return (*i)->long_name(); + } + return string(); + } + + + bool + isComment ( string const & str ) + { + size_t i = str.find_first_not_of ( " \t" ); + + if ( i == string::npos ) + return true; + + return str[i] == '#'; + } + + + void badArg ( string& line ) { + ostringstream msg; + msg << "Bad argument: |" << line << "|\n"; + throw Exception(msg.str()); + } + + + string configFileLine (string& line, bool allowUnknowns=true) { + + if ( isComment ( line ) ) { + return string(); + } + + size_t pos = line.find ('='); + if (pos == string::npos) { + if ( allowUnknowns ) { + return string(); + } + else { + badArg ( line ); + } + } + string key = line.substr (0, pos); +#if (BOOST_VERSION >= 103300) + typedef const std::vector< boost::shared_ptr<po::option_description> > OptDescs; + OptDescs::const_iterator i = + find_if(opts.options().begin(), opts.options().end(), boost::bind(matchCase, key, _1)); + if (i != opts.options().end()) + return string (line) + "\n"; + else { + if ( allowUnknowns ) { + return string(); + } + else { + badArg ( line ); + } + } +#else + // Use 'count' to see if this option exists. Using 'find' will + // SEGV or hang if the option has not been defined yet. + if ( opts.count(key.c_str()) > 0 ) + return string ( line ) + "\n"; + else { + if ( allowUnknowns ) { + return string ( ); + } + else { + badArg ( line ); + } + } +#endif + // Control will not arrive here, but the compiler things it could. + // Calls to badArg(), that I used above, throw. + return string(); + } + + const Options& opts; +}; + +} + +template QPID_COMMON_EXTERN po::value_semantic* create_value(bool& val, const std::string& arg); +template QPID_COMMON_EXTERN po::value_semantic* create_value(int16_t& val, const std::string& arg); +template QPID_COMMON_EXTERN po::value_semantic* create_value(int32_t& val, const std::string& arg); +template QPID_COMMON_EXTERN po::value_semantic* create_value(int64_t& val, const std::string& arg); +template QPID_COMMON_EXTERN po::value_semantic* create_value(uint16_t& val, const std::string& arg); +template QPID_COMMON_EXTERN po::value_semantic* create_value(uint32_t& val, const std::string& arg); +template QPID_COMMON_EXTERN po::value_semantic* create_value(uint64_t& val, const std::string& arg); +#ifdef QPID_SIZE_T_DISTINCT +template QPID_COMMON_EXTERN po::value_semantic* create_value(size_t& val, const std::string& arg); +#endif +template QPID_COMMON_EXTERN po::value_semantic* create_value(double& val, const std::string& arg); + +template QPID_COMMON_EXTERN po::value_semantic* create_value(string& val, const std::string& arg); +template QPID_COMMON_EXTERN po::value_semantic* create_value(vector<string>& val, const std::string& arg); +template QPID_COMMON_EXTERN po::value_semantic* create_value(vector<int>& val, const std::string& arg); + +template QPID_COMMON_EXTERN po::value_semantic* create_value(sys::Duration& val, const std::string& arg); + + +po::value_semantic* optValue(bool& value) { +#if (BOOST_VERSION >= 103500) + return create_value(value, "", true); +#else + return po::bool_switch(&value); +#endif +} + +po::value_semantic* pure_switch(bool& value) { + return po::bool_switch(&value); +} + +std::string prettyArg(const std::string& name, const std::string& value) { + return value.empty() ? name+" " : name+" ("+value+") "; +} + +Options::Options(const string& name) : + poOptions(new po::options_description(name)) +{ +} + +void Options::parse(int argc, char const* const* argv, const std::string& configFile, bool allowUnknown) +{ + string defaultConfigFile = configFile; // May be changed by env/cmdline + string parsing; + try { + po::variables_map vm; + parsing="command line options"; + if (argc > 0 && argv != 0) { + if (allowUnknown) { + // This hideous workaround is required because boost 1.33 has a bug + // that causes 'allow_unregistered' to not work. + po::command_line_parser clp = po::command_line_parser(argc, const_cast<char**>(argv)). + options(*poOptions).allow_unregistered(); + po::parsed_options opts = clp.run(); + po::parsed_options filtopts = clp.run(); + filtopts.options.clear (); + for (std::vector< po::basic_option<char> >::iterator i = opts.options.begin(); + i != opts.options.end(); i++) + if (!i->unregistered) + filtopts.options.push_back (*i); + po::store(filtopts, vm); + + } + else + po::store(po::parse_command_line(argc, const_cast<char**>(argv), *poOptions), vm); + } + parsing="environment variables"; + po::store(po::parse_environment(*poOptions, EnvOptMapper(*this)), vm); + po::notify(vm); // configFile may be updated from arg/env options. + if (!configFile.empty()) { + parsing="configuration file "+configFile; + ifstream conf(configFile.c_str()); + conf.peek(); + if (conf.good()) { + // Remove this hack when we get a stable version of boost that + // can allow unregistered options in config files. + EnvOptMapper mapper(*this); + stringstream filtered; + + while (!conf.eof()) { + string line; + getline (conf, line); + filtered << mapper.configFileLine (line, allowUnknown); + } + + po::store(po::parse_config_file(filtered, *poOptions), vm); + // End of hack + } + else { + // log the inability to read the configuration file + QPID_LOG(debug, "Config file not read: " << configFile); + // No error if default configfile is missing/unreadable + // but complain for non-default config file. + if (configFile != defaultConfigFile) + throw Exception("cannot read configuration file " + +configFile); + } + } + po::notify(vm); + } + catch (const std::exception& e) { + ostringstream msg; + msg << "Error in " << parsing << ": " << e.what() << endl; +#if (BOOST_VERSION >= 103300) + if (find_nothrow("help", false)) + msg << "Use --help to see valid options" << endl; +#endif + throw Exception(msg.str()); + } +} + +options_description_easy_init::options_description_easy_init(po::options_description* o) : + owner(o) +{} + +options_description_easy_init Options::addOptions() +{ + return options_description_easy_init(poOptions.get()); +} + +void Options::add(Options& o) +{ + poOptions->add(*o.poOptions); +} + +const std::vector< boost::shared_ptr<po::option_description> >& Options::options() const +{ + return poOptions->options(); +} + +bool Options::find_nothrow(const std::string& s, bool b) +{ + return poOptions->find_nothrow(s, b); +} + +bool Options::findArg(int argc, char const* const* argv, const std::string& theArg) +{ + const string parsing("command line options"); + bool result(false); + try { + if (argc > 0 && argv != 0) { + po::command_line_parser clp = po::command_line_parser(argc, const_cast<char**>(argv)). + options(*poOptions).allow_unregistered(); + po::parsed_options opts = clp.run(); + + for (std::vector< po::basic_option<char> >::iterator + i = opts.options.begin(); i != opts.options.end(); i++) { + if (theArg.compare(i->string_key) == 0) { + result = true; + break; + } + } + } + return result; + } + catch (const std::exception& e) { + ostringstream msg; + msg << "Error in " << parsing << ": " << e.what() << endl; + throw Exception(msg.str()); + } +} + +void Options::print(ostream& os) +{ + poOptions->print(os); +} + +std::ostream& operator<<(std::ostream& os, const Options& options) +{ + return os << *(options.poOptions); +} + +options_description_easy_init& +options_description_easy_init::operator()(const char* name, + const po::value_semantic* s, + const char* description) +{ + owner->add(boost::shared_ptr<po::option_description>(new po::option_description(name, s, description))); + return *this; +} + + +CommonOptions::CommonOptions(const string& name, const string& configfile, const string& clientfile) +: Options(name), config(configfile), clientConfig(clientfile) +{ + addOptions() + ("help,h", optValue(help), "Displays the help message") + ("version,v", optValue(version), "Displays version information") + ("config", optValue(config, "FILE"), "Reads configuration from FILE") + ("client-config", optValue(clientConfig, "FILE"), "Reads client configuration from FILE (for cluster interconnect)"); +} + +} // namespace qpid + |