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