Encode error type in argpar_error
[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
fb12ac67
PP
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)
7ac57709
SM
24
25#define ARGPAR_ASSERT(_cond) assert(_cond)
26
fc07e526
SM
27/*
28 * An argpar iterator.
29 *
2af370d0
PP
30 * Such a structure contains the state of an iterator between calls to
31 * argpar_iter_next().
fc07e526
SM
32 */
33struct argpar_iter {
34 /*
35 * Data provided by the user to argpar_iter_create(); immutable
36 * afterwards.
37 */
64875a48
PP
38 struct {
39 unsigned int argc;
40 const char * const *argv;
41 const struct argpar_opt_descr *descrs;
42 } user;
fc07e526
SM
43
44 /*
45 * Index of the argument to process in the next
2af370d0 46 * argpar_iter_next() call.
fc07e526
SM
47 */
48 unsigned int i;
49
50 /* Counter of non-option arguments */
51 int non_opt_index;
52
53 /*
d7a82d7f
PP
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.
fc07e526 58 */
d7a82d7f 59 const char *short_opt_group_ch;
d1f7bbdb
PP
60
61 /* Temporary character buffer which only grows */
62 struct {
63 size_t size;
64 char *data;
65 } tmp_buf;
fc07e526
SM
66};
67
d4539a90
PP
68/* Base parsing item */
69struct argpar_item {
70 enum argpar_item_type type;
71};
72
73/* Option parsing item */
74struct 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 */
85struct 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
8b95d883
PP
104/* Parsing error */
105struct argpar_error {
10aefab2
SM
106 /* Error type */
107 enum argpar_error_type type;
108
8b95d883
PP
109 /* Original argument index */
110 unsigned int orig_index;
7ac57709 111
8b95d883
PP
112 /* Name of unknown option; owned by this */
113 char *unknown_opt_name;
f46b5106 114
8b95d883
PP
115 /* Option descriptor */
116 const struct argpar_opt_descr *opt_descr;
7ac57709 117
8b95d883
PP
118 /* `true` if a short option caused the error */
119 bool is_short;
120};
7ac57709 121
d4539a90
PP
122ARGPAR_HIDDEN
123enum argpar_item_type argpar_item_type(const struct argpar_item * const item)
124{
125 ARGPAR_ASSERT(item);
126 return item->type;
127}
128
129ARGPAR_HIDDEN
130const 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
138ARGPAR_HIDDEN
139const 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
146ARGPAR_HIDDEN
147const 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
154ARGPAR_HIDDEN
155unsigned 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
163ARGPAR_HIDDEN
164unsigned 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
fc07e526
SM
172ARGPAR_HIDDEN
173void argpar_item_destroy(const struct argpar_item * const item)
903a5b8a
SM
174{
175 if (!item) {
176 goto end;
177 }
178
1c9a6bde 179 if (item->type == ARGPAR_ITEM_TYPE_OPT) {
fc07e526
SM
180 struct argpar_item_opt * const opt_item =
181 (struct argpar_item_opt *) item;
903a5b8a 182
d4539a90 183 free(opt_item->arg);
903a5b8a
SM
184 }
185
fc07e526 186 free((void *) item);
903a5b8a
SM
187
188end:
189 return;
190}
191
793f1620
PP
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 */
903a5b8a 198static
1c9a6bde
SM
199struct argpar_item_opt *create_opt_item(
200 const struct argpar_opt_descr * const descr,
903a5b8a
SM
201 const char * const arg)
202{
1c9a6bde 203 struct argpar_item_opt *opt_item =
fb12ac67 204 ARGPAR_ZALLOC(struct argpar_item_opt);
903a5b8a
SM
205
206 if (!opt_item) {
207 goto end;
208 }
209
1c9a6bde 210 opt_item->base.type = ARGPAR_ITEM_TYPE_OPT;
903a5b8a
SM
211 opt_item->descr = descr;
212
213 if (arg) {
7ac57709 214 opt_item->arg = strdup(arg);
903a5b8a
SM
215 if (!opt_item->arg) {
216 goto error;
217 }
218 }
219
220 goto end;
221
222error:
fc07e526 223 argpar_item_destroy(&opt_item->base);
903a5b8a
SM
224 opt_item = NULL;
225
226end:
227 return opt_item;
228}
229
793f1620
PP
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 */
903a5b8a 237static
1c9a6bde 238struct argpar_item_non_opt *create_non_opt_item(const char * const arg,
903a5b8a
SM
239 const unsigned int orig_index,
240 const unsigned int non_opt_index)
241{
1c9a6bde 242 struct argpar_item_non_opt * const non_opt_item =
fb12ac67 243 ARGPAR_ZALLOC(struct argpar_item_non_opt);
903a5b8a
SM
244
245 if (!non_opt_item) {
246 goto end;
247 }
248
1c9a6bde 249 non_opt_item->base.type = ARGPAR_ITEM_TYPE_NON_OPT;
903a5b8a
SM
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
254end:
255 return non_opt_item;
256}
257
8b95d883
PP
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 */
269static
270int set_error(struct argpar_error ** const error,
10aefab2 271 enum argpar_error_type type,
8b95d883
PP
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
10aefab2
SM
287 (*error)->type = type;
288
8b95d883
PP
289 if (unknown_opt_name) {
290 (*error)->unknown_opt_name = ARGPAR_CALLOC(char,
c530e1dc 291 strlen(unknown_opt_name) + 1 + (is_short ? 1 : 2));
8b95d883
PP
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
309error:
310 argpar_error_destroy(*error);
311 ret = -1;
312
313end:
314 return ret;
315}
316
10aefab2
SM
317ARGPAR_HIDDEN
318enum argpar_error_type argpar_error_type(
319 const struct argpar_error * const error)
320{
321 ARGPAR_ASSERT(error);
322 return error->type;
323}
324
8b95d883
PP
325ARGPAR_HIDDEN
326unsigned int argpar_error_orig_index(const struct argpar_error * const error)
327{
328 ARGPAR_ASSERT(error);
329 return error->orig_index;
330}
331
332ARGPAR_HIDDEN
333const char *argpar_error_unknown_opt_name(
334 const struct argpar_error * const error)
335{
336 ARGPAR_ASSERT(error);
10aefab2 337 ARGPAR_ASSERT(error->type == ARGPAR_ERROR_TYPE_UNKNOWN_OPT);
8b95d883
PP
338 ARGPAR_ASSERT(error->unknown_opt_name);
339 return error->unknown_opt_name;
340}
341
342ARGPAR_HIDDEN
343const struct argpar_opt_descr *argpar_error_opt_descr(
344 const struct argpar_error * const error, bool * const is_short)
345{
346 ARGPAR_ASSERT(error);
10aefab2
SM
347 ARGPAR_ASSERT(error->type == ARGPAR_ERROR_TYPE_MISSING_OPT_ARG ||
348 error->type == ARGPAR_ERROR_TYPE_UNEXPECTED_OPT_ARG);
8b95d883
PP
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
358ARGPAR_HIDDEN
359void 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
793f1620
PP
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 */
903a5b8a 378static
1c9a6bde
SM
379const struct argpar_opt_descr *find_descr(
380 const struct argpar_opt_descr * const descrs,
903a5b8a
SM
381 const char short_name, const char * const long_name)
382{
1c9a6bde 383 const struct argpar_opt_descr *descr;
903a5b8a
SM
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
397end:
398 return !descr->short_name && !descr->long_name ? NULL : descr;
399}
400
793f1620 401/* Return type of parse_short_opt_group() and parse_long_opt() */
903a5b8a
SM
402enum parse_orig_arg_opt_ret {
403 PARSE_ORIG_ARG_OPT_RET_OK,
10aefab2
SM
404 PARSE_ORIG_ARG_OPT_RET_ERROR = -1,
405 PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY = -2,
903a5b8a
SM
406};
407
793f1620
PP
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 */
903a5b8a 417static
d7a82d7f
PP
418enum parse_orig_arg_opt_ret parse_short_opt_group(
419 const char * const short_opt_group,
903a5b8a 420 const char * const next_orig_arg,
1c9a6bde 421 const struct argpar_opt_descr * const descrs,
fc07e526 422 struct argpar_iter * const iter,
8b95d883
PP
423 struct argpar_error ** const error,
424 struct argpar_item ** const item)
903a5b8a
SM
425{
426 enum parse_orig_arg_opt_ret ret = PARSE_ORIG_ARG_OPT_RET_OK;
fc07e526
SM
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;
903a5b8a 431
d7a82d7f 432 ARGPAR_ASSERT(strlen(short_opt_group) != 0);
903a5b8a 433
d7a82d7f
PP
434 if (!iter->short_opt_group_ch) {
435 iter->short_opt_group_ch = short_opt_group;
fc07e526 436 }
903a5b8a 437
fc07e526 438 /* Find corresponding option descriptor */
d7a82d7f 439 descr = find_descr(descrs, *iter->short_opt_group_ch, NULL);
fc07e526 440 if (!descr) {
8b95d883
PP
441 const char unknown_opt_name[] =
442 {*iter->short_opt_group_ch, '\0'};
443
10aefab2 444 ret = PARSE_ORIG_ARG_OPT_RET_ERROR;
8b95d883 445
10aefab2
SM
446 if (set_error(error, ARGPAR_ERROR_TYPE_UNKNOWN_OPT,
447 unknown_opt_name, NULL, true)) {
8b95d883
PP
448 ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY;
449 }
450
fc07e526
SM
451 goto error;
452 }
903a5b8a 453
fc07e526 454 if (descr->with_arg) {
d7a82d7f 455 if (iter->short_opt_group_ch[1]) {
fc07e526 456 /* `-oarg` form */
d7a82d7f 457 opt_arg = &iter->short_opt_group_ch[1];
fc07e526
SM
458 } else {
459 /* `-o arg` form */
460 opt_arg = next_orig_arg;
461 used_next_orig_arg = true;
903a5b8a
SM
462 }
463
fc07e526
SM
464 /*
465 * We accept `-o ''` (empty option argument), but not
466 * `-o` alone if an option argument is expected.
467 */
d7a82d7f 468 if (!opt_arg || (iter->short_opt_group_ch[1] &&
fb12ac67 469 strlen(opt_arg) == 0)) {
10aefab2 470 ret = PARSE_ORIG_ARG_OPT_RET_ERROR;
8b95d883 471
10aefab2
SM
472 if (set_error(error, ARGPAR_ERROR_TYPE_MISSING_OPT_ARG,
473 NULL, descr, true)) {
8b95d883
PP
474 ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY;
475 }
476
903a5b8a
SM
477 goto error;
478 }
fc07e526 479 }
903a5b8a 480
fc07e526
SM
481 /* Create and append option argument */
482 opt_item = create_opt_item(descr, opt_arg);
483 if (!opt_item) {
871eba32 484 ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY;
fc07e526
SM
485 goto error;
486 }
903a5b8a 487
fc07e526 488 *item = &opt_item->base;
d7a82d7f 489 iter->short_opt_group_ch++;
903a5b8a 490
d7a82d7f 491 if (descr->with_arg || !*iter->short_opt_group_ch) {
fc07e526 492 /* Option has an argument: no more options */
d7a82d7f 493 iter->short_opt_group_ch = NULL;
fc07e526
SM
494
495 if (used_next_orig_arg) {
496 iter->i += 2;
497 } else {
498 iter->i++;
499 }
903a5b8a
SM
500 }
501
502 goto end;
503
504error:
871eba32 505 ARGPAR_ASSERT(ret != PARSE_ORIG_ARG_OPT_RET_OK);
903a5b8a
SM
506
507end:
508 return ret;
509}
510
793f1620
PP
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 */
903a5b8a
SM
519static
520enum parse_orig_arg_opt_ret parse_long_opt(const char * const long_opt_arg,
521 const char * const next_orig_arg,
1c9a6bde 522 const struct argpar_opt_descr * const descrs,
fc07e526 523 struct argpar_iter * const iter,
8b95d883
PP
524 struct argpar_error ** const error,
525 struct argpar_item ** const item)
903a5b8a 526{
903a5b8a 527 enum parse_orig_arg_opt_ret ret = PARSE_ORIG_ARG_OPT_RET_OK;
1c9a6bde
SM
528 const struct argpar_opt_descr *descr;
529 struct argpar_item_opt *opt_item;
fc07e526 530 bool used_next_orig_arg = false;
903a5b8a
SM
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
903a5b8a
SM
538 /* Option name */
539 const char *long_opt_name = long_opt_arg;
540
dd757a65 541 ARGPAR_ASSERT(strlen(long_opt_arg) != 0);
903a5b8a
SM
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 */
d1f7bbdb
PP
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 }
903a5b8a
SM
557 }
558
d1f7bbdb
PP
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;
903a5b8a
SM
562 }
563
564 /* Find corresponding option descriptor */
565 descr = find_descr(descrs, '\0', long_opt_name);
566 if (!descr) {
10aefab2 567 ret = PARSE_ORIG_ARG_OPT_RET_ERROR;
8b95d883 568
10aefab2
SM
569 if (set_error(error, ARGPAR_ERROR_TYPE_UNKNOWN_OPT,
570 long_opt_name, NULL, false)) {
8b95d883
PP
571 ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY;
572 }
573
903a5b8a
SM
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) {
10aefab2 585 ret = PARSE_ORIG_ARG_OPT_RET_ERROR;
8b95d883 586
10aefab2
SM
587 if (set_error(error, ARGPAR_ERROR_TYPE_MISSING_OPT_ARG,
588 NULL, descr, false)) {
8b95d883
PP
589 ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY;
590 }
591
903a5b8a
SM
592 goto error;
593 }
594
595 opt_arg = next_orig_arg;
fc07e526 596 used_next_orig_arg = true;
903a5b8a 597 }
430fe886
SM
598 } else if (eq_pos) {
599 /*
600 * Unexpected `--opt=arg` style for a long option which
601 * doesn't accept an argument.
602 */
10aefab2 603 ret = PARSE_ORIG_ARG_OPT_RET_ERROR;
8b95d883 604
10aefab2
SM
605 if (set_error(error, ARGPAR_ERROR_TYPE_UNEXPECTED_OPT_ARG,
606 NULL, descr, false)) {
8b95d883
PP
607 ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY;
608 }
609
430fe886 610 goto error;
903a5b8a
SM
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
fc07e526
SM
619 if (used_next_orig_arg) {
620 iter->i += 2;
621 } else {
622 iter->i++;
7ac57709
SM
623 }
624
fc07e526 625 *item = &opt_item->base;
903a5b8a
SM
626 goto end;
627
628error:
871eba32 629 ARGPAR_ASSERT(ret != PARSE_ORIG_ARG_OPT_RET_OK);
903a5b8a
SM
630
631end:
632 return ret;
633}
634
793f1620
PP
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 */
903a5b8a
SM
643static
644enum parse_orig_arg_opt_ret parse_orig_arg_opt(const char * const orig_arg,
645 const char * const next_orig_arg,
1c9a6bde 646 const struct argpar_opt_descr * const descrs,
8b95d883
PP
647 struct argpar_iter * const iter,
648 struct argpar_error ** const error,
fc07e526 649 struct argpar_item ** const item)
903a5b8a
SM
650{
651 enum parse_orig_arg_opt_ret ret = PARSE_ORIG_ARG_OPT_RET_OK;
652
7ac57709 653 ARGPAR_ASSERT(orig_arg[0] == '-');
903a5b8a
SM
654
655 if (orig_arg[1] == '-') {
656 /* Long option */
657 ret = parse_long_opt(&orig_arg[2],
fc07e526 658 next_orig_arg, descrs, iter, error, item);
903a5b8a
SM
659 } else {
660 /* Short option */
d7a82d7f 661 ret = parse_short_opt_group(&orig_arg[1],
fc07e526 662 next_orig_arg, descrs, iter, error, item);
903a5b8a
SM
663 }
664
665 return ret;
666}
667
7ac57709 668ARGPAR_HIDDEN
fc07e526
SM
669struct argpar_iter *argpar_iter_create(const unsigned int argc,
670 const char * const * const argv,
671 const struct argpar_opt_descr * const descrs)
903a5b8a 672{
d1f7bbdb 673 struct argpar_iter *iter = ARGPAR_ZALLOC(struct argpar_iter);
903a5b8a 674
fc07e526
SM
675 if (!iter) {
676 goto end;
903a5b8a
SM
677 }
678
64875a48
PP
679 iter->user.argc = argc;
680 iter->user.argv = argv;
681 iter->user.descrs = descrs;
d1f7bbdb
PP
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 }
903a5b8a 689
fc07e526
SM
690end:
691 return iter;
692}
903a5b8a 693
fc07e526
SM
694ARGPAR_HIDDEN
695void argpar_iter_destroy(struct argpar_iter * const iter)
696{
d1f7bbdb
PP
697 if (iter) {
698 free(iter->tmp_buf.data);
699 free(iter);
700 }
fc07e526 701}
903a5b8a 702
fc07e526 703ARGPAR_HIDDEN
2af370d0 704enum argpar_iter_next_status argpar_iter_next(
fc07e526 705 struct argpar_iter * const iter,
8b95d883
PP
706 const struct argpar_item ** const item,
707 const struct argpar_error ** const error)
fc07e526 708{
2af370d0 709 enum argpar_iter_next_status status;
fc07e526
SM
710 enum parse_orig_arg_opt_ret parse_orig_arg_opt_ret;
711 const char *orig_arg;
712 const char *next_orig_arg;
8b95d883 713 struct argpar_error ** const nc_error = (struct argpar_error **) error;
7ac57709 714
64875a48 715 ARGPAR_ASSERT(iter->i <= iter->user.argc);
37bb2b1f
PP
716
717 if (error) {
8b95d883 718 *nc_error = NULL;
37bb2b1f 719 }
fc07e526 720
64875a48 721 if (iter->i == iter->user.argc) {
2af370d0 722 status = ARGPAR_ITER_NEXT_STATUS_END;
fc07e526
SM
723 goto end;
724 }
7ac57709 725
64875a48 726 orig_arg = iter->user.argv[iter->i];
fc07e526 727 next_orig_arg =
64875a48
PP
728 iter->i < (iter->user.argc - 1) ?
729 iter->user.argv[iter->i + 1] : NULL;
fc07e526 730
dd757a65
PP
731 if (strcmp(orig_arg, "-") == 0 || strcmp(orig_arg, "--") == 0 ||
732 orig_arg[0] != '-') {
fc07e526 733 /* Non-option argument */
dd757a65 734 const struct argpar_item_non_opt * const non_opt_item =
fc07e526
SM
735 create_non_opt_item(orig_arg, iter->i,
736 iter->non_opt_index);
737
738 if (!non_opt_item) {
2af370d0 739 status = ARGPAR_ITER_NEXT_STATUS_ERROR_MEMORY;
fc07e526 740 goto end;
903a5b8a
SM
741 }
742
fc07e526
SM
743 iter->non_opt_index++;
744 iter->i++;
745 *item = &non_opt_item->base;
2af370d0 746 status = ARGPAR_ITER_NEXT_STATUS_OK;
fc07e526
SM
747 goto end;
748 }
903a5b8a 749
fc07e526
SM
750 /* Option argument */
751 parse_orig_arg_opt_ret = parse_orig_arg_opt(orig_arg,
64875a48 752 next_orig_arg, iter->user.descrs, iter, nc_error,
fc07e526
SM
753 (struct argpar_item **) item);
754 switch (parse_orig_arg_opt_ret) {
755 case PARSE_ORIG_ARG_OPT_RET_OK:
2af370d0 756 status = ARGPAR_ITER_NEXT_STATUS_OK;
fc07e526 757 break;
10aefab2 758 case PARSE_ORIG_ARG_OPT_RET_ERROR:
8b95d883
PP
759 if (error) {
760 ARGPAR_ASSERT(*error);
761 (*nc_error)->orig_index = iter->i;
762 }
10aefab2 763 status = ARGPAR_ITER_NEXT_STATUS_ERROR;
d4d05805
PP
764 break;
765 case PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY:
2af370d0 766 status = ARGPAR_ITER_NEXT_STATUS_ERROR_MEMORY;
fc07e526
SM
767 break;
768 default:
769 abort();
770 }
771
772end:
773 return status;
774}
775
776ARGPAR_HIDDEN
f3ab5ca1 777unsigned int argpar_iter_ingested_orig_args(
fc07e526
SM
778 const struct argpar_iter * const iter)
779{
780 return iter->i;
781}
This page took 0.057295 seconds and 4 git commands to generate.