diff options
| author | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2025-11-22 14:50:10 +0100 |
|---|---|---|
| committer | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2025-11-22 14:50:10 +0100 |
| commit | 2e314136ed58b6860c667e379bef22190fe84aa2 (patch) | |
| tree | 21f897804c03c3419a0c015ace3bf9fb9c1b8e52 /src/opt.c | |
| parent | 50d223b12c1319b4b9c4a5b8e34866c46996cb36 (diff) | |
New upstream version 5.2upstream/5.2upstream
Diffstat (limited to 'src/opt.c')
| -rw-r--r-- | src/opt.c | 299 |
1 files changed, 152 insertions, 147 deletions
@@ -1,6 +1,6 @@ /* * libHX/opt.c - * Copyright Jan Engelhardt, 2002-2011 + * Copyright Jan Engelhardt, 2025 * * This file is part of libHX. libHX is free software; you can * redistribute it and/or modify it under the terms of the GNU Lesser @@ -8,6 +8,7 @@ * either version 2.1 or (at your option) any later version. */ #include <errno.h> +#include <limits.h> #include <stdbool.h> #include <stddef.h> #include <stdint.h> @@ -119,13 +120,15 @@ enum { /** * struct HX_getopt_vars - option parser working variable set * @arg0: saved program name - * @remaining: list of extracted non-options + * @desc: list of extracted options + * @oarg: list of extracted options + * @uarg: list of extracted non-options * @cbi: callback info * @flags: flags setting the behavior for HX_getopt */ struct HX_getopt_vars { const char *arg0; - struct HXdeque *remaining; + struct HXdeque *desc, *oarg, *uarg; struct HXoptcb cbi; unsigned int flags; }; @@ -218,6 +221,10 @@ static void do_assign(struct HXoptcb *cbi, const char *arg0) if (opt->ptr != NULL) *static_cast(char **, opt->ptr) = HX_strdup(cbi->data); break; + case HXTYPE_STRP: + if (opt->ptr != nullptr) + *static_cast(const char **, opt->ptr) = cbi->data; + break; case HXTYPE_STRDQ: HXdeque_push(opt->ptr, HX_strdup(cbi->data)); break; @@ -237,6 +244,15 @@ static void do_assign(struct HXoptcb *cbi, const char *arg0) opt->cb(cbi); } +static int do_assign_front(struct HX_getopt_vars *par) +{ + if (HXdeque_push(par->desc, par->cbi.current) == nullptr || + HXdeque_push(par->oarg, par->cbi.data) == nullptr) + return -errno; + do_assign(&par->cbi, par->arg0); + return 0; +} + static __inline__ const struct HXoption * lookup_short(const struct HXoption *table, char opt) { @@ -494,27 +510,14 @@ static int HX_getopt_error(int err, const char *key, unsigned int flags) return HXOPT_I_ERROR; } -static int HX_getopt_twolong(const char *const *opt, +static int HX_getopt_twolong(const char *key, const char *value, struct HX_getopt_vars *par) { - const char *key = opt[0], *value = opt[1]; - par->cbi.current = lookup_long_pfx(par->cbi.table, key + 2); if (par->cbi.current == &HXopt_ambig_prefix) return HX_getopt_error(HXOPT_E_AMBIG_PREFIX, key, par->flags); - if (par->cbi.current == NULL) { - if (par->flags & HXOPT_PTHRU) { - char *tmp = HX_strdup(key); - if (tmp == NULL) - return -errno; - if (HXdeque_push(par->remaining, tmp) == NULL) { - free(tmp); - return -errno; - } - return HXOPT_S_NORMAL | HXOPT_I_ADVARG; - } + if (par->cbi.current == nullptr) return HX_getopt_error(HXOPT_E_LONG_UNKNOWN, key, par->flags); - } par->cbi.flags = HXOPTCB_BY_LONG; if (takes_void(par->cbi.current->type)) { @@ -550,11 +553,21 @@ static int HX_getopt_long(const char *cur, struct HX_getopt_vars *par) return -errno; value = strchr(key, '='); if (value == nullptr) { - /* Cannot happen because state is always !S_TWOLONG */ + /* + * Cannot happen because state is always !S_TWOLONG, but make + * static analyzers happy. + */ free(key); return -EINVAL; } - *value++ = '\0'; + *value = '\0'; + value = strchr(cur, '='); + if (value == nullptr) { + /* Cannot happen either */ + free(key); + return -EINVAL; + } + ++value; par->cbi.current = lookup_long_pfx(par->cbi.table, key + 2); if (par->cbi.current == &HXopt_ambig_prefix) { ret = HX_getopt_error(HXOPT_E_AMBIG_PREFIX, key, par->flags); @@ -562,15 +575,6 @@ static int HX_getopt_long(const char *cur, struct HX_getopt_vars *par) return ret; } if (par->cbi.current == NULL) { - if (par->flags & HXOPT_PTHRU) { - /* Undo nuke of '=' and reuse alloc */ - value[-1] = '='; - if (HXdeque_push(par->remaining, key) == NULL) { - free(key); - return -errno; - } - return HXOPT_S_NORMAL | HXOPT_I_ADVARG; - } ret = HX_getopt_error(HXOPT_E_LONG_UNKNOWN, key, par->flags); free(key); return ret; @@ -585,58 +589,48 @@ static int HX_getopt_long(const char *cur, struct HX_getopt_vars *par) return ret; } + free(key); par->cbi.flags = HXOPTCB_BY_LONG; par->cbi.data = value; - /* Not possible to use %HXOPT_I_ASSIGN due to transience of @key. */ - do_assign(&par->cbi, par->arg0); - free(key); + /* + * Not possible to use %HXOPT_I_ASSIGN due to transience of @key. Thus + * manually call do_assign now rather than in the superordinate + * function. + */ + ret = do_assign_front(par); + if (ret < 0) + return ret; return HXOPT_S_NORMAL | HXOPT_I_ADVARG; } -static int HX_getopt_short(const char *const *opt, const char *cur, +static int HX_getopt_short(const char *key, const char *value, struct HX_getopt_vars *par) { - char op = *cur; + char op = *key; if (op == '\0') return HXOPT_S_NORMAL | HXOPT_I_ADVARG; par->cbi.current = lookup_short(par->cbi.table, op); - if (par->cbi.current == NULL) { - if (par->flags & HXOPT_PTHRU) { - /* - * @cur-1 is always valid: it is either the previous - * char, or it is '-'. - */ - char *buf = HX_strdup(cur - 1); - if (buf != NULL) - *buf = '-'; - if (HXdeque_push(par->remaining, buf) == NULL) { - free(buf); - return -errno; - } - return HXOPT_S_NORMAL | HXOPT_I_ADVARG; - } + if (par->cbi.current == nullptr) return HX_getopt_error(HXOPT_E_SHORT_UNKNOWN, &op, par->flags); - } par->cbi.flags = HXOPTCB_BY_SHORT; if (takes_void(par->cbi.current->type)) { /* -A */ par->cbi.data = NULL; return HXOPT_S_SHORT | HXOPT_I_ASSIGN | HXOPT_I_ADVCHAR; - } else if (cur[1] != '\0') { + } else if (key[1] != '\0') { /* -Avalue */ - par->cbi.data = cur + 1; + par->cbi.data = key + 1; return HXOPT_S_NORMAL | HXOPT_I_ASSIGN | HXOPT_I_ADVARG; } - cur = *++opt; if (par->cbi.current->type & HXOPT_OPTIONAL) { - if (cur == NULL || *cur != '-' || - (cur[0] == '-' && cur[1] == '\0')) { + if (value == nullptr || *value != '-' || + (value[0] == '-' && value[1] == '\0')) { /* -f - -f bla */ - par->cbi.data = cur; + par->cbi.data = value; return HXOPT_S_NORMAL | HXOPT_I_ASSIGN | HXOPT_I_ADVARG2; } else { /* -f -a-file --another --file -- endofoptions */ @@ -645,41 +639,28 @@ static int HX_getopt_short(const char *const *opt, const char *cur, } } else { /* -A value */ - if (cur == NULL) + if (value == nullptr) return HX_getopt_error(HXOPT_E_SHORT_MISSING, &op, par->flags); - par->cbi.data = cur; + par->cbi.data = value; return HXOPT_S_NORMAL | HXOPT_I_ASSIGN | HXOPT_I_ADVARG2; } } static int HX_getopt_term(const char *cur, const struct HX_getopt_vars *par) { - char *tmp = HX_strdup(cur); - if (tmp == NULL) - return -errno; - if (HXdeque_push(par->remaining, tmp) == NULL) { - free(tmp); + if (HXdeque_push(par->uarg, cur) == nullptr) return -errno; - } return HXOPT_S_TERMINATED | HXOPT_I_ADVARG; } static int HX_getopt_normal(const char *cur, const struct HX_getopt_vars *par) { if (cur[0] == '-' && cur[1] == '\0') { - /* Note to popt developers: A single dash is NOT an option! */ - HXdeque_push(par->remaining, HX_strdup(cur)); + HXdeque_push(par->uarg, cur); return HXOPT_S_NORMAL | HXOPT_I_ADVARG; } - if (cur[0] == '-' && cur[1] == '-' && cur[2] == '\0') { - /* - * Double dash. If passthrough is on, "--" must be copied into - * @remaining. This is done in the next round. - */ - if (!(par->flags & HXOPT_PTHRU)) - return HXOPT_S_TERMINATED | HXOPT_I_ADVARG; - return HXOPT_S_TERMINATED; - } + if (cur[0] == '-' && cur[1] == '-' && cur[2] == '\0') + return HXOPT_S_TERMINATED | HXOPT_I_ADVARG; if (cur[0] == '-' && cur[1] == '-') { /* long option */ if (strchr(cur + 2, '=') == NULL) return HXOPT_S_TWOLONG; @@ -692,62 +673,71 @@ static int HX_getopt_normal(const char *cur, const struct HX_getopt_vars *par) if (par->flags & HXOPT_RQ_ORDER) /* POSIX: first non-option implies option termination */ return HXOPT_S_TERMINATED; - cur = HX_strdup(cur); - if (cur == NULL || HXdeque_push(par->remaining, cur) == NULL) + if (HXdeque_push(par->uarg, cur) == nullptr) return -errno; return HXOPT_S_NORMAL | HXOPT_I_ADVARG; } -EXPORT_SYMBOL int HX_getopt5(const struct HXoption *table, char **orig_argv, - int *new_argc, char ***new_argv, unsigned int flags) +EXPORT_SYMBOL void HX_getopt6_clean(struct HXopt6_result *r) +{ + free(r->desc); + free(r->oarg); + free(r->uarg); + HX_zvecfree(r->dup_argv); + r->desc = nullptr; + r->oarg = nullptr; + r->uarg = nullptr; + r->dup_argv = nullptr; +} + +EXPORT_SYMBOL int HX_getopt6(const struct HXoption *table, int argc, + char **argv, struct HXopt6_result *result, unsigned int flags) { struct HX_getopt_vars ps; - const char **opt = const_cast(const char **, orig_argv); + const char **opt = const_cast(const char **, argv); int state = HXOPT_S_NORMAL; int ret = -ENOMEM; - unsigned int argk = 0; - if (new_argc != nullptr) - *new_argc = 0; - if (new_argv != nullptr) - *new_argv = nullptr; + if ((flags & (HXOPT_RQ_ORDER | HXOPT_ANY_ORDER)) == (HXOPT_RQ_ORDER | HXOPT_ANY_ORDER)) + return -EINVAL; + if (result == nullptr && flags & (HXOPT_ITER_OPTS | HXOPT_ITER_ARGS | HXOPT_DUP_ARGS)) + return -EINVAL; + if (result != nullptr) + memset(result, 0, sizeof(*result)); memset(&ps, 0, sizeof(ps)); - ps.remaining = HXdeque_init(); - if (ps.remaining == NULL) { + ps.desc = HXdeque_init(); + ps.oarg = HXdeque_init(); + ps.uarg = HXdeque_init(); + if (ps.desc == nullptr || ps.oarg == nullptr || ps.uarg == nullptr) { ret = -errno; goto out; } ps.flags = flags; - ps.arg0 = *opt; - ps.cbi.table = table; - - if (*opt != NULL) { - /* put argv[0] back */ - char *arg = HX_strdup(*opt++); - if (arg == NULL) { - ret = -errno; - goto out; - } - if (HXdeque_push(ps.remaining, arg) == NULL) { - free(arg); - ret = -errno; - goto out; - } + if (argc < 0) { + argc = 0; + for (const char **p = opt; *p != nullptr; ++p) + ++argc; } + if (argc > 0) { + ps.arg0 = *opt++; + --argc; + } + ps.cbi.table = table; - if (posix_me_harder()) + if (!(ps.flags & HXOPT_ANY_ORDER) && posix_me_harder()) ps.flags |= HXOPT_RQ_ORDER; - for (const char *cur = *opt; cur != NULL; ) { + const char *op0 = argc > 0 ? opt[0] : nullptr; + while (argc > 0) { if (state == HXOPT_S_TWOLONG) - state = HX_getopt_twolong(opt, &ps); + state = HX_getopt_twolong(op0, argc > 1 ? opt[1] : nullptr, &ps); else if (state == HXOPT_S_LONG) - state = HX_getopt_long(cur, &ps); + state = HX_getopt_long(op0, &ps); else if (state == HXOPT_S_SHORT) - state = HX_getopt_short(opt, cur, &ps); + state = HX_getopt_short(op0, argc > 1 ? opt[1] : nullptr, &ps); else if (state == HXOPT_S_TERMINATED) - state = HX_getopt_term(cur, &ps); + state = HX_getopt_term(op0, &ps); else if (state == HXOPT_S_NORMAL) - state = HX_getopt_normal(cur, &ps); + state = HX_getopt_normal(op0, &ps); if (state < 0) { ret = state; @@ -757,31 +747,62 @@ EXPORT_SYMBOL int HX_getopt5(const struct HXoption *table, char **orig_argv, ret = state & ~HXOPT_I_ERROR; goto out; } - if (state & HXOPT_I_ASSIGN) - do_assign(&ps.cbi, ps.arg0); - if (state & HXOPT_I_ADVARG) - cur = *++opt; - else if (state & HXOPT_I_ADVARG2) - cur = *(opt += 2); - else if (state & HXOPT_I_ADVCHAR) - ++cur; + if (state & HXOPT_I_ASSIGN) { + ret = do_assign_front(&ps); + if (ret < 0) + goto out; + } + if (state & HXOPT_I_ADVARG) { + --argc; + if (argc > 0) + op0 = *++opt; + } else if (state & HXOPT_I_ADVARG2) { + opt += 2; + argc -= 2; + if (argc > 0) + op0 = *opt; + } else if (state & HXOPT_I_ADVCHAR) { + ++op0; + } state &= ~HXOPT_I_MASK; } - if (new_argv != nullptr) { - *new_argv = reinterpret_cast(char **, HXdeque_to_vec(ps.remaining, &argk)); - if (*new_argv == nullptr) { + if (flags & HXOPT_ITER_OPTS) { + size_t nelem = 0; + result->desc = reinterpret_cast(const struct HXoption **, HXdeque_to_vec(ps.desc, &nelem)); + result->oarg = reinterpret_cast(char **, HXdeque_to_vec(ps.oarg, &nelem)); + if (result->desc == nullptr || result->oarg == nullptr) { + ret = -errno; + goto out; + } + result->nopts = nelem < INT_MAX ? nelem : INT_MAX; + } + if (flags & HXOPT_ITER_ARGS) { + size_t nelem = 0; + result->uarg = reinterpret_cast(char **, HXdeque_to_vecx(ps.uarg, &nelem)); + if (result->uarg == nullptr) { + ret = -errno; + goto out; + } + result->nargs = nelem < INT_MAX ? nelem : INT_MAX; + } + if (flags & HXOPT_DUP_ARGS) { + if (ps.arg0 != nullptr && HXdeque_unshift(ps.uarg, ps.arg0) == nullptr) { ret = -errno; goto out; } - if (new_argc != nullptr) - *new_argc = argk; - /* pointers are owned by new_argv now, so free only the deque head */ - HXdeque_free(ps.remaining); - ps.remaining = nullptr; + size_t nelem = 0; + result->dup_argv = HXdeque_to_vec_strdup(ps.uarg, &nelem); + if (result->dup_argv == nullptr) { + ret = -errno; + goto out; + } + result->dup_argc = nelem < INT_MAX ? nelem : INT_MAX; } ret = HXOPT_ERR_SUCCESS; out: + if (ret != HXOPT_ERR_SUCCESS && result != nullptr) + HX_getopt6_clean(result); if (ret == HXOPT_ERR_SUCCESS) { } else if (ret < 0) { if (!(ps.flags & HXOPT_QUIET)) @@ -793,28 +814,12 @@ EXPORT_SYMBOL int HX_getopt5(const struct HXoption *table, char **orig_argv, else if (ps.flags & HXOPT_USAGEONERR) HX_getopt_usage(&ps.cbi, stderr); } - if (ps.remaining != nullptr) - HXdeque_genocide2(ps.remaining, free); - return ret; -} - -EXPORT_SYMBOL int HX_getopt(const struct HXoption *table, int *argc, - char ***argv, unsigned int flags) -{ - int new_argc = 0; - char **new_argv = nullptr; - int ret = HX_getopt5(table, *argv, &new_argc, - flags & HXOPT_KEEP_ARGV ? nullptr : &new_argv, flags); - if (ret != HXOPT_ERR_SUCCESS) - return ret; - if (flags & HXOPT_KEEP_ARGV) - // NO_CREATE_NEW / DESTROY_NEW - new_argv = *argv; - else if (flags & HXOPT_DESTROY_OLD) - HX_zvecfree(*argv); - if (argc != nullptr) - *argc = new_argc; - *argv = new_argv; + if (ps.uarg != nullptr) + HXdeque_free(ps.uarg); + if (ps.oarg != nullptr) + HXdeque_free(ps.oarg); + if (ps.desc != nullptr) + HXdeque_free(ps.desc); return ret; } |
