Welcome to little lamb

Code » limb » commit 9f89f00

parseopt/loadopt: Remove arg first in favor of ctx->idx

author Olivier Brunel
2023-04-02 13:02:18 UTC
committer Olivier Brunel
2023-05-20 18:06:34 UTC
parent 4e57787a9cae459fb243a4d5ff7332e19ee72d92

parseopt/loadopt: Remove arg first in favor of ctx->idx

Instead of having an optional argument `first` that can be used to know
the index of the (first-matching) option, simply use a member of our
context structure. Simpler/cleaner that way.

src/doc/loadopt.h/loadopt.3.md +3 -4
src/doc/parseopt.h/parseopt.3.md +7 -6
src/include/loadopt.h +2 -2
src/liblimb/include/limb/loadopt.h +2 -1
src/liblimb/include/limb/parseopt.h +2 -1
src/liblimb/loadopt.h/loadopt.c +27 -31
src/liblimb/parseopt.h/parseopt.c +20 -21

diff --git a/src/doc/loadopt.h/loadopt.3.md b/src/doc/loadopt.h/loadopt.3.md
index d17deed..73ee23c 100644
--- a/src/doc/loadopt.h/loadopt.3.md
+++ b/src/doc/loadopt.h/loadopt.3.md
@@ -10,7 +10,7 @@ loadopt - parse options from command-line and (optionally) configuration file
     #include <limb/loadopt.h>
 
 ```pre hl
-int loadopt(int *<em>first</em>, int <em>argc</em>, const char **<em>argv</em>, const struct option *<em>options</em>,
+int loadopt(int <em>argc</em>, const char **<em>argv</em>, const struct option *<em>options</em>,
             const char *<em>file</em>, const char *<em>section</em>, unsigned int <em>flags</em>,
             struct loadopt *<em>ctx</em>)
 ```
@@ -23,9 +23,8 @@ After that it ensures any option marked as required has been set, then may check
 for non-options arguments.
 
 The actual parsing of options is done through [parseopt](3), as such many of the
-arguments to `loadopt`() are the same as to [parseopt](3), namely `first`,
-`argc`, `argv`, `options` and `flags`. Please refer to [parseopt](3) for more
-on those.
+arguments to `loadopt`() are the same as to [parseopt](3), namely `argc`,
+`argv`, `options` and `flags`. Refer to [parseopt](3) for more on those.
 
 It is important to note, however, that the member `flags` of *struct option* is
 relevant here : it allows to define option-specific flags. Specifically, its
diff --git a/src/doc/parseopt.h/parseopt.3.md b/src/doc/parseopt.h/parseopt.3.md
index 148001a..d684d35 100644
--- a/src/doc/parseopt.h/parseopt.3.md
+++ b/src/doc/parseopt.h/parseopt.3.md
@@ -10,7 +10,7 @@ parseopt - parse command-line options
     #include <limb/parseopt.h>
 
 ```pre hl
-int parseopt(int *<em>first</em>, int <em>argc</em>, const char **<em>argv</em>, const struct option *<em>options</em>,
+int parseopt(int <em>argc</em>, const char **<em>argv</em>, const struct option *<em>options</em>,
              unsigned int <em>flags</em>, struct parseopt *<em>ctx</em>)
 ```
 
@@ -23,11 +23,6 @@ on program invocation.
 The `options` argument is a pointer to the first element of an array defining
 all possible options. See [[Options]] below for more.
 
-The `first` argument is optional, as therefore can be NULL. If specified, the
-value it points to will be set to the index of the first-matching option. This
-can notably be useful in the case of abbreviated long options, when more than
-one option did match.
-
 The `flags` argument allows to enable certain options, see [[FLAGS]] below.
 
 The last argument `ctx` is a semi-opaque structure, that should be initialized
@@ -36,6 +31,7 @@ to all zeroes, defined as such :
     struct parseopt {
         u16 cur;
         u16 off;
+        int idx;
         const char *arg;
     };
 
@@ -43,6 +39,11 @@ When `parseopt`() returns a positive value, i.e. an option was successfully
 found, its member `arg` points to the option's argument if any, else it is
 NULL. It is similar to the global `optarg` from [getopt](3).
 
+Additionally its member `idx` is set to the index of said option within
+`options`.  When an error occurs, it can either be set to -1 or, in the case of
+abbreviated long options (and more than one option did match), the
+first-matching option.
+
 When `parseopt`() returns -1, its member `cur` is the index in `argv` of the
 first non-option element. In that way, it is similar to the global `optind` from
 [getopt](3).
diff --git a/src/include/loadopt.h b/src/include/loadopt.h
index 07fe3a8..393bf91 100644
--- a/src/include/loadopt.h
+++ b/src/include/loadopt.h
@@ -26,8 +26,8 @@ enum {
     LOADOPT_EOF         = 1 << 3,
 };
 
-void add_optflags(u8 *optflags, u16 idx, u8 val) gccattr_hidden;
-u8 get_optflags(const u8 *optflags, u16 idx) gccattr_hidden;
+void add_optflags(u8 *optflags, int idx, u8 val) gccattr_hidden;
+u8 get_optflags(const u8 *optflags, int idx) gccattr_hidden;
 
 int loadopt_handle_noconfig(int idx, const struct option *options, struct loadopt *ctx) gccattr_hidden;
 
diff --git a/src/liblimb/include/limb/loadopt.h b/src/liblimb/include/limb/loadopt.h
index 67b139e..0374da3 100644
--- a/src/liblimb/include/limb/loadopt.h
+++ b/src/liblimb/include/limb/loadopt.h
@@ -34,6 +34,7 @@ struct loadopt {
     /* struct parseopt */
     u16 cur;
     u16 off;
+    int idx;
     const char *arg;
     /* loadopt */
     stralloc sa;
@@ -50,7 +51,7 @@ enum {
     LOADOPT_ID_NOCONFIG  = 1,
 };
 
-extern int loadopt(int *first, int argc, const char **argv, const struct option *options,
+extern int loadopt(int argc, const char **argv, const struct option *options,
                    const char *file, const char *section, unsigned int poflags,
                    struct loadopt *ctx);
 
diff --git a/src/liblimb/include/limb/parseopt.h b/src/liblimb/include/limb/parseopt.h
index 9a6b6bb..94a4a05 100644
--- a/src/liblimb/include/limb/parseopt.h
+++ b/src/liblimb/include/limb/parseopt.h
@@ -42,10 +42,11 @@ struct parseopt {
     u16 cur;    /* public when done : index of first argument in argv */
     u16 off;
     /* public (read-only) */
+    int idx;
     const char *arg;
 };
 
-extern int parseopt(int *first, int argc, const char **argv, const struct option *options,
+extern int parseopt(int argc, const char **argv, const struct option *options,
                     unsigned int flags, struct parseopt *ctx);
 
 #endif /* LIMB_PARSEOPT_H */
diff --git a/src/liblimb/loadopt.h/loadopt.c b/src/liblimb/loadopt.h/loadopt.c
index aabbeb5..ce6c143 100644
--- a/src/liblimb/loadopt.h/loadopt.c
+++ b/src/liblimb/loadopt.h/loadopt.c
@@ -10,7 +10,7 @@
 #include "loadopt.h"
 
 void
-add_optflags(u8 *optflags, u16 idx, u8 val)
+add_optflags(u8 *optflags, int idx, u8 val)
 {
     if (idx % 2)
         val <<= 4;
@@ -20,7 +20,7 @@ add_optflags(u8 *optflags, u16 idx, u8 val)
 }
 
 u8
-get_optflags(const u8 *optflags, u16 idx)
+get_optflags(const u8 *optflags, int idx)
 {
     u8 b = optflags[idx / 2];
     if (idx % 2) b >>= 4;
@@ -28,7 +28,7 @@ get_optflags(const u8 *optflags, u16 idx)
 }
 
 static void
-parseopt_warn(int c, int idx, const char **argv, const struct option *options,
+parseopt_warn(int c, const char **argv, const struct option *options,
              const struct parseopt *ctx)
 {
     if (c >= 0 || c == PARSEOPT_DONE)
@@ -48,8 +48,8 @@ parseopt_warn(int c, int idx, const char **argv, const struct option *options,
             break;
         case PARSEOPT_ERR_ARGREQ:
             {
-                char buf[2] = { options[idx].shortopt, 0 };
-                warn("option --", options[idx].longopt,
+                char buf[2] = { options[ctx->idx].shortopt, 0 };
+                warn("option --", options[ctx->idx].longopt,
                      (*buf) ? "/-" : "", buf, " requires an argument");
             }
             break;
@@ -57,19 +57,19 @@ parseopt_warn(int c, int idx, const char **argv, const struct option *options,
 }
 
 static int
-loadopt_handle(int c, int first, const char **argv, const struct option *options,
+loadopt_handle(int c, const char **argv, const struct option *options,
                int from_file, struct parseopt *ctx)
 {
-    if (!from_file && c >= 0 && options[first].id == LOADOPT_ID_NOCONFIG)
-        return loadopt_handle_noconfig(first, options, (struct loadopt *) ctx);
+    if (!from_file && c >= 0 && options[ctx->idx].id == LOADOPT_ID_NOCONFIG)
+        return loadopt_handle_noconfig(ctx->idx, options, (struct loadopt *) ctx);
 
-    if (c == PARSEOPT_ERR_UNKNOWN && first >= 0) {
+    if (c == PARSEOPT_ERR_UNKNOWN && ctx->idx >= 0) {
         const char *s = argv[ctx->cur] + ctx->off;
         size_t l = strlen(s);
-        adde("did you mean --", options[first].longopt);
-        while (options[++first].longopt)
-            if (!strncmp(s, options[first].longopt, l))
-                adde(" or --", options[first].longopt);
+        adde("did you mean --", options[ctx->idx].longopt);
+        for (int i = ctx->idx + 1; options[i].longopt; ++i)
+            if (!strncmp(s, options[i].longopt, l))
+                adde(" or --", options[i].longopt);
         err(" ?");
     }
 
@@ -77,7 +77,7 @@ loadopt_handle(int c, int first, const char **argv, const struct option *options
 }
 
 int
-loadopt(int *first, int argc, const char **argv, const struct option *options,
+loadopt(int argc, const char **argv, const struct option *options,
         const char *file, const char *section, unsigned int poflags,
         struct loadopt *ctx)
 {
@@ -92,24 +92,21 @@ loadopt(int *first, int argc, const char **argv, const struct option *options,
     /* init is done, parse options from command line */
 nextopt:
     if (ctx->state == STATE_INIT) {
-        int c, idx = -1;
-        c = parseopt(&idx, argc, argv, options, poflags, (struct parseopt *) ctx);
+        int c;
+        c = parseopt(argc, argv, options, poflags, (struct parseopt *) ctx);
 
         if (c >= 0)
-            add_optflags(ctx->optflags, idx, OPT_SET);
+            add_optflags(ctx->optflags, ctx->idx, OPT_SET);
         else
-            parseopt_warn(c, idx, argv, options, (struct parseopt *) ctx);
+            parseopt_warn(c, argv, options, (struct parseopt *) ctx);
 
         if (c != PARSEOPT_DONE) {
-            c = loadopt_handle(c, idx, argv, options, 0, (struct parseopt *) ctx);
+            c = loadopt_handle(c, argv, options, 0, (struct parseopt *) ctx);
             if (c == PARSEOPT_DONE)
                 goto nextopt;
         }
-        if (c != PARSEOPT_DONE) {
-            if (first)
-                *first = idx;
+        if (c != PARSEOPT_DONE)
             return c;
-        }
         ctx->state = STATE_CMDLINE;
     }
 
@@ -206,8 +203,8 @@ nextfileopt:
             struct parseopt po = { 0 };
             const char *argv[] = { "", ctx->sa.s + ctx->saoff };
 
-            int c, idx = -1;
-            c = parseopt(&idx, 2, argv, options, PARSEOPT_IS_LONG | PARSEOPT_STRICT, &po);
+            int c;
+            c = parseopt(2, argv, options, PARSEOPT_IS_LONG | PARSEOPT_STRICT, &po);
 
             /* seek past the line */
             ctx->saoff = o + 1;
@@ -215,25 +212,24 @@ nextfileopt:
             /* ignore already set option, and "argument required" error for
              * already set option */
             if ((c >= 0 || c == PARSEOPT_ERR_ARGREQ)
-                    && (get_optflags(ctx->optflags, idx) & OPT_SET)) {
+                    && (get_optflags(ctx->optflags, po.idx) & OPT_SET)) {
                 goto nextfileopt;
             }
 
             if (c >= 0)
-                add_optflags(ctx->optflags, idx, OPT_SET);
+                add_optflags(ctx->optflags, po.idx, OPT_SET);
             else
-                parseopt_warn(c, idx, argv, options, &po);
+                parseopt_warn(c, argv, options, &po);
 
             if (c != PARSEOPT_DONE) {
-                c = loadopt_handle(c, idx, argv, options, 1, &po);
+                c = loadopt_handle(c, argv, options, 1, &po);
                 if (c == PARSEOPT_DONE)
                     goto nextfileopt;
             }
             if (c != PARSEOPT_DONE) {
-                if (first)
-                    *first = idx;
                 /* adjust returned value */
                 ctx->arg = po.arg;
+                ctx->idx = po.idx;
                 ctx->from_file = 1;
                 return c;
             }
diff --git a/src/liblimb/parseopt.h/parseopt.c b/src/liblimb/parseopt.h/parseopt.c
index 8f0bb61..d1d843f 100644
--- a/src/liblimb/parseopt.h/parseopt.c
+++ b/src/liblimb/parseopt.h/parseopt.c
@@ -6,16 +6,16 @@
 #include <limb/parseopt.h>
 
 int
-parseopt(int *first, int argc, const char **argv, const struct option *options,
+parseopt(int argc, const char **argv, const struct option *options,
          unsigned int flags, struct parseopt *ctx)
 {
     const char *arg;
     int is_long, arg_long;
-    int o;
 
     if (!ctx->cur)
         ctx->cur = 1;
 
+    ctx->idx = -1;
 again:
     if (ctx->cur == argc)
         return PARSEOPT_DONE;
@@ -63,19 +63,18 @@ again:
         }
 
         if (flags & PARSEOPT_STRICT) {
-            for (o = 0; options[o].longopt; ++o)
-                if (end == strlen(options[o].longopt)
-                        && !strncmp(options[o].longopt, arg, end))
+            for (ctx->idx = 0; options[ctx->idx].longopt; ++ctx->idx)
+                if (end == strlen(options[ctx->idx].longopt)
+                        && !strncmp(options[ctx->idx].longopt, arg, end))
                     break;
-            if (!options[o].longopt)
-                o = -1;
-        } else {
-            o = byte_get_match_full(first, arg, end, options,
-                                    offsetof(struct option, longopt), sizeof(*options));
-        }
-
-        if (o < 0)
+            if (!options[ctx->idx].longopt) {
+                ctx->idx = -1;
+                return PARSEOPT_ERR_UNKNOWN;
+            }
+        } else if (byte_get_match_full(&ctx->idx, arg, end, options,
+                    offsetof(struct option, longopt), sizeof(*options)) < 0) {
             return PARSEOPT_ERR_UNKNOWN;
+        }
 
         /* --option-name=value : don't look for optarg on the next arg */
         if (end < l) {
@@ -83,17 +82,17 @@ again:
             arg += end;
         }
     } else {
-        for (o = 0; options[o].longopt; ++o)
-            if (*arg == options[o].shortopt)
+        for (ctx->idx = 0; options[ctx->idx].longopt; ++ctx->idx)
+            if (*arg == options[ctx->idx].shortopt)
                 break;
-        if (!options[o].longopt)
+        if (!options[ctx->idx].longopt) {
+            ctx->idx = -1;
             return PARSEOPT_ERR_UNKNOWN;
+        }
     }
 
-    if (first) *first = o;
-
     ctx->arg = NULL;
-    if (options[o].arg == ARG_REQ) {
+    if (options[ctx->idx].arg == ARG_REQ) {
         if (arg_long) {
             ++ctx->cur;
             if (ctx->cur == argc)
@@ -104,7 +103,7 @@ again:
                 return PARSEOPT_ERR_ARGREQ;
             ctx->arg = arg + 1;
         }
-    } else if (options[o].arg == ARG_OPT) {
+    } else if (options[ctx->idx].arg == ARG_OPT) {
         if (!arg_long && arg[1])
             ctx->arg = arg + 1;
     }
@@ -116,5 +115,5 @@ again:
         ctx->off = 0;
     }
 
-    return (options[o].id) ? options[o].id : options[o].shortopt;
+    return (options[ctx->idx].id) ? options[ctx->idx].id : options[ctx->idx].shortopt;
 }