1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
|
// This source code is released into the public domain.
module;
#include <stdlib.h> // getprogname, NOLINT
#include <sysexits.h> // EX_USAGE
#include <unistd.h> // getopt
module nihil.cli;
import nihil.std;
import nihil.core;
import nihil.posix;
namespace nihil {
namespace {
// Print this node's children in a form useful to humans.
auto print_commands(command_tree_node const &node) -> void
{
for (auto &&child : node.children())
std::println(std::cerr, " {}", child.command().path());
}
} // namespace
auto dispatch_command(int const argc, char **argv) -> int
{
// Reset getopt(3) for the command, in case main() used it already.
optreset = 1;
optind = 1;
// Node that tree.find() never fails, at worst it will return the root node.
auto tree = build_command_tree();
auto [node, rest] = tree.find(std::span(argv, argc));
auto const &command = node->command();
// Set the program name to the existing progname plus the full path to the command being
// invoked; this makes error messages nicer. Save the old progname so we can restore it
// after invoking the command.
auto progname_guard =
setprogname(std::format("{} {}", getprogname().value_or(""), command.path()));
// Invoke the command see what it returns. If it's an exit code, just return it.
// Otherwise, handle the error.
auto nrest = static_cast<int>(std::ranges::distance(rest));
// Keep the first argument, because getopt() wants it
if (nrest < argc)
++nrest;
auto ret = command.invoke(nrest, argv + (argc - nrest));
// Restore the old progname.
progname_guard.release();
if (ret)
return *ret;
// Incomplete command: print the list of valid commands at this node.
if (ret.error() == errc::incomplete_command) {
std::println(std::cerr, "{}: usage:", ::getprogname());
print_commands(*node);
return EX_USAGE;
}
// We didn't recognise the error, so just print it and exit.
std::println(std::cerr, "{}", ret.error());
return 1;
}
} // namespace nihil
|