Welcome to little lamb

Code » limb » commit f5c9a97

parseopt/loadopt: Return 0 on success, -1 on error..

author Olivier Brunel
2023-04-02 14:44:48 UTC
committer Olivier Brunel
2023-05-20 18:06:34 UTC
parent 4121821a398ca90b1183d3009d2ec3dfbb30e4f6

parseopt/loadopt: Return 0 on success, -1 on error..

..and set errno to indicate the error.

This is more consistent with /every/ function in limb, whereas those two
were the only exception, which was only due to how getopt(3) works.
But we're already not following it, using a context for optarg and such,
or not using '?' as return value on error, so let's do it fully/properly.

src/doc/loadopt.h.0.md +0 -4
src/doc/loadopt.h/loadopt.3.md +17 -17
src/doc/parseopt.h.0.md +2 -3
src/doc/parseopt.h/parseopt.3.md +14 -17
src/liblimb/include/limb/loadopt.h +3 -12
src/liblimb/include/limb/parseopt.h +3 -7
src/liblimb/loadopt.h/loadopt.c +22 -22
src/liblimb/loadopt.h/loadopt_handle_noconfig.c +3 -2
src/liblimb/parseopt.h/parseopt.c +11 -10

diff --git a/src/doc/loadopt.h.0.md b/src/doc/loadopt.h.0.md
index 4697e5e..3b7bcbb 100644
--- a/src/doc/loadopt.h.0.md
+++ b/src/doc/loadopt.h.0.md
@@ -24,10 +24,6 @@ The following constants are defined :
 : *OPT_REQ*
 :: Set an option to be required
 
-: *LOADOPT_ERR_FILE*, *LOADOPT_ERR_MISSINGOPT*, *LOADOPT_ERR_MISSINGARG*
-: *LOADOPT_ERR_TOOMANY*
-:: Possible return values for [loadopt](3)
-
 ## Macros
 
 The following macros are defined :
diff --git a/src/doc/loadopt.h/loadopt.3.md b/src/doc/loadopt.h/loadopt.3.md
index 73ee23c..25ba818 100644
--- a/src/doc/loadopt.h/loadopt.3.md
+++ b/src/doc/loadopt.h/loadopt.3.md
@@ -99,7 +99,7 @@ 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
 argument was given on command-line, an error occurs. If optional and not
-specified, `loadopt`() successfully ends, returning -1.
+specified, `loadopt`() successfully ends, returning 0.
 
 Finally, after all such argument definitions in the array `options`, you must
 terminate it with either one of the macros *LOADOPT_DONE_OPEN* and
@@ -117,33 +117,33 @@ In other words, *LOADOPT_DONE* is the same as *OPTION_DONE* as described from
 
 # RETURN VALUE
 
-If an option was successfully found, `loadopt`() returns the option `id` if
+If an option was successfully found, `loadopt`() returns the option's `id` if
 non-zero, else its `shortopt`. When all options, and, optionally, arguments,
-have been successfully parsed -1 is returned.
+have been successfully parsed it returns 0.
 
-If an error occurs, a warning is emitted to *stderr* and a negative value (other
-than -1) is returned, depending on the error.
+If an error occurs, a warning is emitted to *stderr* and it returns -1, setting
+`errno` to indicate the error.
 
-## Warnings
-
-The warnings are actually written out using one of the [warn](3) family of
-functions. Refer to it for more on how and where data is being written to.
+! INFO:
+! The warnings are actually written out using one of the [warn](3) family of
+! functions. Refer to it for more on how and where data is being written to, or
+! how to silence them.
 
 # ERRORS
 
-The `loadopt`() function may fail and return :
+The `loadopt`() function may fail if :
 
-: *LOADOPT_ERR_FILE*
-:: Unable to open/read the configuration file
+: *ENOMEM*
+:: Not enough memory to load configuration file
 
-: *LOADOPT_ERR_MISSINGOPT*
+: *ENOKEY*
 :: A required option was missing/not specified
 
-: *LOADOPT_ERR_MISSINGARG*
+: *ENODATA*
 :: A required argument was missing/not specified
 
-: *LOADOPT_ERR_TOOMANY*
+: *ETOOMANYREFS*
 :: Too many arguments were specified
 
-The `loadopt`() function may also fail and return any of the errors specified
-for [parseopt](3).
+The `loadopt`() function may also fail for any of the errors specified for
+[parseopt](3), [openslurpclose](3) save for *ENOENT*.
diff --git a/src/doc/parseopt.h.0.md b/src/doc/parseopt.h.0.md
index df8bc0f..d0eca19 100644
--- a/src/doc/parseopt.h.0.md
+++ b/src/doc/parseopt.h.0.md
@@ -27,9 +27,8 @@ The following constants are defined :
 : *PARSEOPT_IS_LONG*, *PARSEOPT_STRICT*
 :: Flags that can be passed to [parseopt](3).
 
-: *PARSEOPT_DONE*, *PARSEOPT_ERR_NONAME*, *PARSEOPT_ERR_UNKNOWN*,
-: *PARSEOPT_ERR_ARGREQ*
-:: Possible return values for [parseopt](3)
+: *ID_SHORTOPT*
+:: Special ID for an option, meaning to return its `shortopt` instead.
 
 ## Structures
 
diff --git a/src/doc/parseopt.h/parseopt.3.md b/src/doc/parseopt.h/parseopt.3.md
index a9e9d48..620ca56 100644
--- a/src/doc/parseopt.h/parseopt.3.md
+++ b/src/doc/parseopt.h/parseopt.3.md
@@ -44,7 +44,7 @@ Additionally its member `idx` is set to the index of said option within
 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
+When `parseopt`() returns 0, 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).
 
@@ -57,15 +57,13 @@ followed by a second dash, then a long option name is expected to follow, else a
 short option character.
 
 An element of "--" has special meaning, indicating the end of options and
-stopping parsing, i.e. `parseopt`() will return -1 when encountered.
+stopping parsing, i.e. `parseopt`() will return 0 when encountered.
 
 When an option has been identified, `parseopt`() will return its `id` if it is
 non-zero, else its `shortopt` (see [[Options]] below).
 
-When the first non-option element is encountered, or an element "--", parsing
-stops and -1 is returned.
-
-On error, another negative value is returned, see [[ERRORS]] below for more.
+Parsing stops when the first non-option element is encountered, or an element
+"--" is met.
 
 ## Options
 
@@ -137,27 +135,26 @@ constructed as a bitwise-inclusive OR of flags from the following list :
 
 # RETURN VALUE
 
-If an option was successfully found, `parseopt`() returns the option `id` if
-non-zero, else its `shortopt`. When all command-line options have been parsed -1
-is returned.
+If an option was successfully found, `parseopt`() returns the option's `id` if
+non-zero, else its `shortopt`. When all command-line options have been parsed it
+returns 0.
 
-If an error occurs, a negative value (other than -1) is returned, depending on
-the error.
+If an error occurs, it returns -1 and sets `errno` to indicate the error.
 
 ! NOTE:
 ! Unlike [getopt](3), `parseopt`() will not print any warning/error messages.
-! The indication of the error is sent through its return value, and it is up to
-! the caller to inform the user of what failed.
+! The indication of the error is sent through `errno` and it is up to the
+! caller to inform the user of what failed.
 
 # ERRORS
 
-The `parseopt`() function may fail and return :
+The `parseopt`() function may fail if :
 
-: *PARSEOPT_ERR_NONAME*
+: *EINVAL*
 :: Option name was missing, i.e. the element was "-" only
 
-: *PARSEOPT_ERR_UNKNOWN*
+: *ENOENT*
 :: Unknown option
 
-: *PARSEOPT_ERR_ARGREQ*
+: *ENOMSG*
 :: An option requiring an argument was found, but no argument was specified.
diff --git a/src/liblimb/include/limb/loadopt.h b/src/liblimb/include/limb/loadopt.h
index 0374da3..035cd87 100644
--- a/src/liblimb/include/limb/loadopt.h
+++ b/src/liblimb/include/limb/loadopt.h
@@ -17,18 +17,9 @@ enum {
     OPT_REQ     = 1 << 3,   /* must be specified */
 };
 
-#define LOADOPT_ARGUMENTS       { 0, 0, ARG_REQ,  OPT_DONE }
-#define LOADOPT_DONE_OPEN       { 0, 0, ARG_OPT,  OPT_DONE }
-#define LOADOPT_DONE            { 0, 0, ARG_NONE, OPT_DONE }
-
-
-enum {
-    LOADOPT_ERR_FILE        = -6,
-    LOADOPT_ERR_MISSINGOPT  = -7,
-    LOADOPT_ERR_MISSINGARG  = -8,
-    LOADOPT_ERR_TOOMANY     = -9,
-    LOADOPT_ERR_INVALIDARG  = -10,
-};
+#define LOADOPT_ARGUMENTS       { 0, 0, ARG_REQ,  OPT_DONE, 0 }
+#define LOADOPT_DONE_OPEN       { 0, 0, ARG_OPT,  OPT_DONE, 0 }
+#define LOADOPT_DONE            { 0, 0, ARG_NONE, OPT_DONE, 0 }
 
 struct loadopt {
     /* struct parseopt */
diff --git a/src/liblimb/include/limb/parseopt.h b/src/liblimb/include/limb/parseopt.h
index 94a4a05..31cbce9 100644
--- a/src/liblimb/include/limb/parseopt.h
+++ b/src/liblimb/include/limb/parseopt.h
@@ -15,6 +15,9 @@ enum {
 /* last element in struct option[] to indicate the end */
 #define OPTION_DONE             { .flags = 1 }
 
+/* special ID meaning to return shortopt instead */
+#define ID_SHORTOPT             0
+
 struct option {
     const char  shortopt;
     const char *longopt;
@@ -30,13 +33,6 @@ enum {
     PARSEOPT_STRICT     = 1 << 1,
 };
 
-enum {
-    PARSEOPT_DONE           = -1, /* success, all done */
-    PARSEOPT_ERR_NONAME     = -2, /* option name missing (i.e. "-") */
-    PARSEOPT_ERR_UNKNOWN    = -3, /* unknown option */
-    PARSEOPT_ERR_ARGREQ     = -4, /* argument required */
-};
-
 struct parseopt {
     /* private */
     u16 cur;    /* public when done : index of first argument in argv */
diff --git a/src/liblimb/loadopt.h/loadopt.c b/src/liblimb/loadopt.h/loadopt.c
index 1743d41..c18d97d 100644
--- a/src/liblimb/loadopt.h/loadopt.c
+++ b/src/liblimb/loadopt.h/loadopt.c
@@ -31,14 +31,14 @@ static void
 parseopt_warn(int c, const char **argv, const struct option *options,
              const struct parseopt *ctx)
 {
-    if (c >= 0 || c == PARSEOPT_DONE)
+    if (c >= 0)
         return;
 
-    switch (c) {
-        case PARSEOPT_ERR_NONAME:
+    switch (errno) {
+        case EINVAL:
             warn("option name missing");
             break;
-        case PARSEOPT_ERR_UNKNOWN:
+        case ENOENT:
             if (!strncmp(argv[ctx->cur], "--", 2)) {
                 warn("unknown option: ", argv[ctx->cur]);
             } else {
@@ -46,7 +46,7 @@ parseopt_warn(int c, const char **argv, const struct option *options,
                 warn("unknown option: ", buf);
             }
             break;
-        case PARSEOPT_ERR_ARGREQ:
+        case ENOMSG:
             {
                 char buf[2] = { options[ctx->idx].shortopt, 0 };
                 warn("option --", options[ctx->idx].longopt,
@@ -63,7 +63,7 @@ loadopt_handle(int c, const char **argv, const struct option *options,
     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 && ctx->idx >= 0) {
+    if (errno == ENOENT && ctx->idx >= 0) {
         const char *s = argv[ctx->cur] + ctx->off;
         size_t l = byte_chr(s, strlen(s), '=');
         adde("did you mean --", options[ctx->idx].longopt);
@@ -95,17 +95,17 @@ nextopt:
         int c;
         c = parseopt(argc, argv, options, poflags, (struct parseopt *) ctx);
 
-        if (c >= 0)
+        if (c > 0)
             add_optflags(ctx->optflags, ctx->idx, OPT_SET);
         else
             parseopt_warn(c, argv, options, (struct parseopt *) ctx);
 
-        if (c != PARSEOPT_DONE) {
+        if (c) {
             c = loadopt_handle(c, argv, options, 0, (struct parseopt *) ctx);
-            if (c == PARSEOPT_DONE)
+            if (!c)
                 goto nextopt;
         }
-        if (c != PARSEOPT_DONE)
+        if (c)
             return c;
         ctx->state = STATE_CMDLINE;
     }
@@ -138,7 +138,7 @@ nextopt:
             ctx->state = STATE_CONFIG;
             if (errno != ENOENT) {
                 warnusys("read ", ESC, file, ESC);
-                return LOADOPT_ERR_FILE;
+                return -1;
             }
         } else {
             ctx->state = STATE_FILE;
@@ -203,30 +203,30 @@ nextfileopt:
             struct parseopt po = { 0 };
             const char *argv[] = { "", ctx->sa.s + ctx->saoff };
 
-            int c;
-            c = parseopt(2, argv, options, PARSEOPT_IS_LONG | PARSEOPT_STRICT, &po);
+            errno = 0;
+            int c = parseopt(2, argv, options, PARSEOPT_IS_LONG | PARSEOPT_STRICT, &po);
 
             /* seek past the line */
             ctx->saoff = o + 1;
 
             /* ignore already set option, and "argument required" error for
              * already set option */
-            if ((c >= 0 || c == PARSEOPT_ERR_ARGREQ)
+            if ((c > 0 || errno == ENOMSG)
                     && (get_optflags(ctx->optflags, po.idx) & OPT_SET)) {
                 goto nextfileopt;
             }
 
-            if (c >= 0)
+            if (c > 0)
                 add_optflags(ctx->optflags, po.idx, OPT_SET);
             else
                 parseopt_warn(c, argv, options, &po);
 
-            if (c != PARSEOPT_DONE) {
+            if (c) {
                 c = loadopt_handle(c, argv, options, 1, &po);
-                if (c == PARSEOPT_DONE)
+                if (!c)
                     goto nextfileopt;
             }
-            if (c != PARSEOPT_DONE) {
+            if (c) {
                 /* adjust returned value */
                 ctx->arg = po.arg;
                 ctx->idx = po.idx;
@@ -246,7 +246,7 @@ nextfileopt:
             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");
-                return LOADOPT_ERR_MISSINGOPT;
+                return (errno = ENOKEY, -1);
             }
         }
         /* re-use off as the current argument's index from w/in options */
@@ -263,7 +263,7 @@ nextfileopt:
             if (arg->flags & OPT_DONE) {
                 if (arg->arg == ARG_NONE && ctx->cur < argc) {
                     warn("too many arguments");
-                    return LOADOPT_ERR_TOOMANY;
+                    return (errno = ETOOMANYREFS, -1);
                 } else if (arg->arg == ARG_REQ) {
                     ++ctx->off;
                     continue;
@@ -274,7 +274,7 @@ nextfileopt:
 
             if (arg->arg == ARG_REQ && ctx->cur == argc) {
                 warn("argument ", ESC, arg->longopt, ESC," missing");
-                return LOADOPT_ERR_MISSINGARG;
+                return (errno = ENODATA, -1);
             }
 
             /* next argument from command line */
@@ -293,5 +293,5 @@ nextfileopt:
         ctx->state = STATE_DONE;
 
     /* this is the end... */
-    return PARSEOPT_DONE;
+    return 0;
 }
diff --git a/src/liblimb/loadopt.h/loadopt_handle_noconfig.c b/src/liblimb/loadopt.h/loadopt_handle_noconfig.c
index 626d3a7..723fe02 100644
--- a/src/liblimb/loadopt.h/loadopt_handle_noconfig.c
+++ b/src/liblimb/loadopt.h/loadopt_handle_noconfig.c
@@ -1,6 +1,7 @@
 /* This file is part of limb                           https://lila.oss/limb
  * Copyright (C) 2023 Olivier Brunel                          jjk@jjacky.com */
 /* SPDX-License-Identifier: GPL-2.0-only */
+#include <errno.h>
 #include <limb/loadopt.h>
 #include <limb/output.h>
 #include "loadopt.h"
@@ -25,10 +26,10 @@ loadopt_handle_noconfig(int idx, const struct option *options, struct loadopt *c
                 char buf[2] = { options[idx].shortopt, 0 };
                 warn("invalid argument to option --", options[idx].longopt,
                      (*buf) ? "/-" : "", buf, ": ", ctx->arg);
-                return LOADOPT_ERR_INVALIDARG;
+                return (errno = EINVAL, -1);
             }
         }
     }
 
-    return PARSEOPT_DONE;
+    return 0;
 }
diff --git a/src/liblimb/parseopt.h/parseopt.c b/src/liblimb/parseopt.h/parseopt.c
index d1d843f..c922c54 100644
--- a/src/liblimb/parseopt.h/parseopt.c
+++ b/src/liblimb/parseopt.h/parseopt.c
@@ -1,6 +1,7 @@
 /* This file is part of limb                           https://lila.oss/limb
  * Copyright (C) 2023 Olivier Brunel                          jjk@jjacky.com */
 /* SPDX-License-Identifier: GPL-2.0-only */
+#include <errno.h>
 #include <stddef.h> /* offsetof() */
 #include <limb/bytestr.h>
 #include <limb/parseopt.h>
@@ -18,19 +19,19 @@ parseopt(int argc, const char **argv, const struct option *options,
     ctx->idx = -1;
 again:
     if (ctx->cur == argc)
-        return PARSEOPT_DONE;
+        return 0;
 
     arg = argv[ctx->cur] + ctx->off;
 
     if (flags & PARSEOPT_IS_LONG) {
         is_long = arg_long = 1;
         if (!*arg)
-            return PARSEOPT_ERR_NONAME;
+            return (errno = EINVAL, -1);
     } else {
         if (ctx->off == 0) {
             if (*arg != '-')
                 /* not an option, so no more options */
-                return PARSEOPT_DONE;
+                return 0;
             /* move on to the option */
             ++ctx->off;
             ++arg;
@@ -38,7 +39,7 @@ again:
 
         if (!*arg) {
             if (ctx->off == 1)
-                return PARSEOPT_ERR_NONAME;
+                return (errno = EINVAL, -1);
             /* next argument */
             ++ctx->cur;
             ctx->off = 0;
@@ -59,7 +60,7 @@ again:
         if (!l) {
             /* marker "--" for end of options */
             ++ctx->cur;
-            return PARSEOPT_DONE;
+            return 0;
         }
 
         if (flags & PARSEOPT_STRICT) {
@@ -69,11 +70,11 @@ again:
                     break;
             if (!options[ctx->idx].longopt) {
                 ctx->idx = -1;
-                return PARSEOPT_ERR_UNKNOWN;
+                return (errno = ENOENT, -1);
             }
         } else if (byte_get_match_full(&ctx->idx, arg, end, options,
                     offsetof(struct option, longopt), sizeof(*options)) < 0) {
-            return PARSEOPT_ERR_UNKNOWN;
+            return (errno = ENOENT, -1);
         }
 
         /* --option-name=value : don't look for optarg on the next arg */
@@ -87,7 +88,7 @@ again:
                 break;
         if (!options[ctx->idx].longopt) {
             ctx->idx = -1;
-            return PARSEOPT_ERR_UNKNOWN;
+            return (errno = ENOENT, -1);
         }
     }
 
@@ -96,11 +97,11 @@ again:
         if (arg_long) {
             ++ctx->cur;
             if (ctx->cur == argc)
-                return PARSEOPT_ERR_ARGREQ;
+                return (errno = ENOMSG, -1);
             ctx->arg = argv[ctx->cur];
         } else {
             if (!arg[1])
-                return PARSEOPT_ERR_ARGREQ;
+                return (errno = ENOMSG, -1);
             ctx->arg = arg + 1;
         }
     } else if (options[ctx->idx].arg == ARG_OPT) {