v29.4-roots.2 · Commit 2: [PATCH 02/21] runtime: preserve shared operator controls
src/common/args.cpp
Before: src/common/args.cpp · After: src/common/args.cpp
File metadata
index 833a0b28bd2fef8e724e8c79efbc402ec97c7c2b..4d5e3cf31ba3bd474688c2e9d7071162fcb35540 100644
Download original patch · Release notes
Diff rows 1–395 of 395. Line numbers refer to the original before/after files.
+ Added− RemovedUnmarked lines are context.
Scroll the code area horizontally to read long lines. Before is on the left; after is on the right. Unified view fits narrow screens more easily.
src/common/args.cpp
CHANGED
|
@@ -29,15 +29,18 @@
|
|
| 29 |
#include <cstdint>
|
| 30 |
#include <cstdlib>
|
| 31 |
#include <cstring>
|
|
|
|
| 32 |
#include <map>
|
| 33 |
#include <optional>
|
| 34 |
#include <stdexcept>
|
| 35 |
#include <string>
|
|
|
|
| 36 |
#include <utility>
|
| 37 |
#include <variant>
|
| 38 |
|
| 39 |
const char * const BITCOIN_CONF_FILENAME = "bitcoin.conf";
|
| 40 |
const char * const BITCOIN_SETTINGS_FILENAME = "settings.json";
|
|
|
|
| 41 |
|
| 42 |
ArgsManager gArgs;
|
| 43 |
|
|
@@ -115,7 +118,7 @@ std::optional<common::SettingsValue> InterpretValue(const KeyInfo& key, const st
|
|
| 115 |
}
|
| 116 |
// Double negatives like -nofoo=0 are supported (but discouraged)
|
| 117 |
if (value && !InterpretBool(*value)) {
|
| 118 |
-
LogPrintf("Warning: parsed potentially confusing double-negative -%s=%s\n", key.name, *value);
|
| 119 |
return true;
|
| 120 |
}
|
| 121 |
return false;
|
|
@@ -394,7 +397,7 @@ static void SaveErrors(const std::vector<std::string> errors, std::vector<std::s
|
|
| 394 |
if (error_out) {
|
| 395 |
error_out->emplace_back(error);
|
| 396 |
} else {
|
| 397 |
-
LogPrintf("%s\n", error);
|
| 398 |
}
|
| 399 |
}
|
| 400 |
}
|
|
@@ -416,7 +419,7 @@ bool ArgsManager::ReadSettingsFile(std::vector<std::string>* errors)
|
|
| 416 |
for (const auto& setting : m_settings.rw_settings) {
|
| 417 |
KeyInfo key = InterpretKey(setting.first); // Split setting key into section and argname
|
| 418 |
if (!GetArgFlags('-' + key.name)) {
|
| 419 |
-
LogPrintf("Ignoring unknown rw_settings value %s\n", setting.first);
|
| 420 |
}
|
| 421 |
}
|
| 422 |
return true;
|
|
@@ -504,6 +507,25 @@ int64_t SettingToInt(const common::SettingsValue& value, int64_t nDefault)
|
|
| 504 |
return SettingToInt(value).value_or(nDefault);
|
| 505 |
}
|
| 506 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 507 |
bool ArgsManager::GetBoolArg(const std::string& strArg, bool fDefault) const
|
| 508 |
{
|
| 509 |
return GetBoolArg(strArg).value_or(fDefault);
|
|
@@ -517,9 +539,21 @@ std::optional<bool> ArgsManager::GetBoolArg(const std::string& strArg) const
|
|
| 517 |
|
| 518 |
std::optional<bool> SettingToBool(const common::SettingsValue& value)
|
| 519 |
{
|
| 520 |
-
if (value.isNull()) return std::nullopt;
|
| 521 |
-
if (value.isBool()) return value.get_bool();
|
| 522 |
-
return InterpretBool(value.get_str());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 523 |
}
|
| 524 |
|
| 525 |
bool SettingToBool(const common::SettingsValue& value, bool fDefault)
|
|
@@ -544,9 +578,19 @@ bool ArgsManager::SoftSetBoolArg(const std::string& strArg, bool fValue)
|
|
| 544 |
}
|
| 545 |
|
| 546 |
void ArgsManager::ForceSetArg(const std::string& strArg, const std::string& strValue)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 547 |
{
|
| 548 |
LOCK(cs_args);
|
| 549 |
-
m_settings.forced_settings[SettingName(strArg)] = strValue;
|
| 550 |
}
|
| 551 |
|
| 552 |
void ArgsManager::AddCommand(const std::string& cmd, const std::string& help)
|
|
@@ -582,10 +626,10 @@ void ArgsManager::AddArg(const std::string& name, const std::string& help, unsig
|
|
| 582 |
}
|
| 583 |
}
|
| 584 |
|
| 585 |
-
void ArgsManager::AddHiddenArgs(const std::vector<std::string>& names)
|
| 586 |
{
|
| 587 |
for (const std::string& name : names) {
|
| 588 |
-
AddArg(name, "", ArgsManager::ALLOW_ANY, OptionsCategory::HIDDEN);
|
| 589 |
}
|
| 590 |
}
|
| 591 |
|
|
@@ -659,6 +703,9 @@ std::string ArgsManager::GetHelpMessage() const
|
|
| 659 |
case OptionsCategory::CLI_COMMANDS:
|
| 660 |
usage += HelpMessageGroup("CLI Commands:");
|
| 661 |
break;
|
|
|
|
|
|
|
|
|
|
| 662 |
default:
|
| 663 |
break;
|
| 664 |
}
|
|
@@ -688,8 +735,8 @@ bool HelpRequested(const ArgsManager& args)
|
|
| 688 |
|
| 689 |
void SetupHelpOptions(ArgsManager& args)
|
| 690 |
{
|
| 691 |
-
args.AddArg("-help", "Print this help message and exit (also -h or -?)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
| 692 |
-
args.AddHiddenArgs({"-h", "-?"});
|
| 693 |
}
|
| 694 |
|
| 695 |
static const int screenWidth = 79;
|
|
@@ -709,6 +756,7 @@ std::string HelpMessageOpt(const std::string &option, const std::string &message
|
|
| 709 |
|
| 710 |
const std::vector<std::string> TEST_OPTIONS_DOC{
|
| 711 |
"addrman (use deterministic addrman)",
|
|
|
|
| 712 |
"bip94 (enforce BIP94 consensus rules)",
|
| 713 |
};
|
| 714 |
|
|
@@ -771,6 +819,12 @@ void ArgsManager::SetConfigFilePath(fs::path path)
|
|
| 771 |
m_config_path = path;
|
| 772 |
}
|
| 773 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 774 |
ChainType ArgsManager::GetChainType() const
|
| 775 |
{
|
| 776 |
std::variant<ChainType, std::string> arg = GetChainArg();
|
|
@@ -862,9 +916,234 @@ void ArgsManager::LogArgs() const
|
|
| 862 |
for (const auto& setting : m_settings.rw_settings) {
|
| 863 |
LogPrintf("Setting file arg: %s = %s\n", setting.first, setting.second.write());
|
| 864 |
}
|
|
|
|
| 865 |
logArgsPrefix("Command-line arg:", "", m_settings.command_line_options);
|
| 866 |
}
|
| 867 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 868 |
namespace common {
|
| 869 |
#ifdef WIN32
|
| 870 |
WinCmdLineArgs::WinCmdLineArgs()
|
|
|
|
| 29 |
#include <cstdint>
|
| 30 |
#include <cstdlib>
|
| 31 |
#include <cstring>
|
| 32 |
+
#include <fstream>
|
| 33 |
#include <map>
|
| 34 |
#include <optional>
|
| 35 |
#include <stdexcept>
|
| 36 |
#include <string>
|
| 37 |
+
#include <unordered_set>
|
| 38 |
#include <utility>
|
| 39 |
#include <variant>
|
| 40 |
|
| 41 |
const char * const BITCOIN_CONF_FILENAME = "bitcoin.conf";
|
| 42 |
const char * const BITCOIN_SETTINGS_FILENAME = "settings.json";
|
| 43 |
+
const char * const BITCOIN_RW_CONF_FILENAME = "bitcoin_rw.conf";
|
| 44 |
|
| 45 |
ArgsManager gArgs;
|
| 46 |
|
|
|
|
| 118 |
}
|
| 119 |
// Double negatives like -nofoo=0 are supported (but discouraged)
|
| 120 |
if (value && !InterpretBool(*value)) {
|
| 121 |
+
LogWarning("Parsed potentially confusing double-negative -%s=%s", key.name, *value);
|
| 122 |
return true;
|
| 123 |
}
|
| 124 |
return false;
|
|
|
|
| 397 |
if (error_out) {
|
| 398 |
error_out->emplace_back(error);
|
| 399 |
} else {
|
| 400 |
+
LogWarning("%s", error);
|
| 401 |
}
|
| 402 |
}
|
| 403 |
}
|
|
|
|
| 419 |
for (const auto& setting : m_settings.rw_settings) {
|
| 420 |
KeyInfo key = InterpretKey(setting.first); // Split setting key into section and argname
|
| 421 |
if (!GetArgFlags('-' + key.name)) {
|
| 422 |
+
LogWarning("Ignoring unknown rw_settings value %s", setting.first);
|
| 423 |
}
|
| 424 |
}
|
| 425 |
return true;
|
|
|
|
| 507 |
return SettingToInt(value).value_or(nDefault);
|
| 508 |
}
|
| 509 |
|
| 510 |
+
std::optional<int64_t> ArgsManager::GetFixedPointArg(const std::string& arg, int decimals) const
|
| 511 |
+
{
|
| 512 |
+
const common::SettingsValue value = GetSetting(arg);
|
| 513 |
+
return SettingToFixedPoint(value, decimals);
|
| 514 |
+
}
|
| 515 |
+
|
| 516 |
+
std::optional<int64_t> SettingToFixedPoint(const common::SettingsValue& value, int decimals)
|
| 517 |
+
{
|
| 518 |
+
if (value.isNull()) return std::nullopt;
|
| 519 |
+
if (value.isFalse()) return 0;
|
| 520 |
+
if (value.isTrue()) return 1;
|
| 521 |
+
if (!value.isNum()) value.get_str(); // throws an exception if type is wrong
|
| 522 |
+
int64_t v;
|
| 523 |
+
if (!ParseFixedPoint(value.getValStr(), decimals, &v)) {
|
| 524 |
+
throw std::runtime_error(strprintf("Parse error ('%s')", value.getValStr()));
|
| 525 |
+
}
|
| 526 |
+
return v;
|
| 527 |
+
}
|
| 528 |
+
|
| 529 |
bool ArgsManager::GetBoolArg(const std::string& strArg, bool fDefault) const
|
| 530 |
{
|
| 531 |
return GetBoolArg(strArg).value_or(fDefault);
|
|
|
|
| 539 |
|
| 540 |
std::optional<bool> SettingToBool(const common::SettingsValue& value)
|
| 541 |
{
|
| 542 |
+
switch (value.getType()) {
|
| 543 |
+
case UniValue::VNULL:
|
| 544 |
+
return std::nullopt;
|
| 545 |
+
case UniValue::VBOOL:
|
| 546 |
+
return value.get_bool();
|
| 547 |
+
case UniValue::VOBJ:
|
| 548 |
+
case UniValue::VARR:
|
| 549 |
+
// Throws an exception
|
| 550 |
+
value.get_str();
|
| 551 |
+
assert(false);
|
| 552 |
+
case UniValue::VSTR:
|
| 553 |
+
case UniValue::VNUM:
|
| 554 |
+
return InterpretBool(value.getValStr());
|
| 555 |
+
}
|
| 556 |
+
assert(false);
|
| 557 |
}
|
| 558 |
|
| 559 |
bool SettingToBool(const common::SettingsValue& value, bool fDefault)
|
|
|
|
| 578 |
}
|
| 579 |
|
| 580 |
void ArgsManager::ForceSetArg(const std::string& strArg, const std::string& strValue)
|
| 581 |
+
{
|
| 582 |
+
ForceSetArgV(strArg, common::SettingsValue{strValue});
|
| 583 |
+
}
|
| 584 |
+
|
| 585 |
+
void ArgsManager::ForceSetArg(const std::string& arg, const int64_t value)
|
| 586 |
+
{
|
| 587 |
+
ForceSetArg(arg, util::ToString(value));
|
| 588 |
+
}
|
| 589 |
+
|
| 590 |
+
void ArgsManager::ForceSetArgV(const std::string& arg, const common::SettingsValue& value)
|
| 591 |
{
|
| 592 |
LOCK(cs_args);
|
| 593 |
+
m_settings.forced_settings[SettingName(arg)] = value;
|
| 594 |
}
|
| 595 |
|
| 596 |
void ArgsManager::AddCommand(const std::string& cmd, const std::string& help)
|
|
|
|
| 626 |
}
|
| 627 |
}
|
| 628 |
|
| 629 |
+
void ArgsManager::AddHiddenArgs(const std::vector<std::string>& names, unsigned int flags)
|
| 630 |
{
|
| 631 |
for (const std::string& name : names) {
|
| 632 |
+
AddArg(name, "", flags, OptionsCategory::HIDDEN);
|
| 633 |
}
|
| 634 |
}
|
| 635 |
|
|
|
|
| 703 |
case OptionsCategory::CLI_COMMANDS:
|
| 704 |
usage += HelpMessageGroup("CLI Commands:");
|
| 705 |
break;
|
| 706 |
+
case OptionsCategory::STATS:
|
| 707 |
+
usage += HelpMessageGroup("Statistic options:");
|
| 708 |
+
break;
|
| 709 |
default:
|
| 710 |
break;
|
| 711 |
}
|
|
|
|
| 735 |
|
| 736 |
void SetupHelpOptions(ArgsManager& args)
|
| 737 |
{
|
| 738 |
+
args.AddArg("-help", "Print this help message and exit (also -h or -?)", ArgsManager::DISALLOW_NEGATION, OptionsCategory::OPTIONS);
|
| 739 |
+
args.AddHiddenArgs({"-h", "-?"}, ArgsManager::DISALLOW_NEGATION);
|
| 740 |
}
|
| 741 |
|
| 742 |
static const int screenWidth = 79;
|
|
|
|
| 756 |
|
| 757 |
const std::vector<std::string> TEST_OPTIONS_DOC{
|
| 758 |
"addrman (use deterministic addrman)",
|
| 759 |
+
"reindex_after_failure_noninteractive_yes (When asked for a reindex after failure interactively, simulate as-if answered with 'yes')",
|
| 760 |
"bip94 (enforce BIP94 consensus rules)",
|
| 761 |
};
|
| 762 |
|
|
|
|
| 819 |
m_config_path = path;
|
| 820 |
}
|
| 821 |
|
| 822 |
+
fs::path ArgsManager::GetRWConfigFilePath() const
|
| 823 |
+
{
|
| 824 |
+
LOCK(cs_args);
|
| 825 |
+
return *Assert(m_rwconf_path);
|
| 826 |
+
}
|
| 827 |
+
|
| 828 |
ChainType ArgsManager::GetChainType() const
|
| 829 |
{
|
| 830 |
std::variant<ChainType, std::string> arg = GetChainArg();
|
|
|
|
| 916 |
for (const auto& setting : m_settings.rw_settings) {
|
| 917 |
LogPrintf("Setting file arg: %s = %s\n", setting.first, setting.second.write());
|
| 918 |
}
|
| 919 |
+
logArgsPrefix("R/W config file arg:", "", m_settings.rw_config);
|
| 920 |
logArgsPrefix("Command-line arg:", "", m_settings.command_line_options);
|
| 921 |
}
|
| 922 |
|
| 923 |
+
namespace {
|
| 924 |
+
|
| 925 |
+
// Like std::getline, but includes the EOL character in the result
|
| 926 |
+
bool getline_with_eol(std::istream& stream, std::string& result)
|
| 927 |
+
{
|
| 928 |
+
int current_char;
|
| 929 |
+
current_char = stream.get();
|
| 930 |
+
if (current_char == std::char_traits<char>::eof()) {
|
| 931 |
+
return false;
|
| 932 |
+
}
|
| 933 |
+
result.clear();
|
| 934 |
+
result.push_back(char(current_char));
|
| 935 |
+
while (current_char != '\n') {
|
| 936 |
+
current_char = stream.get();
|
| 937 |
+
if (current_char == std::char_traits<char>::eof()) {
|
| 938 |
+
break;
|
| 939 |
+
}
|
| 940 |
+
result.push_back(char(current_char));
|
| 941 |
+
}
|
| 942 |
+
return true;
|
| 943 |
+
}
|
| 944 |
+
|
| 945 |
+
const char * const ModifyRWConfigFile_ws_chars = " \t\r\n";
|
| 946 |
+
|
| 947 |
+
void ModifyRWConfigFile_SanityCheck(const std::string& s)
|
| 948 |
+
{
|
| 949 |
+
if (s.empty()) {
|
| 950 |
+
// Dereferencing .begin or .rbegin below is invalid unless the string has at least one character.
|
| 951 |
+
return;
|
| 952 |
+
}
|
| 953 |
+
|
| 954 |
+
static const char * const newline_chars = "\r\n";
|
| 955 |
+
static std::string ws_chars(ModifyRWConfigFile_ws_chars);
|
| 956 |
+
if (s.find_first_of(newline_chars) != std::string::npos) {
|
| 957 |
+
throw std::invalid_argument("New-line in config name/value");
|
| 958 |
+
}
|
| 959 |
+
if (ws_chars.find(*s.begin()) != std::string::npos || ws_chars.find(*s.rbegin()) != std::string::npos) {
|
| 960 |
+
throw std::invalid_argument("Config name/value has leading/trailing whitespace");
|
| 961 |
+
}
|
| 962 |
+
}
|
| 963 |
+
|
| 964 |
+
void ModifyRWConfigFile_WriteRemaining(std::ostream& stream_out, const std::map<std::string, std::string>& settings_to_change, std::set<std::string>& setFound)
|
| 965 |
+
{
|
| 966 |
+
for (const auto& setting_pair : settings_to_change) {
|
| 967 |
+
const std::string& key = setting_pair.first;
|
| 968 |
+
const std::string& val = setting_pair.second;
|
| 969 |
+
if (setFound.find(key) != setFound.end()) {
|
| 970 |
+
continue;
|
| 971 |
+
}
|
| 972 |
+
setFound.insert(key);
|
| 973 |
+
ModifyRWConfigFile_SanityCheck(key);
|
| 974 |
+
ModifyRWConfigFile_SanityCheck(val);
|
| 975 |
+
stream_out << key << "=" << val << "\n";
|
| 976 |
+
}
|
| 977 |
+
}
|
| 978 |
+
} // namespace
|
| 979 |
+
|
| 980 |
+
void ModifyRWConfigStream(std::istream& stream_in, std::ostream& stream_out, const std::map<std::string, std::string>& settings_to_change)
|
| 981 |
+
{
|
| 982 |
+
static const char * const ws_chars = ModifyRWConfigFile_ws_chars;
|
| 983 |
+
std::set<std::string> setFound;
|
| 984 |
+
std::string s, lineend, linebegin, key;
|
| 985 |
+
std::string::size_type n, n2;
|
| 986 |
+
bool inside_group = false, have_eof_nl = true;
|
| 987 |
+
std::map<std::string, std::string>::const_iterator iterCS;
|
| 988 |
+
size_t lineno = 0;
|
| 989 |
+
while (getline_with_eol(stream_in, s)) {
|
| 990 |
+
++lineno;
|
| 991 |
+
|
| 992 |
+
have_eof_nl = (!s.empty()) && (*s.rbegin() == '\n');
|
| 993 |
+
n = s.find('#');
|
| 994 |
+
const bool has_comment = (n != std::string::npos);
|
| 995 |
+
if (!has_comment) {
|
| 996 |
+
n = s.size();
|
| 997 |
+
}
|
| 998 |
+
if (n > 0) {
|
| 999 |
+
n2 = s.find_last_not_of(ws_chars, n - 1);
|
| 1000 |
+
if (n2 != std::string::npos) {
|
| 1001 |
+
n = n2 + 1;
|
| 1002 |
+
}
|
| 1003 |
+
}
|
| 1004 |
+
n2 = s.find_first_not_of(ws_chars);
|
| 1005 |
+
if (n2 == std::string::npos || n2 >= n) {
|
| 1006 |
+
// Blank or comment-only line
|
| 1007 |
+
stream_out << s;
|
| 1008 |
+
continue;
|
| 1009 |
+
}
|
| 1010 |
+
lineend = s.substr(n);
|
| 1011 |
+
linebegin = s.substr(0, n2);
|
| 1012 |
+
s = s.substr(n2, n - n2);
|
| 1013 |
+
|
| 1014 |
+
// It is impossible for s to be empty here, due to the blank line check above
|
| 1015 |
+
if (*s.begin() == '[' && *s.rbegin() == ']') {
|
| 1016 |
+
// 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
|
| 1017 |
+
ModifyRWConfigFile_WriteRemaining(stream_out, settings_to_change, setFound);
|
| 1018 |
+
inside_group = true;
|
| 1019 |
+
key.clear();
|
| 1020 |
+
|
| 1021 |
+
stream_out << linebegin << s << lineend;
|
| 1022 |
+
continue;
|
| 1023 |
+
}
|
| 1024 |
+
|
| 1025 |
+
n = s.find('=');
|
| 1026 |
+
if (n == std::string::npos) {
|
| 1027 |
+
// Bad line; this causes boost to throw an exception when parsing, so we comment out the entire file
|
| 1028 |
+
stream_in.seekg(0, std::ios_base::beg);
|
| 1029 |
+
stream_out.seekp(0, std::ios_base::beg);
|
| 1030 |
+
if (!(stream_in.good() && stream_out.good())) {
|
| 1031 |
+
throw std::ios_base::failure("Failed to rewind (to comment out existing file)");
|
| 1032 |
+
}
|
| 1033 |
+
// First, write out all the settings we intend to set
|
| 1034 |
+
setFound.clear();
|
| 1035 |
+
ModifyRWConfigFile_WriteRemaining(stream_out, settings_to_change, setFound);
|
| 1036 |
+
// We then define a category to ensure new settings get added before the invalid stuff
|
| 1037 |
+
stream_out << "[INVALID]\n";
|
| 1038 |
+
// Then, describe the problem in a comment
|
| 1039 |
+
stream_out << "# Error parsing line " << lineno << ": " << s << "\n";
|
| 1040 |
+
// Finally, dump the rest of the file commented out
|
| 1041 |
+
while (getline_with_eol(stream_in, s)) {
|
| 1042 |
+
stream_out << "#" << s;
|
| 1043 |
+
}
|
| 1044 |
+
return;
|
| 1045 |
+
}
|
| 1046 |
+
|
| 1047 |
+
if (!inside_group) {
|
| 1048 |
+
// We don't support/use groups, so once we're inside key is always null to avoid setting anything
|
| 1049 |
+
n2 = s.find_last_not_of(ws_chars, n - 1);
|
| 1050 |
+
if (n2 == std::string::npos) {
|
| 1051 |
+
n2 = n - 1;
|
| 1052 |
+
} else {
|
| 1053 |
+
++n2;
|
| 1054 |
+
}
|
| 1055 |
+
key = s.substr(0, n2);
|
| 1056 |
+
}
|
| 1057 |
+
if ((!key.empty()) && (iterCS = settings_to_change.find(key)) != settings_to_change.end() && setFound.find(key) == setFound.end()) {
|
| 1058 |
+
// This is the key we want to change
|
| 1059 |
+
const std::string& val = iterCS->second;
|
| 1060 |
+
setFound.insert(key);
|
| 1061 |
+
ModifyRWConfigFile_SanityCheck(val);
|
| 1062 |
+
if (has_comment) {
|
| 1063 |
+
// Rather than change a commented line, comment it out entirely (the existing comment may relate to the value) and replace it
|
| 1064 |
+
stream_out << key << "=" << val << "\n";
|
| 1065 |
+
linebegin.insert(linebegin.begin(), '#');
|
| 1066 |
+
} else {
|
| 1067 |
+
// Just modify the value in-line otherwise
|
| 1068 |
+
n2 = s.find_first_not_of(ws_chars, n + 1);
|
| 1069 |
+
if (n2 == std::string::npos) {
|
| 1070 |
+
n2 = n + 1;
|
| 1071 |
+
}
|
| 1072 |
+
s = s.substr(0, n2) + val;
|
| 1073 |
+
}
|
| 1074 |
+
}
|
| 1075 |
+
stream_out << linebegin << s << lineend;
|
| 1076 |
+
}
|
| 1077 |
+
if (setFound.size() < settings_to_change.size()) {
|
| 1078 |
+
if (!have_eof_nl) {
|
| 1079 |
+
stream_out << "\n";
|
| 1080 |
+
}
|
| 1081 |
+
ModifyRWConfigFile_WriteRemaining(stream_out, settings_to_change, setFound);
|
| 1082 |
+
}
|
| 1083 |
+
}
|
| 1084 |
+
|
| 1085 |
+
void ArgsManager::ModifyRWConfigFile(const std::map<std::string, std::string>& settings_to_change, const bool also_settings_json)
|
| 1086 |
+
{
|
| 1087 |
+
LOCK(cs_args);
|
| 1088 |
+
fs::path rwconf_path{GetRWConfigFilePath()};
|
| 1089 |
+
fs::path rwconf_new_path{rwconf_path};
|
| 1090 |
+
rwconf_new_path += ".new";
|
| 1091 |
+
try {
|
| 1092 |
+
fs::remove(rwconf_new_path);
|
| 1093 |
+
std::ofstream streamRWConfigOut(rwconf_new_path, std::ios_base::out | std::ios_base::trunc);
|
| 1094 |
+
if (fs::exists(rwconf_path)) {
|
| 1095 |
+
std::ifstream streamRWConfig(rwconf_path);
|
| 1096 |
+
::ModifyRWConfigStream(streamRWConfig, streamRWConfigOut, settings_to_change);
|
| 1097 |
+
} else {
|
| 1098 |
+
std::istringstream streamIn;
|
| 1099 |
+
::ModifyRWConfigStream(streamIn, streamRWConfigOut, settings_to_change);
|
| 1100 |
+
}
|
| 1101 |
+
} catch (...) {
|
| 1102 |
+
fs::remove(rwconf_new_path);
|
| 1103 |
+
throw;
|
| 1104 |
+
}
|
| 1105 |
+
if (!RenameOver(rwconf_new_path, rwconf_path)) {
|
| 1106 |
+
fs::remove(rwconf_new_path);
|
| 1107 |
+
throw std::ios_base::failure(strprintf("Failed to replace %s", fs::PathToString(rwconf_new_path)));
|
| 1108 |
+
}
|
| 1109 |
+
for (const auto& setting_change : settings_to_change) {
|
| 1110 |
+
m_settings.rw_config[setting_change.first] = {setting_change.second};
|
| 1111 |
+
}
|
| 1112 |
+
if (also_settings_json && !IsArgNegated("-settings")) {
|
| 1113 |
+
// Also save to settings.json for Core (0.21+) compatibility
|
| 1114 |
+
for (const auto& setting_change : settings_to_change) {
|
| 1115 |
+
m_settings.rw_settings[setting_change.first] = setting_change.second;
|
| 1116 |
+
}
|
| 1117 |
+
WriteSettingsFile();
|
| 1118 |
+
}
|
| 1119 |
+
if (settings_to_change.count("prune")) {
|
| 1120 |
+
m_rwconf_had_prune_option = true;
|
| 1121 |
+
}
|
| 1122 |
+
}
|
| 1123 |
+
|
| 1124 |
+
void ArgsManager::ModifyRWConfigFile(const std::string& setting_to_change, const std::string& new_value, const bool also_settings_json)
|
| 1125 |
+
{
|
| 1126 |
+
std::map<std::string, std::string> settings_to_change;
|
| 1127 |
+
settings_to_change[setting_to_change] = new_value;
|
| 1128 |
+
ModifyRWConfigFile(settings_to_change, also_settings_json);
|
| 1129 |
+
}
|
| 1130 |
+
|
| 1131 |
+
void ArgsManager::EraseRWConfigFile()
|
| 1132 |
+
{
|
| 1133 |
+
LOCK(cs_args);
|
| 1134 |
+
fs::path rwconf_path{GetRWConfigFilePath()};
|
| 1135 |
+
if (!fs::exists(rwconf_path)) {
|
| 1136 |
+
return;
|
| 1137 |
+
}
|
| 1138 |
+
fs::path rwconf_reset_path = rwconf_path;
|
| 1139 |
+
rwconf_reset_path += ".reset";
|
| 1140 |
+
if (!RenameOver(rwconf_path, rwconf_reset_path)) {
|
| 1141 |
+
if (fs::remove(rwconf_path)) {
|
| 1142 |
+
throw std::ios_base::failure(strprintf("Failed to remove %s", fs::PathToString(rwconf_path)));
|
| 1143 |
+
}
|
| 1144 |
+
}
|
| 1145 |
+
}
|
| 1146 |
+
|
| 1147 |
namespace common {
|
| 1148 |
#ifdef WIN32
|
| 1149 |
WinCmdLineArgs::WinCmdLineArgs()
|