summaryrefslogtreecommitdiff
path: root/src/mongo/util/options_parser/options_parser.cpp
diff options
context:
space:
mode:
authorMark Benvenuto <mark.benvenuto@mongodb.com>2015-06-20 00:22:50 -0400
committerMark Benvenuto <mark.benvenuto@mongodb.com>2015-06-20 10:56:02 -0400
commit9c2ed42daa8fbbef4a919c21ec564e2db55e8d60 (patch)
tree3814f79c10d7b490948d8cb7b112ac1dd41ceff1 /src/mongo/util/options_parser/options_parser.cpp
parent01965cf52bce6976637ecb8f4a622aeb05ab256a (diff)
downloadmongo-9c2ed42daa8fbbef4a919c21ec564e2db55e8d60.tar.gz
SERVER-18579: Clang-Format - reformat code, no comment reflow
Diffstat (limited to 'src/mongo/util/options_parser/options_parser.cpp')
-rw-r--r--src/mongo/util/options_parser/options_parser.cpp1663
1 files changed, 810 insertions, 853 deletions
diff --git a/src/mongo/util/options_parser/options_parser.cpp b/src/mongo/util/options_parser/options_parser.cpp
index eebd7d83e2c..6a7501107c4 100644
--- a/src/mongo/util/options_parser/options_parser.cpp
+++ b/src/mongo/util/options_parser/options_parser.cpp
@@ -51,997 +51,954 @@
namespace mongo {
namespace optionenvironment {
- using namespace std;
- using std::shared_ptr;
-
- namespace po = boost::program_options;
-
- namespace {
-
- // The following section contains utility functions that convert between the various objects
- // we need to deal with while parsing command line options.
- //
- // These conversions are different depending on the data source because our current
- // implementation uses boost::program_options for the command line and INI files and the
- // yaml-cpp YAML parser for YAML config files. Our destination storage in both cases is an
- // Environment which stores Value objects.
- //
- // 1. YAML Config Files
- // The YAML parser parses a YAML config file into a YAML::Node. Therefore, we need:
- // a. A function to convert a YAML::Node to a Value (YAMLNodeToValue)
- // b. A function to iterate a YAML::Node, convert the leaf Nodes to Values, and add
- // them to our Environment (addYAMLNodesToEnvironment)
- //
- // 2. INI Config Files and command line
- // The boost::program_options parsers store their output in a
- // boost::program_options::variables_map. Therefore, we need:
- // a. A function to convert a boost::any to a Value (boostAnyToValue)
- // b. A function to iterate a variables_map, convert the boost::any elements to
- // Values, and add them to our Environment (addBoostVariablesToEnvironment)
-
- // Attempts to convert a string to a value of the given type.
- Status stringToValue(const std::string& stringVal,
- const OptionType& type,
- const Key& key, Value* value) {
-
- Status ret = Status::OK();
- switch (type) {
- double doubleVal;
- int intVal;
- long longVal;
- unsigned long long unsignedLongLongVal;
- unsigned unsignedVal;
- case Switch:
- if (stringVal == "true") {
- *value = Value(true);
- return Status::OK();
- }
- else if (stringVal == "false") {
- *value = Value(false);
- return Status::OK();
- }
- else {
- StringBuilder sb;
- sb << "Expected boolean switch but found string: " << stringVal
- << " for option: " << key;
- return Status(ErrorCodes::BadValue, sb.str());
- }
- case Bool:
- if (stringVal == "true") {
- *value = Value(true);
- return Status::OK();
- }
- else if (stringVal == "false") {
- *value = Value(false);
- return Status::OK();
- }
- else {
- StringBuilder sb;
- sb << "Expected boolean but found string: " << stringVal
- << " for option: " << key;
- return Status(ErrorCodes::BadValue, sb.str());
- }
- case Double:
- ret = parseNumberFromString(stringVal, &doubleVal);
- if (!ret.isOK()) {
- StringBuilder sb;
- sb << "Error parsing option \"" << key
- << "\" as double in: " << ret.reason();
- return Status(ErrorCodes::BadValue, sb.str());
- }
- *value = Value(doubleVal);
- return Status::OK();
- case Int:
- ret = parseNumberFromString(stringVal, &intVal);
- if (!ret.isOK()) {
- StringBuilder sb;
- sb << "Error parsing option \"" << key
- << "\" as int: " << ret.reason();
- return Status(ErrorCodes::BadValue, sb.str());
- }
- *value = Value(intVal);
- return Status::OK();
- case Long:
- ret = parseNumberFromString(stringVal, &longVal);
- if (!ret.isOK()) {
- StringBuilder sb;
- sb << "Error parsing option \"" << key
- << "\" as long: " << ret.reason();
- return Status(ErrorCodes::BadValue, sb.str());
- }
- *value = Value(longVal);
- return Status::OK();
- case String:
- *value = Value(stringVal);
- return Status::OK();
- case UnsignedLongLong:
- ret = parseNumberFromString(stringVal, &unsignedLongLongVal);
- if (!ret.isOK()) {
- StringBuilder sb;
- sb << "Error parsing option \"" << key
- << "\" as unsigned long long: " << ret.reason();
- return Status(ErrorCodes::BadValue, sb.str());
- }
- *value = Value(unsignedLongLongVal);
- return Status::OK();
- case Unsigned:
- ret = parseNumberFromString(stringVal, &unsignedVal);
- if (!ret.isOK()) {
- StringBuilder sb;
- sb << "Error parsing option \"" << key
- << "\" as unsigned int: " << ret.reason();
- return Status(ErrorCodes::BadValue, sb.str());
- }
- *value = Value(unsignedVal);
- return Status::OK();
- default: /* XXX: should not get here */
- return Status(ErrorCodes::InternalError, "Unrecognized option type");
- }
- }
+using namespace std;
+using std::shared_ptr;
- // Convert a boost::any to a Value. See comments at the beginning of this section.
- Status boostAnyToValue(const boost::any& anyValue,
- const OptionType& type,
- const Key& key, Value* value) {
- try {
- if (anyValue.type() == typeid(StringVector_t)) {
- *value = Value(boost::any_cast<StringVector_t>(anyValue));
- }
- else if (anyValue.type() == typeid(bool)) {
- *value = Value(boost::any_cast<bool>(anyValue));
- }
- else if (anyValue.type() == typeid(std::string)) {
- return stringToValue(boost::any_cast<std::string>(anyValue), type, key, value);
- }
- // We should not be telling boost about numerical type information. Instead, for
- // any numerical type we tell boost to read a string value and parse it manually,
- // since boost's parsing is not consistent with ours. See SERVER-14110.
- else if (anyValue.type() == typeid(double) || anyValue.type() == typeid(int) ||
- anyValue.type() == typeid(long) || anyValue.type() == typeid(unsigned) ||
- anyValue.type() == typeid(unsigned long long)) {
- StringBuilder sb;
- sb << "Found int type: " << anyValue.type().name() <<
- " in any to Value conversion, which is not supported";
- return Status(ErrorCodes::InternalError, sb.str());
- }
- else {
- StringBuilder sb;
- sb << "Unrecognized type: " << anyValue.type().name() <<
- " in any to Value conversion";
- return Status(ErrorCodes::InternalError, sb.str());
- }
+namespace po = boost::program_options;
+
+namespace {
+
+// The following section contains utility functions that convert between the various objects
+// we need to deal with while parsing command line options.
+//
+// These conversions are different depending on the data source because our current
+// implementation uses boost::program_options for the command line and INI files and the
+// yaml-cpp YAML parser for YAML config files. Our destination storage in both cases is an
+// Environment which stores Value objects.
+//
+// 1. YAML Config Files
+// The YAML parser parses a YAML config file into a YAML::Node. Therefore, we need:
+// a. A function to convert a YAML::Node to a Value (YAMLNodeToValue)
+// b. A function to iterate a YAML::Node, convert the leaf Nodes to Values, and add
+// them to our Environment (addYAMLNodesToEnvironment)
+//
+// 2. INI Config Files and command line
+// The boost::program_options parsers store their output in a
+// boost::program_options::variables_map. Therefore, we need:
+// a. A function to convert a boost::any to a Value (boostAnyToValue)
+// b. A function to iterate a variables_map, convert the boost::any elements to
+// Values, and add them to our Environment (addBoostVariablesToEnvironment)
+
+// Attempts to convert a string to a value of the given type.
+Status stringToValue(const std::string& stringVal,
+ const OptionType& type,
+ const Key& key,
+ Value* value) {
+ Status ret = Status::OK();
+ switch (type) {
+ double doubleVal;
+ int intVal;
+ long longVal;
+ unsigned long long unsignedLongLongVal;
+ unsigned unsignedVal;
+ case Switch:
+ if (stringVal == "true") {
+ *value = Value(true);
+ return Status::OK();
+ } else if (stringVal == "false") {
+ *value = Value(false);
+ return Status::OK();
+ } else {
+ StringBuilder sb;
+ sb << "Expected boolean switch but found string: " << stringVal
+ << " for option: " << key;
+ return Status(ErrorCodes::BadValue, sb.str());
}
- catch(const boost::bad_any_cast& e) {
+ case Bool:
+ if (stringVal == "true") {
+ *value = Value(true);
+ return Status::OK();
+ } else if (stringVal == "false") {
+ *value = Value(false);
+ return Status::OK();
+ } else {
StringBuilder sb;
- // We already checked the type, so this is just a sanity check
- sb << "boost::any_cast threw exception: " << e.what();
- return Status(ErrorCodes::InternalError, sb.str());
+ sb << "Expected boolean but found string: " << stringVal << " for option: " << key;
+ return Status(ErrorCodes::BadValue, sb.str());
}
+ case Double:
+ ret = parseNumberFromString(stringVal, &doubleVal);
+ if (!ret.isOK()) {
+ StringBuilder sb;
+ sb << "Error parsing option \"" << key << "\" as double in: " << ret.reason();
+ return Status(ErrorCodes::BadValue, sb.str());
+ }
+ *value = Value(doubleVal);
+ return Status::OK();
+ case Int:
+ ret = parseNumberFromString(stringVal, &intVal);
+ if (!ret.isOK()) {
+ StringBuilder sb;
+ sb << "Error parsing option \"" << key << "\" as int: " << ret.reason();
+ return Status(ErrorCodes::BadValue, sb.str());
+ }
+ *value = Value(intVal);
+ return Status::OK();
+ case Long:
+ ret = parseNumberFromString(stringVal, &longVal);
+ if (!ret.isOK()) {
+ StringBuilder sb;
+ sb << "Error parsing option \"" << key << "\" as long: " << ret.reason();
+ return Status(ErrorCodes::BadValue, sb.str());
+ }
+ *value = Value(longVal);
return Status::OK();
+ case String:
+ *value = Value(stringVal);
+ return Status::OK();
+ case UnsignedLongLong:
+ ret = parseNumberFromString(stringVal, &unsignedLongLongVal);
+ if (!ret.isOK()) {
+ StringBuilder sb;
+ sb << "Error parsing option \"" << key
+ << "\" as unsigned long long: " << ret.reason();
+ return Status(ErrorCodes::BadValue, sb.str());
+ }
+ *value = Value(unsignedLongLongVal);
+ return Status::OK();
+ case Unsigned:
+ ret = parseNumberFromString(stringVal, &unsignedVal);
+ if (!ret.isOK()) {
+ StringBuilder sb;
+ sb << "Error parsing option \"" << key << "\" as unsigned int: " << ret.reason();
+ return Status(ErrorCodes::BadValue, sb.str());
+ }
+ *value = Value(unsignedVal);
+ return Status::OK();
+ default: /* XXX: should not get here */
+ return Status(ErrorCodes::InternalError, "Unrecognized option type");
+ }
+}
+
+// Convert a boost::any to a Value. See comments at the beginning of this section.
+Status boostAnyToValue(const boost::any& anyValue,
+ const OptionType& type,
+ const Key& key,
+ Value* value) {
+ try {
+ if (anyValue.type() == typeid(StringVector_t)) {
+ *value = Value(boost::any_cast<StringVector_t>(anyValue));
+ } else if (anyValue.type() == typeid(bool)) {
+ *value = Value(boost::any_cast<bool>(anyValue));
+ } else if (anyValue.type() == typeid(std::string)) {
+ return stringToValue(boost::any_cast<std::string>(anyValue), type, key, value);
+ }
+ // We should not be telling boost about numerical type information. Instead, for
+ // any numerical type we tell boost to read a string value and parse it manually,
+ // since boost's parsing is not consistent with ours. See SERVER-14110.
+ else if (anyValue.type() == typeid(double) || anyValue.type() == typeid(int) ||
+ anyValue.type() == typeid(long) || anyValue.type() == typeid(unsigned) ||
+ anyValue.type() == typeid(unsigned long long)) {
+ StringBuilder sb;
+ sb << "Found int type: " << anyValue.type().name()
+ << " in any to Value conversion, which is not supported";
+ return Status(ErrorCodes::InternalError, sb.str());
+ } else {
+ StringBuilder sb;
+ sb << "Unrecognized type: " << anyValue.type().name() << " in any to Value conversion";
+ return Status(ErrorCodes::InternalError, sb.str());
+ }
+ } catch (const boost::bad_any_cast& e) {
+ StringBuilder sb;
+ // We already checked the type, so this is just a sanity check
+ sb << "boost::any_cast threw exception: " << e.what();
+ return Status(ErrorCodes::InternalError, sb.str());
+ }
+ return Status::OK();
+}
+
+// Returns true if the option for the given key is a StringMap option, and false otherwise
+bool OptionIsStringMap(const std::vector<OptionDescription>& options_vector, const Key& key) {
+ for (std::vector<OptionDescription>::const_iterator iterator = options_vector.begin();
+ iterator != options_vector.end();
+ iterator++) {
+ if (key == iterator->_dottedName && (iterator->_sources & SourceYAMLConfig)) {
+ if (iterator->_type == StringMap) {
+ return true;
+ }
}
+ }
- // Returns true if the option for the given key is a StringMap option, and false otherwise
- bool OptionIsStringMap(const std::vector<OptionDescription>& options_vector,
- const Key& key) {
+ return false;
+}
+
+// Convert a YAML::Node to a Value. See comments at the beginning of this section.
+// 'canonicalKey' holds the dotted name that should be used in the result Environment.
+// This ensures that both canonical and deprecated dotted names in the configuration
+// are mapped to the canonical name.
+Status YAMLNodeToValue(const YAML::Node& YAMLNode,
+ const std::vector<OptionDescription>& options_vector,
+ const Key& key,
+ Key* canonicalKey,
+ Value* value) {
+ bool isRegistered = false;
+
+ // The logic below should ensure that we don't use this uninitialized, but we need to
+ // initialize it here to avoid a compiler warning. Initializing it to a "Bool" since
+ // that's the most restricted type we have and is most likely to result in an early
+ // failure if we have a logic error.
+ OptionType type = Bool;
+
+ // The config file had a ":" as the first non whitespace character on a line
+ if (key.empty()) {
+ StringBuilder sb;
+ sb << "Found empty key in YAML config file";
+ return Status(ErrorCodes::BadValue, sb.str());
+ }
- for (std::vector<OptionDescription>::const_iterator iterator = options_vector.begin();
- iterator != options_vector.end(); iterator++) {
- if (key == iterator->_dottedName && (iterator->_sources & SourceYAMLConfig)) {
- if (iterator->_type == StringMap) {
- return true;
- }
- }
+ // Get expected type
+ for (std::vector<OptionDescription>::const_iterator iterator = options_vector.begin();
+ iterator != options_vector.end();
+ iterator++) {
+ if (!(iterator->_sources & SourceYAMLConfig)) {
+ continue;
+ }
+
+ bool isDeprecated = std::count(iterator->_deprecatedDottedNames.begin(),
+ iterator->_deprecatedDottedNames.end(),
+ key) > 0;
+ if (key == iterator->_dottedName || isDeprecated) {
+ isRegistered = true;
+ type = iterator->_type;
+ *canonicalKey = iterator->_dottedName;
+ if (isDeprecated) {
+ warning() << "Option: " << key << " is deprecated. Please use "
+ << iterator->_dottedName << " instead.";
}
-
- return false;
}
+ }
+
+ if (!isRegistered) {
+ StringBuilder sb;
+ sb << "Unrecognized option: " << key;
+ return Status(ErrorCodes::BadValue, sb.str());
+ }
- // Convert a YAML::Node to a Value. See comments at the beginning of this section.
- // 'canonicalKey' holds the dotted name that should be used in the result Environment.
- // This ensures that both canonical and deprecated dotted names in the configuration
- // are mapped to the canonical name.
- Status YAMLNodeToValue(const YAML::Node& YAMLNode,
- const std::vector<OptionDescription>& options_vector,
- const Key& key,
- Key* canonicalKey,
- Value* value) {
-
- bool isRegistered = false;
-
- // The logic below should ensure that we don't use this uninitialized, but we need to
- // initialize it here to avoid a compiler warning. Initializing it to a "Bool" since
- // that's the most restricted type we have and is most likely to result in an early
- // failure if we have a logic error.
- OptionType type = Bool;
-
- // The config file had a ":" as the first non whitespace character on a line
- if (key.empty()) {
+ // Handle multi keys
+ if (type == StringVector) {
+ if (!YAMLNode.IsSequence()) {
+ StringBuilder sb;
+ sb << "Option: " << key
+ << " is of type StringVector, but value in YAML config is not a list type";
+ return Status(ErrorCodes::BadValue, sb.str());
+ }
+ StringVector_t stringVector;
+ for (YAML::const_iterator it = YAMLNode.begin(); it != YAMLNode.end(); ++it) {
+ if (it->IsSequence()) {
StringBuilder sb;
- sb << "Found empty key in YAML config file";
+ sb << "Option: " << key << " has nested lists, which is not allowed";
return Status(ErrorCodes::BadValue, sb.str());
}
+ stringVector.push_back(it->Scalar());
+ }
+ *value = Value(stringVector);
+ return Status::OK();
+ }
- // Get expected type
- for (std::vector<OptionDescription>::const_iterator iterator = options_vector.begin();
- iterator != options_vector.end(); iterator++) {
- if (!(iterator->_sources & SourceYAMLConfig)) {
- continue;
- }
-
- bool isDeprecated = std::count(iterator->_deprecatedDottedNames.begin(),
- iterator->_deprecatedDottedNames.end(), key) > 0;
- if (key == iterator->_dottedName || isDeprecated) {
- isRegistered = true;
- type = iterator->_type;
- *canonicalKey = iterator->_dottedName;
- if (isDeprecated) {
- warning() << "Option: " << key << " is deprecated. Please use "
- << iterator->_dottedName << " instead.";
- }
- }
+ // Handle a sub map as a value
+ if (type == StringMap) {
+ if (!YAMLNode.IsMap()) {
+ StringBuilder sb;
+ sb << "Option: " << key
+ << " is of type StringMap, but value in YAML config is not a map type";
+ return Status(ErrorCodes::BadValue, sb.str());
+ }
+ StringMap_t stringMap;
+ for (YAML::const_iterator it = YAMLNode.begin(); it != YAMLNode.end(); ++it) {
+ if (it->second.IsSequence() || it->second.IsMap()) {
+ StringBuilder sb;
+ sb << "Option: " << key
+ << " has a map with non scalar values, which is not allowed";
+ return Status(ErrorCodes::BadValue, sb.str());
}
+ if (stringMap.count(it->first.Scalar()) > 0) {
+ StringBuilder sb;
+ sb << "String Map Option: " << key
+ << " has duplicate keys in YAML Config: " << it->first.Scalar();
+ return Status(ErrorCodes::BadValue, sb.str());
+ }
+ stringMap[it->first.Scalar()] = it->second.Scalar();
+ }
+ *value = Value(stringMap);
+ return Status::OK();
+ }
- if (!isRegistered) {
+ // Our YAML parser reads everything as a string, so we need to parse it ourselves.
+ std::string stringVal = YAMLNode.Scalar();
+ return stringToValue(stringVal, type, key, value);
+}
+
+// Add all the values in the given variables_map to our environment. See comments at the
+// beginning of this section.
+Status addBoostVariablesToEnvironment(const po::variables_map& vm,
+ const OptionSection& options,
+ Environment* environment) {
+ std::vector<OptionDescription> options_vector;
+ Status ret = options.getAllOptions(&options_vector);
+ if (!ret.isOK()) {
+ return ret;
+ }
+
+ for (std::vector<OptionDescription>::const_iterator iterator = options_vector.begin();
+ iterator != options_vector.end();
+ iterator++) {
+ // Trim off the short option from our name so we can look it up correctly in our map
+ std::string long_name;
+ std::string::size_type commaOffset = iterator->_singleName.find(',');
+ if (commaOffset != string::npos) {
+ if (commaOffset != iterator->_singleName.size() - 2) {
StringBuilder sb;
- sb << "Unrecognized option: " << key;
+ sb << "Unexpected comma in option name: \"" << iterator->_singleName << "\""
+ << ": option name must be in the format \"option,o\" or \"option\", "
+ << "where \"option\" is the long name and \"o\" is the optional one "
+ << "character short alias";
return Status(ErrorCodes::BadValue, sb.str());
}
+ long_name = iterator->_singleName.substr(0, commaOffset);
+ } else {
+ long_name = iterator->_singleName;
+ }
- // Handle multi keys
- if (type == StringVector) {
- if (!YAMLNode.IsSequence()) {
- StringBuilder sb;
- sb << "Option: " << key
- << " is of type StringVector, but value in YAML config is not a list type";
- return Status(ErrorCodes::BadValue, sb.str());
- }
- StringVector_t stringVector;
- for (YAML::const_iterator it = YAMLNode.begin(); it != YAMLNode.end(); ++it) {
- if (it->IsSequence()) {
- StringBuilder sb;
- sb << "Option: " << key
- << " has nested lists, which is not allowed";
- return Status(ErrorCodes::BadValue, sb.str());
- }
- stringVector.push_back(it->Scalar());
- }
- *value = Value(stringVector);
- return Status::OK();
+ if (vm.count(long_name)) {
+ Value optionValue;
+ Status ret =
+ boostAnyToValue(vm[long_name].value(), iterator->_type, long_name, &optionValue);
+ if (!ret.isOK()) {
+ return ret;
}
- // Handle a sub map as a value
- if (type == StringMap) {
- if (!YAMLNode.IsMap()) {
- StringBuilder sb;
- sb << "Option: " << key
- << " is of type StringMap, but value in YAML config is not a map type";
- return Status(ErrorCodes::BadValue, sb.str());
+ // If this is really a StringMap, try to split on "key=value" for each element
+ // in our StringVector
+ if (iterator->_type == StringMap) {
+ StringVector_t keyValueVector;
+ ret = optionValue.get(&keyValueVector);
+ if (!ret.isOK()) {
+ return ret;
}
- StringMap_t stringMap;
- for (YAML::const_iterator it = YAMLNode.begin(); it != YAMLNode.end(); ++it) {
- if (it->second.IsSequence() || it->second.IsMap()) {
+ StringMap_t mapValue;
+ for (StringVector_t::iterator keyValueVectorIt = keyValueVector.begin();
+ keyValueVectorIt != keyValueVector.end();
+ ++keyValueVectorIt) {
+ std::string key;
+ std::string value;
+ if (!mongoutils::str::splitOn(*keyValueVectorIt, '=', key, value)) {
StringBuilder sb;
- sb << "Option: " << key
- << " has a map with non scalar values, which is not allowed";
+ sb << "Illegal option assignment: \"" << *keyValueVectorIt << "\"";
return Status(ErrorCodes::BadValue, sb.str());
}
- if (stringMap.count(it->first.Scalar()) > 0) {
+ // Make sure we aren't setting an option to two different values
+ if (mapValue.count(key) > 0 && mapValue[key] != value) {
StringBuilder sb;
- sb << "String Map Option: " << key
- << " has duplicate keys in YAML Config: " << it->first.Scalar();
+ sb << "Key Value Option: " << iterator->_dottedName
+ << " has a duplicate key from the same source: " << key;
return Status(ErrorCodes::BadValue, sb.str());
}
- stringMap[it->first.Scalar()] = it->second.Scalar();
+ mapValue[key] = value;
}
- *value = Value(stringMap);
- return Status::OK();
+ optionValue = Value(mapValue);
}
- // Our YAML parser reads everything as a string, so we need to parse it ourselves.
- std::string stringVal = YAMLNode.Scalar();
- return stringToValue(stringVal, type, key, value);
+ environment->set(iterator->_dottedName, optionValue);
}
+ }
+ return Status::OK();
+}
+
+// Add all the values in the given YAML Node to our environment. See comments at the
+// beginning of this section.
+Status addYAMLNodesToEnvironment(const YAML::Node& root,
+ const OptionSection& options,
+ const std::string parentPath,
+ Environment* environment) {
+ std::vector<OptionDescription> options_vector;
+ Status ret = options.getAllOptions(&options_vector);
+ if (!ret.isOK()) {
+ return ret;
+ }
- // Add all the values in the given variables_map to our environment. See comments at the
- // beginning of this section.
- Status addBoostVariablesToEnvironment(const po::variables_map& vm,
- const OptionSection& options,
- Environment* environment) {
-
- std::vector<OptionDescription> options_vector;
- Status ret = options.getAllOptions(&options_vector);
- if (!ret.isOK()) {
- return ret;
- }
-
- for(std::vector<OptionDescription>::const_iterator iterator = options_vector.begin();
- iterator != options_vector.end(); iterator++) {
-
- // Trim off the short option from our name so we can look it up correctly in our map
- std::string long_name;
- std::string::size_type commaOffset = iterator->_singleName.find(',');
- if (commaOffset != string::npos) {
- if (commaOffset != iterator->_singleName.size() - 2) {
- StringBuilder sb;
- sb << "Unexpected comma in option name: \"" << iterator->_singleName << "\""
- << ": option name must be in the format \"option,o\" or \"option\", "
- << "where \"option\" is the long name and \"o\" is the optional one "
- << "character short alias";
- return Status(ErrorCodes::BadValue, sb.str());
- }
- long_name = iterator->_singleName.substr(0, commaOffset);
- } else {
- long_name = iterator->_singleName;
- }
+ // Don't return an error on empty config files
+ if (root.IsNull()) {
+ return Status::OK();
+ }
- if (vm.count(long_name)) {
- Value optionValue;
- Status ret = boostAnyToValue(vm[long_name].value(), iterator->_type, long_name,
- &optionValue);
- if (!ret.isOK()) {
- return ret;
- }
+ if (!root.IsMap() && parentPath.empty()) {
+ StringBuilder sb;
+ sb << "No map found at top level of YAML config";
+ return Status(ErrorCodes::BadValue, sb.str());
+ }
- // If this is really a StringMap, try to split on "key=value" for each element
- // in our StringVector
- if (iterator->_type == StringMap) {
- StringVector_t keyValueVector;
- ret = optionValue.get(&keyValueVector);
- if (!ret.isOK()) {
- return ret;
- }
- StringMap_t mapValue;
- for (StringVector_t::iterator keyValueVectorIt = keyValueVector.begin();
- keyValueVectorIt != keyValueVector.end(); ++keyValueVectorIt) {
- std::string key;
- std::string value;
- if (!mongoutils::str::splitOn(*keyValueVectorIt, '=', key, value)) {
- StringBuilder sb;
- sb << "Illegal option assignment: \"" << *keyValueVectorIt << "\"";
- return Status(ErrorCodes::BadValue, sb.str());
- }
- // Make sure we aren't setting an option to two different values
- if (mapValue.count(key) > 0 && mapValue[key] != value) {
- StringBuilder sb;
- sb << "Key Value Option: " << iterator->_dottedName
- << " has a duplicate key from the same source: " << key;
- return Status(ErrorCodes::BadValue, sb.str());
- }
- mapValue[key] = value;
- }
- optionValue = Value(mapValue);
- }
+ for (YAML::const_iterator it = root.begin(); it != root.end(); ++it) {
+ std::string fieldName = it->first.Scalar();
+ YAML::Node YAMLNode = it->second;
+
+ std::string dottedName;
+ if (parentPath.empty()) {
+ // We are at the top level, so the full specifier is just the current field name
+ dottedName = fieldName;
+ } else {
+ // If our field name is "value", assume this contains the value for the parent
+ if (fieldName == "value") {
+ dottedName = parentPath;
+ }
- environment->set(iterator->_dottedName, optionValue);
- }
+ // If this is not a special field name, and we are in a sub object, append our
+ // current fieldName to the selector for the sub object we are traversing
+ else {
+ dottedName = parentPath + '.' + fieldName;
}
- return Status::OK();
}
- // Add all the values in the given YAML Node to our environment. See comments at the
- // beginning of this section.
- Status addYAMLNodesToEnvironment(const YAML::Node& root,
- const OptionSection& options,
- const std::string parentPath,
- Environment* environment) {
-
- std::vector<OptionDescription> options_vector;
- Status ret = options.getAllOptions(&options_vector);
+ if (YAMLNode.IsMap() && !OptionIsStringMap(options_vector, dottedName)) {
+ Status ret = addYAMLNodesToEnvironment(YAMLNode, options, dottedName, environment);
if (!ret.isOK()) {
return ret;
}
-
- // Don't return an error on empty config files
- if (root.IsNull()) {
- return Status::OK();
+ } else {
+ Key canonicalKey;
+ Value optionValue;
+ Status ret =
+ YAMLNodeToValue(YAMLNode, options_vector, dottedName, &canonicalKey, &optionValue);
+ if (!ret.isOK()) {
+ return ret;
}
- if (!root.IsMap() && parentPath.empty()) {
+ Value dummyVal;
+ if (environment->get(canonicalKey, &dummyVal).isOK()) {
StringBuilder sb;
- sb << "No map found at top level of YAML config";
+ sb << "Error parsing YAML config: duplicate key: " << dottedName
+ << "(canonical key: " << canonicalKey << ")";
return Status(ErrorCodes::BadValue, sb.str());
}
- for (YAML::const_iterator it = root.begin(); it != root.end(); ++it) {
- std::string fieldName = it->first.Scalar();
- YAML::Node YAMLNode = it->second;
-
- std::string dottedName;
- if (parentPath.empty()) {
-
- // We are at the top level, so the full specifier is just the current field name
- dottedName = fieldName;
+ // Only add the value if it is not empty. YAMLNodeToValue will set the
+ // optionValue to an empty Value if we should not set it in the Environment.
+ if (!optionValue.isEmpty()) {
+ ret = environment->set(canonicalKey, optionValue);
+ if (!ret.isOK()) {
+ return ret;
}
- else {
+ }
+ }
+ }
- // If our field name is "value", assume this contains the value for the parent
- if (fieldName == "value") {
- dottedName = parentPath;
- }
+ return Status::OK();
+}
+
+/**
+* For all options that we registered as composable, combine the values from source and dest
+* and set the result in dest. Note that this only works for options that are registered as
+* vectors of strings.
+*/
+Status addCompositions(const OptionSection& options, const Environment& source, Environment* dest) {
+ std::vector<OptionDescription> options_vector;
+ Status ret = options.getAllOptions(&options_vector);
+ if (!ret.isOK()) {
+ return ret;
+ }
- // If this is not a special field name, and we are in a sub object, append our
- // current fieldName to the selector for the sub object we are traversing
- else {
- dottedName = parentPath + '.' + fieldName;
- }
- }
+ for (std::vector<OptionDescription>::const_iterator iterator = options_vector.begin();
+ iterator != options_vector.end();
+ iterator++) {
+ if (!iterator->_isComposing) {
+ continue;
+ }
- if (YAMLNode.IsMap() && !OptionIsStringMap(options_vector, dottedName)) {
- Status ret = addYAMLNodesToEnvironment(YAMLNode, options, dottedName,
- environment);
- if (!ret.isOK()) {
- return ret;
- }
+ if (iterator->_type == StringVector) {
+ StringVector_t sourceValue;
+ ret = source.get(iterator->_dottedName, &sourceValue);
+ if (!ret.isOK() && ret != ErrorCodes::NoSuchKey) {
+ StringBuilder sb;
+ sb << "Error getting composable vector value from source: " << ret.toString();
+ return Status(ErrorCodes::InternalError, sb.str());
+ }
+ // Only do something if our source environment has something to add
+ else if (ret.isOK()) {
+ StringVector_t destValue;
+ ret = dest->get(iterator->_dottedName, &destValue);
+ if (!ret.isOK() && ret != ErrorCodes::NoSuchKey) {
+ StringBuilder sb;
+ sb << "Error getting composable vector value from dest: " << ret.toString();
+ return Status(ErrorCodes::InternalError, sb.str());
}
- else {
- Key canonicalKey;
- Value optionValue;
- Status ret = YAMLNodeToValue(YAMLNode, options_vector, dottedName,
- &canonicalKey, &optionValue);
- if (!ret.isOK()) {
- return ret;
- }
- Value dummyVal;
- if (environment->get(canonicalKey, &dummyVal).isOK()) {
- StringBuilder sb;
- sb << "Error parsing YAML config: duplicate key: " << dottedName
- << "(canonical key: " << canonicalKey << ")";
- return Status(ErrorCodes::BadValue, sb.str());
- }
+ // Append sourceValue on the end of destValue
+ destValue.insert(destValue.end(), sourceValue.begin(), sourceValue.end());
- // Only add the value if it is not empty. YAMLNodeToValue will set the
- // optionValue to an empty Value if we should not set it in the Environment.
- if (!optionValue.isEmpty()) {
- ret = environment->set(canonicalKey, optionValue);
- if (!ret.isOK()) {
- return ret;
- }
- }
+ // Set the resulting value in our output environment
+ ret = dest->set(Key(iterator->_dottedName), Value(destValue));
+ if (!ret.isOK()) {
+ return ret;
}
}
-
- return Status::OK();
- }
-
- /**
- * For all options that we registered as composable, combine the values from source and dest
- * and set the result in dest. Note that this only works for options that are registered as
- * vectors of strings.
- */
- Status addCompositions(const OptionSection& options,
- const Environment& source,
- Environment* dest) {
- std::vector<OptionDescription> options_vector;
- Status ret = options.getAllOptions(&options_vector);
- if (!ret.isOK()) {
- return ret;
+ } else if (iterator->_type == StringMap) {
+ StringMap_t sourceValue;
+ ret = source.get(iterator->_dottedName, &sourceValue);
+ if (!ret.isOK() && ret != ErrorCodes::NoSuchKey) {
+ StringBuilder sb;
+ sb << "Error getting composable map value from source: " << ret.toString();
+ return Status(ErrorCodes::InternalError, sb.str());
}
-
- for (std::vector<OptionDescription>::const_iterator iterator = options_vector.begin();
- iterator != options_vector.end(); iterator++) {
-
- if (!iterator->_isComposing) {
- continue;
+ // Only do something if our source environment has something to add
+ else if (ret.isOK()) {
+ StringMap_t destValue;
+ ret = dest->get(iterator->_dottedName, &destValue);
+ if (!ret.isOK() && ret != ErrorCodes::NoSuchKey) {
+ StringBuilder sb;
+ sb << "Error getting composable map value from dest: " << ret.toString();
+ return Status(ErrorCodes::InternalError, sb.str());
}
- if (iterator->_type == StringVector) {
- StringVector_t sourceValue;
- ret = source.get(iterator->_dottedName, &sourceValue);
- if (!ret.isOK() && ret != ErrorCodes::NoSuchKey) {
- StringBuilder sb;
- sb << "Error getting composable vector value from source: "
- << ret.toString();
- return Status(ErrorCodes::InternalError, sb.str());
- }
- // Only do something if our source environment has something to add
- else if (ret.isOK()) {
- StringVector_t destValue;
- ret = dest->get(iterator->_dottedName, &destValue);
- if (!ret.isOK() && ret != ErrorCodes::NoSuchKey) {
- StringBuilder sb;
- sb << "Error getting composable vector value from dest: "
- << ret.toString();
- return Status(ErrorCodes::InternalError, sb.str());
- }
-
- // Append sourceValue on the end of destValue
- destValue.insert(destValue.end(),
- sourceValue.begin(),
- sourceValue.end());
-
- // Set the resulting value in our output environment
- ret = dest->set(Key(iterator->_dottedName), Value(destValue));
- if (!ret.isOK()) {
- return ret;
- }
- }
+ // Iterate sourceValue and add elements to destValue
+ for (StringMap_t::iterator sourceValueIt = sourceValue.begin();
+ sourceValueIt != sourceValue.end();
+ sourceValueIt++) {
+ destValue[sourceValueIt->first] = sourceValueIt->second;
}
- else if (iterator->_type == StringMap) {
- StringMap_t sourceValue;
- ret = source.get(iterator->_dottedName, &sourceValue);
- if (!ret.isOK() && ret != ErrorCodes::NoSuchKey) {
- StringBuilder sb;
- sb << "Error getting composable map value from source: "
- << ret.toString();
- return Status(ErrorCodes::InternalError, sb.str());
- }
- // Only do something if our source environment has something to add
- else if (ret.isOK()) {
- StringMap_t destValue;
- ret = dest->get(iterator->_dottedName, &destValue);
- if (!ret.isOK() && ret != ErrorCodes::NoSuchKey) {
- StringBuilder sb;
- sb << "Error getting composable map value from dest: "
- << ret.toString();
- return Status(ErrorCodes::InternalError, sb.str());
- }
-
- // Iterate sourceValue and add elements to destValue
- for (StringMap_t::iterator sourceValueIt = sourceValue.begin();
- sourceValueIt != sourceValue.end(); sourceValueIt++) {
- destValue[sourceValueIt->first] = sourceValueIt->second;
- }
-
- // Set the resulting value in our output environment
- ret = dest->set(Key(iterator->_dottedName), Value(destValue));
- if (!ret.isOK()) {
- return ret;
- }
- }
- } else {
- StringBuilder sb;
- sb << "Found composable option that is not of StringVector or "
- << "StringMap Type: " << iterator->_dottedName;
- return Status(ErrorCodes::InternalError, sb.str());
+
+ // Set the resulting value in our output environment
+ ret = dest->set(Key(iterator->_dottedName), Value(destValue));
+ if (!ret.isOK()) {
+ return ret;
}
}
-
- return Status::OK();
+ } else {
+ StringBuilder sb;
+ sb << "Found composable option that is not of StringVector or "
+ << "StringMap Type: " << iterator->_dottedName;
+ return Status(ErrorCodes::InternalError, sb.str());
}
+ }
- /**
- * For all options that have constraints, add those constraints to our environment so that
- * they run when the environment gets validated.
- */
- Status addConstraints(const OptionSection& options, Environment* dest) {
- std::vector<std::shared_ptr<Constraint> > constraints_vector;
+ return Status::OK();
+}
- Status ret = options.getConstraints(&constraints_vector);
- if (!ret.isOK()) {
- return ret;
- }
+/**
+* For all options that have constraints, add those constraints to our environment so that
+* they run when the environment gets validated.
+*/
+Status addConstraints(const OptionSection& options, Environment* dest) {
+ std::vector<std::shared_ptr<Constraint>> constraints_vector;
- std::vector<std::shared_ptr<Constraint> >::const_iterator citerator;
- for (citerator = constraints_vector.begin();
- citerator != constraints_vector.end(); citerator++) {
- dest->addConstraint(citerator->get());
- }
+ Status ret = options.getConstraints(&constraints_vector);
+ if (!ret.isOK()) {
+ return ret;
+ }
- return Status::OK();
- }
+ std::vector<std::shared_ptr<Constraint>>::const_iterator citerator;
+ for (citerator = constraints_vector.begin(); citerator != constraints_vector.end();
+ citerator++) {
+ dest->addConstraint(citerator->get());
+ }
- /**
- * Remove any options of type "Switch" that are set to false. This is needed because boost
- * defaults switches to false, and we need to be able to tell the difference between
- * whether an option is set explicitly to false in config files or not present at all.
- */
- Status removeFalseSwitches(const OptionSection& options, Environment* environment) {
- std::vector<OptionDescription> options_vector;
- Status ret = options.getAllOptions(&options_vector);
- if (!ret.isOK()) {
- return ret;
- }
+ return Status::OK();
+}
- for (std::vector<OptionDescription>::const_iterator iterator = options_vector.begin();
- iterator != options_vector.end(); iterator++) {
+/**
+ * Remove any options of type "Switch" that are set to false. This is needed because boost
+ * defaults switches to false, and we need to be able to tell the difference between
+ * whether an option is set explicitly to false in config files or not present at all.
+ */
+Status removeFalseSwitches(const OptionSection& options, Environment* environment) {
+ std::vector<OptionDescription> options_vector;
+ Status ret = options.getAllOptions(&options_vector);
+ if (!ret.isOK()) {
+ return ret;
+ }
- if (iterator->_type == Switch) {
- bool switchValue;
- Status ret = environment->get(iterator->_dottedName, &switchValue);
- if (!ret.isOK() && ret != ErrorCodes::NoSuchKey) {
- StringBuilder sb;
- sb << "Error getting switch value for option: " << iterator->_dottedName
- << " from source: " << ret.toString();
- return Status(ErrorCodes::InternalError, sb.str());
- }
- else if (ret.isOK() && switchValue == false) {
- Status ret = environment->remove(iterator->_dottedName);
- if (!ret.isOK()) {
- StringBuilder sb;
- sb << "Error removing false flag: " << iterator->_dottedName << ": "
- << ret.toString();
- return Status(ErrorCodes::InternalError, sb.str());
- }
- }
+ for (std::vector<OptionDescription>::const_iterator iterator = options_vector.begin();
+ iterator != options_vector.end();
+ iterator++) {
+ if (iterator->_type == Switch) {
+ bool switchValue;
+ Status ret = environment->get(iterator->_dottedName, &switchValue);
+ if (!ret.isOK() && ret != ErrorCodes::NoSuchKey) {
+ StringBuilder sb;
+ sb << "Error getting switch value for option: " << iterator->_dottedName
+ << " from source: " << ret.toString();
+ return Status(ErrorCodes::InternalError, sb.str());
+ } else if (ret.isOK() && switchValue == false) {
+ Status ret = environment->remove(iterator->_dottedName);
+ if (!ret.isOK()) {
+ StringBuilder sb;
+ sb << "Error removing false flag: " << iterator->_dottedName << ": "
+ << ret.toString();
+ return Status(ErrorCodes::InternalError, sb.str());
}
}
-
- return Status::OK();
}
+ }
+
+ return Status::OK();
+}
+
+} // namespace
- } // namespace
+/**
+ * This function delegates the command line parsing to boost program_options.
+ *
+ * 1. Extract the boost readable option descriptions and positional option descriptions from the
+ * OptionSection
+ * 2. Passes them to the boost command line parser
+ * 3. Copy everything from the variables map returned by boost into the Environment
+ */
+Status OptionsParser::parseCommandLine(const OptionSection& options,
+ const std::vector<std::string>& argv,
+ Environment* environment) {
+ po::options_description boostOptions;
+ po::positional_options_description boostPositionalOptions;
+ po::variables_map vm;
+
+ // Need to convert to an argc and a vector of char* since that is what
+ // boost::program_options expects as input to its command line parser
+ int argc = 0;
+ std::vector<const char*> argv_buffer;
+ for (std::vector<std::string>::const_iterator iterator = argv.begin(); iterator != argv.end();
+ iterator++) {
+ argv_buffer.push_back(iterator->c_str());
+ argc++;
+ }
/**
- * This function delegates the command line parsing to boost program_options.
+ * Style options for boost command line parser
*
- * 1. Extract the boost readable option descriptions and positional option descriptions from the
- * OptionSection
- * 2. Passes them to the boost command line parser
- * 3. Copy everything from the variables map returned by boost into the Environment
+ * unix_style is an alias for a group of basic style options. We are deviating from that
+ * base style in the following ways:
+ *
+ * 1. Don't allow guessing - '--dbpat' != '--dbpath'
+ * 2. Don't allow sticky - '-hf' != '-h -f'
+ * 3. Allow long disguises - '--dbpath' == '-dbpath'
+ *
+ * In some executables, we are using multiple 'v' options to set the verbosity (e.g. '-vvv')
+ * To make this work, we need to allow long disguises and disallow guessing.
*/
- Status OptionsParser::parseCommandLine(const OptionSection& options,
- const std::vector<std::string>& argv,
- Environment* environment) {
- po::options_description boostOptions;
- po::positional_options_description boostPositionalOptions;
- po::variables_map vm;
-
- // Need to convert to an argc and a vector of char* since that is what
- // boost::program_options expects as input to its command line parser
- int argc = 0;
- std::vector<const char*> argv_buffer;
- for (std::vector<std::string>::const_iterator iterator = argv.begin();
- iterator != argv.end(); iterator++) {
- argv_buffer.push_back(iterator->c_str());
- argc++;
- }
+ int style = (((po::command_line_style::unix_style ^ po::command_line_style::allow_guessing) |
+ po::command_line_style::allow_long_disguise) ^
+ po::command_line_style::allow_sticky);
- /**
- * Style options for boost command line parser
- *
- * unix_style is an alias for a group of basic style options. We are deviating from that
- * base style in the following ways:
- *
- * 1. Don't allow guessing - '--dbpat' != '--dbpath'
- * 2. Don't allow sticky - '-hf' != '-h -f'
- * 3. Allow long disguises - '--dbpath' == '-dbpath'
- *
- * In some executables, we are using multiple 'v' options to set the verbosity (e.g. '-vvv')
- * To make this work, we need to allow long disguises and disallow guessing.
- */
- int style = (((po::command_line_style::unix_style ^
- po::command_line_style::allow_guessing) |
- po::command_line_style::allow_long_disguise) ^
- po::command_line_style::allow_sticky);
-
- Status ret = options.getBoostOptions(&boostOptions, false, false, SourceCommandLine);
- if (!ret.isOK()) {
- return ret;
- }
-
- ret = options.getBoostPositionalOptions(&boostPositionalOptions);
- if (!ret.isOK()) {
- return ret;
- }
+ Status ret = options.getBoostOptions(&boostOptions, false, false, SourceCommandLine);
+ if (!ret.isOK()) {
+ return ret;
+ }
- try {
- po::store(po::command_line_parser(argc, (argc > 0 ? &argv_buffer[0] : NULL)).
- options(boostOptions).
- positional(boostPositionalOptions).
- style(style).
- run(), vm);
+ ret = options.getBoostPositionalOptions(&boostPositionalOptions);
+ if (!ret.isOK()) {
+ return ret;
+ }
- ret = addBoostVariablesToEnvironment(vm, options, environment);
- if (!ret.isOK()) {
- return ret;
- }
- }
- catch (po::multiple_occurrences& e) {
- StringBuilder sb;
- sb << "Error parsing command line: Multiple occurrences of option \"--"
- << e.get_option_name() << "\"";
- return Status(ErrorCodes::BadValue, sb.str());
- }
- catch (po::error& e) {
- StringBuilder sb;
- sb << "Error parsing command line: " << e.what();
- return Status(ErrorCodes::BadValue, sb.str());
- }
+ try {
+ po::store(po::command_line_parser(argc, (argc > 0 ? &argv_buffer[0] : NULL))
+ .options(boostOptions)
+ .positional(boostPositionalOptions)
+ .style(style)
+ .run(),
+ vm);
- // This is needed because "switches" default to false in boost, and we don't want to
- // erroneously think that they were present but set to false in a config file.
- ret = removeFalseSwitches(options, environment);
+ ret = addBoostVariablesToEnvironment(vm, options, environment);
if (!ret.isOK()) {
return ret;
}
+ } catch (po::multiple_occurrences& e) {
+ StringBuilder sb;
+ sb << "Error parsing command line: Multiple occurrences of option \"--"
+ << e.get_option_name() << "\"";
+ return Status(ErrorCodes::BadValue, sb.str());
+ } catch (po::error& e) {
+ StringBuilder sb;
+ sb << "Error parsing command line: " << e.what();
+ return Status(ErrorCodes::BadValue, sb.str());
+ }
- return Status::OK();
+ // This is needed because "switches" default to false in boost, and we don't want to
+ // erroneously think that they were present but set to false in a config file.
+ ret = removeFalseSwitches(options, environment);
+ if (!ret.isOK()) {
+ return ret;
}
- /**
- * This function delegates the INI config parsing to boost program_options.
- *
- * 1. Extract the boost readable option descriptions from the OptionSection
- * 2. Passes them to the boost config file parser
- * 3. Copy everything from the variables map returned by boost into the Environment
- */
- Status OptionsParser::parseINIConfigFile(const OptionSection& options,
- const std::string& config,
- Environment* environment) {
- po::options_description boostOptions;
- po::variables_map vm;
+ return Status::OK();
+}
+
+/**
+ * This function delegates the INI config parsing to boost program_options.
+ *
+ * 1. Extract the boost readable option descriptions from the OptionSection
+ * 2. Passes them to the boost config file parser
+ * 3. Copy everything from the variables map returned by boost into the Environment
+ */
+Status OptionsParser::parseINIConfigFile(const OptionSection& options,
+ const std::string& config,
+ Environment* environment) {
+ po::options_description boostOptions;
+ po::variables_map vm;
+
+ Status ret = options.getBoostOptions(&boostOptions, false, false, SourceINIConfig);
+ if (!ret.isOK()) {
+ return ret;
+ }
- Status ret = options.getBoostOptions(&boostOptions, false, false, SourceINIConfig);
+ std::istringstream is(config);
+ try {
+ po::store(po::parse_config_file(is, boostOptions), vm);
+ ret = addBoostVariablesToEnvironment(vm, options, environment);
if (!ret.isOK()) {
return ret;
}
-
- std::istringstream is(config);
- try {
- po::store(po::parse_config_file(is, boostOptions), vm);
- ret = addBoostVariablesToEnvironment(vm, options, environment);
- if (!ret.isOK()) {
- return ret;
- }
- }
- catch (po::multiple_occurrences& e) {
- StringBuilder sb;
- sb << "Error parsing INI config file: Multiple occurrences of option \""
- << e.get_option_name() << "\"";
- return Status(ErrorCodes::BadValue, sb.str());
- }
- catch (po::error& e) {
- StringBuilder sb;
- sb << "Error parsing INI config file: " << e.what();
- return Status(ErrorCodes::BadValue, sb.str());
- }
- return Status::OK();
+ } catch (po::multiple_occurrences& e) {
+ StringBuilder sb;
+ sb << "Error parsing INI config file: Multiple occurrences of option \""
+ << e.get_option_name() << "\"";
+ return Status(ErrorCodes::BadValue, sb.str());
+ } catch (po::error& e) {
+ StringBuilder sb;
+ sb << "Error parsing INI config file: " << e.what();
+ return Status(ErrorCodes::BadValue, sb.str());
}
+ return Status::OK();
+}
namespace {
- /**
- * This function delegates the YAML config parsing to the third party YAML parser. It does no
- * error checking other than the parse error checking done by the YAML parser.
- */
- Status parseYAMLConfigFile(const std::string& config,
- YAML::Node* YAMLConfig) {
-
- try {
- *YAMLConfig = YAML::Load(config);
- } catch (const YAML::Exception &e) {
- StringBuilder sb;
- sb << "Error parsing YAML config file: " << e.what();
- return Status(ErrorCodes::BadValue, sb.str());
- } catch (const std::runtime_error &e) {
- StringBuilder sb;
- sb << "Unexpected exception parsing YAML config file: " << e.what();
- return Status(ErrorCodes::BadValue, sb.str());
- }
-
- return Status::OK();
+/**
+ * This function delegates the YAML config parsing to the third party YAML parser. It does no
+ * error checking other than the parse error checking done by the YAML parser.
+ */
+Status parseYAMLConfigFile(const std::string& config, YAML::Node* YAMLConfig) {
+ try {
+ *YAMLConfig = YAML::Load(config);
+ } catch (const YAML::Exception& e) {
+ StringBuilder sb;
+ sb << "Error parsing YAML config file: " << e.what();
+ return Status(ErrorCodes::BadValue, sb.str());
+ } catch (const std::runtime_error& e) {
+ StringBuilder sb;
+ sb << "Unexpected exception parsing YAML config file: " << e.what();
+ return Status(ErrorCodes::BadValue, sb.str());
}
- bool isYAMLConfig(const YAML::Node& config) {
- // The YAML parser is very forgiving, and for the INI config files we've parsed so far using
- // the YAML parser, the YAML parser just slurps the entire config file into a single string
- // rather than erroring out. Thus, we assume that having a scalar (string) as the root node
- // means that this is not meant to be a YAML config file, since even a very simple YAML
- // config file should be parsed as a Map, and thus "config.IsScalar()" would return false.
- //
- // This requires more testing, both to ensure that all INI style files get parsed as a
- // single string, and to ensure that the user experience does not suffer (in terms of this
- // causing confusing error messages for users writing a brand new YAML config file that
- // incorrectly triggers this check).
- if (config.IsScalar()) {
- return false;
- }
- else {
- return true;
- }
+ return Status::OK();
+}
+
+bool isYAMLConfig(const YAML::Node& config) {
+ // The YAML parser is very forgiving, and for the INI config files we've parsed so far using
+ // the YAML parser, the YAML parser just slurps the entire config file into a single string
+ // rather than erroring out. Thus, we assume that having a scalar (string) as the root node
+ // means that this is not meant to be a YAML config file, since even a very simple YAML
+ // config file should be parsed as a Map, and thus "config.IsScalar()" would return false.
+ //
+ // This requires more testing, both to ensure that all INI style files get parsed as a
+ // single string, and to ensure that the user experience does not suffer (in terms of this
+ // causing confusing error messages for users writing a brand new YAML config file that
+ // incorrectly triggers this check).
+ if (config.IsScalar()) {
+ return false;
+ } else {
+ return true;
}
+}
-} // namespace
+} // namespace
- /**
- * Add default values from the given OptionSection to the given Environment
- */
- Status OptionsParser::addDefaultValues(const OptionSection& options,
- Environment* environment) {
- std::map <Key, Value> defaultOptions;
+/**
+ * Add default values from the given OptionSection to the given Environment
+ */
+Status OptionsParser::addDefaultValues(const OptionSection& options, Environment* environment) {
+ std::map<Key, Value> defaultOptions;
- Status ret = options.getDefaults(&defaultOptions);
+ Status ret = options.getDefaults(&defaultOptions);
+ if (!ret.isOK()) {
+ return ret;
+ }
+
+ typedef std::map<Key, Value>::iterator it_type;
+ for (it_type iterator = defaultOptions.begin(); iterator != defaultOptions.end(); iterator++) {
+ ret = environment->setDefault(iterator->first, iterator->second);
if (!ret.isOK()) {
return ret;
}
-
- typedef std::map<Key, Value>::iterator it_type;
- for(it_type iterator = defaultOptions.begin();
- iterator != defaultOptions.end(); iterator++) {
- ret = environment->setDefault(iterator->first, iterator->second);
- if (!ret.isOK()) {
- return ret;
- }
- }
-
- return Status::OK();
}
- /**
- * Reads the entire config file into the output string. This was done this way because the JSON
- * parser only takes complete strings, and we were using that to parse the config file before.
- * We could redesign the parser to use some kind of streaming interface, but for now this is
- * simple and works for the current use case of config files which should be limited in size.
- */
- Status OptionsParser::readConfigFile(const std::string& filename, std::string* contents) {
-
- FILE* config;
- config = fopen(filename.c_str(), "r");
- if (config == NULL) {
- const int current_errno = errno;
- StringBuilder sb;
- sb << "Error reading config file: " << strerror(current_errno);
- return Status(ErrorCodes::InternalError, sb.str());
- }
- ON_BLOCK_EXIT(fclose, config);
+ return Status::OK();
+}
- // Get length of config file by seeking to the end and getting the cursor position
- if (fseek(config, 0L, SEEK_END) != 0) {
- const int current_errno = errno;
- // TODO: Make sure errno is the correct way to do this
- // Confirmed that errno gets set in Mac OSX, but not documented
- StringBuilder sb;
- sb << "Error seeking in config file: " << strerror(current_errno);
- return Status(ErrorCodes::InternalError, sb.str());
- }
- long configSize = ftell(config);
-
- // Seek back to the beginning of the file for reading
- if (fseek(config, 0L, SEEK_SET) != 0) {
- const int current_errno = errno;
- // TODO: Make sure errno is the correct way to do this
- // Confirmed that errno gets set in Mac OSX, but not documented
- StringBuilder sb;
- sb << "Error seeking in config file: " << strerror(current_errno);
- return Status(ErrorCodes::InternalError, sb.str());
- }
+/**
+ * Reads the entire config file into the output string. This was done this way because the JSON
+ * parser only takes complete strings, and we were using that to parse the config file before.
+ * We could redesign the parser to use some kind of streaming interface, but for now this is
+ * simple and works for the current use case of config files which should be limited in size.
+ */
+Status OptionsParser::readConfigFile(const std::string& filename, std::string* contents) {
+ FILE* config;
+ config = fopen(filename.c_str(), "r");
+ if (config == NULL) {
+ const int current_errno = errno;
+ StringBuilder sb;
+ sb << "Error reading config file: " << strerror(current_errno);
+ return Status(ErrorCodes::InternalError, sb.str());
+ }
+ ON_BLOCK_EXIT(fclose, config);
+
+ // Get length of config file by seeking to the end and getting the cursor position
+ if (fseek(config, 0L, SEEK_END) != 0) {
+ const int current_errno = errno;
+ // TODO: Make sure errno is the correct way to do this
+ // Confirmed that errno gets set in Mac OSX, but not documented
+ StringBuilder sb;
+ sb << "Error seeking in config file: " << strerror(current_errno);
+ return Status(ErrorCodes::InternalError, sb.str());
+ }
+ long configSize = ftell(config);
+
+ // Seek back to the beginning of the file for reading
+ if (fseek(config, 0L, SEEK_SET) != 0) {
+ const int current_errno = errno;
+ // TODO: Make sure errno is the correct way to do this
+ // Confirmed that errno gets set in Mac OSX, but not documented
+ StringBuilder sb;
+ sb << "Error seeking in config file: " << strerror(current_errno);
+ return Status(ErrorCodes::InternalError, sb.str());
+ }
- // Read into a vector first since it's guaranteed to have contiguous storage
- std::vector<char> configVector;
- configVector.resize(configSize);
-
- if (configSize > 0) {
- long nread = 0;
- while (!feof(config) && nread < configSize) {
- nread = nread + fread(&configVector[nread], sizeof(char),
- configSize - nread, config);
- if (ferror(config)) {
- const int current_errno = errno;
- // TODO: Make sure errno is the correct way to do this
- StringBuilder sb;
- sb << "Error reading in config file: " << strerror(current_errno);
- return Status(ErrorCodes::InternalError, sb.str());
- }
+ // Read into a vector first since it's guaranteed to have contiguous storage
+ std::vector<char> configVector;
+ configVector.resize(configSize);
+
+ if (configSize > 0) {
+ long nread = 0;
+ while (!feof(config) && nread < configSize) {
+ nread = nread + fread(&configVector[nread], sizeof(char), configSize - nread, config);
+ if (ferror(config)) {
+ const int current_errno = errno;
+ // TODO: Make sure errno is the correct way to do this
+ StringBuilder sb;
+ sb << "Error reading in config file: " << strerror(current_errno);
+ return Status(ErrorCodes::InternalError, sb.str());
}
- // Resize our config vector to the number of bytes we actually read
- configVector.resize(nread);
}
-
- // Copy the vector contents into our result string
- *contents = std::string(configVector.begin(), configVector.end());
-
- return Status::OK();
+ // Resize our config vector to the number of bytes we actually read
+ configVector.resize(nread);
}
- /**
- * Run the OptionsParser
- *
- * Overview:
- *
- * 1. Parse argc and argv using the given OptionSection as a description of expected options
- * 2. Check for a "config" argument
- * 3. If "config" found, read config file
- * 4. Detect config file type (YAML or INI)
- * 5. Parse config file using the given OptionSection as a description of expected options
- * 6. Add the results to the output Environment in the proper order to ensure correct precedence
- */
- Status OptionsParser::run(const OptionSection& options,
- const std::vector<std::string>& argv,
- const std::map<std::string, std::string>& env, // XXX: Currently unused
- Environment* environment) {
+ // Copy the vector contents into our result string
+ *contents = std::string(configVector.begin(), configVector.end());
+
+ return Status::OK();
+}
- Environment commandLineEnvironment;
- Environment configEnvironment;
- Environment composedEnvironment;
+/**
+ * Run the OptionsParser
+ *
+ * Overview:
+ *
+ * 1. Parse argc and argv using the given OptionSection as a description of expected options
+ * 2. Check for a "config" argument
+ * 3. If "config" found, read config file
+ * 4. Detect config file type (YAML or INI)
+ * 5. Parse config file using the given OptionSection as a description of expected options
+ * 6. Add the results to the output Environment in the proper order to ensure correct precedence
+ */
+Status OptionsParser::run(const OptionSection& options,
+ const std::vector<std::string>& argv,
+ const std::map<std::string, std::string>& env, // XXX: Currently unused
+ Environment* environment) {
+ Environment commandLineEnvironment;
+ Environment configEnvironment;
+ Environment composedEnvironment;
+
+ Status ret = parseCommandLine(options, argv, &commandLineEnvironment);
+ if (!ret.isOK()) {
+ return ret;
+ }
- Status ret = parseCommandLine(options, argv, &commandLineEnvironment);
+ Value config_value;
+ ret = commandLineEnvironment.get(Key("config"), &config_value);
+ // We had an error other than "config" not existing in our environment
+ if (!ret.isOK() && ret != ErrorCodes::NoSuchKey) {
+ return ret;
+ }
+ // "config" exists in our environment
+ else if (ret.isOK()) {
+ // Environment::get returns a bad status if config was not set
+ std::string config_filename;
+ ret = config_value.get(&config_filename);
if (!ret.isOK()) {
return ret;
}
- Value config_value;
- ret = commandLineEnvironment.get(Key("config"), &config_value);
- // We had an error other than "config" not existing in our environment
- if (!ret.isOK() && ret != ErrorCodes::NoSuchKey) {
+ std::string config_file;
+ ret = readConfigFile(config_filename, &config_file);
+ if (!ret.isOK()) {
return ret;
}
- // "config" exists in our environment
- else if (ret.isOK()) {
- // Environment::get returns a bad status if config was not set
- std::string config_filename;
- ret = config_value.get(&config_filename);
- if (!ret.isOK()) {
- return ret;
- }
+ YAML::Node YAMLConfig;
+ ret = parseYAMLConfigFile(config_file, &YAMLConfig);
+ if (!ret.isOK()) {
+ return ret;
+ }
- std::string config_file;
- ret = readConfigFile(config_filename, &config_file);
+ if (isYAMLConfig(YAMLConfig)) {
+ ret = addYAMLNodesToEnvironment(YAMLConfig, options, "", &configEnvironment);
if (!ret.isOK()) {
return ret;
}
-
- YAML::Node YAMLConfig;
- ret = parseYAMLConfigFile(config_file, &YAMLConfig);
+ } else {
+ ret = parseINIConfigFile(options, config_file, &configEnvironment);
if (!ret.isOK()) {
return ret;
}
-
- if (isYAMLConfig(YAMLConfig)) {
- ret = addYAMLNodesToEnvironment(YAMLConfig, options, "", &configEnvironment);
- if (!ret.isOK()) {
- return ret;
- }
- }
- else {
- ret = parseINIConfigFile(options, config_file, &configEnvironment);
- if (!ret.isOK()) {
- return ret;
- }
- }
- }
-
- // Adds the values for all our options that were registered as composable to the composed
- // environment. addCompositions doesn't override the values like "setAll" on our
- // environment. Instead it aggregates the values in the result environment.
- // NOTE: We must add our configEnvironment compositions first since we have a StringMap type
- // in which some options can be overridden by the command line.
- ret = addCompositions(options, configEnvironment, &composedEnvironment);
- if (!ret.isOK()) {
- return ret;
}
+ }
- ret = addCompositions(options, commandLineEnvironment, &composedEnvironment);
- if (!ret.isOK()) {
- return ret;
- }
+ // Adds the values for all our options that were registered as composable to the composed
+ // environment. addCompositions doesn't override the values like "setAll" on our
+ // environment. Instead it aggregates the values in the result environment.
+ // NOTE: We must add our configEnvironment compositions first since we have a StringMap type
+ // in which some options can be overridden by the command line.
+ ret = addCompositions(options, configEnvironment, &composedEnvironment);
+ if (!ret.isOK()) {
+ return ret;
+ }
- // Add the default values to our resulting environment
- ret = addDefaultValues(options, environment);
- if (!ret.isOK()) {
- return ret;
- }
+ ret = addCompositions(options, commandLineEnvironment, &composedEnvironment);
+ if (!ret.isOK()) {
+ return ret;
+ }
- // Add the values to our result in the order of override
- // NOTE: This should not fail validation as we haven't called environment->validate() yet
- ret = environment->setAll(configEnvironment);
- if (!ret.isOK()) {
- return ret;
- }
- ret = environment->setAll(commandLineEnvironment);
- if (!ret.isOK()) {
- return ret;
- }
+ // Add the default values to our resulting environment
+ ret = addDefaultValues(options, environment);
+ if (!ret.isOK()) {
+ return ret;
+ }
- // Add this last because it has all the composable options aggregated over different
- // sources. For example, if we have a StringMap type with some values set on the command
- // line and some values set in config files, we want to make sure to get them all. This
- // should not override any non composable options, since composedEnvironment should not have
- // them set. See the addCompositions function for more details.
- ret = environment->setAll(composedEnvironment);
- if (!ret.isOK()) {
- return ret;
- }
+ // Add the values to our result in the order of override
+ // NOTE: This should not fail validation as we haven't called environment->validate() yet
+ ret = environment->setAll(configEnvironment);
+ if (!ret.isOK()) {
+ return ret;
+ }
+ ret = environment->setAll(commandLineEnvironment);
+ if (!ret.isOK()) {
+ return ret;
+ }
- // Add the constraints from our options to the result environment
- ret = addConstraints(options, environment);
- if (!ret.isOK()) {
- return ret;
- }
+ // Add this last because it has all the composable options aggregated over different
+ // sources. For example, if we have a StringMap type with some values set on the command
+ // line and some values set in config files, we want to make sure to get them all. This
+ // should not override any non composable options, since composedEnvironment should not have
+ // them set. See the addCompositions function for more details.
+ ret = environment->setAll(composedEnvironment);
+ if (!ret.isOK()) {
+ return ret;
+ }
- return Status::OK();
+ // Add the constraints from our options to the result environment
+ ret = addConstraints(options, environment);
+ if (!ret.isOK()) {
+ return ret;
}
-} // namespace optionenvironment
-} // namespace mongo
+ return Status::OK();
+}
+
+} // namespace optionenvironment
+} // namespace mongo