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