mirror of
https://github.com/Retropex/bitcoin.git
synced 2025-05-12 19:20:42 +02:00
Merge 11082 via rwconf-27+knots
This commit is contained in:
commit
c90495c624
@ -60,6 +60,7 @@ Subdirectory | File(s) | Description
|
|||||||
`./` | `anchors.dat` | Anchor IP address database, created on shutdown and deleted at startup. Anchors are last known outgoing block-relay-only peers that are tried to re-connect to on startup
|
`./` | `anchors.dat` | Anchor IP address database, created on shutdown and deleted at startup. Anchors are last known outgoing block-relay-only peers that are tried to re-connect to on startup
|
||||||
`./` | `banlist.json` | Stores the addresses/subnets of banned nodes.
|
`./` | `banlist.json` | Stores the addresses/subnets of banned nodes.
|
||||||
`./` | `bitcoin.conf` | User-defined [configuration settings](bitcoin-conf.md) for `bitcoind` or `bitcoin-qt`. File is not written to by the software and must be created manually. Path can be specified by `-conf` option
|
`./` | `bitcoin.conf` | User-defined [configuration settings](bitcoin-conf.md) for `bitcoind` or `bitcoin-qt`. File is not written to by the software and must be created manually. Path can be specified by `-conf` option
|
||||||
|
`./` | `bitcoin_rw.conf` | Contains [configuration settings](bitcoin-conf.md) modified by `bitcoind` or `bitcoin-qt`; can be specified by `-confrw` option
|
||||||
`./` | `bitcoind.pid` | Stores the process ID (PID) of `bitcoind` or `bitcoin-qt` while running; created at start and deleted on shutdown; can be specified by `-pid` option
|
`./` | `bitcoind.pid` | Stores the process ID (PID) of `bitcoind` or `bitcoin-qt` while running; created at start and deleted on shutdown; can be specified by `-pid` option
|
||||||
`./` | `debug.log` | Contains debug information and general logging generated by `bitcoind` or `bitcoin-qt`; can be specified by `-debuglogfile` option
|
`./` | `debug.log` | Contains debug information and general logging generated by `bitcoind` or `bitcoin-qt`; can be specified by `-debuglogfile` option
|
||||||
`./` | `fee_estimates.dat` | Stores statistics used to estimate minimum transaction fees required for confirmation
|
`./` | `fee_estimates.dat` | Stores statistics used to estimate minimum transaction fees required for confirmation
|
||||||
|
@ -83,6 +83,7 @@ static void SetupCliArgs(ArgsManager& argsman)
|
|||||||
|
|
||||||
argsman.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
argsman.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
||||||
argsman.AddArg("-conf=<file>", strprintf("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
argsman.AddArg("-conf=<file>", strprintf("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
||||||
|
argsman.AddArg("-confrw=<file>", strprintf("Specify read/write configuration file. Relative paths will be prefixed by the network-specific datadir location. (default: %s)", BITCOIN_RW_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
||||||
argsman.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
argsman.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
||||||
argsman.AddArg("-generate",
|
argsman.AddArg("-generate",
|
||||||
strprintf("Generate blocks, equivalent to RPC getnewaddress followed by RPC generatetoaddress. Optional positional integer "
|
strprintf("Generate blocks, equivalent to RPC getnewaddress followed by RPC generatetoaddress. Optional positional integer "
|
||||||
@ -181,13 +182,6 @@ static int AppInitRPC(int argc, char* argv[])
|
|||||||
tfm::format(std::cerr, "Error reading configuration file: %s\n", error);
|
tfm::format(std::cerr, "Error reading configuration file: %s\n", error);
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
// Check for chain settings (BaseParams() calls are only valid after this clause)
|
|
||||||
try {
|
|
||||||
SelectBaseParams(gArgs.GetChainType());
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
tfm::format(std::cerr, "Error: %s\n", e.what());
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
return CONTINUE_EXECUTION;
|
return CONTINUE_EXECUTION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,15 +29,18 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <fstream>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <unordered_set>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
|
||||||
const char * const BITCOIN_CONF_FILENAME = "bitcoin.conf";
|
const char * const BITCOIN_CONF_FILENAME = "bitcoin.conf";
|
||||||
const char * const BITCOIN_SETTINGS_FILENAME = "settings.json";
|
const char * const BITCOIN_SETTINGS_FILENAME = "settings.json";
|
||||||
|
const char * const BITCOIN_RW_CONF_FILENAME = "bitcoin_rw.conf";
|
||||||
|
|
||||||
ArgsManager gArgs;
|
ArgsManager gArgs;
|
||||||
|
|
||||||
@ -772,6 +775,12 @@ void ArgsManager::SetConfigFilePath(fs::path path)
|
|||||||
m_config_path = path;
|
m_config_path = path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fs::path ArgsManager::GetRWConfigFilePath() const
|
||||||
|
{
|
||||||
|
LOCK(cs_args);
|
||||||
|
return *Assert(m_rwconf_path);
|
||||||
|
}
|
||||||
|
|
||||||
ChainType ArgsManager::GetChainType() const
|
ChainType ArgsManager::GetChainType() const
|
||||||
{
|
{
|
||||||
std::variant<ChainType, std::string> arg = GetChainArg();
|
std::variant<ChainType, std::string> arg = GetChainArg();
|
||||||
@ -863,9 +872,231 @@ void ArgsManager::LogArgs() const
|
|||||||
for (const auto& setting : m_settings.rw_settings) {
|
for (const auto& setting : m_settings.rw_settings) {
|
||||||
LogPrintf("Setting file arg: %s = %s\n", setting.first, setting.second.write());
|
LogPrintf("Setting file arg: %s = %s\n", setting.first, setting.second.write());
|
||||||
}
|
}
|
||||||
|
logArgsPrefix("R/W config file arg:", "", m_settings.rw_config);
|
||||||
logArgsPrefix("Command-line arg:", "", m_settings.command_line_options);
|
logArgsPrefix("Command-line arg:", "", m_settings.command_line_options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// Like std::getline, but includes the EOL character in the result
|
||||||
|
bool getline_with_eol(std::istream& stream, std::string& result)
|
||||||
|
{
|
||||||
|
int current_char;
|
||||||
|
current_char = stream.get();
|
||||||
|
if (current_char == std::char_traits<char>::eof()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
result.clear();
|
||||||
|
result.push_back(char(current_char));
|
||||||
|
while (current_char != '\n') {
|
||||||
|
current_char = stream.get();
|
||||||
|
if (current_char == std::char_traits<char>::eof()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
result.push_back(char(current_char));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * const ModifyRWConfigFile_ws_chars = " \t\r\n";
|
||||||
|
|
||||||
|
void ModifyRWConfigFile_SanityCheck(const std::string& s)
|
||||||
|
{
|
||||||
|
if (s.empty()) {
|
||||||
|
// Dereferencing .begin or .rbegin below is invalid unless the string has at least one character.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char * const newline_chars = "\r\n";
|
||||||
|
static std::string ws_chars(ModifyRWConfigFile_ws_chars);
|
||||||
|
if (s.find_first_of(newline_chars) != std::string::npos) {
|
||||||
|
throw std::invalid_argument("New-line in config name/value");
|
||||||
|
}
|
||||||
|
if (ws_chars.find(*s.begin()) != std::string::npos || ws_chars.find(*s.rbegin()) != std::string::npos) {
|
||||||
|
throw std::invalid_argument("Config name/value has leading/trailing whitespace");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModifyRWConfigFile_WriteRemaining(std::ostream& stream_out, const std::map<std::string, std::string>& settings_to_change, std::set<std::string>& setFound)
|
||||||
|
{
|
||||||
|
for (const auto& setting_pair : settings_to_change) {
|
||||||
|
const std::string& key = setting_pair.first;
|
||||||
|
const std::string& val = setting_pair.second;
|
||||||
|
if (setFound.find(key) != setFound.end()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
setFound.insert(key);
|
||||||
|
ModifyRWConfigFile_SanityCheck(key);
|
||||||
|
ModifyRWConfigFile_SanityCheck(val);
|
||||||
|
stream_out << key << "=" << val << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void ModifyRWConfigStream(std::istream& stream_in, std::ostream& stream_out, const std::map<std::string, std::string>& settings_to_change)
|
||||||
|
{
|
||||||
|
static const char * const ws_chars = ModifyRWConfigFile_ws_chars;
|
||||||
|
std::set<std::string> setFound;
|
||||||
|
std::string s, lineend, linebegin, key;
|
||||||
|
std::string::size_type n, n2;
|
||||||
|
bool inside_group = false, have_eof_nl = true;
|
||||||
|
std::map<std::string, std::string>::const_iterator iterCS;
|
||||||
|
size_t lineno = 0;
|
||||||
|
while (getline_with_eol(stream_in, s)) {
|
||||||
|
++lineno;
|
||||||
|
|
||||||
|
have_eof_nl = (!s.empty()) && (*s.rbegin() == '\n');
|
||||||
|
n = s.find('#');
|
||||||
|
const bool has_comment = (n != std::string::npos);
|
||||||
|
if (!has_comment) {
|
||||||
|
n = s.size();
|
||||||
|
}
|
||||||
|
if (n > 0) {
|
||||||
|
n2 = s.find_last_not_of(ws_chars, n - 1);
|
||||||
|
if (n2 != std::string::npos) {
|
||||||
|
n = n2 + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n2 = s.find_first_not_of(ws_chars);
|
||||||
|
if (n2 == std::string::npos || n2 >= n) {
|
||||||
|
// Blank or comment-only line
|
||||||
|
stream_out << s;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
lineend = s.substr(n);
|
||||||
|
linebegin = s.substr(0, n2);
|
||||||
|
s = s.substr(n2, n - n2);
|
||||||
|
|
||||||
|
// It is impossible for s to be empty here, due to the blank line check above
|
||||||
|
if (*s.begin() == '[' && *s.rbegin() == ']') {
|
||||||
|
// We don't use sections, so we could possibly just write out the rest of the file - but we need to check for unparsable lines, so we just set a flag to ignore settings from here on
|
||||||
|
ModifyRWConfigFile_WriteRemaining(stream_out, settings_to_change, setFound);
|
||||||
|
inside_group = true;
|
||||||
|
key.clear();
|
||||||
|
|
||||||
|
stream_out << linebegin << s << lineend;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
n = s.find('=');
|
||||||
|
if (n == std::string::npos) {
|
||||||
|
// Bad line; this causes boost to throw an exception when parsing, so we comment out the entire file
|
||||||
|
stream_in.seekg(0, std::ios_base::beg);
|
||||||
|
stream_out.seekp(0, std::ios_base::beg);
|
||||||
|
if (!(stream_in.good() && stream_out.good())) {
|
||||||
|
throw std::ios_base::failure("Failed to rewind (to comment out existing file)");
|
||||||
|
}
|
||||||
|
// First, write out all the settings we intend to set
|
||||||
|
setFound.clear();
|
||||||
|
ModifyRWConfigFile_WriteRemaining(stream_out, settings_to_change, setFound);
|
||||||
|
// We then define a category to ensure new settings get added before the invalid stuff
|
||||||
|
stream_out << "[INVALID]\n";
|
||||||
|
// Then, describe the problem in a comment
|
||||||
|
stream_out << "# Error parsing line " << lineno << ": " << s << "\n";
|
||||||
|
// Finally, dump the rest of the file commented out
|
||||||
|
while (getline_with_eol(stream_in, s)) {
|
||||||
|
stream_out << "#" << s;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!inside_group) {
|
||||||
|
// We don't support/use groups, so once we're inside key is always null to avoid setting anything
|
||||||
|
n2 = s.find_last_not_of(ws_chars, n - 1);
|
||||||
|
if (n2 == std::string::npos) {
|
||||||
|
n2 = n - 1;
|
||||||
|
} else {
|
||||||
|
++n2;
|
||||||
|
}
|
||||||
|
key = s.substr(0, n2);
|
||||||
|
}
|
||||||
|
if ((!key.empty()) && (iterCS = settings_to_change.find(key)) != settings_to_change.end() && setFound.find(key) == setFound.end()) {
|
||||||
|
// This is the key we want to change
|
||||||
|
const std::string& val = iterCS->second;
|
||||||
|
setFound.insert(key);
|
||||||
|
ModifyRWConfigFile_SanityCheck(val);
|
||||||
|
if (has_comment) {
|
||||||
|
// Rather than change a commented line, comment it out entirely (the existing comment may relate to the value) and replace it
|
||||||
|
stream_out << key << "=" << val << "\n";
|
||||||
|
linebegin.insert(linebegin.begin(), '#');
|
||||||
|
} else {
|
||||||
|
// Just modify the value in-line otherwise
|
||||||
|
n2 = s.find_first_not_of(ws_chars, n + 1);
|
||||||
|
if (n2 == std::string::npos) {
|
||||||
|
n2 = n + 1;
|
||||||
|
}
|
||||||
|
s = s.substr(0, n2) + val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stream_out << linebegin << s << lineend;
|
||||||
|
}
|
||||||
|
if (setFound.size() < settings_to_change.size()) {
|
||||||
|
if (!have_eof_nl) {
|
||||||
|
stream_out << "\n";
|
||||||
|
}
|
||||||
|
ModifyRWConfigFile_WriteRemaining(stream_out, settings_to_change, setFound);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArgsManager::ModifyRWConfigFile(const std::map<std::string, std::string>& settings_to_change)
|
||||||
|
{
|
||||||
|
LOCK(cs_args);
|
||||||
|
fs::path rwconf_path{GetRWConfigFilePath()};
|
||||||
|
fs::path rwconf_new_path{rwconf_path};
|
||||||
|
rwconf_new_path += ".new";
|
||||||
|
try {
|
||||||
|
fs::remove(rwconf_new_path);
|
||||||
|
std::ofstream streamRWConfigOut(rwconf_new_path, std::ios_base::out | std::ios_base::trunc);
|
||||||
|
if (fs::exists(rwconf_path)) {
|
||||||
|
std::ifstream streamRWConfig(rwconf_path);
|
||||||
|
::ModifyRWConfigStream(streamRWConfig, streamRWConfigOut, settings_to_change);
|
||||||
|
} else {
|
||||||
|
std::istringstream streamIn;
|
||||||
|
::ModifyRWConfigStream(streamIn, streamRWConfigOut, settings_to_change);
|
||||||
|
}
|
||||||
|
} catch (...) {
|
||||||
|
fs::remove(rwconf_new_path);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
if (!RenameOver(rwconf_new_path, rwconf_path)) {
|
||||||
|
fs::remove(rwconf_new_path);
|
||||||
|
throw std::ios_base::failure(strprintf("Failed to replace %s", fs::PathToString(rwconf_new_path)));
|
||||||
|
}
|
||||||
|
for (const auto& setting_change : settings_to_change) {
|
||||||
|
m_settings.rw_config[setting_change.first] = {setting_change.second};
|
||||||
|
}
|
||||||
|
if (!IsArgNegated("-settings")) {
|
||||||
|
// Also save to settings.json for Core (0.21+) compatibility
|
||||||
|
for (const auto& setting_change : settings_to_change) {
|
||||||
|
m_settings.rw_settings[setting_change.first] = setting_change.second;
|
||||||
|
}
|
||||||
|
WriteSettingsFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArgsManager::ModifyRWConfigFile(const std::string& setting_to_change, const std::string& new_value)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> settings_to_change;
|
||||||
|
settings_to_change[setting_to_change] = new_value;
|
||||||
|
ModifyRWConfigFile(settings_to_change);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArgsManager::EraseRWConfigFile()
|
||||||
|
{
|
||||||
|
LOCK(cs_args);
|
||||||
|
fs::path rwconf_path{GetRWConfigFilePath()};
|
||||||
|
if (!fs::exists(rwconf_path)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fs::path rwconf_reset_path = rwconf_path;
|
||||||
|
rwconf_reset_path += ".reset";
|
||||||
|
if (!RenameOver(rwconf_path, rwconf_reset_path)) {
|
||||||
|
if (fs::remove(rwconf_path)) {
|
||||||
|
throw std::ios_base::failure(strprintf("Failed to remove %s", fs::PathToString(rwconf_path)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
namespace common {
|
namespace common {
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
WinCmdLineArgs::WinCmdLineArgs()
|
WinCmdLineArgs::WinCmdLineArgs()
|
||||||
|
@ -25,6 +25,7 @@ class ArgsManager;
|
|||||||
|
|
||||||
extern const char * const BITCOIN_CONF_FILENAME;
|
extern const char * const BITCOIN_CONF_FILENAME;
|
||||||
extern const char * const BITCOIN_SETTINGS_FILENAME;
|
extern const char * const BITCOIN_SETTINGS_FILENAME;
|
||||||
|
extern const char * const BITCOIN_RW_CONF_FILENAME;
|
||||||
|
|
||||||
// Return true if -datadir option points to a valid directory or is not specified.
|
// Return true if -datadir option points to a valid directory or is not specified.
|
||||||
bool CheckDataDirOption(const ArgsManager& args);
|
bool CheckDataDirOption(const ArgsManager& args);
|
||||||
@ -94,6 +95,8 @@ std::optional<int64_t> SettingToInt(const common::SettingsValue&);
|
|||||||
bool SettingToBool(const common::SettingsValue&, bool);
|
bool SettingToBool(const common::SettingsValue&, bool);
|
||||||
std::optional<bool> SettingToBool(const common::SettingsValue&);
|
std::optional<bool> SettingToBool(const common::SettingsValue&);
|
||||||
|
|
||||||
|
void ModifyRWConfigStream(std::istream& stream_in, std::ostream& stream_out, const std::map<std::string, std::string>& settings_to_change);
|
||||||
|
|
||||||
class ArgsManager
|
class ArgsManager
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -139,11 +142,12 @@ protected:
|
|||||||
bool m_accept_any_command GUARDED_BY(cs_args){true};
|
bool m_accept_any_command GUARDED_BY(cs_args){true};
|
||||||
std::list<SectionInfo> m_config_sections GUARDED_BY(cs_args);
|
std::list<SectionInfo> m_config_sections GUARDED_BY(cs_args);
|
||||||
std::optional<fs::path> m_config_path GUARDED_BY(cs_args);
|
std::optional<fs::path> m_config_path GUARDED_BY(cs_args);
|
||||||
|
std::optional<fs::path> m_rwconf_path GUARDED_BY(cs_args);
|
||||||
mutable fs::path m_cached_blocks_path GUARDED_BY(cs_args);
|
mutable fs::path m_cached_blocks_path GUARDED_BY(cs_args);
|
||||||
mutable fs::path m_cached_datadir_path GUARDED_BY(cs_args);
|
mutable fs::path m_cached_datadir_path GUARDED_BY(cs_args);
|
||||||
mutable fs::path m_cached_network_datadir_path GUARDED_BY(cs_args);
|
mutable fs::path m_cached_network_datadir_path GUARDED_BY(cs_args);
|
||||||
|
|
||||||
[[nodiscard]] bool ReadConfigStream(std::istream& stream, const std::string& filepath, std::string& error, bool ignore_invalid_keys = false);
|
[[nodiscard]] bool ReadConfigStream(std::istream& stream, const std::string& filepath, std::string& error, bool ignore_invalid_keys = false, std::map<std::string, std::vector<common::SettingsValue>>* settings_target = nullptr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if settings values from the default section should be used,
|
* Returns true if settings values from the default section should be used,
|
||||||
@ -182,8 +186,13 @@ protected:
|
|||||||
*/
|
*/
|
||||||
fs::path GetConfigFilePath() const;
|
fs::path GetConfigFilePath() const;
|
||||||
void SetConfigFilePath(fs::path);
|
void SetConfigFilePath(fs::path);
|
||||||
|
fs::path GetRWConfigFilePath() const;
|
||||||
[[nodiscard]] bool ReadConfigFiles(std::string& error, bool ignore_invalid_keys = false);
|
[[nodiscard]] bool ReadConfigFiles(std::string& error, bool ignore_invalid_keys = false);
|
||||||
|
|
||||||
|
void ModifyRWConfigFile(const std::map<std::string, std::string>& settings_to_change);
|
||||||
|
void ModifyRWConfigFile(const std::string& setting_to_change, const std::string& new_value);
|
||||||
|
void EraseRWConfigFile();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log warnings for options in m_section_only_args when
|
* Log warnings for options in m_section_only_args when
|
||||||
* they are specified in the default section but not overridden
|
* they are specified in the default section but not overridden
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include <common/args.h>
|
#include <common/args.h>
|
||||||
|
|
||||||
|
#include <chainparamsbase.h>
|
||||||
#include <common/settings.h>
|
#include <common/settings.h>
|
||||||
#include <logging.h>
|
#include <logging.h>
|
||||||
#include <sync.h>
|
#include <sync.h>
|
||||||
@ -90,7 +91,7 @@ bool IsConfSupported(KeyInfo& key, std::string& error) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ArgsManager::ReadConfigStream(std::istream& stream, const std::string& filepath, std::string& error, bool ignore_invalid_keys)
|
bool ArgsManager::ReadConfigStream(std::istream& stream, const std::string& filepath, std::string& error, bool ignore_invalid_keys, std::map<std::string, std::vector<common::SettingsValue>>* settings_target)
|
||||||
{
|
{
|
||||||
LOCK(cs_args);
|
LOCK(cs_args);
|
||||||
std::vector<std::pair<std::string, std::string>> options;
|
std::vector<std::pair<std::string, std::string>> options;
|
||||||
@ -106,6 +107,9 @@ bool ArgsManager::ReadConfigStream(std::istream& stream, const std::string& file
|
|||||||
if (!value) {
|
if (!value) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (settings_target) {
|
||||||
|
(*settings_target)[key.name].push_back(*value);
|
||||||
|
} else
|
||||||
m_settings.ro_config[key.section][key.name].push_back(*value);
|
m_settings.ro_config[key.section][key.name].push_back(*value);
|
||||||
} else {
|
} else {
|
||||||
if (ignore_invalid_keys) {
|
if (ignore_invalid_keys) {
|
||||||
@ -124,6 +128,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
|
|||||||
{
|
{
|
||||||
LOCK(cs_args);
|
LOCK(cs_args);
|
||||||
m_settings.ro_config.clear();
|
m_settings.ro_config.clear();
|
||||||
|
m_settings.rw_config.clear();
|
||||||
m_config_sections.clear();
|
m_config_sections.clear();
|
||||||
m_config_path = AbsPathForConfigVal(*this, GetPathArg("-conf", BITCOIN_CONF_FILENAME), /*net_specific=*/false);
|
m_config_path = AbsPathForConfigVal(*this, GetPathArg("-conf", BITCOIN_CONF_FILENAME), /*net_specific=*/false);
|
||||||
}
|
}
|
||||||
@ -214,12 +219,31 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for chain settings (BaseParams() calls are only valid after this clause)
|
||||||
|
try {
|
||||||
|
SelectBaseParams(gArgs.GetChainType());
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
error = e.what();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// If datadir is changed in .conf file:
|
// If datadir is changed in .conf file:
|
||||||
ClearPathCache();
|
ClearPathCache();
|
||||||
if (!CheckDataDirOption(*this)) {
|
if (!CheckDataDirOption(*this)) {
|
||||||
error = strprintf("specified data directory \"%s\" does not exist.", GetArg("-datadir", ""));
|
error = strprintf("specified data directory \"%s\" does not exist.", GetArg("-datadir", ""));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LOCK(cs_args);
|
||||||
|
m_rwconf_path = AbsPathForConfigVal(*this, GetPathArg("-confrw", BITCOIN_RW_CONF_FILENAME));
|
||||||
|
const auto rwconf_path{GetRWConfigFilePath()};
|
||||||
|
std::ifstream rwconf_stream(rwconf_path);
|
||||||
|
if (rwconf_stream.good()) {
|
||||||
|
if (!ReadConfigStream(rwconf_stream, fs::PathToString(rwconf_path), error, ignore_invalid_keys, &m_settings.rw_config)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ namespace {
|
|||||||
enum class Source {
|
enum class Source {
|
||||||
FORCED,
|
FORCED,
|
||||||
COMMAND_LINE,
|
COMMAND_LINE,
|
||||||
|
CONFIG_FILE_RW,
|
||||||
RW_SETTINGS,
|
RW_SETTINGS,
|
||||||
CONFIG_FILE_NETWORK_SECTION,
|
CONFIG_FILE_NETWORK_SECTION,
|
||||||
CONFIG_FILE_DEFAULT_SECTION
|
CONFIG_FILE_DEFAULT_SECTION
|
||||||
@ -48,6 +49,10 @@ static void MergeSettings(const Settings& settings, const std::string& section,
|
|||||||
if (auto* values = FindKey(settings.command_line_options, name)) {
|
if (auto* values = FindKey(settings.command_line_options, name)) {
|
||||||
fn(SettingsSpan(*values), Source::COMMAND_LINE);
|
fn(SettingsSpan(*values), Source::COMMAND_LINE);
|
||||||
}
|
}
|
||||||
|
// Merge in the rw config file
|
||||||
|
if (auto* values = FindKey(settings.rw_config, name)) {
|
||||||
|
fn(SettingsSpan(*values), Source::CONFIG_FILE_RW);
|
||||||
|
}
|
||||||
// Merge in the read-write settings
|
// Merge in the read-write settings
|
||||||
if (const SettingsValue* value = FindKey(settings.rw_settings, name)) {
|
if (const SettingsValue* value = FindKey(settings.rw_settings, name)) {
|
||||||
fn(SettingsSpan(*value), Source::RW_SETTINGS);
|
fn(SettingsSpan(*value), Source::RW_SETTINGS);
|
||||||
@ -165,7 +170,7 @@ SettingsValue GetSetting(const Settings& settings,
|
|||||||
// the config file the precedence is reversed for all settings except
|
// the config file the precedence is reversed for all settings except
|
||||||
// chain type settings.
|
// chain type settings.
|
||||||
const bool reverse_precedence =
|
const bool reverse_precedence =
|
||||||
(source == Source::CONFIG_FILE_NETWORK_SECTION || source == Source::CONFIG_FILE_DEFAULT_SECTION) &&
|
(source == Source::CONFIG_FILE_RW || source == Source::CONFIG_FILE_NETWORK_SECTION || source == Source::CONFIG_FILE_DEFAULT_SECTION) &&
|
||||||
!get_chain_type;
|
!get_chain_type;
|
||||||
|
|
||||||
// Weird behavior preserved for backwards compatibility: Negated
|
// Weird behavior preserved for backwards compatibility: Negated
|
||||||
@ -217,7 +222,7 @@ std::vector<SettingsValue> GetSettingsList(const Settings& settings,
|
|||||||
// settings will be brought back from the dead (but earlier command
|
// settings will be brought back from the dead (but earlier command
|
||||||
// line settings will still be ignored).
|
// line settings will still be ignored).
|
||||||
const bool add_zombie_config_values =
|
const bool add_zombie_config_values =
|
||||||
(source == Source::CONFIG_FILE_NETWORK_SECTION || source == Source::CONFIG_FILE_DEFAULT_SECTION) &&
|
(source == Source::CONFIG_FILE_RW || source == Source::CONFIG_FILE_NETWORK_SECTION || source == Source::CONFIG_FILE_DEFAULT_SECTION) &&
|
||||||
!prev_negated_empty;
|
!prev_negated_empty;
|
||||||
|
|
||||||
// Ignore settings in default config section if requested.
|
// Ignore settings in default config section if requested.
|
||||||
|
@ -34,6 +34,8 @@ struct Settings {
|
|||||||
std::map<std::string, SettingsValue> forced_settings;
|
std::map<std::string, SettingsValue> forced_settings;
|
||||||
//! Map of setting name to list of command line values.
|
//! Map of setting name to list of command line values.
|
||||||
std::map<std::string, std::vector<SettingsValue>> command_line_options;
|
std::map<std::string, std::vector<SettingsValue>> command_line_options;
|
||||||
|
//! Map of setting name to list of r/w config file values.
|
||||||
|
std::map<std::string, std::vector<SettingsValue>> rw_config;
|
||||||
//! Map of setting name to read-write file setting value.
|
//! Map of setting name to read-write file setting value.
|
||||||
std::map<std::string, SettingsValue> rw_settings;
|
std::map<std::string, SettingsValue> rw_settings;
|
||||||
//! Map of config section name and setting name to list of config file values.
|
//! Map of config section name and setting name to list of config file values.
|
||||||
|
@ -493,6 +493,7 @@ void SetupServerArgs(ArgsManager& argsman)
|
|||||||
argsman.AddArg("-blocksonly", strprintf("Whether to reject transactions from network peers. Disables automatic broadcast and rebroadcast of transactions, unless the source peer has the 'forcerelay' permission. RPC transactions are not affected. (default: %u)", DEFAULT_BLOCKSONLY), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
argsman.AddArg("-blocksonly", strprintf("Whether to reject transactions from network peers. Disables automatic broadcast and rebroadcast of transactions, unless the source peer has the 'forcerelay' permission. RPC transactions are not affected. (default: %u)", DEFAULT_BLOCKSONLY), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
||||||
argsman.AddArg("-coinstatsindex", strprintf("Maintain coinstats index used by the gettxoutsetinfo RPC (default: %u)", DEFAULT_COINSTATSINDEX), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
argsman.AddArg("-coinstatsindex", strprintf("Maintain coinstats index used by the gettxoutsetinfo RPC (default: %u)", DEFAULT_COINSTATSINDEX), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
||||||
argsman.AddArg("-conf=<file>", strprintf("Specify path to read-only configuration file. Relative paths will be prefixed by datadir location (only useable from command line, not configuration file) (default: %s)", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
argsman.AddArg("-conf=<file>", strprintf("Specify path to read-only configuration file. Relative paths will be prefixed by datadir location (only useable from command line, not configuration file) (default: %s)", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
||||||
|
argsman.AddArg("-confrw=<file>", strprintf("Specify read/write configuration file. Relative paths will be prefixed by the network-specific datadir location (default: %s)", BITCOIN_RW_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
||||||
argsman.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
argsman.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
||||||
argsman.AddArg("-dbbatchsize", strprintf("Maximum database write batch size in bytes (default: %u)", nDefaultDbBatchSize), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS);
|
argsman.AddArg("-dbbatchsize", strprintf("Maximum database write batch size in bytes (default: %u)", nDefaultDbBatchSize), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS);
|
||||||
argsman.AddArg("-dbcache=<n>", strprintf("Maximum database cache size <n> MiB (%d to %d, default: %d). In addition, unused mempool memory is shared for this cache (see -maxmempool).", nMinDbCache, nMaxDbCache, nDefaultDbCache), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
argsman.AddArg("-dbcache=<n>", strprintf("Maximum database cache size <n> MiB (%d to %d, default: %d). In addition, unused mempool memory is shared for this cache (see -maxmempool).", nMinDbCache, nMaxDbCache, nDefaultDbCache), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
||||||
|
@ -136,6 +136,15 @@ bool StartLogging(const ArgsManager& args)
|
|||||||
LogPrintf("Config file: %s (not found, skipping)\n", fs::PathToString(config_file_path));
|
LogPrintf("Config file: %s (not found, skipping)\n", fs::PathToString(config_file_path));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fs::path rwconfig_file_path = args.GetRWConfigFilePath();
|
||||||
|
if (fs::exists(rwconfig_file_path)) {
|
||||||
|
LogPrintf("R/W Config file: %s\n", fs::PathToString(rwconfig_file_path));
|
||||||
|
} else if (gArgs.IsArgSet("-confrw")) {
|
||||||
|
InitWarning(strprintf(_("The specified R/W config file %s does not exist"), fs::PathToString(rwconfig_file_path)));
|
||||||
|
} else {
|
||||||
|
LogPrintf("R/W Config file: %s (not found, skipping)\n", fs::PathToString(rwconfig_file_path));
|
||||||
|
}
|
||||||
|
|
||||||
// Log the config arguments to debug.log
|
// Log the config arguments to debug.log
|
||||||
args.LogArgs();
|
args.LogArgs();
|
||||||
|
|
||||||
|
@ -312,6 +312,9 @@ void OptionsModel::Reset()
|
|||||||
QString dataDir = GUIUtil::getDefaultDataDirectory();
|
QString dataDir = GUIUtil::getDefaultDataDirectory();
|
||||||
dataDir = settings.value("strDataDir", dataDir).toString();
|
dataDir = settings.value("strDataDir", dataDir).toString();
|
||||||
|
|
||||||
|
// Remove rw config file
|
||||||
|
gArgs.EraseRWConfigFile();
|
||||||
|
|
||||||
// Remove all entries from our QSettings object
|
// Remove all entries from our QSettings object
|
||||||
settings.clear();
|
settings.clear();
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <qt/test/apptests.h>
|
#include <qt/test/apptests.h>
|
||||||
|
|
||||||
#include <chainparams.h>
|
#include <chainparams.h>
|
||||||
|
#include <common/args.h>
|
||||||
#include <key.h>
|
#include <key.h>
|
||||||
#include <logging.h>
|
#include <logging.h>
|
||||||
#include <qt/bitcoin.h>
|
#include <qt/bitcoin.h>
|
||||||
@ -66,6 +67,14 @@ void AppTests::appTests()
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
{
|
||||||
|
// Need to ensure datadir is setup so resetting settings can delete the non-existent bitcoin_rw.conf
|
||||||
|
std::string error;
|
||||||
|
if (!gArgs.ReadConfigFiles(error, true)) {
|
||||||
|
QWARN("Error in readConfigFiles");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
qRegisterMetaType<interfaces::BlockAndHeaderTipInfo>("interfaces::BlockAndHeaderTipInfo");
|
qRegisterMetaType<interfaces::BlockAndHeaderTipInfo>("interfaces::BlockAndHeaderTipInfo");
|
||||||
m_app.parameterSetup();
|
m_app.parameterSetup();
|
||||||
QVERIFY(m_app.createOptionsModel(/*resetSettings=*/true));
|
QVERIFY(m_app.createOptionsModel(/*resetSettings=*/true));
|
||||||
|
@ -2037,4 +2037,157 @@ BOOST_AUTO_TEST_CASE(clearshrink_test)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::string CheckModifyRWConfigFile(std::map<std::string, std::string>& settings_to_change, const std::string& current_config_file)
|
||||||
|
{
|
||||||
|
std::istringstream stream_in(current_config_file);
|
||||||
|
std::ostringstream stream_out;
|
||||||
|
try {
|
||||||
|
ModifyRWConfigStream(stream_in, stream_out, settings_to_change);
|
||||||
|
} catch (...) {
|
||||||
|
settings_to_change.clear();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
settings_to_change.clear();
|
||||||
|
return stream_out.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(test_ModifyRWConfigFile)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> cs;
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b"), "a=b");
|
||||||
|
|
||||||
|
cs["a"] = "c";
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b"), "a=c");
|
||||||
|
BOOST_CHECK(cs.empty());
|
||||||
|
|
||||||
|
// Multi-char name/value
|
||||||
|
cs["ab"] = "cd";
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "ab=bc"), "ab=cd");
|
||||||
|
|
||||||
|
// Preserved final newline
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\n"), "a=b\n");
|
||||||
|
cs["a"] = "c";
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\n"), "a=c\n");
|
||||||
|
|
||||||
|
// Preserved final tab
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\t"), "a=b\t");
|
||||||
|
cs["a"] = "c";
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\t"), "a=c\t");
|
||||||
|
|
||||||
|
// Preserved final space
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b "), "a=b ");
|
||||||
|
cs["a"] = "c";
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b "), "a=c ");
|
||||||
|
|
||||||
|
// Preserved final crnl
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\r\n"), "a=b\r\n");
|
||||||
|
cs["a"] = "c";
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\r\n"), "a=c\r\n");
|
||||||
|
|
||||||
|
// Empty file
|
||||||
|
cs["a"] = "c";
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, ""), "a=c\n");
|
||||||
|
|
||||||
|
// Ignore k=v in comment
|
||||||
|
cs["a"] = "c";
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "#a=b"), "#a=b\na=c\n");
|
||||||
|
|
||||||
|
// Preserved comment
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\t# c"), "a=b\t# c");
|
||||||
|
|
||||||
|
// Commented out commented value
|
||||||
|
cs["a"] = "c";
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\t# c"), "a=c\n#a=b\t# c");
|
||||||
|
|
||||||
|
// Preserved whitespace before name
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, " \t \ta=b"), " \t \ta=b");
|
||||||
|
cs["a"] = "c";
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, " \t \ta=b"), " \t \ta=c");
|
||||||
|
|
||||||
|
// Preserved whitespace after name
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a \t \t=b"), "a \t \t=b");
|
||||||
|
cs["a"] = "c";
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a \t \t=b"), "a \t \t=c");
|
||||||
|
|
||||||
|
// Preserved whitespace before value
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a= \t \tb"), "a= \t \tb");
|
||||||
|
cs["a"] = "c";
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a= \t \tb"), "a= \t \tc");
|
||||||
|
|
||||||
|
// Modifying value between others
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\nab=bc\nd=e"), "a=b\nab=bc\nd=e");
|
||||||
|
cs["ab"] = "x";
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\nab=bc\nd=e"), "a=b\nab=x\nd=e");
|
||||||
|
|
||||||
|
// Blank key/value
|
||||||
|
cs["ab"] = "";
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\nab=bc\nd=e"), "a=b\nab=\nd=e");
|
||||||
|
cs[""] = "x";
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\nab=bc\nd=e"), "a=b\nab=bc\nd=e\n=x\n");
|
||||||
|
|
||||||
|
// Blank line in source
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\n\nab=bc\n\nd=e"), "a=b\n\nab=bc\n\nd=e");
|
||||||
|
cs["ab"] = "x";
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\n\nab=bc\n\nd=e"), "a=b\n\nab=x\n\nd=e");
|
||||||
|
|
||||||
|
// Duplicate keys in the source
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\nab=bc\nf=x\nab=zx\nd=e"), "a=b\nab=bc\nf=x\nab=zx\nd=e");
|
||||||
|
cs["ab"] = "x";
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\nab=bc\nf=x\nab=zx\nd=e"), "a=b\nab=x\nf=x\nab=zx\nd=e");
|
||||||
|
|
||||||
|
// Comment out entire file if invalid input line
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\nab=bc\nGARBAGE\nd=e"), "[INVALID]\n# Error parsing line 3: GARBAGE\n#a=b\n#ab=bc\n#GARBAGE\n#d=e");
|
||||||
|
cs["ab"] = "x";
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\nab=bc\nGARBAGE\nd=e"), "ab=x\n[INVALID]\n# Error parsing line 3: GARBAGE\n#a=b\n#ab=bc\n#GARBAGE\n#d=e");
|
||||||
|
cs["ab"] = "x";
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\nab=bc\nGARBAGE\nd=e\n"), "ab=x\n[INVALID]\n# Error parsing line 3: GARBAGE\n#a=b\n#ab=bc\n#GARBAGE\n#d=e\n");
|
||||||
|
|
||||||
|
// Whitespace inside values
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\nab=b\t \t c\nd=e"), "a=b\nab=b\t \t c\nd=e");
|
||||||
|
cs["ab"] = "x \t \tx";
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\nab=b\t \t c\nd=e"), "a=b\nab=x \t \tx\nd=e");
|
||||||
|
|
||||||
|
// Newline inside name/value
|
||||||
|
cs["a"] = "x\nx";
|
||||||
|
BOOST_REQUIRE_THROW(CheckModifyRWConfigFile(cs, ""), std::invalid_argument);
|
||||||
|
cs["a"] = "x\rx";
|
||||||
|
BOOST_REQUIRE_THROW(CheckModifyRWConfigFile(cs, ""), std::invalid_argument);
|
||||||
|
cs["a\nb"] = "x";
|
||||||
|
BOOST_REQUIRE_THROW(CheckModifyRWConfigFile(cs, ""), std::invalid_argument);
|
||||||
|
cs["a\rb"] = "x";
|
||||||
|
BOOST_REQUIRE_THROW(CheckModifyRWConfigFile(cs, ""), std::invalid_argument);
|
||||||
|
|
||||||
|
// Whitespace leading/trailing name/value
|
||||||
|
cs["a"] = " x";
|
||||||
|
BOOST_REQUIRE_THROW(CheckModifyRWConfigFile(cs, ""), std::invalid_argument);
|
||||||
|
cs["a"] = "\tx";
|
||||||
|
BOOST_REQUIRE_THROW(CheckModifyRWConfigFile(cs, ""), std::invalid_argument);
|
||||||
|
cs[" a"] = "x";
|
||||||
|
BOOST_REQUIRE_THROW(CheckModifyRWConfigFile(cs, ""), std::invalid_argument);
|
||||||
|
cs["\ta"] = "x";
|
||||||
|
BOOST_REQUIRE_THROW(CheckModifyRWConfigFile(cs, ""), std::invalid_argument);
|
||||||
|
cs["a"] = "x ";
|
||||||
|
BOOST_REQUIRE_THROW(CheckModifyRWConfigFile(cs, ""), std::invalid_argument);
|
||||||
|
cs["a"] = "x\t";
|
||||||
|
BOOST_REQUIRE_THROW(CheckModifyRWConfigFile(cs, ""), std::invalid_argument);
|
||||||
|
cs["a "] = "x";
|
||||||
|
BOOST_REQUIRE_THROW(CheckModifyRWConfigFile(cs, ""), std::invalid_argument);
|
||||||
|
cs["a\t"] = "x";
|
||||||
|
BOOST_REQUIRE_THROW(CheckModifyRWConfigFile(cs, ""), std::invalid_argument);
|
||||||
|
|
||||||
|
// Ignore groups
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\n[group]\nab=bc\nd=e"), "a=b\n[group]\nab=bc\nd=e");
|
||||||
|
cs["ab"] = "x";
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\n[group]\nab=bc\nd=e"), "a=b\nab=x\n[group]\nab=bc\nd=e");
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\n\t [group] \t#c\nab=bc\nd=e"), "a=b\n\t [group] \t#c\nab=bc\nd=e");
|
||||||
|
cs["ab"] = "x";
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\n\t [group] \t#c\nab=bc\nd=e"), "a=b\nab=x\n\t [group] \t#c\nab=bc\nd=e");
|
||||||
|
|
||||||
|
// Comment out entire file if invalid input line, even after a group
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\n[group]\nab=bc\nGARBAGE\nd=e"), "[INVALID]\n# Error parsing line 4: GARBAGE\n#a=b\n#[group]\n#ab=bc\n#GARBAGE\n#d=e");
|
||||||
|
cs["ab"] = "x";
|
||||||
|
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\n[group]\nab=bc\nGARBAGE\nd=e"), "ab=x\n[INVALID]\n# Error parsing line 4: GARBAGE\n#a=b\n#[group]\n#ab=bc\n#GARBAGE\n#d=e");
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
Loading…
Reference in New Issue
Block a user