tests/test-argpar.c: use fancy bullet points in comments
[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;
7554f0b6 49 const argpar_opt_descr_t *descrs;
64875a48 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 {
7554f0b6 78 argpar_item_type_t type;
d4539a90
PP
79};
80
81/* Option parsing item */
7554f0b6
PP
82typedef struct argpar_item_opt {
83 argpar_item_t base;
d4539a90
PP
84
85 /* Corresponding descriptor */
7554f0b6 86 const argpar_opt_descr_t *descr;
d4539a90
PP
87
88 /* Argument, or `NULL` if none; owned by this */
89 char *arg;
7554f0b6 90} argpar_item_opt_t;
d4539a90
PP
91
92/* Non-option parsing item */
7554f0b6
PP
93typedef struct argpar_item_non_opt {
94 argpar_item_t base;
d4539a90
PP
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;
7554f0b6 110} argpar_item_non_opt_t;
d4539a90 111
8b95d883
PP
112/* Parsing error */
113struct argpar_error {
10aefab2 114 /* Error type */
7554f0b6 115 argpar_error_type_t type;
10aefab2 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 123 /* Option descriptor */
7554f0b6 124 const argpar_opt_descr_t *opt_descr;
7ac57709 125
8b95d883
PP
126 /* `true` if a short option caused the error */
127 bool is_short;
128};
7ac57709 129
d4539a90 130ARGPAR_HIDDEN
7554f0b6 131argpar_item_type_t argpar_item_type(const argpar_item_t * const item)
d4539a90
PP
132{
133 ARGPAR_ASSERT(item);
134 return item->type;
135}
136
137ARGPAR_HIDDEN
7554f0b6
PP
138const argpar_opt_descr_t *argpar_item_opt_descr(
139 const argpar_item_t * const item)
d4539a90
PP
140{
141 ARGPAR_ASSERT(item);
142 ARGPAR_ASSERT(item->type == ARGPAR_ITEM_TYPE_OPT);
7554f0b6 143 return ((const argpar_item_opt_t *) item)->descr;
d4539a90
PP
144}
145
146ARGPAR_HIDDEN
7554f0b6 147const char *argpar_item_opt_arg(const argpar_item_t * const item)
d4539a90
PP
148{
149 ARGPAR_ASSERT(item);
150 ARGPAR_ASSERT(item->type == ARGPAR_ITEM_TYPE_OPT);
7554f0b6 151 return ((const argpar_item_opt_t *) item)->arg;
d4539a90
PP
152}
153
154ARGPAR_HIDDEN
7554f0b6 155const char *argpar_item_non_opt_arg(const argpar_item_t * const item)
d4539a90
PP
156{
157 ARGPAR_ASSERT(item);
158 ARGPAR_ASSERT(item->type == ARGPAR_ITEM_TYPE_NON_OPT);
7554f0b6 159 return ((const argpar_item_non_opt_t *) item)->arg;
d4539a90
PP
160}
161
162ARGPAR_HIDDEN
163unsigned int argpar_item_non_opt_orig_index(
7554f0b6 164 const argpar_item_t * const item)
d4539a90
PP
165{
166 ARGPAR_ASSERT(item);
167 ARGPAR_ASSERT(item->type == ARGPAR_ITEM_TYPE_NON_OPT);
7554f0b6 168 return ((const argpar_item_non_opt_t *) item)->orig_index;
d4539a90
PP
169}
170
171ARGPAR_HIDDEN
172unsigned int argpar_item_non_opt_non_opt_index(
7554f0b6 173 const argpar_item_t * const item)
d4539a90
PP
174{
175 ARGPAR_ASSERT(item);
176 ARGPAR_ASSERT(item->type == ARGPAR_ITEM_TYPE_NON_OPT);
7554f0b6 177 return ((const argpar_item_non_opt_t *) item)->non_opt_index;
d4539a90
PP
178}
179
fc07e526 180ARGPAR_HIDDEN
7554f0b6 181void argpar_item_destroy(const argpar_item_t * const item)
903a5b8a
SM
182{
183 if (!item) {
184 goto end;
185 }
186
1c9a6bde 187 if (item->type == ARGPAR_ITEM_TYPE_OPT) {
7554f0b6 188 argpar_item_opt_t * const opt_item = (argpar_item_opt_t *) item;
903a5b8a 189
d4539a90 190 free(opt_item->arg);
903a5b8a
SM
191 }
192
fc07e526 193 free((void *) item);
903a5b8a
SM
194
195end:
196 return;
197}
198
793f1620
PP
199/*
200 * Creates and returns an option parsing item for the descriptor `descr`
201 * and having the argument `arg` (copied; may be `NULL`).
202 *
203 * Returns `NULL` on memory error.
204 */
903a5b8a 205static
7554f0b6 206argpar_item_opt_t *create_opt_item(const argpar_opt_descr_t * const descr,
903a5b8a
SM
207 const char * const arg)
208{
7554f0b6 209 argpar_item_opt_t *opt_item = ARGPAR_ZALLOC(argpar_item_opt_t);
903a5b8a
SM
210
211 if (!opt_item) {
212 goto end;
213 }
214
1c9a6bde 215 opt_item->base.type = ARGPAR_ITEM_TYPE_OPT;
903a5b8a
SM
216 opt_item->descr = descr;
217
218 if (arg) {
7ac57709 219 opt_item->arg = strdup(arg);
903a5b8a
SM
220 if (!opt_item->arg) {
221 goto error;
222 }
223 }
224
225 goto end;
226
227error:
fc07e526 228 argpar_item_destroy(&opt_item->base);
903a5b8a
SM
229 opt_item = NULL;
230
231end:
232 return opt_item;
233}
234
793f1620
PP
235/*
236 * Creates and returns a non-option parsing item for the original
237 * argument `arg` having the original index `orig_index` and the
238 * non-option index `non_opt_index`.
239 *
240 * Returns `NULL` on memory error.
241 */
903a5b8a 242static
7554f0b6 243argpar_item_non_opt_t *create_non_opt_item(const char * const arg,
903a5b8a
SM
244 const unsigned int orig_index,
245 const unsigned int non_opt_index)
246{
7554f0b6
PP
247 argpar_item_non_opt_t * const non_opt_item =
248 ARGPAR_ZALLOC(argpar_item_non_opt_t);
903a5b8a
SM
249
250 if (!non_opt_item) {
251 goto end;
252 }
253
1c9a6bde 254 non_opt_item->base.type = ARGPAR_ITEM_TYPE_NON_OPT;
903a5b8a
SM
255 non_opt_item->arg = arg;
256 non_opt_item->orig_index = orig_index;
257 non_opt_item->non_opt_index = non_opt_index;
258
259end:
260 return non_opt_item;
261}
262
8b95d883
PP
263/*
264 * If `error` is not `NULL`, sets the error `error` to a new parsing
265 * error object, setting its `unknown_opt_name`, `opt_descr`, and
266 * `is_short` members from the parameters.
267 *
268 * `unknown_opt_name` is the unknown option name without any `-` or `--`
269 * prefix: `is_short` controls which type of unknown option it is.
270 *
271 * Returns 0 on success (including if `error` is `NULL`) or -1 on memory
272 * error.
273 */
274static
7554f0b6 275int set_error(argpar_error_t ** const error, argpar_error_type_t type,
8b95d883 276 const char * const unknown_opt_name,
7554f0b6 277 const argpar_opt_descr_t * const opt_descr, const bool is_short)
8b95d883
PP
278{
279 int ret = 0;
280
281 if (!error) {
282 goto end;
283 }
284
7554f0b6 285 *error = ARGPAR_ZALLOC(argpar_error_t);
8b95d883
PP
286 if (!*error) {
287 goto error;
288 }
289
10aefab2
SM
290 (*error)->type = type;
291
8b95d883
PP
292 if (unknown_opt_name) {
293 (*error)->unknown_opt_name = ARGPAR_CALLOC(char,
c530e1dc 294 strlen(unknown_opt_name) + 1 + (is_short ? 1 : 2));
8b95d883
PP
295 if (!(*error)->unknown_opt_name) {
296 goto error;
297 }
298
299 if (is_short) {
300 strcpy((*error)->unknown_opt_name, "-");
301 } else {
302 strcpy((*error)->unknown_opt_name, "--");
303 }
304
305 strcat((*error)->unknown_opt_name, unknown_opt_name);
306 }
307
308 (*error)->opt_descr = opt_descr;
309 (*error)->is_short = is_short;
310 goto end;
311
312error:
313 argpar_error_destroy(*error);
314 ret = -1;
315
316end:
317 return ret;
318}
319
10aefab2 320ARGPAR_HIDDEN
7554f0b6
PP
321argpar_error_type_t argpar_error_type(
322 const argpar_error_t * const error)
10aefab2
SM
323{
324 ARGPAR_ASSERT(error);
325 return error->type;
326}
327
8b95d883 328ARGPAR_HIDDEN
7554f0b6 329unsigned int argpar_error_orig_index(const argpar_error_t * const error)
8b95d883
PP
330{
331 ARGPAR_ASSERT(error);
332 return error->orig_index;
333}
334
335ARGPAR_HIDDEN
336const char *argpar_error_unknown_opt_name(
7554f0b6 337 const argpar_error_t * const error)
8b95d883
PP
338{
339 ARGPAR_ASSERT(error);
10aefab2 340 ARGPAR_ASSERT(error->type == ARGPAR_ERROR_TYPE_UNKNOWN_OPT);
8b95d883
PP
341 ARGPAR_ASSERT(error->unknown_opt_name);
342 return error->unknown_opt_name;
343}
344
345ARGPAR_HIDDEN
7554f0b6
PP
346const argpar_opt_descr_t *argpar_error_opt_descr(
347 const argpar_error_t * const error, bool * const is_short)
8b95d883
PP
348{
349 ARGPAR_ASSERT(error);
10aefab2
SM
350 ARGPAR_ASSERT(error->type == ARGPAR_ERROR_TYPE_MISSING_OPT_ARG ||
351 error->type == ARGPAR_ERROR_TYPE_UNEXPECTED_OPT_ARG);
8b95d883
PP
352 ARGPAR_ASSERT(error->opt_descr);
353
354 if (is_short) {
355 *is_short = error->is_short;
356 }
357
358 return error->opt_descr;
359}
360
361ARGPAR_HIDDEN
7554f0b6 362void argpar_error_destroy(const argpar_error_t * const error)
8b95d883
PP
363{
364 if (error) {
365 free(error->unknown_opt_name);
366 free((void *) error);
367 }
368}
369
793f1620
PP
370/*
371 * Finds and returns the _first_ descriptor having the short option name
372 * `short_name` or the long option name `long_name` within the option
373 * descriptors `descrs`.
374 *
375 * `short_name` may be `'\0'` to not consider it.
376 *
377 * `long_name` may be `NULL` to not consider it.
378 *
379 * Returns `NULL` if no descriptor is found.
380 */
903a5b8a 381static
7554f0b6 382const argpar_opt_descr_t *find_descr(const argpar_opt_descr_t * const descrs,
903a5b8a
SM
383 const char short_name, const char * const long_name)
384{
7554f0b6 385 const argpar_opt_descr_t *descr;
903a5b8a
SM
386
387 for (descr = descrs; descr->short_name || descr->long_name; descr++) {
388 if (short_name && descr->short_name &&
389 short_name == descr->short_name) {
390 goto end;
391 }
392
393 if (long_name && descr->long_name &&
394 strcmp(long_name, descr->long_name) == 0) {
395 goto end;
396 }
397 }
398
399end:
400 return !descr->short_name && !descr->long_name ? NULL : descr;
401}
402
793f1620 403/* Return type of parse_short_opt_group() and parse_long_opt() */
7554f0b6 404typedef enum parse_orig_arg_opt_ret {
903a5b8a 405 PARSE_ORIG_ARG_OPT_RET_OK,
10aefab2
SM
406 PARSE_ORIG_ARG_OPT_RET_ERROR = -1,
407 PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY = -2,
7554f0b6 408} parse_orig_arg_opt_ret_t;
903a5b8a 409
793f1620
PP
410/*
411 * Parses the short option group argument `short_opt_group`, starting
412 * where needed depending on the state of `iter`.
413 *
414 * On success, sets `*item`.
415 *
416 * On error (except for `PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY`), sets
417 * `*error`.
418 */
903a5b8a 419static
7554f0b6 420parse_orig_arg_opt_ret_t parse_short_opt_group(
d7a82d7f 421 const char * const short_opt_group,
903a5b8a 422 const char * const next_orig_arg,
7554f0b6
PP
423 const argpar_opt_descr_t * const descrs,
424 argpar_iter_t * const iter, argpar_error_t ** const error,
425 argpar_item_t ** const item)
903a5b8a 426{
7554f0b6 427 parse_orig_arg_opt_ret_t ret = PARSE_ORIG_ARG_OPT_RET_OK;
fc07e526
SM
428 bool used_next_orig_arg = false;
429 const char *opt_arg = NULL;
7554f0b6
PP
430 const argpar_opt_descr_t *descr;
431 argpar_item_opt_t *opt_item;
903a5b8a 432
d7a82d7f 433 ARGPAR_ASSERT(strlen(short_opt_group) != 0);
903a5b8a 434
d7a82d7f
PP
435 if (!iter->short_opt_group_ch) {
436 iter->short_opt_group_ch = short_opt_group;
fc07e526 437 }
903a5b8a 438
fc07e526 439 /* Find corresponding option descriptor */
d7a82d7f 440 descr = find_descr(descrs, *iter->short_opt_group_ch, NULL);
fc07e526 441 if (!descr) {
8b95d883
PP
442 const char unknown_opt_name[] =
443 {*iter->short_opt_group_ch, '\0'};
444
10aefab2 445 ret = PARSE_ORIG_ARG_OPT_RET_ERROR;
8b95d883 446
10aefab2
SM
447 if (set_error(error, ARGPAR_ERROR_TYPE_UNKNOWN_OPT,
448 unknown_opt_name, NULL, true)) {
8b95d883
PP
449 ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY;
450 }
451
fc07e526
SM
452 goto error;
453 }
903a5b8a 454
fc07e526 455 if (descr->with_arg) {
d7a82d7f 456 if (iter->short_opt_group_ch[1]) {
fc07e526 457 /* `-oarg` form */
d7a82d7f 458 opt_arg = &iter->short_opt_group_ch[1];
fc07e526
SM
459 } else {
460 /* `-o arg` form */
461 opt_arg = next_orig_arg;
462 used_next_orig_arg = true;
903a5b8a
SM
463 }
464
fc07e526
SM
465 /*
466 * We accept `-o ''` (empty option argument), but not
467 * `-o` alone if an option argument is expected.
468 */
d7a82d7f 469 if (!opt_arg || (iter->short_opt_group_ch[1] &&
fb12ac67 470 strlen(opt_arg) == 0)) {
10aefab2 471 ret = PARSE_ORIG_ARG_OPT_RET_ERROR;
8b95d883 472
10aefab2
SM
473 if (set_error(error, ARGPAR_ERROR_TYPE_MISSING_OPT_ARG,
474 NULL, descr, true)) {
8b95d883
PP
475 ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY;
476 }
477
903a5b8a
SM
478 goto error;
479 }
fc07e526 480 }
903a5b8a 481
fc07e526
SM
482 /* Create and append option argument */
483 opt_item = create_opt_item(descr, opt_arg);
484 if (!opt_item) {
871eba32 485 ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY;
fc07e526
SM
486 goto error;
487 }
903a5b8a 488
fc07e526 489 *item = &opt_item->base;
d7a82d7f 490 iter->short_opt_group_ch++;
903a5b8a 491
d7a82d7f 492 if (descr->with_arg || !*iter->short_opt_group_ch) {
fc07e526 493 /* Option has an argument: no more options */
d7a82d7f 494 iter->short_opt_group_ch = NULL;
fc07e526
SM
495
496 if (used_next_orig_arg) {
497 iter->i += 2;
498 } else {
499 iter->i++;
500 }
903a5b8a
SM
501 }
502
503 goto end;
504
505error:
871eba32 506 ARGPAR_ASSERT(ret != PARSE_ORIG_ARG_OPT_RET_OK);
903a5b8a
SM
507
508end:
509 return ret;
510}
511
793f1620
PP
512/*
513 * Parses the long option argument `long_opt_arg`.
514 *
515 * On success, sets `*item`.
516 *
517 * On error (except for `PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY`), sets
518 * `*error`.
519 */
903a5b8a 520static
7554f0b6 521parse_orig_arg_opt_ret_t parse_long_opt(const char * const long_opt_arg,
903a5b8a 522 const char * const next_orig_arg,
7554f0b6
PP
523 const argpar_opt_descr_t * const descrs,
524 argpar_iter_t * const iter, argpar_error_t ** const error,
525 argpar_item_t ** const item)
903a5b8a 526{
7554f0b6
PP
527 parse_orig_arg_opt_ret_t ret = PARSE_ORIG_ARG_OPT_RET_OK;
528 const argpar_opt_descr_t *descr;
529 argpar_item_opt_t *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 643static
7554f0b6 644parse_orig_arg_opt_ret_t parse_orig_arg_opt(const char * const orig_arg,
903a5b8a 645 const char * const next_orig_arg,
7554f0b6
PP
646 const argpar_opt_descr_t * const descrs,
647 argpar_iter_t * const iter, argpar_error_t ** const error,
648 argpar_item_t ** const item)
903a5b8a 649{
7554f0b6 650 parse_orig_arg_opt_ret_t ret = PARSE_ORIG_ARG_OPT_RET_OK;
903a5b8a 651
7ac57709 652 ARGPAR_ASSERT(orig_arg[0] == '-');
903a5b8a
SM
653
654 if (orig_arg[1] == '-') {
655 /* Long option */
7554f0b6
PP
656 ret = parse_long_opt(&orig_arg[2], next_orig_arg, descrs, iter,
657 error, item);
903a5b8a
SM
658 } else {
659 /* Short option */
7554f0b6
PP
660 ret = parse_short_opt_group(&orig_arg[1], next_orig_arg, descrs,
661 iter, error, item);
903a5b8a
SM
662 }
663
664 return ret;
665}
666
7ac57709 667ARGPAR_HIDDEN
7554f0b6 668argpar_iter_t *argpar_iter_create(const unsigned int argc,
fc07e526 669 const char * const * const argv,
7554f0b6 670 const argpar_opt_descr_t * const descrs)
903a5b8a 671{
7554f0b6 672 argpar_iter_t *iter = ARGPAR_ZALLOC(argpar_iter_t);
903a5b8a 673
fc07e526
SM
674 if (!iter) {
675 goto end;
903a5b8a
SM
676 }
677
64875a48
PP
678 iter->user.argc = argc;
679 iter->user.argv = argv;
680 iter->user.descrs = descrs;
d1f7bbdb
PP
681 iter->tmp_buf.size = 128;
682 iter->tmp_buf.data = ARGPAR_CALLOC(char, iter->tmp_buf.size);
683 if (!iter->tmp_buf.data) {
684 argpar_iter_destroy(iter);
685 iter = NULL;
686 goto end;
687 }
903a5b8a 688
fc07e526
SM
689end:
690 return iter;
691}
903a5b8a 692
fc07e526 693ARGPAR_HIDDEN
7554f0b6 694void argpar_iter_destroy(argpar_iter_t * const iter)
fc07e526 695{
d1f7bbdb
PP
696 if (iter) {
697 free(iter->tmp_buf.data);
698 free(iter);
699 }
fc07e526 700}
903a5b8a 701
fc07e526 702ARGPAR_HIDDEN
7554f0b6
PP
703argpar_iter_next_status_t argpar_iter_next(argpar_iter_t * const iter,
704 const argpar_item_t ** const item,
705 const argpar_error_t ** const error)
fc07e526 706{
7554f0b6
PP
707 argpar_iter_next_status_t status;
708 parse_orig_arg_opt_ret_t parse_orig_arg_opt_ret;
fc07e526
SM
709 const char *orig_arg;
710 const char *next_orig_arg;
7554f0b6 711 argpar_error_t ** const nc_error = (argpar_error_t **) error;
7ac57709 712
64875a48 713 ARGPAR_ASSERT(iter->i <= iter->user.argc);
37bb2b1f
PP
714
715 if (error) {
8b95d883 716 *nc_error = NULL;
37bb2b1f 717 }
fc07e526 718
64875a48 719 if (iter->i == iter->user.argc) {
2af370d0 720 status = ARGPAR_ITER_NEXT_STATUS_END;
fc07e526
SM
721 goto end;
722 }
7ac57709 723
64875a48 724 orig_arg = iter->user.argv[iter->i];
fc07e526 725 next_orig_arg =
64875a48
PP
726 iter->i < (iter->user.argc - 1) ?
727 iter->user.argv[iter->i + 1] : NULL;
fc07e526 728
dd757a65
PP
729 if (strcmp(orig_arg, "-") == 0 || strcmp(orig_arg, "--") == 0 ||
730 orig_arg[0] != '-') {
fc07e526 731 /* Non-option argument */
7554f0b6 732 const argpar_item_non_opt_t * const non_opt_item =
fc07e526
SM
733 create_non_opt_item(orig_arg, iter->i,
734 iter->non_opt_index);
735
736 if (!non_opt_item) {
2af370d0 737 status = ARGPAR_ITER_NEXT_STATUS_ERROR_MEMORY;
fc07e526 738 goto end;
903a5b8a
SM
739 }
740
fc07e526
SM
741 iter->non_opt_index++;
742 iter->i++;
743 *item = &non_opt_item->base;
2af370d0 744 status = ARGPAR_ITER_NEXT_STATUS_OK;
fc07e526
SM
745 goto end;
746 }
903a5b8a 747
fc07e526
SM
748 /* Option argument */
749 parse_orig_arg_opt_ret = parse_orig_arg_opt(orig_arg,
64875a48 750 next_orig_arg, iter->user.descrs, iter, nc_error,
7554f0b6 751 (argpar_item_t **) item);
fc07e526
SM
752 switch (parse_orig_arg_opt_ret) {
753 case PARSE_ORIG_ARG_OPT_RET_OK:
2af370d0 754 status = ARGPAR_ITER_NEXT_STATUS_OK;
fc07e526 755 break;
10aefab2 756 case PARSE_ORIG_ARG_OPT_RET_ERROR:
8b95d883
PP
757 if (error) {
758 ARGPAR_ASSERT(*error);
759 (*nc_error)->orig_index = iter->i;
760 }
10aefab2 761 status = ARGPAR_ITER_NEXT_STATUS_ERROR;
d4d05805
PP
762 break;
763 case PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY:
2af370d0 764 status = ARGPAR_ITER_NEXT_STATUS_ERROR_MEMORY;
fc07e526
SM
765 break;
766 default:
767 abort();
768 }
769
770end:
771 return status;
772}
773
774ARGPAR_HIDDEN
7554f0b6 775unsigned int argpar_iter_ingested_orig_args(const argpar_iter_t * const iter)
fc07e526
SM
776{
777 return iter->i;
778}
This page took 0.061703 seconds and 4 git commands to generate.