RetroShell needs a new argument parser
To implement decent file system commands, RetroShell needs a more flexible parser. E.g., I want to support commands with the following example syntax:
fs create c=<count> h=<count> s=<count> [name=<string>]
type [-h] <filename>
In other words, I want to support key-value pairs and boolean flags.
Also, the new shell should print a small help message for each argument when pressing TAB twice, so we don't need external documentation for all commands.
Boolean flags and argument descriptions are working. As a showcase, I've implemented a find command in the new navigator shell:
Interestingly, by smartly juggling around with C++ lambdas for filtering and formatting, the code for the entire find command is just a few lines:
root.add({
.tokens = { "find" },
.argx = {
{ .name = { "name", "Search pattern" } },
{ .name = { "path", "Directory to search in" }, .flags = arg::opt },
{ .name = { "d", "Find directories only" }, .flags = arg::flag },
{ .name = { "f", "Find files only" }, .flags = arg::flag },
{ .name = { "r", "Search subdirectories, too" }, .flags = arg::flag },
{ .name = { "s", "Sort output" }, .flags = arg::flag } },
.help = { "Find files or directories." },
.func = [this] (Arguments& argv, const ParsedArguments &args, const std::vector<isize> &values) {
auto pattern = FSPattern(args.at("name"));
auto path = FSString(args, "path", ".");
auto d = args.contains("d");
auto f = args.contains("f");
auto r = args.contains("r");
auto s = args.contains("s");
auto dir = fs.pwd().seek(path);
FSOpt opt = {
.sort = s,
.recursive = r,
.filter = [&](const FSPath &item) {
return pattern.match(item.last()) &&
(!d || item.isDirectory()) &&
(!f || item.isFile());
},
.formatter = [&](const FSPath &item) {
std::stringstream ss;
ss << item.name() + (item.isDirectory() ? " (dir)" : "");
return ss.str();
}
};
std::vector<FSPath> matches;
fs.collect(dir, matches, opt);
for (auto &it : matches) { *this << opt.formatter(it) << '\n'; }
}
});
that’s a lot of juggling lol
The FileSystem class had code for printing debug information about block contents to stdout. I've prettified the output and made it available in the navigator via the block command:
Hex dumps can be generated via the -h flag:
The command handler is again very simple:
root.add({
.tokens = { "block", "" },
.argx = {
{ .name = { "nr", "Block number" }, .flags = arg::opt },
{ .name = { "h", "Print a hex dump" }, .flags = arg::flag },
},
.help = { "Analyze a block" },
.func = [this] (Arguments& argv, const ParsedArguments &args, const std::vector<isize> &values) {
auto nr = args.contains("nr") ? parseBlock(args.at("nr")) : fs.pwd().ref;
if (auto ptr = fs.blockPtr(nr)) {
std::stringstream ss;
if (args.contains("h")) {
ptr->hexDump(ss);
} else {
ptr->dump(ss);
}
*this << '\n' << ss << '\n';
}
}
});
New argument parser is in place.