From d24315268c11d435bb9accbce87b7f46dda6ed3e Mon Sep 17 00:00:00 2001 From: Lexi Winter Date: Sun, 29 Jun 2025 00:42:31 +0100 Subject: cli: improve command dispatch a bit --- nihil.cli/dispatch_command.cc | 82 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 nihil.cli/dispatch_command.cc (limited to 'nihil.cli/dispatch_command.cc') diff --git a/nihil.cli/dispatch_command.cc b/nihil.cli/dispatch_command.cc new file mode 100644 index 0000000..736e16e --- /dev/null +++ b/nihil.cli/dispatch_command.cc @@ -0,0 +1,82 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +module nihil.cli; + +import nihil.core; + +namespace nihil { + +auto dispatch_command(int argc, char **argv) -> int +{ + auto tree = build_command_tree(); + + // The caller should have stripped argv[0] already. find() will + // strip all the remaining elements except the last, which means + // argv[0] will be set to something reasonable for the next call + // to getopt(). + + // find() never returns nullptr; at worst it will return the + // root node. + auto const *node = tree.find(argc, argv); + + // Get the command_node. + auto const &command = node->command(); + + // Reset getopt(3) for the command, in case main() used it already. + optreset = 1; + optind = 1; + + /* + * Set the program name to the existing progname plus the full path + * to the command being invoked; this makes error messages nicer. + */ + auto *old_progname = ::getprogname(); + + { + auto cprogname = std::format("{} {}", ::getprogname(), + command->path()); + ::setprogname(cprogname.c_str()); + } + + // Invoke the command see what it returns. + auto ret = command->invoke(argc, argv); + + // Restore the old progname. + ::setprogname(old_progname); + + // If the command produced an exit code, return it. + if (ret) + return *ret; + + /* + * We have special handling for some errors. + */ + + // Incomplete command: print the list of valid commands at this node. + if (ret.error() == errc::incomplete_command) { + std::print(std::cerr, "{}: usage:\n", ::getprogname()); + node->print_commands(); + return 1; + } + + // We didn't recognise the error, so just print it and exit. + std::print(std::cerr, "{}\n", ret.error()); + return 1; +} + +} // namespace nihil -- cgit v1.2.3