]>
Commit | Line | Data |
---|---|---|
ad5611d8 TR |
1 | /* |
2 | * Copyright (c), Edward Thomson <ethomson@edwardthomson.com> | |
3 | * All rights reserved. | |
4 | * | |
5 | * This file is part of adopt, distributed under the MIT license. | |
6 | * For full terms and conditions, see the included LICENSE file. | |
7 | * | |
8 | * THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT. | |
9 | * | |
10 | * This file was produced by using the `rename.pl` script included with | |
11 | * adopt. The command-line specified was: | |
12 | * | |
13 | * ./rename.pl cli_opt --filename=opt --include=cli.h --inline=GIT_INLINE --header-guard=CLI_opt_h__ --lowercase-status --without-usage | |
14 | */ | |
15 | ||
16 | #include <stdlib.h> | |
17 | #include <string.h> | |
18 | #include <stdio.h> | |
19 | #include <limits.h> | |
20 | #include <assert.h> | |
21 | ||
22 | #include "cli.h" | |
23 | #include "opt.h" | |
24 | ||
25 | #ifdef _WIN32 | |
26 | # include <Windows.h> | |
27 | #else | |
28 | # include <fcntl.h> | |
29 | # include <sys/ioctl.h> | |
30 | #endif | |
31 | ||
32 | #ifdef _MSC_VER | |
33 | # define alloca _alloca | |
34 | #endif | |
35 | ||
36 | #define spec_is_option_type(x) \ | |
37 | ((x)->type == CLI_OPT_TYPE_BOOL || \ | |
38 | (x)->type == CLI_OPT_TYPE_SWITCH || \ | |
39 | (x)->type == CLI_OPT_TYPE_VALUE) | |
40 | ||
41 | GIT_INLINE(const cli_opt_spec *) spec_for_long( | |
42 | int *is_negated, | |
43 | int *has_value, | |
44 | const char **value, | |
45 | const cli_opt_parser *parser, | |
46 | const char *arg) | |
47 | { | |
48 | const cli_opt_spec *spec; | |
49 | char *eql; | |
50 | size_t eql_pos; | |
51 | ||
52 | eql = strchr(arg, '='); | |
53 | eql_pos = (eql = strchr(arg, '=')) ? (size_t)(eql - arg) : strlen(arg); | |
54 | ||
55 | for (spec = parser->specs; spec->type; ++spec) { | |
56 | /* Handle -- (everything after this is literal) */ | |
57 | if (spec->type == CLI_OPT_TYPE_LITERAL && arg[0] == '\0') | |
58 | return spec; | |
59 | ||
60 | /* Handle --no-option arguments for bool types */ | |
61 | if (spec->type == CLI_OPT_TYPE_BOOL && | |
62 | strncmp(arg, "no-", 3) == 0 && | |
63 | strcmp(arg + 3, spec->name) == 0) { | |
64 | *is_negated = 1; | |
65 | return spec; | |
66 | } | |
67 | ||
68 | /* Handle the typical --option arguments */ | |
69 | if (spec_is_option_type(spec) && | |
70 | spec->name && | |
71 | strcmp(arg, spec->name) == 0) | |
72 | return spec; | |
73 | ||
74 | /* Handle --option=value arguments */ | |
75 | if (spec->type == CLI_OPT_TYPE_VALUE && | |
76 | eql && | |
77 | strncmp(arg, spec->name, eql_pos) == 0 && | |
78 | spec->name[eql_pos] == '\0') { | |
79 | *has_value = 1; | |
80 | *value = arg[eql_pos + 1] ? &arg[eql_pos + 1] : NULL; | |
81 | return spec; | |
82 | } | |
83 | } | |
84 | ||
85 | return NULL; | |
86 | } | |
87 | ||
88 | GIT_INLINE(const cli_opt_spec *) spec_for_short( | |
89 | const char **value, | |
90 | const cli_opt_parser *parser, | |
91 | const char *arg) | |
92 | { | |
93 | const cli_opt_spec *spec; | |
94 | ||
95 | for (spec = parser->specs; spec->type; ++spec) { | |
96 | /* Handle -svalue short options with a value */ | |
97 | if (spec->type == CLI_OPT_TYPE_VALUE && | |
98 | arg[0] == spec->alias && | |
99 | arg[1] != '\0') { | |
100 | *value = &arg[1]; | |
101 | return spec; | |
102 | } | |
103 | ||
104 | /* Handle typical -s short options */ | |
105 | if (arg[0] == spec->alias) { | |
106 | *value = NULL; | |
107 | return spec; | |
108 | } | |
109 | } | |
110 | ||
111 | return NULL; | |
112 | } | |
113 | ||
114 | GIT_INLINE(const cli_opt_spec *) spec_for_arg(cli_opt_parser *parser) | |
115 | { | |
116 | const cli_opt_spec *spec; | |
117 | size_t args = 0; | |
118 | ||
119 | for (spec = parser->specs; spec->type; ++spec) { | |
120 | if (spec->type == CLI_OPT_TYPE_ARG) { | |
121 | if (args == parser->arg_idx) { | |
122 | parser->arg_idx++; | |
123 | return spec; | |
124 | } | |
125 | ||
126 | args++; | |
127 | } | |
128 | ||
129 | if (spec->type == CLI_OPT_TYPE_ARGS && args == parser->arg_idx) | |
130 | return spec; | |
131 | } | |
132 | ||
133 | return NULL; | |
134 | } | |
135 | ||
136 | GIT_INLINE(int) spec_is_choice(const cli_opt_spec *spec) | |
137 | { | |
138 | return ((spec + 1)->type && | |
139 | ((spec + 1)->usage & CLI_OPT_USAGE_CHOICE)); | |
140 | } | |
141 | ||
142 | /* | |
143 | * If we have a choice with switches and bare arguments, and we see | |
144 | * the switch, then we no longer expect the bare argument. | |
145 | */ | |
146 | GIT_INLINE(void) consume_choices(const cli_opt_spec *spec, cli_opt_parser *parser) | |
147 | { | |
148 | /* back up to the beginning of the choices */ | |
149 | while (spec->type && (spec->usage & CLI_OPT_USAGE_CHOICE)) | |
150 | --spec; | |
151 | ||
152 | if (!spec_is_choice(spec)) | |
153 | return; | |
154 | ||
155 | do { | |
156 | if (spec->type == CLI_OPT_TYPE_ARG) | |
157 | parser->arg_idx++; | |
158 | ++spec; | |
159 | } while(spec->type && (spec->usage & CLI_OPT_USAGE_CHOICE)); | |
160 | } | |
161 | ||
162 | static cli_opt_status_t parse_long(cli_opt *opt, cli_opt_parser *parser) | |
163 | { | |
164 | const cli_opt_spec *spec; | |
165 | char *arg = parser->args[parser->idx++]; | |
166 | const char *value = NULL; | |
167 | int is_negated = 0, has_value = 0; | |
168 | ||
169 | opt->arg = arg; | |
170 | ||
171 | if ((spec = spec_for_long(&is_negated, &has_value, &value, parser, &arg[2])) == NULL) { | |
172 | opt->spec = NULL; | |
173 | opt->status = CLI_OPT_STATUS_UNKNOWN_OPTION; | |
174 | goto done; | |
175 | } | |
176 | ||
177 | opt->spec = spec; | |
178 | ||
179 | /* Future options parsed as literal */ | |
180 | if (spec->type == CLI_OPT_TYPE_LITERAL) | |
181 | parser->in_literal = 1; | |
182 | ||
183 | /* --bool or --no-bool */ | |
184 | else if (spec->type == CLI_OPT_TYPE_BOOL && spec->value) | |
185 | *((int *)spec->value) = !is_negated; | |
186 | ||
187 | /* --accumulate */ | |
188 | else if (spec->type == CLI_OPT_TYPE_ACCUMULATOR && spec->value) | |
189 | *((int *)spec->value) += spec->switch_value ? spec->switch_value : 1; | |
190 | ||
191 | /* --switch */ | |
192 | else if (spec->type == CLI_OPT_TYPE_SWITCH && spec->value) | |
193 | *((int *)spec->value) = spec->switch_value; | |
194 | ||
195 | /* Parse values as "--foo=bar" or "--foo bar" */ | |
196 | else if (spec->type == CLI_OPT_TYPE_VALUE) { | |
197 | if (has_value) | |
198 | opt->value = (char *)value; | |
199 | else if ((parser->idx + 1) <= parser->args_len) | |
200 | opt->value = parser->args[parser->idx++]; | |
201 | ||
202 | if (spec->value) | |
203 | *((char **)spec->value) = opt->value; | |
204 | } | |
205 | ||
206 | /* Required argument was not provided */ | |
207 | if (spec->type == CLI_OPT_TYPE_VALUE && | |
208 | !opt->value && | |
209 | !(spec->usage & CLI_OPT_USAGE_VALUE_OPTIONAL)) | |
210 | opt->status = CLI_OPT_STATUS_MISSING_VALUE; | |
211 | else | |
212 | opt->status = CLI_OPT_STATUS_OK; | |
213 | ||
214 | consume_choices(opt->spec, parser); | |
215 | ||
216 | done: | |
217 | return opt->status; | |
218 | } | |
219 | ||
220 | static cli_opt_status_t parse_short(cli_opt *opt, cli_opt_parser *parser) | |
221 | { | |
222 | const cli_opt_spec *spec; | |
223 | char *arg = parser->args[parser->idx++]; | |
224 | const char *value; | |
225 | ||
226 | opt->arg = arg; | |
227 | ||
228 | if ((spec = spec_for_short(&value, parser, &arg[1 + parser->in_short])) == NULL) { | |
229 | opt->spec = NULL; | |
230 | opt->status = CLI_OPT_STATUS_UNKNOWN_OPTION; | |
231 | goto done; | |
232 | } | |
233 | ||
234 | opt->spec = spec; | |
235 | ||
236 | if (spec->type == CLI_OPT_TYPE_BOOL && spec->value) | |
237 | *((int *)spec->value) = 1; | |
238 | ||
239 | else if (spec->type == CLI_OPT_TYPE_ACCUMULATOR && spec->value) | |
240 | *((int *)spec->value) += spec->switch_value ? spec->switch_value : 1; | |
241 | ||
242 | else if (spec->type == CLI_OPT_TYPE_SWITCH && spec->value) | |
243 | *((int *)spec->value) = spec->switch_value; | |
244 | ||
245 | /* Parse values as "-ifoo" or "-i foo" */ | |
246 | else if (spec->type == CLI_OPT_TYPE_VALUE) { | |
247 | if (value) | |
248 | opt->value = (char *)value; | |
249 | else if ((parser->idx + 1) <= parser->args_len) | |
250 | opt->value = parser->args[parser->idx++]; | |
251 | ||
252 | if (spec->value) | |
253 | *((char **)spec->value) = opt->value; | |
254 | } | |
255 | ||
256 | /* | |
257 | * Handle compressed short arguments, like "-fbcd"; see if there's | |
258 | * another character after the one we processed. If not, advance | |
259 | * the parser index. | |
260 | */ | |
261 | if (spec->type != CLI_OPT_TYPE_VALUE && arg[2 + parser->in_short] != '\0') { | |
262 | parser->in_short++; | |
263 | parser->idx--; | |
264 | } else { | |
265 | parser->in_short = 0; | |
266 | } | |
267 | ||
268 | /* Required argument was not provided */ | |
269 | if (spec->type == CLI_OPT_TYPE_VALUE && !opt->value) | |
270 | opt->status = CLI_OPT_STATUS_MISSING_VALUE; | |
271 | else | |
272 | opt->status = CLI_OPT_STATUS_OK; | |
273 | ||
274 | consume_choices(opt->spec, parser); | |
275 | ||
276 | done: | |
277 | return opt->status; | |
278 | } | |
279 | ||
280 | static cli_opt_status_t parse_arg(cli_opt *opt, cli_opt_parser *parser) | |
281 | { | |
282 | const cli_opt_spec *spec = spec_for_arg(parser); | |
283 | ||
284 | opt->spec = spec; | |
285 | opt->arg = parser->args[parser->idx]; | |
286 | ||
287 | if (!spec) { | |
288 | parser->idx++; | |
289 | opt->status = CLI_OPT_STATUS_UNKNOWN_OPTION; | |
290 | } else if (spec->type == CLI_OPT_TYPE_ARGS) { | |
291 | if (spec->value) | |
292 | *((char ***)spec->value) = &parser->args[parser->idx]; | |
293 | ||
294 | /* | |
295 | * We have started a list of arguments; the remainder of | |
296 | * given arguments need not be examined. | |
297 | */ | |
298 | parser->in_args = (parser->args_len - parser->idx); | |
299 | parser->idx = parser->args_len; | |
300 | opt->args_len = parser->in_args; | |
301 | opt->status = CLI_OPT_STATUS_OK; | |
302 | } else { | |
303 | if (spec->value) | |
304 | *((char **)spec->value) = parser->args[parser->idx]; | |
305 | ||
306 | parser->idx++; | |
307 | opt->status = CLI_OPT_STATUS_OK; | |
308 | } | |
309 | ||
310 | return opt->status; | |
311 | } | |
312 | ||
313 | static int support_gnu_style(unsigned int flags) | |
314 | { | |
315 | if ((flags & CLI_OPT_PARSE_FORCE_GNU) != 0) | |
316 | return 1; | |
317 | ||
318 | if ((flags & CLI_OPT_PARSE_GNU) == 0) | |
319 | return 0; | |
320 | ||
321 | /* TODO: Windows */ | |
322 | #if defined(_WIN32) && defined(UNICODE) | |
323 | if (_wgetenv(L"POSIXLY_CORRECT") != NULL) | |
324 | return 0; | |
325 | #else | |
326 | if (getenv("POSIXLY_CORRECT") != NULL) | |
327 | return 0; | |
328 | #endif | |
329 | ||
330 | return 1; | |
331 | } | |
332 | ||
333 | void cli_opt_parser_init( | |
334 | cli_opt_parser *parser, | |
335 | const cli_opt_spec specs[], | |
336 | char **args, | |
337 | size_t args_len, | |
338 | unsigned int flags) | |
339 | { | |
340 | assert(parser); | |
341 | ||
342 | memset(parser, 0x0, sizeof(cli_opt_parser)); | |
343 | ||
344 | parser->specs = specs; | |
345 | parser->args = args; | |
346 | parser->args_len = args_len; | |
347 | parser->flags = flags; | |
348 | ||
349 | parser->needs_sort = support_gnu_style(flags); | |
350 | } | |
351 | ||
352 | GIT_INLINE(const cli_opt_spec *) spec_for_sort( | |
353 | int *needs_value, | |
354 | const cli_opt_parser *parser, | |
355 | const char *arg) | |
356 | { | |
357 | int is_negated, has_value = 0; | |
358 | const char *value; | |
359 | const cli_opt_spec *spec = NULL; | |
360 | size_t idx = 0; | |
361 | ||
362 | *needs_value = 0; | |
363 | ||
364 | if (strncmp(arg, "--", 2) == 0) { | |
365 | spec = spec_for_long(&is_negated, &has_value, &value, parser, &arg[2]); | |
366 | *needs_value = !has_value; | |
367 | } | |
368 | ||
369 | else if (strncmp(arg, "-", 1) == 0) { | |
370 | spec = spec_for_short(&value, parser, &arg[1]); | |
371 | ||
372 | /* | |
373 | * Advance through compressed short arguments to see if | |
374 | * the last one has a value, eg "-xvffilename". | |
375 | */ | |
376 | while (spec && !value && arg[1 + ++idx] != '\0') | |
377 | spec = spec_for_short(&value, parser, &arg[1 + idx]); | |
378 | ||
379 | *needs_value = (value == NULL); | |
380 | } | |
381 | ||
382 | return spec; | |
383 | } | |
384 | ||
385 | /* | |
386 | * Some parsers allow for handling arguments like "file1 --help file2"; | |
387 | * this is done by re-sorting the arguments in-place; emulate that. | |
388 | */ | |
389 | static int sort_gnu_style(cli_opt_parser *parser) | |
390 | { | |
391 | size_t i, j, insert_idx = parser->idx, offset; | |
392 | const cli_opt_spec *spec; | |
393 | char *option, *value; | |
394 | int needs_value, changed = 0; | |
395 | ||
396 | parser->needs_sort = 0; | |
397 | ||
398 | for (i = parser->idx; i < parser->args_len; i++) { | |
399 | spec = spec_for_sort(&needs_value, parser, parser->args[i]); | |
400 | ||
401 | /* Not a "-" or "--" prefixed option. No change. */ | |
402 | if (!spec) | |
403 | continue; | |
404 | ||
405 | /* A "--" alone means remaining args are literal. */ | |
406 | if (spec->type == CLI_OPT_TYPE_LITERAL) | |
407 | break; | |
408 | ||
409 | option = parser->args[i]; | |
410 | ||
411 | /* | |
412 | * If the argument is a value type and doesn't already | |
413 | * have a value (eg "--foo=bar" or "-fbar") then we need | |
414 | * to copy the next argument as its value. | |
415 | */ | |
416 | if (spec->type == CLI_OPT_TYPE_VALUE && needs_value) { | |
417 | /* | |
418 | * A required value is not provided; set parser | |
419 | * index to this value so that we fail on it. | |
420 | */ | |
421 | if (i + 1 >= parser->args_len) { | |
422 | parser->idx = i; | |
423 | return 1; | |
424 | } | |
425 | ||
426 | value = parser->args[i + 1]; | |
427 | offset = 1; | |
428 | } else { | |
429 | value = NULL; | |
430 | offset = 0; | |
431 | } | |
432 | ||
433 | /* Caller error if args[0] is an option. */ | |
434 | if (i == 0) | |
435 | return 0; | |
436 | ||
437 | /* Shift args up one (or two) and insert the option */ | |
438 | for (j = i; j > insert_idx; j--) | |
439 | parser->args[j + offset] = parser->args[j - 1]; | |
440 | ||
441 | parser->args[insert_idx] = option; | |
442 | ||
443 | if (value) | |
444 | parser->args[insert_idx + 1] = value; | |
445 | ||
446 | insert_idx += (1 + offset); | |
447 | i += offset; | |
448 | ||
449 | changed = 1; | |
450 | } | |
451 | ||
452 | return changed; | |
453 | } | |
454 | ||
455 | cli_opt_status_t cli_opt_parser_next(cli_opt *opt, cli_opt_parser *parser) | |
456 | { | |
457 | assert(opt && parser); | |
458 | ||
459 | memset(opt, 0x0, sizeof(cli_opt)); | |
460 | ||
461 | if (parser->idx >= parser->args_len) { | |
462 | opt->args_len = parser->in_args; | |
463 | return CLI_OPT_STATUS_DONE; | |
464 | } | |
465 | ||
466 | /* Handle options in long form, those beginning with "--" */ | |
467 | if (strncmp(parser->args[parser->idx], "--", 2) == 0 && | |
468 | !parser->in_short && | |
469 | !parser->in_literal) | |
470 | return parse_long(opt, parser); | |
471 | ||
472 | /* Handle options in short form, those beginning with "-" */ | |
473 | else if (parser->in_short || | |
474 | (strncmp(parser->args[parser->idx], "-", 1) == 0 && | |
475 | !parser->in_literal)) | |
476 | return parse_short(opt, parser); | |
477 | ||
478 | /* | |
479 | * We've reached the first "bare" argument. In POSIX mode, all | |
480 | * remaining items on the command line are arguments. In GNU | |
481 | * mode, there may be long or short options after this. Sort any | |
482 | * options up to this position then re-parse the current position. | |
483 | */ | |
484 | if (parser->needs_sort && sort_gnu_style(parser)) | |
485 | return cli_opt_parser_next(opt, parser); | |
486 | ||
487 | return parse_arg(opt, parser); | |
488 | } | |
489 | ||
490 | GIT_INLINE(int) spec_included(const cli_opt_spec **specs, const cli_opt_spec *spec) | |
491 | { | |
492 | const cli_opt_spec **i; | |
493 | ||
494 | for (i = specs; *i; ++i) { | |
495 | if (spec == *i) | |
496 | return 1; | |
497 | } | |
498 | ||
499 | return 0; | |
500 | } | |
501 | ||
502 | static cli_opt_status_t validate_required( | |
503 | cli_opt *opt, | |
504 | const cli_opt_spec specs[], | |
505 | const cli_opt_spec **given_specs) | |
506 | { | |
507 | const cli_opt_spec *spec, *required; | |
508 | int given; | |
509 | ||
510 | /* | |
511 | * Iterate over the possible specs to identify requirements and | |
512 | * ensure that those have been given on the command-line. | |
513 | * Note that we can have required *choices*, where one in a | |
514 | * list of choices must be specified. | |
515 | */ | |
516 | for (spec = specs, required = NULL, given = 0; spec->type; ++spec) { | |
517 | if (!required && (spec->usage & CLI_OPT_USAGE_REQUIRED)) { | |
518 | required = spec; | |
519 | given = 0; | |
520 | } else if (!required) { | |
521 | continue; | |
522 | } | |
523 | ||
524 | if (!given) | |
525 | given = spec_included(given_specs, spec); | |
526 | ||
527 | /* | |
528 | * Validate the requirement unless we're in a required | |
529 | * choice. In that case, keep the required state and | |
530 | * validate at the end of the choice list. | |
531 | */ | |
532 | if (!spec_is_choice(spec)) { | |
533 | if (!given) { | |
534 | opt->spec = required; | |
535 | opt->status = CLI_OPT_STATUS_MISSING_ARGUMENT; | |
536 | break; | |
537 | } | |
538 | ||
539 | required = NULL; | |
540 | given = 0; | |
541 | } | |
542 | } | |
543 | ||
544 | return opt->status; | |
545 | } | |
546 | ||
547 | cli_opt_status_t cli_opt_parse( | |
548 | cli_opt *opt, | |
549 | const cli_opt_spec specs[], | |
550 | char **args, | |
551 | size_t args_len, | |
552 | unsigned int flags) | |
553 | { | |
554 | cli_opt_parser parser; | |
555 | const cli_opt_spec **given_specs; | |
556 | size_t given_idx = 0; | |
557 | ||
558 | cli_opt_parser_init(&parser, specs, args, args_len, flags); | |
559 | ||
560 | given_specs = alloca(sizeof(const cli_opt_spec *) * (args_len + 1)); | |
561 | ||
562 | while (cli_opt_parser_next(opt, &parser)) { | |
563 | if (opt->status != CLI_OPT_STATUS_OK && | |
564 | opt->status != CLI_OPT_STATUS_DONE) | |
565 | return opt->status; | |
566 | ||
567 | if ((opt->spec->usage & CLI_OPT_USAGE_STOP_PARSING)) | |
568 | return (opt->status = CLI_OPT_STATUS_DONE); | |
569 | ||
570 | given_specs[given_idx++] = opt->spec; | |
571 | } | |
572 | ||
573 | given_specs[given_idx] = NULL; | |
574 | ||
575 | return validate_required(opt, specs, given_specs); | |
576 | } | |
577 | ||
578 | static int spec_name_fprint(FILE *file, const cli_opt_spec *spec) | |
579 | { | |
580 | int error; | |
581 | ||
582 | if (spec->type == CLI_OPT_TYPE_ARG) | |
583 | error = fprintf(file, "%s", spec->value_name); | |
584 | else if (spec->type == CLI_OPT_TYPE_ARGS) | |
585 | error = fprintf(file, "%s", spec->value_name); | |
586 | else if (spec->alias && !(spec->usage & CLI_OPT_USAGE_SHOW_LONG)) | |
587 | error = fprintf(file, "-%c", spec->alias); | |
588 | else | |
589 | error = fprintf(file, "--%s", spec->name); | |
590 | ||
591 | return error; | |
592 | } | |
593 | ||
594 | int cli_opt_status_fprint( | |
595 | FILE *file, | |
596 | const char *command, | |
597 | const cli_opt *opt) | |
598 | { | |
599 | const cli_opt_spec *choice; | |
600 | int error; | |
601 | ||
602 | if (command && (error = fprintf(file, "%s: ", command)) < 0) | |
603 | return error; | |
604 | ||
605 | switch (opt->status) { | |
606 | case CLI_OPT_STATUS_DONE: | |
607 | error = fprintf(file, "finished processing arguments (no error)\n"); | |
608 | break; | |
609 | case CLI_OPT_STATUS_OK: | |
610 | error = fprintf(file, "no error\n"); | |
611 | break; | |
612 | case CLI_OPT_STATUS_UNKNOWN_OPTION: | |
613 | error = fprintf(file, "unknown option: %s\n", opt->arg); | |
614 | break; | |
615 | case CLI_OPT_STATUS_MISSING_VALUE: | |
616 | if ((error = fprintf(file, "argument '")) < 0 || | |
617 | (error = spec_name_fprint(file, opt->spec)) < 0 || | |
618 | (error = fprintf(file, "' requires a value.\n")) < 0) | |
619 | break; | |
620 | break; | |
621 | case CLI_OPT_STATUS_MISSING_ARGUMENT: | |
622 | if (spec_is_choice(opt->spec)) { | |
623 | int is_choice = 1; | |
624 | ||
625 | if (spec_is_choice((opt->spec)+1)) | |
626 | error = fprintf(file, "one of"); | |
627 | else | |
628 | error = fprintf(file, "either"); | |
629 | ||
630 | if (error < 0) | |
631 | break; | |
632 | ||
633 | for (choice = opt->spec; is_choice; ++choice) { | |
634 | is_choice = spec_is_choice(choice); | |
635 | ||
636 | if (!is_choice) | |
637 | error = fprintf(file, " or"); | |
638 | else if (choice != opt->spec) | |
639 | error = fprintf(file, ","); | |
640 | ||
641 | if ((error < 0) || | |
642 | (error = fprintf(file, " '")) < 0 || | |
643 | (error = spec_name_fprint(file, choice)) < 0 || | |
644 | (error = fprintf(file, "'")) < 0) | |
645 | break; | |
646 | ||
647 | if (!spec_is_choice(choice)) | |
648 | break; | |
649 | } | |
650 | ||
651 | if ((error < 0) || | |
652 | (error = fprintf(file, " is required.\n")) < 0) | |
653 | break; | |
654 | } else { | |
655 | if ((error = fprintf(file, "argument '")) < 0 || | |
656 | (error = spec_name_fprint(file, opt->spec)) < 0 || | |
657 | (error = fprintf(file, "' is required.\n")) < 0) | |
658 | break; | |
659 | } | |
660 | ||
661 | break; | |
662 | default: | |
663 | error = fprintf(file, "unknown status: %d\n", opt->status); | |
664 | break; | |
665 | } | |
666 | ||
667 | return error; | |
668 | } | |
669 |