summaryrefslogtreecommitdiff
path: root/libmisc
diff options
context:
space:
mode:
Diffstat (limited to 'libmisc')
-rw-r--r--libmisc/CMakeLists.txt2
-rw-r--r--libmisc/error.c26
-rw-r--r--libmisc/error_generated.c186
-rwxr-xr-xlibmisc/error_generated.c.gen35
-rw-r--r--libmisc/include/libmisc/error.h167
5 files changed, 416 insertions, 0 deletions
diff --git a/libmisc/CMakeLists.txt b/libmisc/CMakeLists.txt
index 07f154b..9bb282b 100644
--- a/libmisc/CMakeLists.txt
+++ b/libmisc/CMakeLists.txt
@@ -8,6 +8,8 @@ target_include_directories(libmisc PUBLIC INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/
target_sources(libmisc INTERFACE
assert.c
endian.c
+ error.c
+ error_generated.c
fmt.c
hash.c
intercept.c
diff --git a/libmisc/error.c b/libmisc/error.c
new file mode 100644
index 0000000..dfe4e80
--- /dev/null
+++ b/libmisc/error.c
@@ -0,0 +1,26 @@
+/* libmisc/error.c - Go-esque errors
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libmisc/error.h>
+
+const char *error_msg(error err) {
+ return (err._msg && err._msg[0])
+ ? err._msg
+ : _errnum_str_msg(err.num);
+}
+
+void error_cleanup(error *errptr) {
+ if (!errptr)
+ return;
+ if (errptr->_msg)
+ free(errptr->_msg);
+ errptr->num = E_NOERROR;
+ errptr->_msg = NULL;
+}
+
+void fmt_print_error(lo_interface fmt_dest w, error err) {
+ fmt_print(w, (str, error_msg(err)), " (", _errnum_str_sym(err.num), ")");
+}
diff --git a/libmisc/error_generated.c b/libmisc/error_generated.c
new file mode 100644
index 0000000..e0afcab
--- /dev/null
+++ b/libmisc/error_generated.c
@@ -0,0 +1,186 @@
+/* libmisc/error_generated.c - Generated by libmisc/error_generated.c.gen. DO NOT EDIT! */
+
+#include <libmisc/error.h>
+
+const char *_errnum_str_sym(_errnum errnum) {
+ switch (errnum) {
+ case E_NOERROR: return "E_NOERROR";
+ case E_EOF: return "E_EOF";
+ case E_NET_EARP_TIMEOUT: return "E_NET_EARP_TIMEOUT";
+ case E_NET_EACK_TIMEOUT: return "E_NET_EACK_TIMEOUT";
+ case E_NET_ERECV_TIMEOUT: return "E_NET_ERECV_TIMEOUT";
+ case E_NET_ECLOSED: return "E_NET_ECLOSED";
+ case E_POSIX_E2BIG: return "E_POSIX_E2BIG";
+ case E_POSIX_EACCES: return "E_POSIX_EACCES";
+ case E_POSIX_EADDRINUSE: return "E_POSIX_EADDRINUSE";
+ case E_POSIX_EADDRNOTAVAIL: return "E_POSIX_EADDRNOTAVAIL";
+ case E_POSIX_EAFNOSUPPORT: return "E_POSIX_EAFNOSUPPORT";
+ case E_POSIX_EAGAIN: return "E_POSIX_EAGAIN";
+ case E_POSIX_EALREADY: return "E_POSIX_EALREADY";
+ case E_POSIX_EBADF: return "E_POSIX_EBADF";
+ case E_POSIX_EBADMSG: return "E_POSIX_EBADMSG";
+ case E_POSIX_EBUSY: return "E_POSIX_EBUSY";
+ case E_POSIX_ECANCELED: return "E_POSIX_ECANCELED";
+ case E_POSIX_ECHILD: return "E_POSIX_ECHILD";
+ case E_POSIX_ECONNABORTED: return "E_POSIX_ECONNABORTED";
+ case E_POSIX_ECONNREFUSED: return "E_POSIX_ECONNREFUSED";
+ case E_POSIX_ECONNRESET: return "E_POSIX_ECONNRESET";
+ case E_POSIX_EDEADLK: return "E_POSIX_EDEADLK";
+ case E_POSIX_EDESTADDRREQ: return "E_POSIX_EDESTADDRREQ";
+ case E_POSIX_EDOM: return "E_POSIX_EDOM";
+ case E_POSIX_EDQUOT: return "E_POSIX_EDQUOT";
+ case E_POSIX_EEXIST: return "E_POSIX_EEXIST";
+ case E_POSIX_EFAULT: return "E_POSIX_EFAULT";
+ case E_POSIX_EFBIG: return "E_POSIX_EFBIG";
+ case E_POSIX_EHOSTUNREACH: return "E_POSIX_EHOSTUNREACH";
+ case E_POSIX_EIDRM: return "E_POSIX_EIDRM";
+ case E_POSIX_EILSEQ: return "E_POSIX_EILSEQ";
+ case E_POSIX_EINPROGRESS: return "E_POSIX_EINPROGRESS";
+ case E_POSIX_EINTR: return "E_POSIX_EINTR";
+ case E_POSIX_EINVAL: return "E_POSIX_EINVAL";
+ case E_POSIX_EIO: return "E_POSIX_EIO";
+ case E_POSIX_EISCONN: return "E_POSIX_EISCONN";
+ case E_POSIX_EISDIR: return "E_POSIX_EISDIR";
+ case E_POSIX_ELOOP: return "E_POSIX_ELOOP";
+ case E_POSIX_EMFILE: return "E_POSIX_EMFILE";
+ case E_POSIX_EMLINK: return "E_POSIX_EMLINK";
+ case E_POSIX_EMSGSIZE: return "E_POSIX_EMSGSIZE";
+ case E_POSIX_EMULTIHOP: return "E_POSIX_EMULTIHOP";
+ case E_POSIX_ENAMETOOLONG: return "E_POSIX_ENAMETOOLONG";
+ case E_POSIX_ENETDOWN: return "E_POSIX_ENETDOWN";
+ case E_POSIX_ENETRESET: return "E_POSIX_ENETRESET";
+ case E_POSIX_ENETUNREACH: return "E_POSIX_ENETUNREACH";
+ case E_POSIX_ENFILE: return "E_POSIX_ENFILE";
+ case E_POSIX_ENOBUFS: return "E_POSIX_ENOBUFS";
+ case E_POSIX_ENODEV: return "E_POSIX_ENODEV";
+ case E_POSIX_ENOENT: return "E_POSIX_ENOENT";
+ case E_POSIX_ENOEXEC: return "E_POSIX_ENOEXEC";
+ case E_POSIX_ENOLCK: return "E_POSIX_ENOLCK";
+ case E_POSIX_ENOLINK: return "E_POSIX_ENOLINK";
+ case E_POSIX_ENOMEM: return "E_POSIX_ENOMEM";
+ case E_POSIX_ENOMSG: return "E_POSIX_ENOMSG";
+ case E_POSIX_ENOPROTOOPT: return "E_POSIX_ENOPROTOOPT";
+ case E_POSIX_ENOSPC: return "E_POSIX_ENOSPC";
+ case E_POSIX_ENOSYS: return "E_POSIX_ENOSYS";
+ case E_POSIX_ENOTCONN: return "E_POSIX_ENOTCONN";
+ case E_POSIX_ENOTDIR: return "E_POSIX_ENOTDIR";
+ case E_POSIX_ENOTEMPTY: return "E_POSIX_ENOTEMPTY";
+ case E_POSIX_ENOTRECOVERABLE: return "E_POSIX_ENOTRECOVERABLE";
+ case E_POSIX_ENOTSOCK: return "E_POSIX_ENOTSOCK";
+ case E_POSIX_ENOTSUP: return "E_POSIX_ENOTSUP";
+ case E_POSIX_ENOTTY: return "E_POSIX_ENOTTY";
+ case E_POSIX_ENXIO: return "E_POSIX_ENXIO";
+ case E_POSIX_EOPNOTSUPP: return "E_POSIX_EOPNOTSUPP";
+ case E_POSIX_EOVERFLOW: return "E_POSIX_EOVERFLOW";
+ case E_POSIX_EOWNERDEAD: return "E_POSIX_EOWNERDEAD";
+ case E_POSIX_EPERM: return "E_POSIX_EPERM";
+ case E_POSIX_EPIPE: return "E_POSIX_EPIPE";
+ case E_POSIX_EPROTO: return "E_POSIX_EPROTO";
+ case E_POSIX_EPROTONOSUPPORT: return "E_POSIX_EPROTONOSUPPORT";
+ case E_POSIX_EPROTOTYPE: return "E_POSIX_EPROTOTYPE";
+ case E_POSIX_ERANGE: return "E_POSIX_ERANGE";
+ case E_POSIX_EROFS: return "E_POSIX_EROFS";
+ case E_POSIX_ESOCKTNOSUPPORT: return "E_POSIX_ESOCKTNOSUPPORT";
+ case E_POSIX_ESPIPE: return "E_POSIX_ESPIPE";
+ case E_POSIX_ESRCH: return "E_POSIX_ESRCH";
+ case E_POSIX_ESTALE: return "E_POSIX_ESTALE";
+ case E_POSIX_ETIMEDOUT: return "E_POSIX_ETIMEDOUT";
+ case E_POSIX_ETXTBSY: return "E_POSIX_ETXTBSY";
+ case E_POSIX_EWOULDBLOCK: return "E_POSIX_EWOULDBLOCK";
+ case E_POSIX_EXDEV: return "E_POSIX_EXDEV";
+ case E_EUNKNOWN: return "E_EUNKNOWN";
+ default: return "E_<invalid>";
+ }
+}
+
+const char *_errnum_str_msg(_errnum errnum) {
+ switch (errnum) {
+ case E_NOERROR: return "no error";
+ case E_EOF: return "EOF";
+ case E_NET_EARP_TIMEOUT: return "ARP timeout";
+ case E_NET_EACK_TIMEOUT: return "TCP ACK timeout";
+ case E_NET_ERECV_TIMEOUT: return "receive timeout";
+ case E_NET_ECLOSED: return "already closed";
+ case E_POSIX_E2BIG: return "Argument list too long";
+ case E_POSIX_EACCES: return "Permission denied";
+ case E_POSIX_EADDRINUSE: return "Address in use";
+ case E_POSIX_EADDRNOTAVAIL: return "Address not available";
+ case E_POSIX_EAFNOSUPPORT: return "Address family not supported";
+ case E_POSIX_EAGAIN: return "Resource unavailable, try again";
+ case E_POSIX_EALREADY: return "Connection already in progress";
+ case E_POSIX_EBADF: return "Bad file descriptor";
+ case E_POSIX_EBADMSG: return "Bad message";
+ case E_POSIX_EBUSY: return "Device or resource busy";
+ case E_POSIX_ECANCELED: return "Operation canceled";
+ case E_POSIX_ECHILD: return "No child processes";
+ case E_POSIX_ECONNABORTED: return "Connection aborted";
+ case E_POSIX_ECONNREFUSED: return "Connection refused";
+ case E_POSIX_ECONNRESET: return "Connection reset";
+ case E_POSIX_EDEADLK: return "Resource deadlock would occur";
+ case E_POSIX_EDESTADDRREQ: return "Destination address required";
+ case E_POSIX_EDOM: return "Mathematics argument out of domain of function";
+ case E_POSIX_EDQUOT: return "Reserved";
+ case E_POSIX_EEXIST: return "File exists";
+ case E_POSIX_EFAULT: return "Bad address";
+ case E_POSIX_EFBIG: return "File too large";
+ case E_POSIX_EHOSTUNREACH: return "Host is unreachable";
+ case E_POSIX_EIDRM: return "Identifier removed";
+ case E_POSIX_EILSEQ: return "Illegal byte sequence";
+ case E_POSIX_EINPROGRESS: return "Operation in progress";
+ case E_POSIX_EINTR: return "Interrupted function";
+ case E_POSIX_EINVAL: return "Invalid argument";
+ case E_POSIX_EIO: return "I/O error";
+ case E_POSIX_EISCONN: return "Socket is connected";
+ case E_POSIX_EISDIR: return "Is a directory";
+ case E_POSIX_ELOOP: return "Too many levels of symbolic links";
+ case E_POSIX_EMFILE: return "File descriptor value too large";
+ case E_POSIX_EMLINK: return "Too many hard links";
+ case E_POSIX_EMSGSIZE: return "Message too large";
+ case E_POSIX_EMULTIHOP: return "Reserved";
+ case E_POSIX_ENAMETOOLONG: return "Filename too long";
+ case E_POSIX_ENETDOWN: return "Network is down";
+ case E_POSIX_ENETRESET: return "Connection aborted by network";
+ case E_POSIX_ENETUNREACH: return "Network unreachable";
+ case E_POSIX_ENFILE: return "Too many files open in system";
+ case E_POSIX_ENOBUFS: return "No buffer space available";
+ case E_POSIX_ENODEV: return "No such device";
+ case E_POSIX_ENOENT: return "No such file or directory";
+ case E_POSIX_ENOEXEC: return "Executable file format error";
+ case E_POSIX_ENOLCK: return "No locks available";
+ case E_POSIX_ENOLINK: return "Reserved";
+ case E_POSIX_ENOMEM: return "Not enough space";
+ case E_POSIX_ENOMSG: return "No message of the desired type";
+ case E_POSIX_ENOPROTOOPT: return "Protocol not available";
+ case E_POSIX_ENOSPC: return "No space left on device";
+ case E_POSIX_ENOSYS: return "Functionality not supported";
+ case E_POSIX_ENOTCONN: return "The socket is not connected";
+ case E_POSIX_ENOTDIR: return "Not a directory or a symbolic link to a directory";
+ case E_POSIX_ENOTEMPTY: return "Directory not empty";
+ case E_POSIX_ENOTRECOVERABLE: return "State not recoverable";
+ case E_POSIX_ENOTSOCK: return "Not a socket";
+ case E_POSIX_ENOTSUP: return "Not supported";
+ case E_POSIX_ENOTTY: return "Inappropriate I/O control operation";
+ case E_POSIX_ENXIO: return "No such device or address";
+ case E_POSIX_EOPNOTSUPP: return "Operation not supported on socket";
+ case E_POSIX_EOVERFLOW: return "Value too large to be stored in data type";
+ case E_POSIX_EOWNERDEAD: return "Previous owner died";
+ case E_POSIX_EPERM: return "Operation not permitted";
+ case E_POSIX_EPIPE: return "Broken pipe";
+ case E_POSIX_EPROTO: return "Protocol error";
+ case E_POSIX_EPROTONOSUPPORT: return "Protocol not supported";
+ case E_POSIX_EPROTOTYPE: return "Protocol wrong type for socket";
+ case E_POSIX_ERANGE: return "Result too large";
+ case E_POSIX_EROFS: return "Read-only file system";
+ case E_POSIX_ESOCKTNOSUPPORT: return "Socket type not supported";
+ case E_POSIX_ESPIPE: return "Invalid seek";
+ case E_POSIX_ESRCH: return "No such process";
+ case E_POSIX_ESTALE: return "Reserved";
+ case E_POSIX_ETIMEDOUT: return "Connection timed out";
+ case E_POSIX_ETXTBSY: return "Text file busy";
+ case E_POSIX_EWOULDBLOCK: return "Operation would block";
+ case E_POSIX_EXDEV: return "Improper hard link";
+ case E_EUNKNOWN:
+ default:
+ return "unknown error";
+ }
+}
diff --git a/libmisc/error_generated.c.gen b/libmisc/error_generated.c.gen
new file mode 100755
index 0000000..944d909
--- /dev/null
+++ b/libmisc/error_generated.c.gen
@@ -0,0 +1,35 @@
+#!/usr/bin/env bash
+# libmisc/error_generated.c.gen - Generate _errnum strings
+#
+# Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+error_h=$1
+outfile=$2
+
+{
+ echo "/* ${outfile} - Generated by $0. DO NOT EDIT! */"
+ echo
+ echo '#include <libmisc/error.h>'
+ echo
+ echo 'const char *_errnum_str_sym(_errnum errnum) {'
+ echo $'\tswitch (errnum) {'
+ sed -nE \
+ -e 's@^(#define)?\s+(E_[_A-Z0-9]+)[ ,][^/]*/\* ([^*(]+) (\*/|\().*@'$'\tcase \\2: return "\\2";''@p' \
+ -- "$error_h"
+ echo $'\tcase E_EUNKNOWN: return "E_EUNKNOWN";'
+ echo $'\tdefault: return "E_<invalid>";'
+ echo $'\t}'
+ echo '}'
+ echo
+ echo 'const char *_errnum_str_msg(_errnum errnum) {'
+ echo $'\tswitch (errnum) {'
+ sed -nE \
+ -e 's@^(#define)?\s+(E_[_A-Z0-9]+)[ ,][^/]*/\* ([^*(]+) (\*/|\().*@'$'\tcase \\2: return "\\3";''@p' \
+ -- "$error_h"
+ echo $'\tcase E_EUNKNOWN:'
+ echo $'\tdefault:'
+ echo $'\t\treturn "unknown error";'
+ echo $'\t}'
+ echo '}'
+} >"$outfile"
diff --git a/libmisc/include/libmisc/error.h b/libmisc/include/libmisc/error.h
new file mode 100644
index 0000000..4110626
--- /dev/null
+++ b/libmisc/include/libmisc/error.h
@@ -0,0 +1,167 @@
+/* libmisc/error.h - Go-esque errors
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _LIBMISC_ERROR_H_
+#define _LIBMISC_ERROR_H_
+
+#include <stddef.h> /* for size_t */
+#include <stdint.h> /* for uint{n}_t */
+
+#include <libmisc/assert.h>
+#include <libmisc/fmt.h>
+
+/* _errnum ********************************************************************/
+
+typedef enum {
+ /* Original to libmisc */
+ E_NOERROR = 0, /* no error */
+ E_EOF, /* EOF */
+ E_NET_EARP_TIMEOUT, /* ARP timeout */
+ E_NET_EACK_TIMEOUT, /* TCP ACK timeout */
+ E_NET_ERECV_TIMEOUT, /* receive timeout */
+ E_NET_ECLOSED, /* already closed */
+ /* At least C99 through C23 */
+ E_STDC_EDOM,
+ E_STDC_EILSEQ,
+ E_STDC_ERANGE,
+ /* POSIX-2024 */
+ E_POSIX_E2BIG, /* Argument list too long */
+ E_POSIX_EACCES, /* Permission denied */
+ E_POSIX_EADDRINUSE, /* Address in use */
+ E_POSIX_EADDRNOTAVAIL, /* Address not available */
+ E_POSIX_EAFNOSUPPORT, /* Address family not supported */
+ E_POSIX_EAGAIN, /* Resource unavailable, try again (may be the same value as [EWOULDBLOCK]) */
+ E_POSIX_EALREADY, /* Connection already in progress */
+ E_POSIX_EBADF, /* Bad file descriptor */
+ E_POSIX_EBADMSG, /* Bad message */
+ E_POSIX_EBUSY, /* Device or resource busy */
+ E_POSIX_ECANCELED, /* Operation canceled */
+ E_POSIX_ECHILD, /* No child processes */
+ E_POSIX_ECONNABORTED, /* Connection aborted */
+ E_POSIX_ECONNREFUSED, /* Connection refused */
+ E_POSIX_ECONNRESET, /* Connection reset */
+ E_POSIX_EDEADLK, /* Resource deadlock would occur */
+ E_POSIX_EDESTADDRREQ, /* Destination address required */
+#define E_POSIX_EDOM E_STDC_EDOM /* Mathematics argument out of domain of function */
+ E_POSIX_EDQUOT, /* Reserved */
+ E_POSIX_EEXIST, /* File exists */
+ E_POSIX_EFAULT, /* Bad address */
+ E_POSIX_EFBIG, /* File too large */
+ E_POSIX_EHOSTUNREACH, /* Host is unreachable */
+ E_POSIX_EIDRM, /* Identifier removed */
+#define E_POSIX_EILSEQ E_STDC_EILSEQ /* Illegal byte sequence */ /* (in the context of text encoding) */
+ E_POSIX_EINPROGRESS, /* Operation in progress */
+ E_POSIX_EINTR, /* Interrupted function */
+ E_POSIX_EINVAL, /* Invalid argument */
+ E_POSIX_EIO, /* I/O error */
+ E_POSIX_EISCONN, /* Socket is connected */
+ E_POSIX_EISDIR, /* Is a directory */
+ E_POSIX_ELOOP, /* Too many levels of symbolic links */
+ E_POSIX_EMFILE, /* File descriptor value too large */
+ E_POSIX_EMLINK, /* Too many hard links */
+ E_POSIX_EMSGSIZE, /* Message too large */
+ E_POSIX_EMULTIHOP, /* Reserved */
+ E_POSIX_ENAMETOOLONG, /* Filename too long */
+ E_POSIX_ENETDOWN, /* Network is down */
+ E_POSIX_ENETRESET, /* Connection aborted by network */
+ E_POSIX_ENETUNREACH, /* Network unreachable */
+ E_POSIX_ENFILE, /* Too many files open in system */
+ E_POSIX_ENOBUFS, /* No buffer space available */
+ E_POSIX_ENODEV, /* No such device */
+ E_POSIX_ENOENT, /* No such file or directory */
+ E_POSIX_ENOEXEC, /* Executable file format error */
+ E_POSIX_ENOLCK, /* No locks available */
+ E_POSIX_ENOLINK, /* Reserved */
+ E_POSIX_ENOMEM, /* Not enough space */
+ E_POSIX_ENOMSG, /* No message of the desired type */
+ E_POSIX_ENOPROTOOPT, /* Protocol not available */
+ E_POSIX_ENOSPC, /* No space left on device */
+ E_POSIX_ENOSYS, /* Functionality not supported */
+ E_POSIX_ENOTCONN, /* The socket is not connected */
+ E_POSIX_ENOTDIR, /* Not a directory or a symbolic link to a directory */
+ E_POSIX_ENOTEMPTY, /* Directory not empty */
+ E_POSIX_ENOTRECOVERABLE, /* State not recoverable */ /* Added in POSIX-2008 */
+ E_POSIX_ENOTSOCK, /* Not a socket */
+ E_POSIX_ENOTSUP, /* Not supported (may be the same value as [EOPNOTSUPP]) */
+ E_POSIX_ENOTTY, /* Inappropriate I/O control operation */
+ E_POSIX_ENXIO, /* No such device or address */
+ E_POSIX_EOPNOTSUPP, /* Operation not supported on socket (may be the same value as [ENOTSUP]) */
+ E_POSIX_EOVERFLOW, /* Value too large to be stored in data type */
+ E_POSIX_EOWNERDEAD, /* Previous owner died */ /* Added in POSIX-2008 */
+ E_POSIX_EPERM, /* Operation not permitted */
+ E_POSIX_EPIPE, /* Broken pipe */
+ E_POSIX_EPROTO, /* Protocol error */
+ E_POSIX_EPROTONOSUPPORT, /* Protocol not supported */
+ E_POSIX_EPROTOTYPE, /* Protocol wrong type for socket */
+#define E_POSIX_ERANGE E_STDC_ERANGE /* Result too large */
+ E_POSIX_EROFS, /* Read-only file system */
+ E_POSIX_ESOCKTNOSUPPORT, /* Socket type not supported */
+ E_POSIX_ESPIPE, /* Invalid seek */
+ E_POSIX_ESRCH, /* No such process */
+ E_POSIX_ESTALE, /* Reserved */
+ E_POSIX_ETIMEDOUT, /* Connection timed out */
+ E_POSIX_ETXTBSY, /* Text file busy */
+ E_POSIX_EWOULDBLOCK, /* Operation would block (may be the same value as [EAGAIN]) */
+ E_POSIX_EXDEV, /* Improper hard link */
+ /* End cap */
+ E_EUNKNOWN,
+} _errnum;
+
+const char *_errnum_str_sym(_errnum);
+const char *_errnum_str_msg(_errnum);
+
+/* error **********************************************************************/
+
+typedef struct {
+ _errnum num;
+ char *_msg;
+} error;
+
+#ifdef NDEBUG
+#define error_new(ERRNUM, ...) ((error){ \
+ .num = ERRNUM , \
+ __VA_OPT__(._msg = fmt_asprint(__VA_ARGS__),) \
+})
+#else
+#define error_new(ERRNUM, ...) ((error){ \
+ .num = ERRNUM, \
+ ._msg = fmt_asprint("" __VA_OPT__(,) __VA_ARGS__), \
+})
+#endif
+
+#define ERROR_NULL ((error){})
+#define ERROR_IS_NULL(err) ((err).num == 0 && (err)._msg == NULL)
+
+const char *error_msg(error err);
+void error_cleanup(error *errptr);
+void fmt_print_error(lo_interface fmt_dest w, error err);
+
+/* or_error ******************************************************************/
+
+#define DECLARE_ERROR_OR(TYP) typedef struct { union { TYP TYP; error err; }; bool is_err; } TYP##_or_error
+#define ERROR_NEW_VAL(TYP, VAL) ((TYP##_or_error){ .TYP = (VAL), .is_err = false })
+#define ERROR_NEW_ERR(TYP, ERR) ((TYP##_or_error){ .err = (ERR), .is_err = true })
+
+/* and_error *****************************************************************/
+
+#define DECLARE_ERROR_AND(TYP) typedef struct { TYP TYP; error err; } TYP##_and_error
+#define ERROR_AND(TYP, VAL, ERR) ((TYP##_and_error){ .TYP = (VAL), .err = (ERR) })
+
+/* some pre-defined types ****************************************************/
+
+DECLARE_ERROR_OR(size_t);
+DECLARE_ERROR_OR(uint8_t);
+DECLARE_ERROR_OR(uint16_t);
+DECLARE_ERROR_OR(uint32_t);
+DECLARE_ERROR_OR(uint64_t);
+
+DECLARE_ERROR_AND(size_t);
+DECLARE_ERROR_AND(uint8_t);
+DECLARE_ERROR_AND(uint16_t);
+DECLARE_ERROR_AND(uint32_t);
+DECLARE_ERROR_AND(uint64_t);
+
+#endif /* _LIBMISC_ERROR_H_ */