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