aboutsummaryrefslogtreecommitdiffstats
path: root/nihil.cli/dispatch_command.cc
blob: 736e16e72bd0f4b57769a3035c6b379b5da1bea9 (plain) (blame)
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
75
76
77
78
79
80
81
82
/*
 * This source code is released into the public domain.
 */

module;

#include <cstdio>
#include <functional>
#include <iostream>
#include <map>
#include <print>
#include <ranges>
#include <string>
#include <utility>

#include <unistd.h>

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