]> git.saurik.com Git - apple/libc.git/blame - libdarwin/ctl.c
Libc-1353.41.1.tar.gz
[apple/libc.git] / libdarwin / ctl.c
CommitLineData
507116e3
A
1#include "internal.h"
2
3#pragma mark Definitions
4#define CTL_OUTPUT_WIDTH (80)
5#define CTL_OUTPUT_OPTARG_PAD (28)
6#define CTL_OUTPUT_LIST_PAD (4)
7#define SUBCOMMAND_LINKER_SET "__subcommands"
8
9#pragma mark Module Globals
10static const os_subcommand_t _help_cmd;
11
12#pragma mark Module Routines
13static char *
14_os_subcommand_copy_optarg_usage(const os_subcommand_t *osc,
15 const struct option *opt, os_subcommand_optarg_format_t format,
16 os_subcommand_option_t *scopt)
17{
18 char optbuff[64] = "";
19 char argbuff[64] = "";
20 char *final = NULL;
21 int ret = -1;
22
23 snprintf(optbuff, sizeof(optbuff), "--%s", opt->name);
24
25 if (osc->osc_info) {
26 osc->osc_info(osc, format, opt, scopt);
27 }
28
29 switch (opt->has_arg) {
30 case no_argument:
31 break;
32 case optional_argument:
33 snprintf(argbuff, sizeof(argbuff), "[=%s]", scopt->osco_argdesc);
34 break;
35 case required_argument:
36 snprintf(argbuff, sizeof(argbuff), "=<%s>", scopt->osco_argdesc);
37 break;
38 default:
39 __builtin_unreachable();
40 }
41
42 ret = asprintf(&final, "%s%s", optbuff, argbuff);
43 if (ret < 0) {
44 os_assert_zero(ret);
45 }
46
47 return final;
48}
49
50static void
51_os_subcommand_print_optarg_usage(const os_subcommand_t *osc,
52 const struct option *opt, FILE *f)
53{
54 os_subcommand_option_t scopt = {
55 .osco_flags = 0,
56 .osco_argdesc = opt->name,
57 };
58 char *__os_free usage = NULL;
59 char *braces[2] = {
60 "",
61 "",
62 };
63
64 usage = _os_subcommand_copy_optarg_usage(osc, opt,
65 OS_SUBCOMMAND_OPTARG_USAGE, &scopt);
66 if (scopt.osco_flags & OS_SUBCOMMAND_OPTION_FLAG_OPTIONAL) {
67 braces[0] = "[";
68 braces[1] = "]";
69 }
70
71 fprintf(f, " %s%s%s", braces[0], usage, braces[1]);
72}
73
74static void
75_os_subcommand_print_usage(const os_subcommand_t *osc, FILE *f)
76{
77 const struct option *opts = osc->osc_options;
78 const struct option *curopt = NULL;
79 size_t i = 0;
80
81 fprintf(f, "usage: %s %s", getprogname(), osc->osc_name);
82
83 while ((curopt = &opts[i]) && curopt->name) {
84 _os_subcommand_print_optarg_usage(osc, curopt, f);
85 i++;
86 }
87
88 fprintf(f, "\n");
89}
90
91static void
92_os_subcommand_print_optarg_human(const os_subcommand_t *osc,
93 const struct option *opt, FILE *f)
94{
95 os_subcommand_option_t scopt = {
96 .osco_flags = 0,
97 .osco_argdesc = opt->name,
98 };
99 char *__os_free usage = NULL;
100 char *__os_free human = NULL;
101
102 usage = _os_subcommand_copy_optarg_usage(osc, opt,
103 OS_SUBCOMMAND_OPTARG_USAGE, &scopt);
104 fprintf(f, " %-24s", usage);
105
106 human = _os_subcommand_copy_optarg_usage(osc, opt,
107 OS_SUBCOMMAND_OPTARG_HUMAN, &scopt);
108 wfprintf_np(f, -CTL_OUTPUT_OPTARG_PAD, CTL_OUTPUT_OPTARG_PAD,
109 CTL_OUTPUT_WIDTH, "%s", scopt.osco_argdesc);
110}
111
112static void
113_os_subcommand_print_human(const os_subcommand_t *osc, FILE *f)
114{
115 const struct option *opts = osc->osc_options;
116 const struct option *curopt = NULL;
117 size_t i = 0;
118
119 _os_subcommand_print_usage(osc, f);
120
121 while ((curopt = &opts[i]) && curopt->name) {
122 _os_subcommand_print_optarg_human(osc, curopt, f);
123 i++;
124 }
125}
126
127static void
128_os_subcommand_print_list(const os_subcommand_t *osc, FILE *f)
129{
130 wfprintf_np(f, CTL_OUTPUT_LIST_PAD, 0, 0, "%-24s %s",
131 osc->osc_name, osc->osc_desc);
132}
133
134static const os_subcommand_t *
135_os_subcommand_find(const char *name)
136{
137 const os_subcommand_t **oscip = NULL;
138
139 if (strcmp(_help_cmd.osc_name, name) == 0) {
140 return &_help_cmd;
141 }
142
143 LINKER_SET_FOREACH(oscip, const os_subcommand_t **, SUBCOMMAND_LINKER_SET) {
144 const os_subcommand_t *osci = *oscip;
145
146 if (strcmp(osci->osc_name, name) == 0) {
147 return osci;
148 }
149 }
150
151 return NULL;
152}
153
154#pragma mark Default Usage
155static void
156_usage_default(FILE *f)
157{
158 const os_subcommand_t **oscip = NULL;
159
160 crfprintf_np(f, "usage: %s <subcommand> [...] | help [subcommand]",
161 getprogname());
162 crfprintf_np(f, "");
163
164 crfprintf_np(f, "subcommands:");
165 LINKER_SET_FOREACH(oscip, const os_subcommand_t **, SUBCOMMAND_LINKER_SET) {
166 const os_subcommand_t *osci = *oscip;
167 _os_subcommand_print_list(osci, f);
168 }
169
170 _os_subcommand_print_list(&_help_cmd, f);
171}
172
173static int
174_usage(FILE *f)
175{
176 _usage_default(f);
177 return EX_USAGE;
178}
179
180#pragma mark Help Subcommand
181static int _help_invoke(const os_subcommand_t *osc,
182 int argc,
183 const char *argv[]
184);
185
186static const os_subcommand_t _help_cmd = {
187 .osc_version = OS_SUBCOMMAND_VERSION,
188 .osc_flags = 0,
189 .osc_name = "help",
190 .osc_desc = "prints helpful information",
191 .osc_optstring = NULL,
192 .osc_options = NULL,
193 .osc_info = NULL,
194 .osc_invoke = &_help_invoke,
195};
196
197static void
198_help_print_subcommand(const os_subcommand_t *osc, FILE *f)
199{
200 wfprintf_np(f, 4, 4, 76, "%-16s%s", osc->osc_name, osc->osc_desc);
201}
202
203static void
204_help_print_all(FILE *f)
205{
206 const os_subcommand_t **oscip = NULL;
207
208 _usage_default(f);
209 crfprintf_np(f, "");
210
211 crfprintf_np(f, "subcommands:");
212 LINKER_SET_FOREACH(oscip, const os_subcommand_t **, SUBCOMMAND_LINKER_SET) {
213 const os_subcommand_t *osci = *oscip;
214 if (osci->osc_flags & OS_SUBCOMMAND_FLAG_HIDDEN) {
215 continue;
216 }
217 _help_print_subcommand(osci, f);
218 }
219}
220
221static int
222_help_invoke(const os_subcommand_t *osc, int argc, const char *argv[])
223{
224 const os_subcommand_t *target = NULL;
225
226 if (argc == 1) {
227 _help_print_all(stdout);
228 } else {
229 target = _os_subcommand_find(argv[1]);
230 if (!target) {
231 crfprintf_np(stderr, "unrecognized subcommand: %s", argv[1]);
232 _usage_default(stderr);
233 return EX_USAGE;
234 }
235
236 _os_subcommand_print_human(target, stdout);
237 }
238
239 return 0;
240}
241
242#pragma mark API
243int
244os_subcommand_main(int argc, const char *argv[])
245{
246 int exitcode = -1;
247 const char *cmdname = NULL;
248 const os_subcommand_t *osci = NULL;
249
250 if (argc < 2) {
251 exitcode = _usage(stderr);
252 goto __out;
253 }
254
255 // Advance argument pointer and make the subcommand argv[0].
256 argc -= 1;
257 argv += 1;
258 cmdname = argv[0];
259
260 osci = _os_subcommand_find(cmdname);
261 if (osci) {
262 if (osci->osc_flags & OS_SUBCOMMAND_FLAG_REQUIRE_ROOT) {
263 if (geteuid()) {
264 crfprintf_np(stderr, "subcommand requires root: %s", cmdname);
265 exitcode = EX_NOPERM;
266 goto __out;
267 }
268 }
269
270 if (osci->osc_flags & OS_SUBCOMMAND_FLAG_TTYONLY) {
271 if (!isatty(STDOUT_FILENO) || !isatty(STDIN_FILENO)) {
272 crfprintf_np(stderr, "subcommand requires a tty: %s", cmdname);
273 exitcode = EX_UNAVAILABLE;
274 goto __out;
275 }
276 }
277
278 exitcode = osci->osc_invoke(osci, argc, argv);
279 if (exitcode == EX_USAGE) {
280 _os_subcommand_print_usage(osci, stderr);
281 }
282 } else {
283 crfprintf_np(stderr, "unrecognized subcommand: %s", cmdname);
284 exitcode = _usage(stderr);
285 }
286
287__out:
288 return exitcode;
289}