Welcome to little lamb

Code » limb » commit dd77362

Add autoopt.h and related functions

author Olivier Brunel
2023-04-05 17:59:59 UTC
committer Olivier Brunel
2023-05-20 18:06:35 UTC
parent 6167454bfb52608a73da7edde8af11b89533257b

Add autoopt.h and related functions

This allows to define some options and have them parsed automatically,
so the same options (through different shortopt/longopt can be used as
needed) can be implemented/processed in the same/consistent manner.

Options supported are :
- quiet : bump obuffer_1's level down
- verbose : raise obuffer_1's level up
- log : set up a log buffer to specified file/fd
- debug : raise obuffer_1's level to debug, or set up a debug buffer to
  specified file/fd

src/doc/autoopt.h.0.md +40 -0
src/doc/autoopt.h/autoopt_parse_buffer_dest.3.md +56 -0
src/doc/autoopt.h/autoopt_quiet.3.md +81 -0
src/doc/loadopt.h/loadopt.3.md +4 -0
src/doc/parseopt.h/parseopt.3.md +4 -0
src/include/autoopt.h +17 -0
src/liblimb/autoopt.h/autoopt_debug.c +15 -0
src/liblimb/autoopt.h/autoopt_log.c +11 -0
src/liblimb/autoopt.h/autoopt_parse_buffer_dest.c +28 -0
src/liblimb/autoopt.h/autoopt_quiet.c +14 -0
src/liblimb/autoopt.h/autoopt_verbose.c +14 -0
src/liblimb/autoopt.h/logdbg.c +20 -0
src/liblimb/autoopt.h/parse_buffer_dest.c +37 -0
src/liblimb/include/limb/autoopt.h +25 -0

diff --git a/src/doc/autoopt.h.0.md b/src/doc/autoopt.h.0.md
new file mode 100644
index 0000000..b929b66
--- /dev/null
+++ b/src/doc/autoopt.h.0.md
@@ -0,0 +1,40 @@
+% limb manual
+% autoopt.h(0)
+
+# NAME
+
+autoopt.h - automatic handling/parsing of options
+
+# SYNOPSIS
+
+    #include <limb/autoopt.h>
+
+# DESCRIPTION
+
+This header defines functions to handle options automatically.
+
+## Macros
+
+The following macros are defined :
+
+: *AUTOOPT_QUIET*, *AUTOOPT_VERBOSE*, *AUTOOPT_LOG*, *AUTOOPT_DEBUG*
+:: Macros to define options that can be handled via functions below.
+
+## Functions
+
+The following functions are defined :
+
+: [autoopt_quiet](3)
+:: To handle option to bump output level down.
+
+: [autoopt_verbose](3)
+:: To handle option to raise output level up.
+
+: [autoopt_log](3)
+:: To handle option to set up a log output.
+
+: [autoopt_debug](3)
+:: To handle option to raise output to debug level, or set up a debug output.
+
+: [autoopt_parse_buffer_dest](3)
+:: To parse the destination for an output buffer.
diff --git a/src/doc/autoopt.h/autoopt_parse_buffer_dest.3.md b/src/doc/autoopt.h/autoopt_parse_buffer_dest.3.md
new file mode 100644
index 0000000..10ee8e5
--- /dev/null
+++ b/src/doc/autoopt.h/autoopt_parse_buffer_dest.3.md
@@ -0,0 +1,56 @@
+% limb manual
+% autoopt_parse_buffer_dest(3)
+
+# NAME
+
+autoopt\_parse\_buffer\_dest - parse destination for an output buffer
+
+# SYNOPSIS
+
+    #include <limb/autoopt.h>
+
+```pre hl
+int autoopt_parse_buffer_dest(const struct option *<em>option</em>, const char *<em>arg</em>, u8 *<em>lvl</em>)
+```
+
+# DESCRIPTION
+
+The `autoopt_parse_buffer_dest`() function parses `arg` to be the destination
+for an output buffer. That is, it is expected to be either a file descriptor or
+the name of a file that will be opened in append mode, parsed according to
+[open_parsed_name](3).
+
+Additionally, it may be prefixed with an at-sign (`@`) followed by an output
+level (as described in [obuffer_parse_level](3)) and a colon (`:`), in order to
+set the output level to specified level.
+
+It is also possible not to specify a level (i.e. use `@:` as prefix) in order to
+have the default level bumped (e.g. from *OLVL_NORMAL* to *OLVL_VERBOSE*).
+
+To specify a file whose name begins with an at-sign, simply use two at-signs at
+the beginning of the file.
+
+The `option` argument is only used in case of an error, to include the option's
+name in the emitted warning.
+
+The `lvl` argument is expected to point to the default level of the output
+buffer, and might be changed according to the parsed `arg`, or set to -1 in case
+of error.
+
+# RETURN VALUE
+
+The function return the file descriptor to be used for the output buffer on
+success. It can either be the specified file descriptor on `arg`, or - in case
+of a file name - the file descriptor of the file opened in append mode.
+Otherwise, it returns -1 and sets `errno` to indicate the error.
+
+# ERRORS
+
+The function may fail if :
+
+: *EINVAL*
+:: Invalid output level specified, in which case the value pointed by `lvl` is
+:: set to -1
+
+It may also fail for the errors described for [open_parsed_name](3) and
+[open_append](3).
diff --git a/src/doc/autoopt.h/autoopt_quiet.3.md b/src/doc/autoopt.h/autoopt_quiet.3.md
new file mode 100644
index 0000000..5ed02ee
--- /dev/null
+++ b/src/doc/autoopt.h/autoopt_quiet.3.md
@@ -0,0 +1,81 @@
+% limb manual
+% autoopt_quiet(3)
+
+# NAME
+
+autoopt\_quiet, autoopt\_verbose, autoopt\_log, autoopt\_debug - automatic
+handling of some options
+
+# SYNOPSIS
+
+    #include <limb/autoopt.h>
+
+```pre hl
+AUTOOPT_QUIET(<em>shortopt</em>, <em>longopt</em>, <em>flags</em>, <em>id</em>)
+AUTOOPT_VERBOSE(<em>shortopt</em>, <em>longopt</em>, <em>flags</em>, <em>id</em>)
+AUTOOPT_LOG(<em>shortopt</em>, <em>longopt</em>, <em>flags</em>, <em>id</em>)
+AUTOOPT_DEBUG(<em>shortopt</em>, <em>longopt</em>, <em>flags</em>, <em>id</em>)
+
+int autoopt_quiet(const struct option *<em>option</em>, const char *<em>arg</em>)
+int autoopt_verbose(const struct option *<em>option</em>, const char *<em>arg</em>)
+int autoopt_log(const struct option *<em>option</em>, const char *<em>arg</em>)
+int autoopt_debug(const struct option *<em>option</em>, const char *<em>arg</em>)
+```
+
+# DESCRIPTION
+
+All of those functions share the same signature, even though they may not be
+used. They are intended to be used during parsing of command-line options via
+[parseopt](3) (or [loadopt](3)).
+
+As such, the `option` argument is expected to point to the option that is being
+parsed/processed. It is used in case of an error (e.g. invalid argument) in
+order to mention the option's name in the emitted warning.
+
+All the functions also expected to have been defined through their corresponding
+macros, notably if they take a required argument, their argument `arg` is
+expected to point to a NULL-terminated string without checking (unlike with an
+optional argument).
+
+The *AUTOOPT_QUIET*(), *AUTOOPT_VERBOSE*(), *AUTOOPT_LOG*() and
+*AUTOOPT_DEBUG*() macros can be used when defining an array of *struct option*
+to be passed to [parseopt](3) or [loadopt](3).
+
+The `autoopt_quiet`() function will bump the output level (of `obuffer_1`, for
+*stdout*) down by one level. If already at *OLVL_SILENT* then nothing is done.
+
+The `autoopt_verbose`() function will raise the output level (of `obuffer_1`,
+for *stdout*) up by one level. If already at *OLVL_MAXIMUM* then nothing is
+done.
+
+! INFO:
+! The `obuffer_1` output buffer is used by [out_putmsg](3) and all the macros
+! based on it, as can be found in [output.h](0).
+
+The `autoopt_log`() function parses `arg` as the destination to set up the log
+output buffer via [obuffers_addlog_full](3). Said parsing is done through
+[autoopt_parse_buffer_dest](3).
+
+The `autoopt_debug`() function raises the output level (of `obuffer_1`) to
+*OLVL_DEBUG* if no argument was specified. Else, it parses `arg` as the
+destination to set up the debug output buffer via [obuffers_adddbg_full](3).
+Said parsing is done through [autoopt_parse_buffer_dest](3).
+
+# RETURN VALUE
+
+The `autoopt_quiet`() and `autoopt_verbose`() functions always returns 1.
+
+The `autoopt_log`() and `autoopt_debug`() functions return 1 on success.
+Otherwise they return 0 and set `errno` to indicate the error. A warning is
+also emitted (through the [warn](3) family of functions) describing the error.
+
+# ERRORS
+
+The `autoopt_log`() function may fail for the errors described in
+[obuffers_addlog_full](3).
+
+The `autoopt_dbg`() function may fail for the errors described in
+[obuffers_adddbg_full](3).
+
+The `autoopt_log`() and `autoopt_debug`() functions may also fail for the errors
+described for [autoopt_parse_buffer_dest](3).
diff --git a/src/doc/loadopt.h/loadopt.3.md b/src/doc/loadopt.h/loadopt.3.md
index 2e8ede5..1324e38 100644
--- a/src/doc/loadopt.h/loadopt.3.md
+++ b/src/doc/loadopt.h/loadopt.3.md
@@ -157,3 +157,7 @@ The `loadopt`() function may fail if :
 
 The `loadopt`() function may also fail for any of the errors specified for
 [parseopt](3), [openslurpclose](3) save for *ENOENT*.
+
+# SEE ALSO
+
+[autoopt](3)
diff --git a/src/doc/parseopt.h/parseopt.3.md b/src/doc/parseopt.h/parseopt.3.md
index 8f2b916..942621e 100644
--- a/src/doc/parseopt.h/parseopt.3.md
+++ b/src/doc/parseopt.h/parseopt.3.md
@@ -168,3 +168,7 @@ The `parseopt`() function may fail if :
 
 : *ENOMSG*
 :: An option requiring an argument was found, but no argument was specified.
+
+# SEE ALSO
+
+[autoopt](3)
diff --git a/src/include/autoopt.h b/src/include/autoopt.h
new file mode 100644
index 0000000..02d1414
--- /dev/null
+++ b/src/include/autoopt.h
@@ -0,0 +1,17 @@
+/* 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 */
+#ifndef LIMB_LIMB_AUTOOPT_H
+#define LIMB_LIMB_AUTOOPT_H
+
+#include <stddef.h> /* size_t */
+#include <limb/autoopt.h>
+#include <limb/gccattributes.h>
+
+typedef int (*obufadd) (int fd, u8 deflvl);
+
+int logdbg(const struct option *option, const char *arg, u8 deflvl, obufadd obufadd) gccattr_hidden;
+
+int parse_buffer_dest(u8 *lvl, const char ** const arg, size_t len) gccattr_hidden;
+
+#endif /* LIMB_LIMB_AUTOOPT_H */
diff --git a/src/liblimb/autoopt.h/autoopt_debug.c b/src/liblimb/autoopt.h/autoopt_debug.c
new file mode 100644
index 0000000..46085e9
--- /dev/null
+++ b/src/liblimb/autoopt.h/autoopt_debug.c
@@ -0,0 +1,15 @@
+/* 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 <limb/obuffers.h>
+#include "autoopt.h"
+
+int
+autoopt_debug(const struct option *option, const char *arg)
+{
+    if (arg)
+        return logdbg(option, arg, OLVL_DEBUG, obuffers_adddbg_full);
+
+    obuffer_1->lvl = OLVL_DEBUG;
+    return 1;
+}
diff --git a/src/liblimb/autoopt.h/autoopt_log.c b/src/liblimb/autoopt.h/autoopt_log.c
new file mode 100644
index 0000000..e7b6f6e
--- /dev/null
+++ b/src/liblimb/autoopt.h/autoopt_log.c
@@ -0,0 +1,11 @@
+/* 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 <limb/obuffers.h>
+#include "autoopt.h"
+
+int
+autoopt_log(const struct option *option, const char *arg)
+{
+    return logdbg(option, arg, OLVL_NORMAL, obuffers_addlog_full);
+}
diff --git a/src/liblimb/autoopt.h/autoopt_parse_buffer_dest.c b/src/liblimb/autoopt.h/autoopt_parse_buffer_dest.c
new file mode 100644
index 0000000..735458a
--- /dev/null
+++ b/src/liblimb/autoopt.h/autoopt_parse_buffer_dest.c
@@ -0,0 +1,28 @@
+/* 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/output.h>
+#include "autoopt.h"
+
+int
+autoopt_parse_buffer_dest(const struct option *option, const char *arg, u8 *lvl)
+{
+    int fd = parse_buffer_dest(lvl, &arg, strlen(arg));
+    if (fd < 0) {
+        char buf[2] = { option->shortopt, 0 };
+        if (*lvl == (u8) -1)
+            warn("invalid output level via --", option->longopt,
+                 (*buf) ? "/-" : "", buf, ": ", arg);
+        else if (errno == EINVAL || errno == ERANGE)
+            warn("invalid file descriptor via --", option->longopt,
+                 (*buf) ? "/-" : "", buf, ": ", arg);
+        else
+            warnusys("open ", ESC, arg, ESC, " via --", option->longopt,
+                 (*buf) ? "/-" : "", buf);
+
+        return -1;
+    }
+
+    return fd;
+}
diff --git a/src/liblimb/autoopt.h/autoopt_quiet.c b/src/liblimb/autoopt.h/autoopt_quiet.c
new file mode 100644
index 0000000..da88726
--- /dev/null
+++ b/src/liblimb/autoopt.h/autoopt_quiet.c
@@ -0,0 +1,14 @@
+/* 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 <skalibs/gccattributes.h>
+#include <limb/autoopt.h>
+#include <limb/obuffers.h>
+
+int
+autoopt_quiet(const struct option *option gccattr_unused, const char *arg gccattr_unused)
+{
+    if (obuffer_1->lvl > OLVL_SILENT)
+        obuffer_1->lvl -= 50;
+    return 1;
+}
diff --git a/src/liblimb/autoopt.h/autoopt_verbose.c b/src/liblimb/autoopt.h/autoopt_verbose.c
new file mode 100644
index 0000000..30566b1
--- /dev/null
+++ b/src/liblimb/autoopt.h/autoopt_verbose.c
@@ -0,0 +1,14 @@
+/* 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 <skalibs/gccattributes.h>
+#include <limb/autoopt.h>
+#include <limb/obuffers.h>
+
+int
+autoopt_verbose(const struct option *option gccattr_unused, const char *arg gccattr_unused)
+{
+    if (obuffer_1->lvl < OLVL_MAXIMUM)
+        obuffer_1->lvl += 50;
+    return 1;
+}
diff --git a/src/liblimb/autoopt.h/logdbg.c b/src/liblimb/autoopt.h/logdbg.c
new file mode 100644
index 0000000..10bd9dc
--- /dev/null
+++ b/src/liblimb/autoopt.h/logdbg.c
@@ -0,0 +1,20 @@
+/* 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 <limb/djbunix.h>
+#include "autoopt.h"
+
+int
+logdbg(const struct option *option, const char *arg, u8 deflvl, obufadd obufadd)
+{
+    u8 lvl = deflvl;
+    int fd = autoopt_parse_buffer_dest(option, arg, &lvl);
+    if (fd < 0) return 0;
+
+    if (!obufadd(fd, lvl)) {
+        fd_close(fd);
+        return 0;
+    }
+
+    return 1;
+}
diff --git a/src/liblimb/autoopt.h/parse_buffer_dest.c b/src/liblimb/autoopt.h/parse_buffer_dest.c
new file mode 100644
index 0000000..85b3e9e
--- /dev/null
+++ b/src/liblimb/autoopt.h/parse_buffer_dest.c
@@ -0,0 +1,37 @@
+/* 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/bytestr.h>
+#include <limb/djbunix.h>
+#include <limb/obuffer.h>
+#include "autoopt.h"
+
+int
+parse_buffer_dest(u8 *lvl, const char ** const arg, size_t len)
+{
+    /* is there a level specified via @[level]: */
+    if (**arg == '@') {
+        ++*arg;
+        --len;
+        /* another '@' would mean file name starts with a '@' */
+        if (**arg != '@') {
+            /* until the ':' we have the level */
+            size_t end = byte_chr(*arg, len, ':');
+            if (end == len)
+                return (errno = EINVAL, *lvl = (u8) -1, -1);
+            if (end == 0) {
+                /* @: w/out level means bumping it from its default value */
+                if (*lvl < OLVL_MAXIMUM)
+                    *lvl += 50;
+            } else {
+                *lvl = obuffer_parse_level(*arg, end);
+                if (*lvl == (u8) -1)
+                    return (errno = EINVAL, -1);
+            }
+            *arg += end + 1;
+        }
+    }
+
+    return open_parsed_name(*arg, open_append);
+}
diff --git a/src/liblimb/include/limb/autoopt.h b/src/liblimb/include/limb/autoopt.h
new file mode 100644
index 0000000..cd49956
--- /dev/null
+++ b/src/liblimb/include/limb/autoopt.h
@@ -0,0 +1,25 @@
+/* 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 */
+#ifndef LIMB_AUTOOPT_H
+#define LIMB_AUTOOPT_H
+
+#include <limb/parseopt.h>
+
+/* bump output level down to silent */
+#define AUTOOPT_QUIET(s,l,f,i)      OPTION_ARG_NONE(s, l, f, i)
+/* raise output level up to maximum */
+#define AUTOOPT_VERBOSE(s,l,f,i)    OPTION_ARG_NONE(s, l, f, i)
+/* set up log output to specified fd/file */
+#define AUTOOPT_LOG(s,l,f,i)        OPTION_ARG_REQ( s, l, f, i)
+/* set up debut output to specified fd/file */
+#define AUTOOPT_DEBUG(s,l,f,i)      OPTION_ARG_OPT( s, l, f, i)
+
+extern int autoopt_quiet(const struct option *option, const char *arg);
+extern int autoopt_verbose(const struct option *option, const char *arg);
+extern int autoopt_log(const struct option *option, const char *arg);
+extern int autoopt_debug(const struct option *option, const char *arg);
+
+extern int autoopt_parse_buffer_dest(const struct option *option, const char *arg, u8 *lvl);
+
+#endif /* LIMB_AUTOOPT_H */