Changeset View
Standalone View
src/util.cpp
Show First 20 Lines • Show All 162 Lines • ▼ Show 20 Lines | |||||
*/ | */ | ||||
static bool InterpretBool(const std::string &strValue) { | static bool InterpretBool(const std::string &strValue) { | ||||
if (strValue.empty()) { | if (strValue.empty()) { | ||||
return true; | return true; | ||||
} | } | ||||
return (atoi(strValue) != 0); | return (atoi(strValue) != 0); | ||||
} | } | ||||
/** Internal helper functions for ArgsManager */ | |||||
class ArgsManagerHelper { | |||||
public: | |||||
typedef std::map<std::string, std::vector<std::string>> MapArgs; | |||||
/** Find arguments in a map and add them to a vector */ | |||||
static inline void AddArgs(std::vector<std::string> &res, | |||||
const MapArgs &map_args, | |||||
const std::string &arg) { | |||||
auto it = map_args.find(arg); | |||||
if (it != map_args.end()) { | |||||
res.insert(res.end(), it->second.begin(), it->second.end()); | |||||
} | |||||
} | |||||
/** Return true/false if an argument is set in a map, and also | |||||
* return the first (or last) of the possibly multiple values it has | |||||
*/ | |||||
static inline std::pair<bool, std::string> | |||||
GetArgHelper(const MapArgs &map_args, const std::string &arg, | |||||
bool getLast = false) { | |||||
auto it = map_args.find(arg); | |||||
if (it == map_args.end() || it->second.empty()) { | |||||
return std::make_pair(false, std::string()); | |||||
} | |||||
if (getLast) { | |||||
return std::make_pair(true, it->second.back()); | |||||
} else { | |||||
return std::make_pair(true, it->second.front()); | |||||
} | |||||
} | |||||
/* Get the string value of an argument, returning a pair of a boolean | |||||
* indicating the argument was found, and the value for the argument | |||||
* if it was found (or the empty string if not found). | |||||
*/ | |||||
static inline std::pair<bool, std::string> GetArg(const ArgsManager &am, | |||||
const std::string &arg) { | |||||
LOCK(am.cs_args); | |||||
std::pair<bool, std::string> found_result(false, std::string()); | |||||
// We pass "true" to GetArgHelper in order to return the last | |||||
// argument value seen from the command line (so "bitcoind -foo=bar | |||||
// -foo=baz" gives GetArg(am,"foo")=={true,"baz"} | |||||
found_result = GetArgHelper(am.m_override_args, arg, true); | |||||
if (found_result.first) { | |||||
return found_result; | |||||
} | |||||
// But in contrast we return the first argument seen in a config file, | |||||
jasonbcox: I don't know why they did this, but the disparity between config and CLI argument reading seems… | |||||
FabienAuthorUnsubmitted Done Inline ActionsIt actually makes sense for the command-line argument to rely on the last one (every time the argument is encountered, the previous one get overridden). Up to my knowledge this is the behavior of every command line software. Fabien: It actually makes sense for the command-line argument to rely on the last one (every time the… | |||||
jasonbcoxUnsubmitted Not Done Inline ActionsYa, the first part makes sense. As for why config should be treated the opposite puzzles me. It may have to do with the not-yet-backported feature that allows specifying different network configs in the same file. But I fail to see why first-seen is somehow better/easier to implement than last-seen even in that case. jasonbcox: Ya, the first part makes sense. As for why config should be treated the opposite puzzles me. | |||||
FabienAuthorUnsubmitted Done Inline Actions
This is in the same PR, see D2434 Fabien: > It may have to do with the not-yet-backported feature that allows specifying different… | |||||
// so "foo=bar \n foo=baz" in the config file gives | |||||
// GetArg(am,"foo")={true,"bar"} | |||||
found_result = GetArgHelper(am.m_config_args, arg); | |||||
if (found_result.first) { | |||||
return found_result; | |||||
} | |||||
return found_result; | |||||
} | |||||
}; | |||||
/** | /** | ||||
* Interpret -nofoo as if the user supplied -foo=0. | * Interpret -nofoo as if the user supplied -foo=0. | ||||
* | * | ||||
* This method also tracks when the -no form was supplied, and treats "-foo" as | * This method also tracks when the -no form was supplied, and treats "-foo" as | ||||
* a negated option when this happens. This can be later checked using the | * a negated option when this happens. This can be later checked using the | ||||
* IsArgNegated() method. One use case for this is to have a way to disable | * IsArgNegated() method. One use case for this is to have a way to disable | ||||
* options that are not normally boolean (e.g. using -nodebuglogfile to request | * options that are not normally boolean (e.g. using -nodebuglogfile to request | ||||
* that debug log output is not sent to any file at all). | * that debug log output is not sent to any file at all). | ||||
Show All 14 Lines | if (key.substr(0, 3) == "-no") { | ||||
// In an invocation like "bitcoind -nofoo -foo" we want to unmark -foo | // In an invocation like "bitcoind -nofoo -foo" we want to unmark -foo | ||||
// as negated when we see the second option. | // as negated when we see the second option. | ||||
m_negated_args.erase(key); | m_negated_args.erase(key); | ||||
} | } | ||||
} | } | ||||
void ArgsManager::ParseParameters(int argc, const char *const argv[]) { | void ArgsManager::ParseParameters(int argc, const char *const argv[]) { | ||||
LOCK(cs_args); | LOCK(cs_args); | ||||
mapArgs.clear(); | m_override_args.clear(); | ||||
mapMultiArgs.clear(); | |||||
m_negated_args.clear(); | m_negated_args.clear(); | ||||
for (int i = 1; i < argc; i++) { | for (int i = 1; i < argc; i++) { | ||||
std::string key(argv[i]); | std::string key(argv[i]); | ||||
std::string val; | std::string val; | ||||
size_t is_index = key.find('='); | size_t is_index = key.find('='); | ||||
if (is_index != std::string::npos) { | if (is_index != std::string::npos) { | ||||
val = key.substr(is_index + 1); | val = key.substr(is_index + 1); | ||||
Show All 13 Lines | #endif | ||||
// Transform --foo to -foo | // Transform --foo to -foo | ||||
if (key.length() > 1 && key[1] == '-') { | if (key.length() > 1 && key[1] == '-') { | ||||
key.erase(0, 1); | key.erase(0, 1); | ||||
} | } | ||||
// Transform -nofoo to -foo=0 | // Transform -nofoo to -foo=0 | ||||
InterpretNegatedOption(key, val); | InterpretNegatedOption(key, val); | ||||
mapArgs[key] = val; | m_override_args[key].push_back(val); | ||||
mapMultiArgs[key].push_back(val); | |||||
} | } | ||||
} | } | ||||
std::vector<std::string> ArgsManager::GetArgs(const std::string &strArg) const { | std::vector<std::string> ArgsManager::GetArgs(const std::string &strArg) const { | ||||
std::vector<std::string> result = {}; | |||||
LOCK(cs_args); | LOCK(cs_args); | ||||
auto it = mapMultiArgs.find(strArg); | ArgsManagerHelper::AddArgs(result, m_override_args, strArg); | ||||
if (it != mapMultiArgs.end()) { | ArgsManagerHelper::AddArgs(result, m_config_args, strArg); | ||||
return it->second; | return result; | ||||
} | |||||
return {}; | |||||
} | } | ||||
bool ArgsManager::IsArgSet(const std::string &strArg) const { | bool ArgsManager::IsArgSet(const std::string &strArg) const { | ||||
LOCK(cs_args); | return ArgsManagerHelper::GetArg(*this, strArg).first; | ||||
return mapArgs.count(strArg); | |||||
} | } | ||||
bool ArgsManager::IsArgNegated(const std::string &strArg) const { | bool ArgsManager::IsArgNegated(const std::string &strArg) const { | ||||
LOCK(cs_args); | LOCK(cs_args); | ||||
return m_negated_args.find(strArg) != m_negated_args.end(); | return m_negated_args.find(strArg) != m_negated_args.end(); | ||||
} | } | ||||
std::string ArgsManager::GetArg(const std::string &strArg, | std::string ArgsManager::GetArg(const std::string &strArg, | ||||
const std::string &strDefault) const { | const std::string &strDefault) const { | ||||
LOCK(cs_args); | std::pair<bool, std::string> found_res = | ||||
auto it = mapArgs.find(strArg); | ArgsManagerHelper::GetArg(*this, strArg); | ||||
if (it != mapArgs.end()) { | if (found_res.first) { | ||||
return it->second; | return found_res.second; | ||||
} | } | ||||
return strDefault; | return strDefault; | ||||
} | } | ||||
int64_t ArgsManager::GetArg(const std::string &strArg, int64_t nDefault) const { | int64_t ArgsManager::GetArg(const std::string &strArg, int64_t nDefault) const { | ||||
LOCK(cs_args); | std::pair<bool, std::string> found_res = | ||||
auto it = mapArgs.find(strArg); | ArgsManagerHelper::GetArg(*this, strArg); | ||||
if (it != mapArgs.end()) { | if (found_res.first) { | ||||
return atoi64(it->second); | return atoi64(found_res.second); | ||||
} | } | ||||
return nDefault; | return nDefault; | ||||
} | } | ||||
bool ArgsManager::GetBoolArg(const std::string &strArg, bool fDefault) const { | bool ArgsManager::GetBoolArg(const std::string &strArg, bool fDefault) const { | ||||
LOCK(cs_args); | std::pair<bool, std::string> found_res = | ||||
auto it = mapArgs.find(strArg); | ArgsManagerHelper::GetArg(*this, strArg); | ||||
if (it != mapArgs.end()) { | if (found_res.first) { | ||||
return InterpretBool(it->second); | return InterpretBool(found_res.second); | ||||
} | } | ||||
return fDefault; | return fDefault; | ||||
} | } | ||||
bool ArgsManager::SoftSetArg(const std::string &strArg, | bool ArgsManager::SoftSetArg(const std::string &strArg, | ||||
const std::string &strValue) { | const std::string &strValue) { | ||||
LOCK(cs_args); | LOCK(cs_args); | ||||
if (IsArgSet(strArg)) { | if (IsArgSet(strArg)) { | ||||
Show All 9 Lines | bool ArgsManager::SoftSetBoolArg(const std::string &strArg, bool fValue) { | ||||
} else { | } else { | ||||
return SoftSetArg(strArg, std::string("0")); | return SoftSetArg(strArg, std::string("0")); | ||||
} | } | ||||
} | } | ||||
void ArgsManager::ForceSetArg(const std::string &strArg, | void ArgsManager::ForceSetArg(const std::string &strArg, | ||||
const std::string &strValue) { | const std::string &strValue) { | ||||
LOCK(cs_args); | LOCK(cs_args); | ||||
mapArgs[strArg] = strValue; | m_override_args[strArg] = {strValue}; | ||||
mapMultiArgs[strArg] = {strValue}; | |||||
} | } | ||||
/** | /** | ||||
* This function is only used for testing purpose so | * This function is only used for testing purpose so | ||||
* so we should not worry about element uniqueness and | * so we should not worry about element uniqueness and | ||||
* integrity of mapMultiArgs data structure | * integrity of mapMultiArgs data structure | ||||
*/ | */ | ||||
void ArgsManager::ForceSetMultiArg(const std::string &strArg, | void ArgsManager::ForceSetMultiArg(const std::string &strArg, | ||||
const std::string &strValue) { | const std::string &strValue) { | ||||
LOCK(cs_args); | LOCK(cs_args); | ||||
if (mapArgs.count(strArg) == 0) { | m_override_args[strArg].push_back(strValue); | ||||
mapArgs[strArg] = strValue; | |||||
} | |||||
mapMultiArgs[strArg].push_back(strValue); | |||||
} | } | ||||
void ArgsManager::ClearArg(const std::string &strArg) { | void ArgsManager::ClearArg(const std::string &strArg) { | ||||
LOCK(cs_args); | LOCK(cs_args); | ||||
mapArgs.erase(strArg); | m_override_args.erase(strArg); | ||||
mapMultiArgs.erase(strArg); | m_config_args.erase(strArg); | ||||
} | } | ||||
bool HelpRequested(const ArgsManager &args) { | bool HelpRequested(const ArgsManager &args) { | ||||
return args.IsArgSet("-?") || args.IsArgSet("-h") || args.IsArgSet("-help"); | return args.IsArgSet("-?") || args.IsArgSet("-h") || args.IsArgSet("-help"); | ||||
} | } | ||||
static const int screenWidth = 79; | static const int screenWidth = 79; | ||||
static const int optIndent = 2; | static const int optIndent = 2; | ||||
▲ Show 20 Lines • Show All 124 Lines • ▼ Show 20 Lines | for (boost::program_options::detail::config_file_iterator | ||||
it(stream, setOptions), | it(stream, setOptions), | ||||
end; | end; | ||||
it != end; ++it) { | it != end; ++it) { | ||||
// Don't overwrite existing settings so command line settings override | // Don't overwrite existing settings so command line settings override | ||||
// bitcoin.conf | // bitcoin.conf | ||||
std::string strKey = std::string("-") + it->string_key; | std::string strKey = std::string("-") + it->string_key; | ||||
std::string strValue = it->value[0]; | std::string strValue = it->value[0]; | ||||
InterpretNegatedOption(strKey, strValue); | InterpretNegatedOption(strKey, strValue); | ||||
if (mapArgs.count(strKey) == 0) { | m_config_args[strKey].push_back(strValue); | ||||
mapArgs[strKey] = strValue; | |||||
} | |||||
mapMultiArgs[strKey].push_back(strValue); | |||||
} | } | ||||
} | } | ||||
void ArgsManager::ReadConfigFile(const std::string &confPath) { | void ArgsManager::ReadConfigFile(const std::string &confPath) { | ||||
{ | |||||
LOCK(cs_args); | |||||
m_config_args.clear(); | |||||
} | |||||
fs::ifstream stream(GetConfigFile(confPath)); | fs::ifstream stream(GetConfigFile(confPath)); | ||||
// ok to not have a config file | // ok to not have a config file | ||||
if (stream.good()) { | if (stream.good()) { | ||||
ReadConfigStream(stream); | ReadConfigStream(stream); | ||||
} | } | ||||
// If datadir is changed in .conf file: | // If datadir is changed in .conf file: | ||||
▲ Show 20 Lines • Show All 268 Lines • Show Last 20 Lines |
I don't know why they did this, but the disparity between config and CLI argument reading seems like a poor design... But I don't intend to own this, so will let it slide.