2 * SPDX-License-Identifier: MIT
4 * Copyright (c) 2019-2021 Philippe Proulx <pproulx@efficios.com>
5 * Copyright (c) 2020-2021 Simon Marchi <simon.marchi@efficios.com>
16 #define ARGPAR_REALLOC(_ptr, _type, _nmemb) \
17 ((_type *) realloc(_ptr, (_nmemb) * sizeof(_type)))
19 #define ARGPAR_CALLOC(_type, _nmemb) \
20 ((_type *) calloc((_nmemb), sizeof(_type)))
22 #define ARGPAR_ZALLOC(_type) ARGPAR_CALLOC(_type, 1)
26 * Force usage of the assertion condition to prevent unused variable warnings
27 * when `assert()` are disabled by the `NDEBUG` definition.
29 # define ARGPAR_ASSERT(_cond) ((void) sizeof((void) (_cond), 0))
32 # define ARGPAR_ASSERT(_cond) assert(_cond)
38 * Such a structure contains the state of an iterator between calls to
43 * Data provided by the user to argpar_iter_create(); immutable
48 const char * const *argv
;
49 const struct argpar_opt_descr
*descrs
;
53 * Index of the argument to process in the next
54 * argpar_iter_next() call.
58 /* Counter of non-option arguments */
62 * Current character within the current short option group: if
63 * it's not `NULL`, the parser is within a short option group,
64 * therefore it must resume there in the next argpar_iter_next()
67 const char *short_opt_group_ch
;
69 /* Temporary character buffer which only grows */
76 /* Base parsing item */
78 enum argpar_item_type type
;
81 /* Option parsing item */
82 struct argpar_item_opt
{
83 struct argpar_item base
;
85 /* Corresponding descriptor */
86 const struct argpar_opt_descr
*descr
;
88 /* Argument, or `NULL` if none; owned by this */
92 /* Non-option parsing item */
93 struct argpar_item_non_opt
{
94 struct argpar_item base
;
97 * Complete argument, pointing to one of the entries of the
98 * original arguments (`argv`).
103 * Index of this argument amongst all original arguments
106 unsigned int orig_index
;
108 /* Index of this argument amongst other non-option arguments */
109 unsigned int non_opt_index
;
113 struct argpar_error
{
115 enum argpar_error_type type
;
117 /* Original argument index */
118 unsigned int orig_index
;
120 /* Name of unknown option; owned by this */
121 char *unknown_opt_name
;
123 /* Option descriptor */
124 const struct argpar_opt_descr
*opt_descr
;
126 /* `true` if a short option caused the error */
131 enum argpar_item_type
argpar_item_type(const struct argpar_item
* const item
)
138 const struct argpar_opt_descr
*argpar_item_opt_descr(
139 const struct argpar_item
* const item
)
142 ARGPAR_ASSERT(item
->type
== ARGPAR_ITEM_TYPE_OPT
);
143 return ((const struct argpar_item_opt
*) item
)->descr
;
147 const char *argpar_item_opt_arg(const struct argpar_item
* const item
)
150 ARGPAR_ASSERT(item
->type
== ARGPAR_ITEM_TYPE_OPT
);
151 return ((const struct argpar_item_opt
*) item
)->arg
;
155 const char *argpar_item_non_opt_arg(const struct argpar_item
* const item
)
158 ARGPAR_ASSERT(item
->type
== ARGPAR_ITEM_TYPE_NON_OPT
);
159 return ((const struct argpar_item_non_opt
*) item
)->arg
;
163 unsigned int argpar_item_non_opt_orig_index(
164 const struct argpar_item
* const item
)
167 ARGPAR_ASSERT(item
->type
== ARGPAR_ITEM_TYPE_NON_OPT
);
168 return ((const struct argpar_item_non_opt
*) item
)->orig_index
;
172 unsigned int argpar_item_non_opt_non_opt_index(
173 const struct argpar_item
* const item
)
176 ARGPAR_ASSERT(item
->type
== ARGPAR_ITEM_TYPE_NON_OPT
);
177 return ((const struct argpar_item_non_opt
*) item
)->non_opt_index
;
181 void argpar_item_destroy(const struct argpar_item
* const item
)
187 if (item
->type
== ARGPAR_ITEM_TYPE_OPT
) {
188 struct argpar_item_opt
* const opt_item
=
189 (struct argpar_item_opt
*) item
;
201 * Creates and returns an option parsing item for the descriptor `descr`
202 * and having the argument `arg` (copied; may be `NULL`).
204 * Returns `NULL` on memory error.
207 struct argpar_item_opt
*create_opt_item(
208 const struct argpar_opt_descr
* const descr
,
209 const char * const arg
)
211 struct argpar_item_opt
*opt_item
=
212 ARGPAR_ZALLOC(struct argpar_item_opt
);
218 opt_item
->base
.type
= ARGPAR_ITEM_TYPE_OPT
;
219 opt_item
->descr
= descr
;
222 opt_item
->arg
= strdup(arg
);
223 if (!opt_item
->arg
) {
231 argpar_item_destroy(&opt_item
->base
);
239 * Creates and returns a non-option parsing item for the original
240 * argument `arg` having the original index `orig_index` and the
241 * non-option index `non_opt_index`.
243 * Returns `NULL` on memory error.
246 struct argpar_item_non_opt
*create_non_opt_item(const char * const arg
,
247 const unsigned int orig_index
,
248 const unsigned int non_opt_index
)
250 struct argpar_item_non_opt
* const non_opt_item
=
251 ARGPAR_ZALLOC(struct argpar_item_non_opt
);
257 non_opt_item
->base
.type
= ARGPAR_ITEM_TYPE_NON_OPT
;
258 non_opt_item
->arg
= arg
;
259 non_opt_item
->orig_index
= orig_index
;
260 non_opt_item
->non_opt_index
= non_opt_index
;
267 * If `error` is not `NULL`, sets the error `error` to a new parsing
268 * error object, setting its `unknown_opt_name`, `opt_descr`, and
269 * `is_short` members from the parameters.
271 * `unknown_opt_name` is the unknown option name without any `-` or `--`
272 * prefix: `is_short` controls which type of unknown option it is.
274 * Returns 0 on success (including if `error` is `NULL`) or -1 on memory
278 int set_error(struct argpar_error
** const error
,
279 enum argpar_error_type type
,
280 const char * const unknown_opt_name
,
281 const struct argpar_opt_descr
* const opt_descr
,
290 *error
= ARGPAR_ZALLOC(struct argpar_error
);
295 (*error
)->type
= type
;
297 if (unknown_opt_name
) {
298 (*error
)->unknown_opt_name
= ARGPAR_CALLOC(char,
299 strlen(unknown_opt_name
) + 1 + (is_short
? 1 : 2));
300 if (!(*error
)->unknown_opt_name
) {
305 strcpy((*error
)->unknown_opt_name
, "-");
307 strcpy((*error
)->unknown_opt_name
, "--");
310 strcat((*error
)->unknown_opt_name
, unknown_opt_name
);
313 (*error
)->opt_descr
= opt_descr
;
314 (*error
)->is_short
= is_short
;
318 argpar_error_destroy(*error
);
326 enum argpar_error_type
argpar_error_type(
327 const struct argpar_error
* const error
)
329 ARGPAR_ASSERT(error
);
334 unsigned int argpar_error_orig_index(const struct argpar_error
* const error
)
336 ARGPAR_ASSERT(error
);
337 return error
->orig_index
;
341 const char *argpar_error_unknown_opt_name(
342 const struct argpar_error
* const error
)
344 ARGPAR_ASSERT(error
);
345 ARGPAR_ASSERT(error
->type
== ARGPAR_ERROR_TYPE_UNKNOWN_OPT
);
346 ARGPAR_ASSERT(error
->unknown_opt_name
);
347 return error
->unknown_opt_name
;
351 const struct argpar_opt_descr
*argpar_error_opt_descr(
352 const struct argpar_error
* const error
, bool * const is_short
)
354 ARGPAR_ASSERT(error
);
355 ARGPAR_ASSERT(error
->type
== ARGPAR_ERROR_TYPE_MISSING_OPT_ARG
||
356 error
->type
== ARGPAR_ERROR_TYPE_UNEXPECTED_OPT_ARG
);
357 ARGPAR_ASSERT(error
->opt_descr
);
360 *is_short
= error
->is_short
;
363 return error
->opt_descr
;
367 void argpar_error_destroy(const struct argpar_error
* const error
)
370 free(error
->unknown_opt_name
);
371 free((void *) error
);
376 * Finds and returns the _first_ descriptor having the short option name
377 * `short_name` or the long option name `long_name` within the option
378 * descriptors `descrs`.
380 * `short_name` may be `'\0'` to not consider it.
382 * `long_name` may be `NULL` to not consider it.
384 * Returns `NULL` if no descriptor is found.
387 const struct argpar_opt_descr
*find_descr(
388 const struct argpar_opt_descr
* const descrs
,
389 const char short_name
, const char * const long_name
)
391 const struct argpar_opt_descr
*descr
;
393 for (descr
= descrs
; descr
->short_name
|| descr
->long_name
; descr
++) {
394 if (short_name
&& descr
->short_name
&&
395 short_name
== descr
->short_name
) {
399 if (long_name
&& descr
->long_name
&&
400 strcmp(long_name
, descr
->long_name
) == 0) {
406 return !descr
->short_name
&& !descr
->long_name
? NULL
: descr
;
409 /* Return type of parse_short_opt_group() and parse_long_opt() */
410 enum parse_orig_arg_opt_ret
{
411 PARSE_ORIG_ARG_OPT_RET_OK
,
412 PARSE_ORIG_ARG_OPT_RET_ERROR
= -1,
413 PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY
= -2,
417 * Parses the short option group argument `short_opt_group`, starting
418 * where needed depending on the state of `iter`.
420 * On success, sets `*item`.
422 * On error (except for `PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY`), sets
426 enum parse_orig_arg_opt_ret
parse_short_opt_group(
427 const char * const short_opt_group
,
428 const char * const next_orig_arg
,
429 const struct argpar_opt_descr
* const descrs
,
430 struct argpar_iter
* const iter
,
431 struct argpar_error
** const error
,
432 struct argpar_item
** const item
)
434 enum parse_orig_arg_opt_ret ret
= PARSE_ORIG_ARG_OPT_RET_OK
;
435 bool used_next_orig_arg
= false;
436 const char *opt_arg
= NULL
;
437 const struct argpar_opt_descr
*descr
;
438 struct argpar_item_opt
*opt_item
;
440 ARGPAR_ASSERT(strlen(short_opt_group
) != 0);
442 if (!iter
->short_opt_group_ch
) {
443 iter
->short_opt_group_ch
= short_opt_group
;
446 /* Find corresponding option descriptor */
447 descr
= find_descr(descrs
, *iter
->short_opt_group_ch
, NULL
);
449 const char unknown_opt_name
[] =
450 {*iter
->short_opt_group_ch
, '\0'};
452 ret
= PARSE_ORIG_ARG_OPT_RET_ERROR
;
454 if (set_error(error
, ARGPAR_ERROR_TYPE_UNKNOWN_OPT
,
455 unknown_opt_name
, NULL
, true)) {
456 ret
= PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY
;
462 if (descr
->with_arg
) {
463 if (iter
->short_opt_group_ch
[1]) {
465 opt_arg
= &iter
->short_opt_group_ch
[1];
468 opt_arg
= next_orig_arg
;
469 used_next_orig_arg
= true;
473 * We accept `-o ''` (empty option argument), but not
474 * `-o` alone if an option argument is expected.
476 if (!opt_arg
|| (iter
->short_opt_group_ch
[1] &&
477 strlen(opt_arg
) == 0)) {
478 ret
= PARSE_ORIG_ARG_OPT_RET_ERROR
;
480 if (set_error(error
, ARGPAR_ERROR_TYPE_MISSING_OPT_ARG
,
481 NULL
, descr
, true)) {
482 ret
= PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY
;
489 /* Create and append option argument */
490 opt_item
= create_opt_item(descr
, opt_arg
);
492 ret
= PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY
;
496 *item
= &opt_item
->base
;
497 iter
->short_opt_group_ch
++;
499 if (descr
->with_arg
|| !*iter
->short_opt_group_ch
) {
500 /* Option has an argument: no more options */
501 iter
->short_opt_group_ch
= NULL
;
503 if (used_next_orig_arg
) {
513 ARGPAR_ASSERT(ret
!= PARSE_ORIG_ARG_OPT_RET_OK
);
520 * Parses the long option argument `long_opt_arg`.
522 * On success, sets `*item`.
524 * On error (except for `PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY`), sets
528 enum parse_orig_arg_opt_ret
parse_long_opt(const char * const long_opt_arg
,
529 const char * const next_orig_arg
,
530 const struct argpar_opt_descr
* const descrs
,
531 struct argpar_iter
* const iter
,
532 struct argpar_error
** const error
,
533 struct argpar_item
** const item
)
535 enum parse_orig_arg_opt_ret ret
= PARSE_ORIG_ARG_OPT_RET_OK
;
536 const struct argpar_opt_descr
*descr
;
537 struct argpar_item_opt
*opt_item
;
538 bool used_next_orig_arg
= false;
540 /* Option's argument, if any */
541 const char *opt_arg
= NULL
;
543 /* Position of first `=`, if any */
547 const char *long_opt_name
= long_opt_arg
;
549 ARGPAR_ASSERT(strlen(long_opt_arg
) != 0);
551 /* Find the first `=` in original argument */
552 eq_pos
= strchr(long_opt_arg
, '=');
554 const size_t long_opt_name_size
= eq_pos
- long_opt_arg
;
556 /* Isolate the option name */
557 while (long_opt_name_size
> iter
->tmp_buf
.size
- 1) {
558 iter
->tmp_buf
.size
*= 2;
559 iter
->tmp_buf
.data
= ARGPAR_REALLOC(iter
->tmp_buf
.data
,
560 char, iter
->tmp_buf
.size
);
561 if (!iter
->tmp_buf
.data
) {
562 ret
= PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY
;
567 memcpy(iter
->tmp_buf
.data
, long_opt_arg
, long_opt_name_size
);
568 iter
->tmp_buf
.data
[long_opt_name_size
] = '\0';
569 long_opt_name
= iter
->tmp_buf
.data
;
572 /* Find corresponding option descriptor */
573 descr
= find_descr(descrs
, '\0', long_opt_name
);
575 ret
= PARSE_ORIG_ARG_OPT_RET_ERROR
;
577 if (set_error(error
, ARGPAR_ERROR_TYPE_UNKNOWN_OPT
,
578 long_opt_name
, NULL
, false)) {
579 ret
= PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY
;
585 /* Find option's argument if any */
586 if (descr
->with_arg
) {
588 /* `--long-opt=arg` style */
589 opt_arg
= eq_pos
+ 1;
591 /* `--long-opt arg` style */
592 if (!next_orig_arg
) {
593 ret
= PARSE_ORIG_ARG_OPT_RET_ERROR
;
595 if (set_error(error
, ARGPAR_ERROR_TYPE_MISSING_OPT_ARG
,
596 NULL
, descr
, false)) {
597 ret
= PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY
;
603 opt_arg
= next_orig_arg
;
604 used_next_orig_arg
= true;
608 * Unexpected `--opt=arg` style for a long option which
609 * doesn't accept an argument.
611 ret
= PARSE_ORIG_ARG_OPT_RET_ERROR
;
613 if (set_error(error
, ARGPAR_ERROR_TYPE_UNEXPECTED_OPT_ARG
,
614 NULL
, descr
, false)) {
615 ret
= PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY
;
621 /* Create and append option argument */
622 opt_item
= create_opt_item(descr
, opt_arg
);
627 if (used_next_orig_arg
) {
633 *item
= &opt_item
->base
;
637 ARGPAR_ASSERT(ret
!= PARSE_ORIG_ARG_OPT_RET_OK
);
644 * Parses the original argument `orig_arg`.
646 * On success, sets `*item`.
648 * On error (except for `PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY`), sets
652 enum parse_orig_arg_opt_ret
parse_orig_arg_opt(const char * const orig_arg
,
653 const char * const next_orig_arg
,
654 const struct argpar_opt_descr
* const descrs
,
655 struct argpar_iter
* const iter
,
656 struct argpar_error
** const error
,
657 struct argpar_item
** const item
)
659 enum parse_orig_arg_opt_ret ret
= PARSE_ORIG_ARG_OPT_RET_OK
;
661 ARGPAR_ASSERT(orig_arg
[0] == '-');
663 if (orig_arg
[1] == '-') {
665 ret
= parse_long_opt(&orig_arg
[2],
666 next_orig_arg
, descrs
, iter
, error
, item
);
669 ret
= parse_short_opt_group(&orig_arg
[1],
670 next_orig_arg
, descrs
, iter
, error
, item
);
677 struct argpar_iter
*argpar_iter_create(const unsigned int argc
,
678 const char * const * const argv
,
679 const struct argpar_opt_descr
* const descrs
)
681 struct argpar_iter
*iter
= ARGPAR_ZALLOC(struct argpar_iter
);
687 iter
->user
.argc
= argc
;
688 iter
->user
.argv
= argv
;
689 iter
->user
.descrs
= descrs
;
690 iter
->tmp_buf
.size
= 128;
691 iter
->tmp_buf
.data
= ARGPAR_CALLOC(char, iter
->tmp_buf
.size
);
692 if (!iter
->tmp_buf
.data
) {
693 argpar_iter_destroy(iter
);
703 void argpar_iter_destroy(struct argpar_iter
* const iter
)
706 free(iter
->tmp_buf
.data
);
712 enum argpar_iter_next_status
argpar_iter_next(
713 struct argpar_iter
* const iter
,
714 const struct argpar_item
** const item
,
715 const struct argpar_error
** const error
)
717 enum argpar_iter_next_status status
;
718 enum parse_orig_arg_opt_ret parse_orig_arg_opt_ret
;
719 const char *orig_arg
;
720 const char *next_orig_arg
;
721 struct argpar_error
** const nc_error
= (struct argpar_error
**) error
;
723 ARGPAR_ASSERT(iter
->i
<= iter
->user
.argc
);
729 if (iter
->i
== iter
->user
.argc
) {
730 status
= ARGPAR_ITER_NEXT_STATUS_END
;
734 orig_arg
= iter
->user
.argv
[iter
->i
];
736 iter
->i
< (iter
->user
.argc
- 1) ?
737 iter
->user
.argv
[iter
->i
+ 1] : NULL
;
739 if (strcmp(orig_arg
, "-") == 0 || strcmp(orig_arg
, "--") == 0 ||
740 orig_arg
[0] != '-') {
741 /* Non-option argument */
742 const struct argpar_item_non_opt
* const non_opt_item
=
743 create_non_opt_item(orig_arg
, iter
->i
,
744 iter
->non_opt_index
);
747 status
= ARGPAR_ITER_NEXT_STATUS_ERROR_MEMORY
;
751 iter
->non_opt_index
++;
753 *item
= &non_opt_item
->base
;
754 status
= ARGPAR_ITER_NEXT_STATUS_OK
;
758 /* Option argument */
759 parse_orig_arg_opt_ret
= parse_orig_arg_opt(orig_arg
,
760 next_orig_arg
, iter
->user
.descrs
, iter
, nc_error
,
761 (struct argpar_item
**) item
);
762 switch (parse_orig_arg_opt_ret
) {
763 case PARSE_ORIG_ARG_OPT_RET_OK
:
764 status
= ARGPAR_ITER_NEXT_STATUS_OK
;
766 case PARSE_ORIG_ARG_OPT_RET_ERROR
:
768 ARGPAR_ASSERT(*error
);
769 (*nc_error
)->orig_index
= iter
->i
;
771 status
= ARGPAR_ITER_NEXT_STATUS_ERROR
;
773 case PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY
:
774 status
= ARGPAR_ITER_NEXT_STATUS_ERROR_MEMORY
;
785 unsigned int argpar_iter_ingested_orig_args(
786 const struct argpar_iter
* const iter
)