2 * Copyright (c), Edward Thomson <ethomson@edwardthomson.com>
5 * This file is part of adopt, distributed under the MIT license.
6 * For full terms and conditions, see the included LICENSE file.
8 * THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT.
10 * This file was produced by using the `rename.pl` script included with
11 * adopt. The command-line specified was:
13 * ./rename.pl cli_opt --filename=opt --include=cli.h --inline=GIT_INLINE --header-guard=CLI_opt_h__ --lowercase-status --without-usage
29 # include <sys/ioctl.h>
33 # define alloca _alloca
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)
41 GIT_INLINE(const cli_opt_spec
*) spec_for_long(
45 const cli_opt_parser
*parser
,
48 const cli_opt_spec
*spec
;
52 eql
= strchr(arg
, '=');
53 eql_pos
= (eql
= strchr(arg
, '=')) ? (size_t)(eql
- arg
) : strlen(arg
);
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')
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) {
68 /* Handle the typical --option arguments */
69 if (spec_is_option_type(spec
) &&
71 strcmp(arg
, spec
->name
) == 0)
74 /* Handle --option=value arguments */
75 if (spec
->type
== CLI_OPT_TYPE_VALUE
&&
77 strncmp(arg
, spec
->name
, eql_pos
) == 0 &&
78 spec
->name
[eql_pos
] == '\0') {
80 *value
= arg
[eql_pos
+ 1] ? &arg
[eql_pos
+ 1] : NULL
;
88 GIT_INLINE(const cli_opt_spec
*) spec_for_short(
90 const cli_opt_parser
*parser
,
93 const cli_opt_spec
*spec
;
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
&&
104 /* Handle typical -s short options */
105 if (arg
[0] == spec
->alias
) {
114 GIT_INLINE(const cli_opt_spec
*) spec_for_arg(cli_opt_parser
*parser
)
116 const cli_opt_spec
*spec
;
119 for (spec
= parser
->specs
; spec
->type
; ++spec
) {
120 if (spec
->type
== CLI_OPT_TYPE_ARG
) {
121 if (args
== parser
->arg_idx
) {
129 if (spec
->type
== CLI_OPT_TYPE_ARGS
&& args
== parser
->arg_idx
)
136 GIT_INLINE(int) spec_is_choice(const cli_opt_spec
*spec
)
138 return ((spec
+ 1)->type
&&
139 ((spec
+ 1)->usage
& CLI_OPT_USAGE_CHOICE
));
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.
146 GIT_INLINE(void) consume_choices(const cli_opt_spec
*spec
, cli_opt_parser
*parser
)
148 /* back up to the beginning of the choices */
149 while (spec
->type
&& (spec
->usage
& CLI_OPT_USAGE_CHOICE
))
152 if (!spec_is_choice(spec
))
156 if (spec
->type
== CLI_OPT_TYPE_ARG
)
159 } while(spec
->type
&& (spec
->usage
& CLI_OPT_USAGE_CHOICE
));
162 static cli_opt_status_t
parse_long(cli_opt
*opt
, cli_opt_parser
*parser
)
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;
171 if ((spec
= spec_for_long(&is_negated
, &has_value
, &value
, parser
, &arg
[2])) == NULL
) {
173 opt
->status
= CLI_OPT_STATUS_UNKNOWN_OPTION
;
179 /* Future options parsed as literal */
180 if (spec
->type
== CLI_OPT_TYPE_LITERAL
)
181 parser
->in_literal
= 1;
183 /* --bool or --no-bool */
184 else if (spec
->type
== CLI_OPT_TYPE_BOOL
&& spec
->value
)
185 *((int *)spec
->value
) = !is_negated
;
188 else if (spec
->type
== CLI_OPT_TYPE_ACCUMULATOR
&& spec
->value
)
189 *((int *)spec
->value
) += spec
->switch_value
? spec
->switch_value
: 1;
192 else if (spec
->type
== CLI_OPT_TYPE_SWITCH
&& spec
->value
)
193 *((int *)spec
->value
) = spec
->switch_value
;
195 /* Parse values as "--foo=bar" or "--foo bar" */
196 else if (spec
->type
== CLI_OPT_TYPE_VALUE
) {
198 opt
->value
= (char *)value
;
199 else if ((parser
->idx
+ 1) <= parser
->args_len
)
200 opt
->value
= parser
->args
[parser
->idx
++];
203 *((char **)spec
->value
) = opt
->value
;
206 /* Required argument was not provided */
207 if (spec
->type
== CLI_OPT_TYPE_VALUE
&&
209 !(spec
->usage
& CLI_OPT_USAGE_VALUE_OPTIONAL
))
210 opt
->status
= CLI_OPT_STATUS_MISSING_VALUE
;
212 opt
->status
= CLI_OPT_STATUS_OK
;
214 consume_choices(opt
->spec
, parser
);
220 static cli_opt_status_t
parse_short(cli_opt
*opt
, cli_opt_parser
*parser
)
222 const cli_opt_spec
*spec
;
223 char *arg
= parser
->args
[parser
->idx
++];
228 if ((spec
= spec_for_short(&value
, parser
, &arg
[1 + parser
->in_short
])) == NULL
) {
230 opt
->status
= CLI_OPT_STATUS_UNKNOWN_OPTION
;
236 if (spec
->type
== CLI_OPT_TYPE_BOOL
&& spec
->value
)
237 *((int *)spec
->value
) = 1;
239 else if (spec
->type
== CLI_OPT_TYPE_ACCUMULATOR
&& spec
->value
)
240 *((int *)spec
->value
) += spec
->switch_value
? spec
->switch_value
: 1;
242 else if (spec
->type
== CLI_OPT_TYPE_SWITCH
&& spec
->value
)
243 *((int *)spec
->value
) = spec
->switch_value
;
245 /* Parse values as "-ifoo" or "-i foo" */
246 else if (spec
->type
== CLI_OPT_TYPE_VALUE
) {
248 opt
->value
= (char *)value
;
249 else if ((parser
->idx
+ 1) <= parser
->args_len
)
250 opt
->value
= parser
->args
[parser
->idx
++];
253 *((char **)spec
->value
) = opt
->value
;
257 * Handle compressed short arguments, like "-fbcd"; see if there's
258 * another character after the one we processed. If not, advance
261 if (spec
->type
!= CLI_OPT_TYPE_VALUE
&& arg
[2 + parser
->in_short
] != '\0') {
265 parser
->in_short
= 0;
268 /* Required argument was not provided */
269 if (spec
->type
== CLI_OPT_TYPE_VALUE
&& !opt
->value
)
270 opt
->status
= CLI_OPT_STATUS_MISSING_VALUE
;
272 opt
->status
= CLI_OPT_STATUS_OK
;
274 consume_choices(opt
->spec
, parser
);
280 static cli_opt_status_t
parse_arg(cli_opt
*opt
, cli_opt_parser
*parser
)
282 const cli_opt_spec
*spec
= spec_for_arg(parser
);
285 opt
->arg
= parser
->args
[parser
->idx
];
289 opt
->status
= CLI_OPT_STATUS_UNKNOWN_OPTION
;
290 } else if (spec
->type
== CLI_OPT_TYPE_ARGS
) {
292 *((char ***)spec
->value
) = &parser
->args
[parser
->idx
];
295 * We have started a list of arguments; the remainder of
296 * given arguments need not be examined.
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
;
304 *((char **)spec
->value
) = parser
->args
[parser
->idx
];
307 opt
->status
= CLI_OPT_STATUS_OK
;
313 static int support_gnu_style(unsigned int flags
)
315 if ((flags
& CLI_OPT_PARSE_FORCE_GNU
) != 0)
318 if ((flags
& CLI_OPT_PARSE_GNU
) == 0)
322 #if defined(_WIN32) && defined(UNICODE)
323 if (_wgetenv(L
"POSIXLY_CORRECT") != NULL
)
326 if (getenv("POSIXLY_CORRECT") != NULL
)
333 void cli_opt_parser_init(
334 cli_opt_parser
*parser
,
335 const cli_opt_spec specs
[],
342 memset(parser
, 0x0, sizeof(cli_opt_parser
));
344 parser
->specs
= specs
;
346 parser
->args_len
= args_len
;
347 parser
->flags
= flags
;
349 parser
->needs_sort
= support_gnu_style(flags
);
352 GIT_INLINE(const cli_opt_spec
*) spec_for_sort(
354 const cli_opt_parser
*parser
,
357 int is_negated
, has_value
= 0;
359 const cli_opt_spec
*spec
= NULL
;
364 if (strncmp(arg
, "--", 2) == 0) {
365 spec
= spec_for_long(&is_negated
, &has_value
, &value
, parser
, &arg
[2]);
366 *needs_value
= !has_value
;
369 else if (strncmp(arg
, "-", 1) == 0) {
370 spec
= spec_for_short(&value
, parser
, &arg
[1]);
373 * Advance through compressed short arguments to see if
374 * the last one has a value, eg "-xvffilename".
376 while (spec
&& !value
&& arg
[1 + ++idx
] != '\0')
377 spec
= spec_for_short(&value
, parser
, &arg
[1 + idx
]);
379 *needs_value
= (value
== NULL
);
386 * Some parsers allow for handling arguments like "file1 --help file2";
387 * this is done by re-sorting the arguments in-place; emulate that.
389 static int sort_gnu_style(cli_opt_parser
*parser
)
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;
396 parser
->needs_sort
= 0;
398 for (i
= parser
->idx
; i
< parser
->args_len
; i
++) {
399 spec
= spec_for_sort(&needs_value
, parser
, parser
->args
[i
]);
401 /* Not a "-" or "--" prefixed option. No change. */
405 /* A "--" alone means remaining args are literal. */
406 if (spec
->type
== CLI_OPT_TYPE_LITERAL
)
409 option
= parser
->args
[i
];
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.
416 if (spec
->type
== CLI_OPT_TYPE_VALUE
&& needs_value
) {
418 * A required value is not provided; set parser
419 * index to this value so that we fail on it.
421 if (i
+ 1 >= parser
->args_len
) {
426 value
= parser
->args
[i
+ 1];
433 /* Caller error if args[0] is an option. */
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];
441 parser
->args
[insert_idx
] = option
;
444 parser
->args
[insert_idx
+ 1] = value
;
446 insert_idx
+= (1 + offset
);
455 cli_opt_status_t
cli_opt_parser_next(cli_opt
*opt
, cli_opt_parser
*parser
)
457 assert(opt
&& parser
);
459 memset(opt
, 0x0, sizeof(cli_opt
));
461 if (parser
->idx
>= parser
->args_len
) {
462 opt
->args_len
= parser
->in_args
;
463 return CLI_OPT_STATUS_DONE
;
466 /* Handle options in long form, those beginning with "--" */
467 if (strncmp(parser
->args
[parser
->idx
], "--", 2) == 0 &&
470 return parse_long(opt
, parser
);
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
);
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.
484 if (parser
->needs_sort
&& sort_gnu_style(parser
))
485 return cli_opt_parser_next(opt
, parser
);
487 return parse_arg(opt
, parser
);
490 GIT_INLINE(int) spec_included(const cli_opt_spec
**specs
, const cli_opt_spec
*spec
)
492 const cli_opt_spec
**i
;
494 for (i
= specs
; *i
; ++i
) {
502 static cli_opt_status_t
validate_required(
504 const cli_opt_spec specs
[],
505 const cli_opt_spec
**given_specs
)
507 const cli_opt_spec
*spec
, *required
;
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.
516 for (spec
= specs
, required
= NULL
, given
= 0; spec
->type
; ++spec
) {
517 if (!required
&& (spec
->usage
& CLI_OPT_USAGE_REQUIRED
)) {
520 } else if (!required
) {
525 given
= spec_included(given_specs
, spec
);
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.
532 if (!spec_is_choice(spec
)) {
534 opt
->spec
= required
;
535 opt
->status
= CLI_OPT_STATUS_MISSING_ARGUMENT
;
547 cli_opt_status_t
cli_opt_parse(
549 const cli_opt_spec specs
[],
554 cli_opt_parser parser
;
555 const cli_opt_spec
**given_specs
;
556 size_t given_idx
= 0;
558 cli_opt_parser_init(&parser
, specs
, args
, args_len
, flags
);
560 given_specs
= alloca(sizeof(const cli_opt_spec
*) * (args_len
+ 1));
562 while (cli_opt_parser_next(opt
, &parser
)) {
563 if (opt
->status
!= CLI_OPT_STATUS_OK
&&
564 opt
->status
!= CLI_OPT_STATUS_DONE
)
567 if ((opt
->spec
->usage
& CLI_OPT_USAGE_STOP_PARSING
))
568 return (opt
->status
= CLI_OPT_STATUS_DONE
);
570 given_specs
[given_idx
++] = opt
->spec
;
573 given_specs
[given_idx
] = NULL
;
575 return validate_required(opt
, specs
, given_specs
);
578 static int spec_name_fprint(FILE *file
, const cli_opt_spec
*spec
)
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
);
589 error
= fprintf(file
, "--%s", spec
->name
);
594 int cli_opt_status_fprint(
599 const cli_opt_spec
*choice
;
602 if (command
&& (error
= fprintf(file
, "%s: ", command
)) < 0)
605 switch (opt
->status
) {
606 case CLI_OPT_STATUS_DONE
:
607 error
= fprintf(file
, "finished processing arguments (no error)\n");
609 case CLI_OPT_STATUS_OK
:
610 error
= fprintf(file
, "no error\n");
612 case CLI_OPT_STATUS_UNKNOWN_OPTION
:
613 error
= fprintf(file
, "unknown option: %s\n", opt
->arg
);
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)
621 case CLI_OPT_STATUS_MISSING_ARGUMENT
:
622 if (spec_is_choice(opt
->spec
)) {
625 if (spec_is_choice((opt
->spec
)+1))
626 error
= fprintf(file
, "one of");
628 error
= fprintf(file
, "either");
633 for (choice
= opt
->spec
; is_choice
; ++choice
) {
634 is_choice
= spec_is_choice(choice
);
637 error
= fprintf(file
, " or");
638 else if (choice
!= opt
->spec
)
639 error
= fprintf(file
, ",");
642 (error
= fprintf(file
, " '")) < 0 ||
643 (error
= spec_name_fprint(file
, choice
)) < 0 ||
644 (error
= fprintf(file
, "'")) < 0)
647 if (!spec_is_choice(choice
))
652 (error
= fprintf(file
, " is required.\n")) < 0)
655 if ((error
= fprintf(file
, "argument '")) < 0 ||
656 (error
= spec_name_fprint(file
, opt
->spec
)) < 0 ||
657 (error
= fprintf(file
, "' is required.\n")) < 0)
663 error
= fprintf(file
, "unknown status: %d\n", opt
->status
);