10f6ee8d42e93a780a65ad4c5cf66347d667c42d
[argpar.git] / argpar / argpar.c
1 /*
2 * SPDX-License-Identifier: MIT
3 *
4 * Copyright (c) 2019-2021 Philippe Proulx <pproulx@efficios.com>
5 * Copyright (c) 2020-2021 Simon Marchi <simon.marchi@efficios.com>
6 */
7
8 #include <assert.h>
9 #include <stdarg.h>
10 #include <stdbool.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14
15 #include "argpar.h"
16
17 #define ARGPAR_REALLOC(_ptr, _type, _nmemb) \
18 ((_type *) realloc(_ptr, (_nmemb) * sizeof(_type)))
19
20 #define ARGPAR_CALLOC(_type, _nmemb) \
21 ((_type *) calloc((_nmemb), sizeof(_type)))
22
23 #define ARGPAR_ZALLOC(_type) ARGPAR_CALLOC(_type, 1)
24
25 #define ARGPAR_ASSERT(_cond) assert(_cond)
26
27 /*
28 * An argpar iterator.
29 *
30 * Such a structure contains the state of an iterator between calls to
31 * argpar_iter_next().
32 */
33 struct argpar_iter {
34 /*
35 * Data provided by the user to argpar_iter_create(); immutable
36 * afterwards.
37 */
38 struct {
39 unsigned int argc;
40 const char * const *argv;
41 const struct argpar_opt_descr *descrs;
42 } user;
43
44 /*
45 * Index of the argument to process in the next
46 * argpar_iter_next() call.
47 */
48 unsigned int i;
49
50 /* Counter of non-option arguments */
51 int non_opt_index;
52
53 /*
54 * Current character within the current short option group: if
55 * it's not `NULL`, the parser is within a short option group,
56 * therefore it must resume there in the next argpar_iter_next()
57 * call.
58 */
59 const char *short_opt_group_ch;
60
61 /* Temporary character buffer which only grows */
62 struct {
63 size_t size;
64 char *data;
65 } tmp_buf;
66 };
67
68 /* Base parsing item */
69 struct argpar_item {
70 enum argpar_item_type type;
71 };
72
73 /* Option parsing item */
74 struct argpar_item_opt {
75 struct argpar_item base;
76
77 /* Corresponding descriptor */
78 const struct argpar_opt_descr *descr;
79
80 /* Argument, or `NULL` if none; owned by this */
81 char *arg;
82 };
83
84 /* Non-option parsing item */
85 struct argpar_item_non_opt {
86 struct argpar_item base;
87
88 /*
89 * Complete argument, pointing to one of the entries of the
90 * original arguments (`argv`).
91 */
92 const char *arg;
93
94 /*
95 * Index of this argument amongst all original arguments
96 * (`argv`).
97 */
98 unsigned int orig_index;
99
100 /* Index of this argument amongst other non-option arguments */
101 unsigned int non_opt_index;
102 };
103
104 /* Parsing error */
105 struct argpar_error {
106 /* Error type */
107 enum argpar_error_type type;
108
109 /* Original argument index */
110 unsigned int orig_index;
111
112 /* Name of unknown option; owned by this */
113 char *unknown_opt_name;
114
115 /* Option descriptor */
116 const struct argpar_opt_descr *opt_descr;
117
118 /* `true` if a short option caused the error */
119 bool is_short;
120 };
121
122 ARGPAR_HIDDEN
123 enum argpar_item_type argpar_item_type(const struct argpar_item * const item)
124 {
125 ARGPAR_ASSERT(item);
126 return item->type;
127 }
128
129 ARGPAR_HIDDEN
130 const struct argpar_opt_descr *argpar_item_opt_descr(
131 const struct argpar_item * const item)
132 {
133 ARGPAR_ASSERT(item);
134 ARGPAR_ASSERT(item->type == ARGPAR_ITEM_TYPE_OPT);
135 return ((const struct argpar_item_opt *) item)->descr;
136 }
137
138 ARGPAR_HIDDEN
139 const char *argpar_item_opt_arg(const struct argpar_item * const item)
140 {
141 ARGPAR_ASSERT(item);
142 ARGPAR_ASSERT(item->type == ARGPAR_ITEM_TYPE_OPT);
143 return ((const struct argpar_item_opt *) item)->arg;
144 }
145
146 ARGPAR_HIDDEN
147 const char *argpar_item_non_opt_arg(const struct argpar_item * const item)
148 {
149 ARGPAR_ASSERT(item);
150 ARGPAR_ASSERT(item->type == ARGPAR_ITEM_TYPE_NON_OPT);
151 return ((const struct argpar_item_non_opt *) item)->arg;
152 }
153
154 ARGPAR_HIDDEN
155 unsigned int argpar_item_non_opt_orig_index(
156 const struct argpar_item * const item)
157 {
158 ARGPAR_ASSERT(item);
159 ARGPAR_ASSERT(item->type == ARGPAR_ITEM_TYPE_NON_OPT);
160 return ((const struct argpar_item_non_opt *) item)->orig_index;
161 }
162
163 ARGPAR_HIDDEN
164 unsigned int argpar_item_non_opt_non_opt_index(
165 const struct argpar_item * const item)
166 {
167 ARGPAR_ASSERT(item);
168 ARGPAR_ASSERT(item->type == ARGPAR_ITEM_TYPE_NON_OPT);
169 return ((const struct argpar_item_non_opt *) item)->non_opt_index;
170 }
171
172 ARGPAR_HIDDEN
173 void argpar_item_destroy(const struct argpar_item * const item)
174 {
175 if (!item) {
176 goto end;
177 }
178
179 if (item->type == ARGPAR_ITEM_TYPE_OPT) {
180 struct argpar_item_opt * const opt_item =
181 (struct argpar_item_opt *) item;
182
183 free(opt_item->arg);
184 }
185
186 free((void *) item);
187
188 end:
189 return;
190 }
191
192 /*
193 * Creates and returns an option parsing item for the descriptor `descr`
194 * and having the argument `arg` (copied; may be `NULL`).
195 *
196 * Returns `NULL` on memory error.
197 */
198 static
199 struct argpar_item_opt *create_opt_item(
200 const struct argpar_opt_descr * const descr,
201 const char * const arg)
202 {
203 struct argpar_item_opt *opt_item =
204 ARGPAR_ZALLOC(struct argpar_item_opt);
205
206 if (!opt_item) {
207 goto end;
208 }
209
210 opt_item->base.type = ARGPAR_ITEM_TYPE_OPT;
211 opt_item->descr = descr;
212
213 if (arg) {
214 opt_item->arg = strdup(arg);
215 if (!opt_item->arg) {
216 goto error;
217 }
218 }
219
220 goto end;
221
222 error:
223 argpar_item_destroy(&opt_item->base);
224 opt_item = NULL;
225
226 end:
227 return opt_item;
228 }
229
230 /*
231 * Creates and returns a non-option parsing item for the original
232 * argument `arg` having the original index `orig_index` and the
233 * non-option index `non_opt_index`.
234 *
235 * Returns `NULL` on memory error.
236 */
237 static
238 struct argpar_item_non_opt *create_non_opt_item(const char * const arg,
239 const unsigned int orig_index,
240 const unsigned int non_opt_index)
241 {
242 struct argpar_item_non_opt * const non_opt_item =
243 ARGPAR_ZALLOC(struct argpar_item_non_opt);
244
245 if (!non_opt_item) {
246 goto end;
247 }
248
249 non_opt_item->base.type = ARGPAR_ITEM_TYPE_NON_OPT;
250 non_opt_item->arg = arg;
251 non_opt_item->orig_index = orig_index;
252 non_opt_item->non_opt_index = non_opt_index;
253
254 end:
255 return non_opt_item;
256 }
257
258 /*
259 * If `error` is not `NULL`, sets the error `error` to a new parsing
260 * error object, setting its `unknown_opt_name`, `opt_descr`, and
261 * `is_short` members from the parameters.
262 *
263 * `unknown_opt_name` is the unknown option name without any `-` or `--`
264 * prefix: `is_short` controls which type of unknown option it is.
265 *
266 * Returns 0 on success (including if `error` is `NULL`) or -1 on memory
267 * error.
268 */
269 static
270 int set_error(struct argpar_error ** const error,
271 enum argpar_error_type type,
272 const char * const unknown_opt_name,
273 const struct argpar_opt_descr * const opt_descr,
274 const bool is_short)
275 {
276 int ret = 0;
277
278 if (!error) {
279 goto end;
280 }
281
282 *error = ARGPAR_ZALLOC(struct argpar_error);
283 if (!*error) {
284 goto error;
285 }
286
287 (*error)->type = type;
288
289 if (unknown_opt_name) {
290 (*error)->unknown_opt_name = ARGPAR_CALLOC(char,
291 strlen(unknown_opt_name) + 1 + (is_short ? 1 : 2));
292 if (!(*error)->unknown_opt_name) {
293 goto error;
294 }
295
296 if (is_short) {
297 strcpy((*error)->unknown_opt_name, "-");
298 } else {
299 strcpy((*error)->unknown_opt_name, "--");
300 }
301
302 strcat((*error)->unknown_opt_name, unknown_opt_name);
303 }
304
305 (*error)->opt_descr = opt_descr;
306 (*error)->is_short = is_short;
307 goto end;
308
309 error:
310 argpar_error_destroy(*error);
311 ret = -1;
312
313 end:
314 return ret;
315 }
316
317 ARGPAR_HIDDEN
318 enum argpar_error_type argpar_error_type(
319 const struct argpar_error * const error)
320 {
321 ARGPAR_ASSERT(error);
322 return error->type;
323 }
324
325 ARGPAR_HIDDEN
326 unsigned int argpar_error_orig_index(const struct argpar_error * const error)
327 {
328 ARGPAR_ASSERT(error);
329 return error->orig_index;
330 }
331
332 ARGPAR_HIDDEN
333 const char *argpar_error_unknown_opt_name(
334 const struct argpar_error * const error)
335 {
336 ARGPAR_ASSERT(error);
337 ARGPAR_ASSERT(error->type == ARGPAR_ERROR_TYPE_UNKNOWN_OPT);
338 ARGPAR_ASSERT(error->unknown_opt_name);
339 return error->unknown_opt_name;
340 }
341
342 ARGPAR_HIDDEN
343 const struct argpar_opt_descr *argpar_error_opt_descr(
344 const struct argpar_error * const error, bool * const is_short)
345 {
346 ARGPAR_ASSERT(error);
347 ARGPAR_ASSERT(error->type == ARGPAR_ERROR_TYPE_MISSING_OPT_ARG ||
348 error->type == ARGPAR_ERROR_TYPE_UNEXPECTED_OPT_ARG);
349 ARGPAR_ASSERT(error->opt_descr);
350
351 if (is_short) {
352 *is_short = error->is_short;
353 }
354
355 return error->opt_descr;
356 }
357
358 ARGPAR_HIDDEN
359 void argpar_error_destroy(const struct argpar_error * const error)
360 {
361 if (error) {
362 free(error->unknown_opt_name);
363 free((void *) error);
364 }
365 }
366
367 /*
368 * Finds and returns the _first_ descriptor having the short option name
369 * `short_name` or the long option name `long_name` within the option
370 * descriptors `descrs`.
371 *
372 * `short_name` may be `'\0'` to not consider it.
373 *
374 * `long_name` may be `NULL` to not consider it.
375 *
376 * Returns `NULL` if no descriptor is found.
377 */
378 static
379 const struct argpar_opt_descr *find_descr(
380 const struct argpar_opt_descr * const descrs,
381 const char short_name, const char * const long_name)
382 {
383 const struct argpar_opt_descr *descr;
384
385 for (descr = descrs; descr->short_name || descr->long_name; descr++) {
386 if (short_name && descr->short_name &&
387 short_name == descr->short_name) {
388 goto end;
389 }
390
391 if (long_name && descr->long_name &&
392 strcmp(long_name, descr->long_name) == 0) {
393 goto end;
394 }
395 }
396
397 end:
398 return !descr->short_name && !descr->long_name ? NULL : descr;
399 }
400
401 /* Return type of parse_short_opt_group() and parse_long_opt() */
402 enum parse_orig_arg_opt_ret {
403 PARSE_ORIG_ARG_OPT_RET_OK,
404 PARSE_ORIG_ARG_OPT_RET_ERROR = -1,
405 PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY = -2,
406 };
407
408 /*
409 * Parses the short option group argument `short_opt_group`, starting
410 * where needed depending on the state of `iter`.
411 *
412 * On success, sets `*item`.
413 *
414 * On error (except for `PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY`), sets
415 * `*error`.
416 */
417 static
418 enum parse_orig_arg_opt_ret parse_short_opt_group(
419 const char * const short_opt_group,
420 const char * const next_orig_arg,
421 const struct argpar_opt_descr * const descrs,
422 struct argpar_iter * const iter,
423 struct argpar_error ** const error,
424 struct argpar_item ** const item)
425 {
426 enum parse_orig_arg_opt_ret ret = PARSE_ORIG_ARG_OPT_RET_OK;
427 bool used_next_orig_arg = false;
428 const char *opt_arg = NULL;
429 const struct argpar_opt_descr *descr;
430 struct argpar_item_opt *opt_item;
431
432 ARGPAR_ASSERT(strlen(short_opt_group) != 0);
433
434 if (!iter->short_opt_group_ch) {
435 iter->short_opt_group_ch = short_opt_group;
436 }
437
438 /* Find corresponding option descriptor */
439 descr = find_descr(descrs, *iter->short_opt_group_ch, NULL);
440 if (!descr) {
441 const char unknown_opt_name[] =
442 {*iter->short_opt_group_ch, '\0'};
443
444 ret = PARSE_ORIG_ARG_OPT_RET_ERROR;
445
446 if (set_error(error, ARGPAR_ERROR_TYPE_UNKNOWN_OPT,
447 unknown_opt_name, NULL, true)) {
448 ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY;
449 }
450
451 goto error;
452 }
453
454 if (descr->with_arg) {
455 if (iter->short_opt_group_ch[1]) {
456 /* `-oarg` form */
457 opt_arg = &iter->short_opt_group_ch[1];
458 } else {
459 /* `-o arg` form */
460 opt_arg = next_orig_arg;
461 used_next_orig_arg = true;
462 }
463
464 /*
465 * We accept `-o ''` (empty option argument), but not
466 * `-o` alone if an option argument is expected.
467 */
468 if (!opt_arg || (iter->short_opt_group_ch[1] &&
469 strlen(opt_arg) == 0)) {
470 ret = PARSE_ORIG_ARG_OPT_RET_ERROR;
471
472 if (set_error(error, ARGPAR_ERROR_TYPE_MISSING_OPT_ARG,
473 NULL, descr, true)) {
474 ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY;
475 }
476
477 goto error;
478 }
479 }
480
481 /* Create and append option argument */
482 opt_item = create_opt_item(descr, opt_arg);
483 if (!opt_item) {
484 ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY;
485 goto error;
486 }
487
488 *item = &opt_item->base;
489 iter->short_opt_group_ch++;
490
491 if (descr->with_arg || !*iter->short_opt_group_ch) {
492 /* Option has an argument: no more options */
493 iter->short_opt_group_ch = NULL;
494
495 if (used_next_orig_arg) {
496 iter->i += 2;
497 } else {
498 iter->i++;
499 }
500 }
501
502 goto end;
503
504 error:
505 ARGPAR_ASSERT(ret != PARSE_ORIG_ARG_OPT_RET_OK);
506
507 end:
508 return ret;
509 }
510
511 /*
512 * Parses the long option argument `long_opt_arg`.
513 *
514 * On success, sets `*item`.
515 *
516 * On error (except for `PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY`), sets
517 * `*error`.
518 */
519 static
520 enum parse_orig_arg_opt_ret parse_long_opt(const char * const long_opt_arg,
521 const char * const next_orig_arg,
522 const struct argpar_opt_descr * const descrs,
523 struct argpar_iter * const iter,
524 struct argpar_error ** const error,
525 struct argpar_item ** const item)
526 {
527 enum parse_orig_arg_opt_ret ret = PARSE_ORIG_ARG_OPT_RET_OK;
528 const struct argpar_opt_descr *descr;
529 struct argpar_item_opt *opt_item;
530 bool used_next_orig_arg = false;
531
532 /* Option's argument, if any */
533 const char *opt_arg = NULL;
534
535 /* Position of first `=`, if any */
536 const char *eq_pos;
537
538 /* Option name */
539 const char *long_opt_name = long_opt_arg;
540
541 ARGPAR_ASSERT(strlen(long_opt_arg) != 0);
542
543 /* Find the first `=` in original argument */
544 eq_pos = strchr(long_opt_arg, '=');
545 if (eq_pos) {
546 const size_t long_opt_name_size = eq_pos - long_opt_arg;
547
548 /* Isolate the option name */
549 while (long_opt_name_size > iter->tmp_buf.size - 1) {
550 iter->tmp_buf.size *= 2;
551 iter->tmp_buf.data = ARGPAR_REALLOC(iter->tmp_buf.data,
552 char, iter->tmp_buf.size);
553 if (!iter->tmp_buf.data) {
554 ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY;
555 goto error;
556 }
557 }
558
559 memcpy(iter->tmp_buf.data, long_opt_arg, long_opt_name_size);
560 iter->tmp_buf.data[long_opt_name_size] = '\0';
561 long_opt_name = iter->tmp_buf.data;
562 }
563
564 /* Find corresponding option descriptor */
565 descr = find_descr(descrs, '\0', long_opt_name);
566 if (!descr) {
567 ret = PARSE_ORIG_ARG_OPT_RET_ERROR;
568
569 if (set_error(error, ARGPAR_ERROR_TYPE_UNKNOWN_OPT,
570 long_opt_name, NULL, false)) {
571 ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY;
572 }
573
574 goto error;
575 }
576
577 /* Find option's argument if any */
578 if (descr->with_arg) {
579 if (eq_pos) {
580 /* `--long-opt=arg` style */
581 opt_arg = eq_pos + 1;
582 } else {
583 /* `--long-opt arg` style */
584 if (!next_orig_arg) {
585 ret = PARSE_ORIG_ARG_OPT_RET_ERROR;
586
587 if (set_error(error, ARGPAR_ERROR_TYPE_MISSING_OPT_ARG,
588 NULL, descr, false)) {
589 ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY;
590 }
591
592 goto error;
593 }
594
595 opt_arg = next_orig_arg;
596 used_next_orig_arg = true;
597 }
598 } else if (eq_pos) {
599 /*
600 * Unexpected `--opt=arg` style for a long option which
601 * doesn't accept an argument.
602 */
603 ret = PARSE_ORIG_ARG_OPT_RET_ERROR;
604
605 if (set_error(error, ARGPAR_ERROR_TYPE_UNEXPECTED_OPT_ARG,
606 NULL, descr, false)) {
607 ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY;
608 }
609
610 goto error;
611 }
612
613 /* Create and append option argument */
614 opt_item = create_opt_item(descr, opt_arg);
615 if (!opt_item) {
616 goto error;
617 }
618
619 if (used_next_orig_arg) {
620 iter->i += 2;
621 } else {
622 iter->i++;
623 }
624
625 *item = &opt_item->base;
626 goto end;
627
628 error:
629 ARGPAR_ASSERT(ret != PARSE_ORIG_ARG_OPT_RET_OK);
630
631 end:
632 return ret;
633 }
634
635 /*
636 * Parses the original argument `orig_arg`.
637 *
638 * On success, sets `*item`.
639 *
640 * On error (except for `PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY`), sets
641 * `*error`.
642 */
643 static
644 enum parse_orig_arg_opt_ret parse_orig_arg_opt(const char * const orig_arg,
645 const char * const next_orig_arg,
646 const struct argpar_opt_descr * const descrs,
647 struct argpar_iter * const iter,
648 struct argpar_error ** const error,
649 struct argpar_item ** const item)
650 {
651 enum parse_orig_arg_opt_ret ret = PARSE_ORIG_ARG_OPT_RET_OK;
652
653 ARGPAR_ASSERT(orig_arg[0] == '-');
654
655 if (orig_arg[1] == '-') {
656 /* Long option */
657 ret = parse_long_opt(&orig_arg[2],
658 next_orig_arg, descrs, iter, error, item);
659 } else {
660 /* Short option */
661 ret = parse_short_opt_group(&orig_arg[1],
662 next_orig_arg, descrs, iter, error, item);
663 }
664
665 return ret;
666 }
667
668 ARGPAR_HIDDEN
669 struct argpar_iter *argpar_iter_create(const unsigned int argc,
670 const char * const * const argv,
671 const struct argpar_opt_descr * const descrs)
672 {
673 struct argpar_iter *iter = ARGPAR_ZALLOC(struct argpar_iter);
674
675 if (!iter) {
676 goto end;
677 }
678
679 iter->user.argc = argc;
680 iter->user.argv = argv;
681 iter->user.descrs = descrs;
682 iter->tmp_buf.size = 128;
683 iter->tmp_buf.data = ARGPAR_CALLOC(char, iter->tmp_buf.size);
684 if (!iter->tmp_buf.data) {
685 argpar_iter_destroy(iter);
686 iter = NULL;
687 goto end;
688 }
689
690 end:
691 return iter;
692 }
693
694 ARGPAR_HIDDEN
695 void argpar_iter_destroy(struct argpar_iter * const iter)
696 {
697 if (iter) {
698 free(iter->tmp_buf.data);
699 free(iter);
700 }
701 }
702
703 ARGPAR_HIDDEN
704 enum argpar_iter_next_status argpar_iter_next(
705 struct argpar_iter * const iter,
706 const struct argpar_item ** const item,
707 const struct argpar_error ** const error)
708 {
709 enum argpar_iter_next_status status;
710 enum parse_orig_arg_opt_ret parse_orig_arg_opt_ret;
711 const char *orig_arg;
712 const char *next_orig_arg;
713 struct argpar_error ** const nc_error = (struct argpar_error **) error;
714
715 ARGPAR_ASSERT(iter->i <= iter->user.argc);
716
717 if (error) {
718 *nc_error = NULL;
719 }
720
721 if (iter->i == iter->user.argc) {
722 status = ARGPAR_ITER_NEXT_STATUS_END;
723 goto end;
724 }
725
726 orig_arg = iter->user.argv[iter->i];
727 next_orig_arg =
728 iter->i < (iter->user.argc - 1) ?
729 iter->user.argv[iter->i + 1] : NULL;
730
731 if (strcmp(orig_arg, "-") == 0 || strcmp(orig_arg, "--") == 0 ||
732 orig_arg[0] != '-') {
733 /* Non-option argument */
734 const struct argpar_item_non_opt * const non_opt_item =
735 create_non_opt_item(orig_arg, iter->i,
736 iter->non_opt_index);
737
738 if (!non_opt_item) {
739 status = ARGPAR_ITER_NEXT_STATUS_ERROR_MEMORY;
740 goto end;
741 }
742
743 iter->non_opt_index++;
744 iter->i++;
745 *item = &non_opt_item->base;
746 status = ARGPAR_ITER_NEXT_STATUS_OK;
747 goto end;
748 }
749
750 /* Option argument */
751 parse_orig_arg_opt_ret = parse_orig_arg_opt(orig_arg,
752 next_orig_arg, iter->user.descrs, iter, nc_error,
753 (struct argpar_item **) item);
754 switch (parse_orig_arg_opt_ret) {
755 case PARSE_ORIG_ARG_OPT_RET_OK:
756 status = ARGPAR_ITER_NEXT_STATUS_OK;
757 break;
758 case PARSE_ORIG_ARG_OPT_RET_ERROR:
759 if (error) {
760 ARGPAR_ASSERT(*error);
761 (*nc_error)->orig_index = iter->i;
762 }
763 status = ARGPAR_ITER_NEXT_STATUS_ERROR;
764 break;
765 case PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY:
766 status = ARGPAR_ITER_NEXT_STATUS_ERROR_MEMORY;
767 break;
768 default:
769 abort();
770 }
771
772 end:
773 return status;
774 }
775
776 ARGPAR_HIDDEN
777 unsigned int argpar_iter_ingested_orig_args(
778 const struct argpar_iter * const iter)
779 {
780 return iter->i;
781 }
This page took 0.04275 seconds and 3 git commands to generate.