diff --git a/src/util/system.cpp b/src/util/system.cpp --- a/src/util/system.cpp +++ b/src/util/system.cpp @@ -865,11 +865,12 @@ return str.substr(front, end - front + 1); } -static std::vector> -GetConfigOptions(std::istream &stream) { - std::vector> options; +static bool +GetConfigOptions(std::istream &stream, std::string &error, + std::vector> &options) { std::string str, prefix; std::string::size_type pos; + int linenr = 1; while (std::getline(stream, str)) { if ((pos = str.find('#')) != std::string::npos) { str = str.substr(0, pos); @@ -879,23 +880,40 @@ if (!str.empty()) { if (*str.begin() == '[' && *str.rbegin() == ']') { prefix = str.substr(1, str.size() - 2) + '.'; + } else if (*str.begin() == '-') { + error = strprintf( + "parse error on line %i: %s, options in configuration file " + "must be specified without leading -", + linenr, str); + return false; } else if ((pos = str.find('=')) != std::string::npos) { std::string name = prefix + TrimString(str.substr(0, pos), pattern); std::string value = TrimString(str.substr(pos + 1), pattern); options.emplace_back(name, value); + } else { + error = strprintf("parse error on line %i: %s", linenr, str); + if (str.size() >= 2 && str.substr(0, 2) == "no") { + error += strprintf(", if you intended to specify a negated " + "option, use %s=1 instead", + str); + } + return false; } } + ++linenr; } - return options; + return true; } bool ArgsManager::ReadConfigStream(std::istream &stream, std::string &error, bool ignore_invalid_keys) { LOCK(cs_args); - - for (const std::pair &option : - GetConfigOptions(stream)) { + std::vector> options; + if (!GetConfigOptions(stream, error, options)) { + return false; + } + for (const std::pair &option : options) { std::string strKey = std::string("-") + option.first; std::string strValue = option.second; diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py --- a/test/functional/feature_config_args.py +++ b/test/functional/feature_config_args.py @@ -13,8 +13,32 @@ self.setup_clean_chain = True self.num_nodes = 1 + def test_config_file_parser(self): + # Assume node is stopped + + inc_conf_file_path = os.path.join( + self.nodes[0].datadir, 'include.conf') + with open(os.path.join(self.nodes[0].datadir, 'bitcoin.conf'), 'a', encoding='utf-8') as conf: + conf.write('includeconf={}\n'.format(inc_conf_file_path)) + + with open(inc_conf_file_path, 'w', encoding='utf-8') as conf: + conf.write('-dash=1\n') + self.nodes[0].assert_start_raises_init_error( + expected_msg='Error reading configuration file: parse error on line 1: -dash=1, options in configuration file must be specified without leading -') + + with open(inc_conf_file_path, 'w', encoding='utf-8') as conf: + conf.write('nono\n') + self.nodes[0].assert_start_raises_init_error( + expected_msg='Error reading configuration file: parse error on line 1: nono, if you intended to specify a negated option, use nono=1 instead') + + with open(inc_conf_file_path, 'w', encoding='utf-8') as conf: + conf.write('') # clear + def run_test(self): self.stop_node(0) + + self.test_config_file_parser() + # Remove the -datadir argument so it doesn't override the config file self.nodes[0].remove_default_args(["-datadir"])