|
71 | 71 | #endif |
72 | 72 |
|
73 | 73 | #include <boost/algorithm/string/replace.hpp> |
| 74 | +#include <optional> |
74 | 75 | #include <thread> |
75 | 76 | #include <typeinfo> |
76 | 77 | #include <univalue.h> |
@@ -185,59 +186,59 @@ static std::string SettingName(const std::string& arg) |
185 | 186 | } |
186 | 187 |
|
187 | 188 | /** |
188 | | - * Interpret -nofoo as if the user supplied -foo=0. |
| 189 | + * Split up setting key string, removing "section." and "no" prefixes. |
189 | 190 | * |
190 | | - * This method also tracks when the -no form was supplied, and if so, |
191 | | - * checks whether there was a double-negative (-nofoo=0 -> -foo=1). |
192 | | - * |
193 | | - * If there was not a double negative, it removes the "no" from the key |
194 | | - * and returns false. |
195 | | - * |
196 | | - * If there was a double negative, it removes "no" from the key, and |
197 | | - * returns true. |
198 | | - * |
199 | | - * If there was no "no", it returns the string value untouched. |
200 | | - * |
201 | | - * Where an option was negated can be later checked using the |
| 191 | + * @note Where an option was negated can be later checked using the |
202 | 192 | * IsArgNegated() method. One use case for this is to have a way to disable |
203 | 193 | * options that are not normally boolean (e.g. using -nodebuglogfile to request |
204 | 194 | * that debug log output is not sent to any file at all). |
205 | 195 | */ |
206 | | - |
207 | | -static util::SettingsValue InterpretOption(std::string& section, std::string& key, const std::string& value) |
| 196 | +struct KeyInfo { std::string section; bool negated{false}; }; |
| 197 | +KeyInfo InterpretKey(std::string& key) |
208 | 198 | { |
| 199 | + KeyInfo result; |
209 | 200 | // Split section name from key name for keys like "testnet.foo" or "regtest.bar" |
210 | 201 | size_t option_index = key.find('.'); |
211 | 202 | if (option_index != std::string::npos) { |
212 | | - section = key.substr(0, option_index); |
| 203 | + result.section = key.substr(0, option_index); |
213 | 204 | key.erase(0, option_index + 1); |
214 | 205 | } |
215 | 206 | if (key.substr(0, 2) == "no") { |
216 | 207 | key.erase(0, 2); |
217 | | - // Double negatives like -nofoo=0 are supported (but discouraged) |
218 | | - if (!InterpretBool(value)) { |
219 | | - LogPrintf("Warning: parsed potentially confusing double-negative -%s=%s\n", key, value); |
220 | | - return true; |
221 | | - } |
222 | | - return false; |
| 208 | + result.negated = true; |
223 | 209 | } |
224 | | - return value; |
| 210 | + return result; |
225 | 211 | } |
226 | 212 |
|
227 | 213 | /** |
228 | | - * Check settings value validity according to flags. |
| 214 | + * Interpret settings value based on registered flags. |
229 | 215 | * |
230 | | - * TODO: Add more meaningful error checks here in the future |
231 | | - * See "here's how the flags are meant to behave" in |
232 | | - * https://github.com/bitcoin/bitcoin/pull/16097#issuecomment-514627823 |
| 216 | + * @param[in] key name of settings key, only used for error messages |
| 217 | + * @param[in] value string value of setting to be parsed |
| 218 | + * @param[in] negated whether to treat setting as negated |
| 219 | + * @param[in] flags ArgsManager registered argument flags |
| 220 | + * @param[out] error Error description if settings value is not valid |
| 221 | + * |
| 222 | + * @return parsed settings value if it is valid, otherwise nullopt accompanied |
| 223 | + * by a descriptive error string |
233 | 224 | */ |
234 | | -static bool CheckValid(const std::string& key, const util::SettingsValue& val, unsigned int flags, std::string& error) |
235 | | -{ |
236 | | - if (val.isBool() && !(flags & ArgsManager::ALLOW_BOOL)) { |
237 | | - error = strprintf("Negating of -%s is meaningless and therefore forbidden", key); |
| 225 | +static std::optional<util::SettingsValue> InterpretValue(const std::string& key, const std::string& value, |
| 226 | + bool negated, unsigned int flags, std::string& error) |
| 227 | +{ |
| 228 | + // Return negated settings as false values. |
| 229 | + if (negated) { |
| 230 | + if (!(flags & ArgsManager::ALLOW_BOOL)) { |
| 231 | + error = strprintf("Negating of -%s is meaningless and therefore forbidden", key); |
| 232 | + return std::nullopt; |
| 233 | + } |
| 234 | + // Double negatives like -nofoo=0 are supported (but discouraged) |
| 235 | + if (!InterpretBool(value)) { |
| 236 | + LogPrintf("Warning: parsed potentially confusing double-negative -%s=%s\n", key, value); |
| 237 | + return true; |
| 238 | + } |
238 | 239 | return false; |
239 | 240 | } |
240 | | - return true; |
| 241 | + return value; |
241 | 242 | } |
242 | 243 |
|
243 | 244 | namespace { |
@@ -353,21 +354,21 @@ bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::strin |
353 | 354 |
|
354 | 355 | // Transform -foo to foo |
355 | 356 | key.erase(0, 1); |
356 | | - std::string section; |
357 | | - util::SettingsValue value = InterpretOption(section, key, val); |
| 357 | + KeyInfo keyinfo = InterpretKey(key); |
358 | 358 | std::optional<unsigned int> flags = GetArgFlags('-' + key); |
359 | 359 |
|
360 | 360 | // Unknown command line options and command line options with dot |
361 | | - // characters (which are returned from InterpretOption with nonempty |
| 361 | + // characters (which are returned from InterpretKey with nonempty |
362 | 362 | // section strings) are not valid. |
363 | | - if (!flags || !section.empty()) { |
| 363 | + if (!flags || !keyinfo.section.empty()) { |
364 | 364 | error = strprintf("Invalid parameter %s", argv[i]); |
365 | 365 | return false; |
366 | 366 | } |
367 | 367 |
|
368 | | - if (!CheckValid(key, value, *flags, error)) return false; |
| 368 | + std::optional<util::SettingsValue> value = InterpretValue(key, val, keyinfo.negated, *flags, error); |
| 369 | + if (!value) return false; |
369 | 370 |
|
370 | | - m_settings.command_line_options[key].push_back(value); |
| 371 | + m_settings.command_line_options[key].push_back(*value); |
371 | 372 | } |
372 | 373 |
|
373 | 374 | // we do not allow -includeconf from command line, only -noincludeconf |
@@ -550,9 +551,8 @@ bool ArgsManager::ReadSettingsFile(std::vector<std::string>* errors) |
550 | 551 | return false; |
551 | 552 | } |
552 | 553 | for (const auto& setting : m_settings.rw_settings) { |
553 | | - std::string section; |
554 | 554 | std::string key = setting.first; |
555 | | - (void)InterpretOption(section, key, /* value */ {}); // Split setting key into section and argname |
| 555 | + (void)InterpretKey(key); // Split setting key into section and argname |
556 | 556 | if (!GetArgFlags('-' + key)) { |
557 | 557 | LogPrintf("Ignoring unknown rw_settings value %s\n", setting.first); |
558 | 558 | } |
@@ -872,15 +872,14 @@ bool ArgsManager::ReadConfigStream(std::istream& stream, const std::string& file |
872 | 872 | return false; |
873 | 873 | } |
874 | 874 | for (const std::pair<std::string, std::string>& option : options) { |
875 | | - std::string section; |
876 | 875 | std::string key = option.first; |
877 | | - util::SettingsValue value = InterpretOption(section, key, option.second); |
| 876 | + KeyInfo keyinfo = InterpretKey(key); |
878 | 877 | std::optional<unsigned int> flags = GetArgFlags('-' + key); |
879 | 878 | if (flags) { |
880 | | - if (!CheckValid(key, value, *flags, error)) { |
881 | | - return false; |
882 | | - } |
883 | | - m_settings.ro_config[section][key].push_back(value); |
| 879 | + std::optional<util::SettingsValue> value = |
| 880 | + InterpretValue(key, option.second, keyinfo.negated, *flags, error); |
| 881 | + if (!value) return false; |
| 882 | + m_settings.ro_config[keyinfo.section][key].push_back(*value); |
884 | 883 | } else { |
885 | 884 | if (ignore_invalid_keys) { |
886 | 885 | LogPrintf("Ignoring unknown configuration value %s\n", option.first); |
|
0 commit comments