summaryrefslogtreecommitdiff
path: root/src/mongo/util/options_parser/options_parser.cpp
diff options
context:
space:
mode:
authorShaun Verch <shaun.verch@mongodb.com>2014-05-30 23:56:24 -0400
committerShaun Verch <shaun.verch@mongodb.com>2014-06-02 20:51:22 -0400
commit4add46aa8dd05a5c6d8af2c798eef6e9b5e4164b (patch)
tree4e3131933338b44304c7ae9a8b8c697627f2aba1 /src/mongo/util/options_parser/options_parser.cpp
parentd68aacd27b7abb5981d312e858bf5c5cc3b971b2 (diff)
downloadmongo-4add46aa8dd05a5c6d8af2c798eef6e9b5e4164b.tar.gz
SERVER-14110 Take responsibility for numerical conversions away from boost program options
Diffstat (limited to 'src/mongo/util/options_parser/options_parser.cpp')
-rw-r--r--src/mongo/util/options_parser/options_parser.cpp228
1 files changed, 118 insertions, 110 deletions
diff --git a/src/mongo/util/options_parser/options_parser.cpp b/src/mongo/util/options_parser/options_parser.cpp
index 3689d340d8b..d9368fa9528 100644
--- a/src/mongo/util/options_parser/options_parser.cpp
+++ b/src/mongo/util/options_parser/options_parser.cpp
@@ -75,8 +75,110 @@ namespace optionenvironment {
// 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");
+ }
+ }
+
// Convert a boost::any to a Value. See comments at the beginning of this section.
- Status boostAnyToValue(const boost::any& anyValue, Value* value) {
+ 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));
@@ -84,23 +186,19 @@ namespace optionenvironment {
else if (anyValue.type() == typeid(bool)) {
*value = Value(boost::any_cast<bool>(anyValue));
}
- else if (anyValue.type() == typeid(double)) {
- *value = Value(boost::any_cast<double>(anyValue));
- }
- else if (anyValue.type() == typeid(int)) {
- *value = Value(boost::any_cast<int>(anyValue));
- }
- else if (anyValue.type() == typeid(long)) {
- *value = Value(boost::any_cast<long>(anyValue));
- }
else if (anyValue.type() == typeid(std::string)) {
- *value = Value(boost::any_cast<std::string>(anyValue));
- }
- else if (anyValue.type() == typeid(unsigned long long)) {
- *value = Value(boost::any_cast<unsigned long long>(anyValue));
+ return stringToValue(boost::any_cast<std::string>(anyValue), type, key, value);
}
- else if (anyValue.type() == typeid(unsigned)) {
- *value = Value(boost::any_cast<unsigned>(anyValue));
+ // 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;
@@ -219,100 +317,9 @@ namespace optionenvironment {
return Status::OK();
}
- Status ret = Status::OK();
+ // Our YAML parser reads everything as a string, so we need to parse it ourselves.
std::string stringVal = YAMLNode.Scalar();
- 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 config file: " << 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 in config file: " << 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 in config file: " << 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 in config file: " << 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 in config file: " << 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");
- }
+ return stringToValue(stringVal, type, key, value);
}
// Add all the values in the given variables_map to our environment. See comments at the
@@ -349,7 +356,8 @@ namespace optionenvironment {
if (vm.count(long_name)) {
Value optionValue;
- Status ret = boostAnyToValue(vm[long_name].value(), &optionValue);
+ Status ret = boostAnyToValue(vm[long_name].value(), iterator->_type, long_name,
+ &optionValue);
if (!ret.isOK()) {
return ret;
}