mirror of https://github.com/WerWolv/ImHex
impr: Rework command line argument parsing (#2440)
The current CLI argument parsing did not work as documented and had a number of issues related to multi-flag (subcommand) parsing. I've reworked the logic in such a way that should maintain full compatibility with any existing scripts/use-cases but with added functionality. --------- Co-authored-by: Nik <werwolv98@gmail.com>
This commit is contained in:
parent
6169078c04
commit
dcbba9cbfc
|
|
@ -13,6 +13,9 @@
|
||||||
namespace hex::subcommands {
|
namespace hex::subcommands {
|
||||||
|
|
||||||
std::optional<SubCommand> findSubCommand(const std::string &arg) {
|
std::optional<SubCommand> findSubCommand(const std::string &arg) {
|
||||||
|
if (arg == "-" || arg == "--")
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
for (auto &plugin : PluginManager::getPlugins()) {
|
for (auto &plugin : PluginManager::getPlugins()) {
|
||||||
for (auto &subCommand : plugin.getSubCommands()) {
|
for (auto &subCommand : plugin.getSubCommands()) {
|
||||||
if (fmt::format("--{}", subCommand.commandLong) == arg || fmt::format("-{}", subCommand.commandShort) == arg) {
|
if (fmt::format("--{}", subCommand.commandLong) == arg || fmt::format("-{}", subCommand.commandShort) == arg) {
|
||||||
|
|
@ -35,46 +38,62 @@ namespace hex::subcommands {
|
||||||
|
|
||||||
auto argsIter = args.begin();
|
auto argsIter = args.begin();
|
||||||
|
|
||||||
// Get subcommand associated with the first argument
|
std::optional<SubCommand> currentSubCommand;
|
||||||
std::optional<SubCommand> currentSubCommand = findSubCommand(*argsIter);
|
|
||||||
|
|
||||||
if (currentSubCommand) {
|
|
||||||
argsIter += 1;
|
|
||||||
// If it is a valid subcommand, remove it from the argument list
|
|
||||||
} else {
|
|
||||||
// If no (valid) subcommand was provided, the default one is --open
|
|
||||||
currentSubCommand = findSubCommand("--open");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Arguments of the current subcommand
|
|
||||||
std::vector<std::string> currentSubCommandArgs;
|
std::vector<std::string> currentSubCommandArgs;
|
||||||
|
|
||||||
// Compute all subcommands to run
|
if (*argsIter == "--") {
|
||||||
while (argsIter != args.end()) {
|
// Treat the rest of the args as files
|
||||||
const std::string &arg = *argsIter;
|
++argsIter;
|
||||||
|
std::vector<std::string> remainingArgs(argsIter, args.end());
|
||||||
|
subCommands.emplace_back(*findSubCommand("--open"), remainingArgs);
|
||||||
|
argsIter = args.end(); // Skip while loop
|
||||||
|
} else if (!findSubCommand(*argsIter)) {
|
||||||
|
// First argument not a subcommand, treat all args as files
|
||||||
|
subCommands.emplace_back(*findSubCommand("--open"), args);
|
||||||
|
argsIter = args.end(); // Skip while loop
|
||||||
|
}
|
||||||
|
|
||||||
if (!currentSubCommandArgs.empty() && arg.starts_with("--") && !(currentSubCommand.has_value() && currentSubCommand->type == SubCommand::Type::SubCommand)) {
|
while (argsIter != args.end()) {
|
||||||
// Save command to run
|
// Get subcommand associated with the argument
|
||||||
if (currentSubCommand) {
|
// Guaranteed to find a match on the first argument, as the other case has been handled above
|
||||||
|
const auto newSubCommand = findSubCommand(*argsIter);
|
||||||
|
if (*argsIter == "--") {
|
||||||
|
// Treat the rest of the args as files
|
||||||
|
subCommands.emplace_back(*currentSubCommand, currentSubCommandArgs);
|
||||||
|
|
||||||
|
++argsIter;
|
||||||
|
std::vector<std::string> remainingArgs(argsIter, args.end());
|
||||||
|
|
||||||
|
subCommands.emplace_back(*findSubCommand("--open"), remainingArgs);
|
||||||
|
|
||||||
|
currentSubCommand = std::nullopt;
|
||||||
|
currentSubCommandArgs.clear();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Will always take this `if` statement on the first time through the loop
|
||||||
|
if (newSubCommand.has_value()) {
|
||||||
|
if (currentSubCommand.has_value()) {
|
||||||
subCommands.emplace_back(*currentSubCommand, currentSubCommandArgs);
|
subCommands.emplace_back(*currentSubCommand, currentSubCommandArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
currentSubCommand = std::nullopt;
|
if (newSubCommand->type == SubCommand::Type::SubCommand) {
|
||||||
currentSubCommandArgs = { };
|
++argsIter;
|
||||||
} else if (currentSubCommand) {
|
std::vector<std::string> remainingArgs(argsIter, args.end());
|
||||||
// Add current argument to the current command
|
subCommands.emplace_back(*newSubCommand, remainingArgs);
|
||||||
currentSubCommandArgs.push_back(arg);
|
|
||||||
argsIter += 1;
|
currentSubCommand = std::nullopt;
|
||||||
} else {
|
currentSubCommandArgs.clear();
|
||||||
// Get next subcommand from current argument
|
break;
|
||||||
currentSubCommand = findSubCommand(arg);
|
|
||||||
if (!currentSubCommand) {
|
|
||||||
log::error("No subcommand named '{}' found", arg);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
argsIter += 1;
|
currentSubCommand = newSubCommand;
|
||||||
|
currentSubCommandArgs.clear();
|
||||||
|
} else {
|
||||||
|
currentSubCommandArgs.push_back(*argsIter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
++argsIter;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save last command to run
|
// Save last command to run
|
||||||
|
|
@ -83,7 +102,7 @@ namespace hex::subcommands {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the subcommands
|
// Run the subcommands
|
||||||
for (auto &[subcommand, subCommandArgs] : subCommands) {
|
for (const auto &[subcommand, subCommandArgs] : subCommands) {
|
||||||
subcommand.callback(subCommandArgs);
|
subcommand.callback(subCommandArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -99,29 +99,22 @@ namespace hex::plugin::builtin {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> fullPaths;
|
std::vector<std::string> fullPaths;
|
||||||
bool doubleDashFound = false;
|
for (const auto &arg : args) {
|
||||||
for (auto &arg : args) {
|
try {
|
||||||
|
std::fs::path path;
|
||||||
|
|
||||||
// Skip the first argument named `--`
|
|
||||||
if (arg == "--" && !doubleDashFound) {
|
|
||||||
doubleDashFound = true;
|
|
||||||
} else {
|
|
||||||
try {
|
try {
|
||||||
std::fs::path path;
|
path = std::fs::weakly_canonical(arg);
|
||||||
|
} catch(std::fs::filesystem_error &) {
|
||||||
try {
|
path = arg;
|
||||||
path = std::fs::weakly_canonical(arg);
|
|
||||||
} catch(std::fs::filesystem_error &) {
|
|
||||||
path = arg;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path.empty())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
fullPaths.push_back(wolv::util::toUTF8String(path));
|
|
||||||
} catch (std::exception &e) {
|
|
||||||
log::error("Failed to open file '{}'\n {}", arg, e.what());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (path.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
fullPaths.push_back(wolv::util::toUTF8String(path));
|
||||||
|
} catch (std::exception &e) {
|
||||||
|
log::error("Failed to open file '{}'\n {}", arg, e.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -541,9 +534,6 @@ namespace hex::plugin::builtin {
|
||||||
void registerCommandForwarders() {
|
void registerCommandForwarders() {
|
||||||
hex::subcommands::registerSubCommand("open", [](const std::vector<std::string> &args){
|
hex::subcommands::registerSubCommand("open", [](const std::vector<std::string> &args){
|
||||||
for (auto &arg : args) {
|
for (auto &arg : args) {
|
||||||
if (arg.starts_with("--"))
|
|
||||||
break;
|
|
||||||
|
|
||||||
RequestOpenFile::post(arg);
|
RequestOpenFile::post(arg);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -586,4 +576,4 @@ namespace hex::plugin::builtin {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue