author | Olivier Brunel
<jjk@jjacky.com> 2023-05-12 07:51:56 UTC |
committer | Olivier Brunel
<jjk@jjacky.com> 2023-07-05 07:37:02 UTC |
parent | c1732b0d03e9454f1c688772e936f16aa1f743dd |
src/doc/loadopt.h.0.md | +15 | -14 |
src/doc/loadopt.h/loadopt.3.md | +71 | -21 |
src/doc/parseopt.h/parseopt.3.md | +6 | -8 |
src/liblimb/include/limb/loadopt.h | +22 | -12 |
src/liblimb/include/limb/parseopt.h | +6 | -5 |
src/liblimb/loadopt.h/loadopt.c | +148 | -40 |
diff --git a/src/doc/loadopt.h.0.md b/src/doc/loadopt.h.0.md index 53c61ee..4068082 100644 --- a/src/doc/loadopt.h.0.md +++ b/src/doc/loadopt.h.0.md @@ -18,41 +18,42 @@ optionally configuration file. The following constants are defined : -: *OPT_SKIP* -:: Set an option not be loaded from configuration file - -: *OPT_REQ* -:: Set an option to be required +: *OPT_SKIP*, *OPT_REQ*, *OPT_SA*, *OPT_ESC*, *OPT_PATH*, *OPT_FILE*, *OPT_RPT* +:: Flags that can be used when defining options/arguments to [loadopt](3). ## Macros The following macros are defined : -: *LOADOPT_ARGUMENTS*, *LOADOPT_DONE_OPEN*, *LOADOPT_DONE* +: *LOADOPT_ARGUMENTS*, *LOADOPT_DONE*, *LOADOPT_STOP* :: To be used as element in a *struct option* array : *LOADOPT_INIT* :: Value to initialize a *struct loadopt*. -: *ARGUMENT_REQ*, *ARGUMENT_OPT* +: *ARGUMENT_REQ*(`arg`), *ARGUMENT_OPT*(`arg`) :: To define required or optional argument in a *struct option* array to be :: checked by [loadopt](3) -: *AUTOOPT_NOCONFIG* +: *AUTOOPT_NOCONFIG*(`shortopt`, `longopt`, `flags`, `id`) :: To define the option to disable loading (specified) options from :: configuration -: *LO_ARG(`ctx`)* +: *LO_ARG*(`ctx`) :: To get a pointer to the current option's argument. -: *LO_CUR(`ctx`)* -:: To get the index of the first argument in argv. +: *LO_OFF*(`ctx`) +:: To get the offset to the current option's argument. See [loadopt](3) for +:: more. + +: *LO_CUR*(`ctx`) +:: To get the index of the first argument in `argv`. -: *LO_IDX(`ctx`)* +: *LO_IDX*(`ctx`) :: To get the index of the current option. -: *LO_FROMFILE(`ctx`)* -:: Return whether or not the option was set in configuration file. +: *LO_FROMFILE*(`ctx`) +:: Return whether or not the option was set from configuration directory. ## Structures diff --git a/src/doc/loadopt.h/loadopt.3.md b/src/doc/loadopt.h/loadopt.3.md index 0395f37..767ee5b 100644 --- a/src/doc/loadopt.h/loadopt.3.md +++ b/src/doc/loadopt.h/loadopt.3.md @@ -10,19 +10,20 @@ loadopt - parse options from command-line and (optionally) configuration file #include <limb/loadopt.h> ```pre hl -int loadopt(int <em>argc</em>, const char **<em>argv</em>, const struct option *<em>options</em>, - int dirfd, const char *<em>confdir</em>, char *<em>buf</em>, size_t <em>blen</em>, - unsigned int <em>flags</em>, struct loadopt *<em>ctx</em>) +int loadopt(stralloc *<em>sa</em>, int <em>argc</em>, const char **<em>argv</em>, const struct option *<em>options</em>, + int dirfd, const char *<em>confdir</em>, unsigned int <em>poflags</em>, struct loadopt *<em>ctx</em>) const char *LO_ARG(struct loadopt *<em>ctx</em>) -u16 LO_CUR(struct loadopt *<em>ctx</em>) -int LO_IDX(struct loadopt *<em>ctx</em>) +size_t *LO_OFF*(struct loadopt *<em>ctx</em>) +u16 *LO_CUR*(struct loadopt *<em>ctx</em>) +int *LO_IDX*(struct loadopt *<em>ctx</em>) +int *LO_FROMFILE*(struct loadopt *<em>ctx</em>) ``` # DESCRIPTION -The `loadopt`() function parses command-line arguments. Then, `confdir` is not -*NULL*, it will try to load any options that hasn't been set yet nor marked +The `loadopt`() function parses command-line arguments. Then, if `confdir` is +not *NULL*, it will try to load any options that hasn't been set yet nor marked *OPT_SKIP* from the configuration directory `confdir`. If `confdir` describes a relative path, it is relative to the directory associated with the file descriptor `dirfd`, which may be *AT_FDCWD* to use the current working @@ -49,14 +50,45 @@ value is constructed as a bitwise-inclusive OR of the following : :: directory), the option has not been set. :: Mostly useful with options that require an argument. +: *OPT_SA* +:: The value of the option's argument will be appended into the stralloc `sa`, +:: and available through the macro *LO_OFF*(), indicating the offset at which +:: it begins. When used, the macro *LO_ARG*() will always return *NULL*. +:: This can be useful if the argument's value must be kept after option parsing, +:: even when the option is set from configuration directory. +:: Additionally, in case some pre-processing is applied, see below. + +: *OPT_ESC* +:: Implies *OPT_SA*. If the option's argument is within double-quotes (`"`) then +:: it will be un-escaped (as described in [esc_scan](3)) before being placed +:: into `sa`, thus ensuring the available value is always ready for use. + +: *OPT_PATH* +:: Implies *OPT_ESC*. Ensures that the (possibly unescaped) value does /not/ +:: contain any NUL byte, and is thusly a valid path name. + +: *OPT_FILE* +:: Implies *OPT_PATH*. Ensures that the (possibly unescaped) value does /not/ +:: contain any slashes (`/`), and is thusly a valid file name only. + +: *OPT_RPT* +:: Can only be used for the last-defined argument, not options. It indicates +:: that this argument can be "repeated", i.e. from this position (in `argv`) on, +:: any and all further arguments are processed as this one. + The last argument `ctx` is an opaque structure that should be initialized to *LOADOPT_ZERO*. +The stralloc pointed by `sa` is required when `confdir` is not *NULL*, as it +will be used as temporary space to read files as needed. If at also required if +at least one element of `options` has its *OPT_SA* flag set. + Accessing information is done through macros `LO_ARG`(), `LO_IDX`() and `LO_CUR`(), which are similar to their [parseopt](3) counterparts *PO_ARG*(), *PO_IDX*() and *PO_CUR*() respectively. -Additionally, macro `LO_FROMFILE`() will return 1 when the current option came +Additionally, macro `LO_OFF`() must be used when *OPT_SA* was set, as described +above, and macro `LO_FROMFILE`() will return 1 when the current option came from configuration directory, zero otherwise (i.e. from command-line). ## Configuration Directory @@ -83,6 +115,9 @@ specified on command-line. ! Note that when an option was specified on configuration directory, its ! argument, if any, will /not/ be preserved/remain valid once parsing the option ! is done. Thus, if its value is needed for later use it needs to be copied. +! An easy way to achieve this is using the *OPT_SA* flag, to have it placed into +! the stralloc `sa` automatically (whether or not it comes from configuration +! directory). ## Command-line Arguments @@ -97,9 +132,8 @@ referring to an argument instead of an option. Note that *LOADOPT_ARGUMENTS* can be the first element of the array, if there are no options. -For elements referring to arguments, members `shortopt`, `id` and `flags` are -ignored. The `longopt` member is only used in warnings to the user, to refer to -said argument. +For elements referring to arguments, member `shortopt` is ignored. The `longopt` +member is only used in warnings to the user, to refer to said argument. The important member is `arg` which is treated as defining whether the argument is required (*ARG_REQ*) or optional (*ARG_OPT*). If required and no @@ -108,26 +142,42 @@ specified, `loadopt`() successfully ends, returning 0. To define them, you can use the following convenience macros : -: *ARGUMENT_OPT*(`name`) +: *ARGUMENT_OPT*(`name`, `flags`, `id`) :: To define argument `arg` as optional -: *ARGUMENT_REQ*(`name`) +: *ARGUMENT_REQ*(`name`, `flags`, `id`) : To define argument `arg` as required +The actual parsing/handling of arguments is no different than of options, +`loadopt`() will return the corresponding `id` (which must be valid as described +above for options, with the exception that *OPTID_SHORTOPT* cannot be used), the +same macros are to be used. + +Flags can also be used, so it is possible to have arguments' values be copied +into the stralloc `sa` automatically, unescaped if needed and checked. Note +however that *OPT_SKIP* and *OPT_REQ* cannot be used here. + Finally, after all such argument definitions in the array `options`, you must -terminate it with either one of the macros *LOADOPT_DONE_OPEN* and -*LOADOPT_DONE* +terminate it with either one of the macros *LOADOPT_DONE* or *LOADOPT_STOP*. -If the former was specified, `loadopt`() ends successfully regardless of whether -more arguments were specified on command-line or not. With the later however, +If the later was specified, `loadopt`() ends successfully regardless of whether +more arguments were specified on command-line or not. With the former however, any more arguments will result in an error for "too many arguments". +! NOTE: +! *LOADOPT_DONE* is actually the same as *OPTION_DONE* as described from +! [parseopt](3). + Note that either of those can be specified even without any actual arguments, -i.e. as last element right after all the options, and must be specified as last -element. +i.e. as last element right after all the options, but must always be specified +as last element. + +### Argument Repeating -In other words, *LOADOPT_DONE* is the same as *OPTION_DONE* as described from -[parseopt](3). +It is possible, instead of using one of *LOADOPT_DONE* or *LOADOPT_STOP*, to set +the flag *OPT_RPT* in the last argument. In which case any more argument +specified on command-line will be processed as such an argument, over & over +until all command-line has been parsed. # RETURN VALUE diff --git a/src/doc/parseopt.h/parseopt.3.md b/src/doc/parseopt.h/parseopt.3.md index 82763de..652e489 100644 --- a/src/doc/parseopt.h/parseopt.3.md +++ b/src/doc/parseopt.h/parseopt.3.md @@ -78,16 +78,14 @@ optional or not), what follows next within the element will be treated as the option's argument. (Note that it remains possible to specify a /required/ argument as next element.) -`options` must be a pointer to the first element of an array of *struct option* -declared as such : +`options` must be a pointer to the first element of an array of *struct option*, +which contains the following members : - struct option { const char shortopt; const char *longopt; - u8 arg : 2; - u8 flags : 6; + u16 arg : 2; + u16 flags : 10; int id; - }; The meanings of different members are : @@ -102,10 +100,10 @@ The meanings of different members are : : `arg` :: One of the constants *ARG_NONE*, *ARG_REQ* or *ARG_OPT* to indicate whether :: the option takes no argument, requires an argument, or accepts an optional -:: argument. +:: argument, respectively. : `flags` -:: Not used, leave at 0. +:: Not used, leave at 0. (See [loadopt](3) for possible use.) : `id` :: An integer value unique to this option, to recognize when it is parsed. It diff --git a/src/liblimb/include/limb/loadopt.h b/src/liblimb/include/limb/loadopt.h index c18fc15..32f0fa3 100644 --- a/src/liblimb/include/limb/loadopt.h +++ b/src/liblimb/include/limb/loadopt.h @@ -6,6 +6,7 @@ #include <limb/direntry.h> #include <limb/parseopt.h> +#include <limb/stralloc.h> enum { /* private/internal */ @@ -13,26 +14,35 @@ enum { OPT_DONE = 1 << 0, OPT_SET = 1 << 1, /* public * */ - OPT_SKIP = 1 << 2, /* don't load from file */ - OPT_REQ = 1 << 3, /* must be specified */ + OPT_SKIP = 1 << 2, /* don't load from file */ + OPT_REQ = 1 << 3, /* must be specified */ + OPT_SA = 1 << 4, /* copy into sa */ + OPT_ESC_ = 1 << 5, + OPT_ESC = OPT_ESC_ | OPT_SA, /* + unesc if w/in quotes */ + OPT_PATH_ = 1 << 6, + OPT_PATH = OPT_PATH_ | OPT_ESC, /* + ensure no NUL byte */ + OPT_FILE_ = 1 << 7, + OPT_FILE = OPT_FILE_ | OPT_PATH, /* + ensure no '/' */ + OPT_RPT = 1 << 8, /* for last argument only */ }; /* end of options, on to arguments */ -#define LOADOPT_ARGUMENTS { 0, 0, ARG_REQ, OPT_DONE, 0 } +#define LOADOPT_ARGUMENTS { 0, 0, ARG_REQ, OPT_DONE, 0, 0 } /* to define required argument, optional argument */ -#define ARGUMENT_REQ(a) { 0, a, ARG_REQ, 0, 0 } -#define ARGUMENT_OPT(a) { 0, a, ARG_OPT, 0, 0 } +#define ARGUMENT_REQ(a,f,id) { 0, a, ARG_REQ, f, 0, id } +#define ARGUMENT_OPT(a,f,id) { 0, a, ARG_OPT, f, 0, id } -/* eof of arguments, either no more arguments or there can be more */ -#define LOADOPT_DONE { 0, 0, ARG_NONE, OPT_DONE, 0 } -#define LOADOPT_DONE_OPEN { 0, 0, ARG_OPT, OPT_DONE, 0 } +/* end of arguments (more == "too many args" */ +#define LOADOPT_DONE { 0, 0, ARG_NONE, OPT_DONE, 0, 0 } +/* stop parsing here, any more args will not our call */ +#define LOADOPT_STOP { 0, 0, ARG_OPT, OPT_DONE, 0, 0 } /* opaque structure */ struct loadopt { struct parseopt po; DIR *confdir; - size_t saoff; + size_t optoff; /* public, when OPT_SA is used */ u8 optflags[64]; u16 loflags : 4; /* LOADOPT_* */ u16 state : 4; @@ -44,13 +54,13 @@ struct loadopt { #define LOADOPT_ZERO { PARSEOPT_ZERO, NULL, 0, { 0 }, 0, 0, 0, 0 } #define LO_ARG(ctx) (ctx)->po.arg +#define LO_OFF(ctx) (ctx)->optoff #define LO_IDX(ctx) (ctx)->po.idx #define LO_CUR(ctx) (ctx)->po.cur #define LO_FROMFILE(ctx) (ctx)->from_file -extern int loadopt(int argc, const char **argv, const struct option *options, - int bfd, const char *confdir, char *buf, size_t blen, - unsigned int poflags, struct loadopt *ctx); +extern int loadopt(stralloc *sa, int argc, const char **argv, const struct option *options, + int bfd, const char *confdir, unsigned int poflags, struct loadopt *ctx); /* dont load (specified) options fro config file */ #define AUTOOPT_NOCONFIG(s,l,f,i) OPTION_ARG_OPT( s, l, OPT_SKIP | f, i) diff --git a/src/liblimb/include/limb/parseopt.h b/src/liblimb/include/limb/parseopt.h index d346da7..ad214a4 100644 --- a/src/liblimb/include/limb/parseopt.h +++ b/src/liblimb/include/limb/parseopt.h @@ -13,9 +13,9 @@ enum { }; /* to define options with no arg, optinal arg, required arg */ -#define OPTION_ARG_NONE(s,l,f,i) { s, l, ARG_NONE, f, i } -#define OPTION_ARG_OPT(s,l,f,i) { s, l, ARG_OPT, f, i } -#define OPTION_ARG_REQ(s,l,f,i) { s, l, ARG_REQ, f, i } +#define OPTION_ARG_NONE(s,l,f,i) { s, l, ARG_NONE, f, 0, i } +#define OPTION_ARG_OPT(s,l,f,i) { s, l, ARG_OPT, f, 0, i } +#define OPTION_ARG_REQ(s,l,f,i) { s, l, ARG_REQ, f, 0, i } /* last element in struct option[] to indicate the end */ #define OPTION_DONE { .flags = 1 } @@ -29,8 +29,9 @@ enum { struct option { const char shortopt; const char *longopt; - u8 arg : 2; /* ARG_* */ - u8 flags : 6; /* OPT_* -- loadopt only */ + u16 arg : 2; /* ARG_* */ + u16 flags : 10; /* OPT_* -- loadopt only */ + u16 _unused : 4; int id; }; diff --git a/src/liblimb/loadopt.h/loadopt.c b/src/liblimb/loadopt.h/loadopt.c index 3097938..d39905c 100644 --- a/src/liblimb/loadopt.h/loadopt.c +++ b/src/liblimb/loadopt.h/loadopt.c @@ -5,6 +5,7 @@ #include <errno.h> #include <limb/bytestr.h> #include <limb/djbunix.h> +#include <limb/esc.h> #include <limb/loadopt.h> #include <limb/output.h> #include <limb/readopt.h> @@ -29,10 +30,58 @@ get_optflags(const u8 *optflags, int idx) return b & 0xf; } +enum { + ERR_PA_BADESC = -1, + ERR_PA_INVAL_PATH = -2, + ERR_PA_INVAL_FILE = -3, +}; + +static int +process_arg(stralloc *sa, const struct option *option, struct loadopt *ctx) +{ + const char * const arg = sa->s + ctx->optoff; + size_t len = sa->len - ctx->optoff - 1; + + /* need to unescape the argument? */ + if (len >= 2 && (option->flags & OPT_ESC_) && arg[0] == '"' && arg[len - 1] == '"') { + len -= 2; + ssize_t rr = esc_scan(sa->s + ctx->optoff, len, arg + 1, len); + if (rr < 0) return ERR_PA_BADESC; + sa->len = ctx->optoff + rr; + sa->s[sa->len++] = 0; + len = rr; + } + + /* check for NUL */ + if ((option->flags & OPT_PATH_) && byte_chr(arg, len, 0) < len) + return ERR_PA_INVAL_PATH; + + /* check for '/' */ + if ((option->flags & OPT_FILE_) && byte_chr(arg, len, '/') < len) + return ERR_PA_INVAL_FILE; + + return 0; +} + +static void +pa_warn(int r, int is_arg, const struct option *option) +{ + const char *m; + switch (r) { + case ERR_PA_BADESC: m = "bad escaping"; break; + case ERR_PA_INVAL_PATH: m = "invalid path: contains a NUL byte"; break; + case ERR_PA_INVAL_FILE: m = "invalid file: contains a '/'"; break; + default: return; + + } + char buf[2] = { option->shortopt, 0 }; + warn("invalid argument ", (is_arg) ? "<" : "for option --", option->longopt, + (*buf) ? "/-" : "", (is_arg) ? ">" : buf, ": ", m); +} + int -loadopt(int argc, const char **argv, const struct option *options, - int bfd, const char *confdir, char *buf, size_t blen, - unsigned int poflags, struct loadopt *ctx) +loadopt(stralloc *sa, int argc, const char **argv, const struct option *options, + int bfd, const char *confdir, unsigned int poflags, struct loadopt *ctx) { /* init */ if (!ctx->state) { @@ -47,8 +96,24 @@ loadopt(int argc, const char **argv, const struct option *options, int c; c = parseopt(argc, argv, options, poflags, &ctx->po); - if (c > 0) + if (c > 0) { add_optflags(ctx->optflags, ctx->po.idx, OPT_SET); + /* put argument into sa? */ + if (ctx->po.arg && (options[ctx->po.idx].flags & OPT_SA)) { + ctx->optoff = sa->len; + if (!stralloc_cats0(sa, ctx->po.arg)) + return -1; + ctx->po.arg = NULL; + + /* extra processing: unescaping, path/file checking */ + int r = process_arg(sa, &options[ctx->po.idx], ctx); + if (r < 0) { + if (!(poflags & PARSEOPT_SILENT)) + pa_warn(r, 0, &options[ctx->po.idx]); + return -1; + } + } + } if (c) return c; @@ -103,10 +168,18 @@ loadopt(int argc, const char **argv, const struct option *options, continue; ssize_t r; - r = readopt(buf, blen - 1, dirfd(ctx->confdir), de->d_name); - if (r < 0) { - warnusys("read ", ESC, confdir, "/", de->d_name, ESC); - return -1; + for (int i = 1; ; ++i) { + if (!stralloc_readyplus(sa, i * 256)) + r = -1; + else + r = readopt(sa->s + sa->len, sa->a - sa->len - 1, + dirfd(ctx->confdir), de->d_name); + + if (r >= 0) break; + if (r < 0 && errno != ENOBUFS) { + warnusys("read ", ESC, confdir, "/", de->d_name, ESC); + return -1; + } } ctx->po.idx = i; @@ -128,10 +201,26 @@ loadopt(int argc, const char **argv, const struct option *options, if (options[i].arg == ARG_NONE || !r) { ctx->po.arg = NULL; } else { - buf[r] = 0; - ctx->po.arg = buf; + sa->s[sa->len + r] = 0; + ctx->po.arg = sa->s + sa->len; } ctx->from_file = 1; + + /* put argument into sa */ + if (ctx->po.arg && (options[i].flags & OPT_SA)) { + ctx->optoff = sa->len; + sa->len += r + 1; + ctx->po.arg = NULL; + + /* extra processing: unescaping, path/file checking */ + int r = process_arg(sa, &options[i], ctx); + if (r < 0) { + if (!(poflags & PARSEOPT_SILENT)) + pa_warn(r, 0, &options[i]); + return -1; + } + } + return (options[i].id) ? options[i].id : options[i].shortopt; } dir_close(ctx->confdir); @@ -144,51 +233,70 @@ loadopt(int argc, const char **argv, const struct option *options, /* confdir done, check all required options were set */ if (ctx->state == STATE_CONFIG) { - int i; - for (i = 0; options[i].longopt; ++i) { - if ((get_optflags(ctx->optflags, i) & (OPT_REQ | OPT_SET)) == OPT_REQ) { - char buf[2] = { options[i].shortopt, 0 }; - warn("option --", options[i].longopt, (*buf) ? "/-" : "", buf, " missing"); + for (ctx->po.idx = 0; options[ctx->po.idx].longopt; ++ctx->po.idx) { + if ((get_optflags(ctx->optflags, ctx->po.idx) & (OPT_REQ | OPT_SET)) == OPT_REQ) { + char buf[2] = { options[ctx->po.idx].shortopt, 0 }; + warn("option --", options[ctx->po.idx].longopt, (*buf) ? "/-" : "", buf, " missing"); return (errno = ENOKEY, -1); } } - /* re-use off as the current argument's index from w/in options */ - ctx->po.off = i; - /* set arg as the first argument */ - ctx->po.arg = (const char *) (uintptr_t) ctx->po.cur; + --ctx->po.idx; + ctx->left = 0; ctx->state = STATE_OPTIONS; } - /* options done, on to arguments */ + /* options done, arguments? */ if (ctx->state == STATE_OPTIONS) { - while (ctx->state == STATE_OPTIONS) { - const struct option *arg = &options[ctx->po.off]; - if (arg->flags & OPT_DONE) { +nextarg: + ++ctx->po.idx; + const struct option *arg = &options[ctx->po.idx]; + if (arg->flags & OPT_DONE) { + /* LOADOPT_DONE / OPTION_DONE || LOADOPT_STOP */ + if (arg->arg == ARG_NONE || arg->arg == ARG_OPT) { + /* DONE means there shouldn't be any more args */ if (arg->arg == ARG_NONE && ctx->po.cur < argc) { warn("too many arguments"); return (errno = ETOOMANYREFS, -1); - } else if (arg->arg == ARG_REQ) { - ++ctx->po.off; - continue; } - /* ARG_NONE w/out args, or ARG_OPT; i.e. ok we're done */ - break; + ctx->state = STATE_ARGS; + } else if (arg->arg == ARG_REQ) { + /* LOADOPT_ARGUMENTS: on to arguments now... */ + goto nextarg; } + } else { + if (ctx->po.cur == argc) { + if (arg->arg == ARG_REQ && !ctx->left) { + warn("argument <", arg->longopt, "> missing"); + return (errno = ENODATA, -1); + } + ctx->state = STATE_ARGS; + } else { + ctx->po.arg = argv[ctx->po.cur]; - if (arg->arg == ARG_REQ && ctx->po.cur == argc) { - warn("argument ", ESC, arg->longopt, ESC," missing"); - return (errno = ENODATA, -1); - } + /* put argument into sa? */ + if (arg->flags & OPT_SA) { + ctx->optoff = sa->len; + if (!stralloc_cats0(sa, ctx->po.arg)) + return -1; + ctx->po.arg = NULL; - /* next argument from command line */ - ++ctx->po.cur; - /* next argument defined/to check for */ - ++ctx->po.off; + /* extra processing: unescaping, path/file checking */ + int r = process_arg(sa, arg, ctx); + if (r < 0) { + if (!(poflags & PARSEOPT_SILENT)) + pa_warn(r, 1, arg); + return -1; + } + } + + if (arg->flags & OPT_RPT) { + ctx->left = 1; + --ctx->po.idx; + } + ++ctx->po.cur; + return arg->id; + } } - /* set cur to the first argument */ - ctx->po.cur = (uintptr_t) ctx->po.arg & 0xffff; - /* done */ - ctx->state = STATE_ARGS; } /* arguments done, we're finally done */