Skip to content

Commit

Permalink
argsman, cli: Allow options to be passed after command
Browse files Browse the repository at this point in the history
This would make that the order of [options] passed to any command
doesn't matter and will validate those options correctly.
  • Loading branch information
pablomartin4btc committed Oct 1, 2024
1 parent fc642c3 commit 7fda2d1
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 27 deletions.
17 changes: 11 additions & 6 deletions src/bitcoin-cli.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1159,16 +1159,21 @@ static void SetGenerateToAddressArgs(const std::string& address, std::vector<std
args.emplace(args.begin() + 1, address);
}

std::vector<std::string> GetCommandArgs() {
std::optional<const ArgsManager::Command> command = gArgs.GetCommand();

// Return command args if command is present, otherwise return an empty vector
if (command.has_value()) {
return command->args;
}
return {}; // Return an empty vector
}

static int CommandLineRPC(int argc, char *argv[])
{
std::string strPrint;
int nRet = 0;
try {
// Skip switches
while (argc > 1 && IsSwitchChar(argv[1][0])) {
argc--;
argv++;
}
std::string rpcPass;
if (gArgs.GetBoolArg("-stdinrpcpass", false)) {
NO_STDIN_ECHO();
Expand All @@ -1184,7 +1189,7 @@ static int CommandLineRPC(int argc, char *argv[])
}
gArgs.ForceSetArg("-rpcpassword", rpcPass);
}
std::vector<std::string> args = std::vector<std::string>(&argv[1], &argv[argc]);
std::vector<std::string> args = GetCommandArgs();
if (gArgs.GetBoolArg("-stdinwalletpassphrase", false)) {
NO_STDIN_ECHO();
std::string walletPass;
Expand Down
74 changes: 53 additions & 21 deletions src/common/args.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,43 @@ void ArgsManager::SelectConfigNetwork(const std::string& network)
m_network = network;
}

bool ArgsManager::ProcessOptionKey(std::string& key, std::optional<std::string>& val, std::string& error) {

// Transform --foo to -foo
if (key.length() > 1 && key[1] == '-')
key.erase(0, 1);

// Transform -foo to foo
key.erase(0, 1);

KeyInfo keyinfo = InterpretKey(key);
std::optional<unsigned int> flags = GetArgFlags('-' + keyinfo.name);

// Unknown command line options and command line options with dot characters
// (which are returned from InterpretKey with nonempty section strings)are not valid.
if (!flags || !keyinfo.section.empty()) {
error = strprintf("Invalid parameter %s", key);
return false;
}

std::optional<common::SettingsValue> value = InterpretValue(keyinfo, val ? &*val : nullptr, *flags, error);
if (!value) return false;

// Store the option
m_settings.command_line_options[keyinfo.name].push_back(*value);
// or we could update it to avoid having multiple values for an option
// but some args mgr tests would break cos we are changing current behaviour, i'd leave it as is...
//if (auto it = m_settings.command_line_options.find(keyinfo.name); it != m_settings.command_line_options.end()) {
// Key exists, update the value (overwrite the previous value)
//it->second[0] = *value; // Update the first (or only) entry
//} else {
// Key doesn't exist, add a new entry
//m_settings.command_line_options[keyinfo.name].push_back(*value);
//}

return true;
}

bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::string& error)
{
LOCK(cs_args);
Expand Down Expand Up @@ -217,32 +254,27 @@ bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::strin
m_command.push_back(key);
while (++i < argc) {
// The remaining args are command args
m_command.emplace_back(argv[i]);
if (argv[i][0] == '-') {
// except it starts with dash "-" then will check if it's a valid option
key = argv[i];
val.reset();
is_index = key.find('=');
if (is_index != std::string::npos) {
val = key.substr(is_index + 1);
key.erase(is_index);
}
if (!ProcessOptionKey(key, val, error)) {
return false;
}
} else {
m_command.emplace_back(argv[i]);
}
}
break;
}

// Transform --foo to -foo
if (key.length() > 1 && key[1] == '-')
key.erase(0, 1);

// Transform -foo to foo
key.erase(0, 1);
KeyInfo keyinfo = InterpretKey(key);
std::optional<unsigned int> flags = GetArgFlags('-' + keyinfo.name);

// Unknown command line options and command line options with dot
// characters (which are returned from InterpretKey with nonempty
// section strings) are not valid.
if (!flags || !keyinfo.section.empty()) {
error = strprintf("Invalid parameter %s", argv[i]);
if (!ProcessOptionKey(key, val, error)) {
return false;
}

std::optional<common::SettingsValue> value = InterpretValue(keyinfo, val ? &*val : nullptr, *flags, error);
if (!value) return false;

m_settings.command_line_options[keyinfo.name].push_back(*value);
}

// we do not allow -includeconf from command line, only -noincludeconf
Expand Down
2 changes: 2 additions & 0 deletions src/common/args.h
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,8 @@ class ArgsManager
const std::string& prefix,
const std::string& section,
const std::map<std::string, std::vector<common::SettingsValue>>& args) const;

bool ProcessOptionKey(std::string& key, std::optional<std::string>& val, std::string& error);
};

extern ArgsManager gArgs;
Expand Down

0 comments on commit 7fda2d1

Please sign in to comment.