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
69
70
71
72
73
74
|
// 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;
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::print(std::cerr, " {}\n", child.command().path());
}
}
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);
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. Save the old progname so we can restore it
// after invoking the command.
auto const *old_progname = ::getprogname();
{
auto cprogname = std::format("{} {}", ::getprogname(),
command.path());
::setprogname(cprogname.c_str());
}
// Invoke the command see what it returns. If it's an exit code, just return it.
// Otherwise, handle the error.
auto ret = command.invoke(argc, argv);
// Restore the old progname.
::setprogname(old_progname);
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
|