diff options
Diffstat (limited to 'libmisc')
-rw-r--r-- | libmisc/CMakeLists.txt | 7 | ||||
-rw-r--r-- | libmisc/error.c | 26 | ||||
-rw-r--r-- | libmisc/error_generated.c | 186 | ||||
-rwxr-xr-x | libmisc/error_generated.c.gen | 35 | ||||
-rw-r--r-- | libmisc/include/libmisc/error.h | 167 | ||||
-rw-r--r-- | libmisc/include/libmisc/fmt.h | 52 | ||||
-rw-r--r-- | libmisc/include/libmisc/macro.h | 34 | ||||
-rw-r--r-- | libmisc/include/libmisc/obj.h | 74 | ||||
-rw-r--r-- | libmisc/tests/test_assert.c | 1 | ||||
-rw-r--r-- | libmisc/tests/test_log.c | 1 | ||||
-rw-r--r-- | libmisc/tests/test_macro.c | 9 | ||||
-rw-r--r-- | libmisc/tests/test_obj.c | 2 | ||||
-rw-r--r-- | libmisc/tests/test_obj_nest.c | 2 | ||||
-rwxr-xr-x | libmisc/wrap-cc | 186 |
14 files changed, 711 insertions, 71 deletions
diff --git a/libmisc/CMakeLists.txt b/libmisc/CMakeLists.txt index c6405ad..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 @@ -18,6 +20,11 @@ target_sources(libmisc INTERFACE utf8.c ) +target_compile_options(libmisc INTERFACE + -no-integrated-cpp + -wrapper "${CMAKE_CURRENT_SOURCE_DIR}/wrap-cc" +) + add_lib_test(libmisc test_assert) add_lib_test(libmisc test_assert_min) add_lib_test(libmisc test_endian) 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_ */ diff --git a/libmisc/include/libmisc/fmt.h b/libmisc/include/libmisc/fmt.h index 6c04d99..135e48b 100644 --- a/libmisc/include/libmisc/fmt.h +++ b/libmisc/include/libmisc/fmt.h @@ -113,27 +113,27 @@ struct fmt_buf { }; LO_IMPLEMENTATION_H(fmt_dest, struct fmt_buf, fmt_buf); -#define fmt_snprint(buf, n, ...) ({ \ - struct fmt_buf _w = { .dat = buf, .cap = n }; \ - lo_interface fmt_dest w = lo_box_fmt_buf_as_fmt_dest(&_w); \ - fmt_print(w, __VA_ARGS__); \ - if (_w.len < _w.cap) \ - ((char *)_w.dat)[_w.len] = '\0'; \ - _w.len; \ +#define fmt_snprint(buf, n, ...) ({ \ + struct fmt_buf _w = { .dat = buf, .cap = n }; \ + lo_interface fmt_dest w = LO_BOX(fmt_dest, &_w); \ + fmt_print(w, __VA_ARGS__); \ + if (_w.len < _w.cap) \ + ((char *)_w.dat)[_w.len] = '\0'; \ + _w.len; \ }) -#define fmt_asprint(...) ({ \ - struct fmt_buf _w = {}; \ - lo_interface fmt_dest w = lo_box_fmt_buf_as_fmt_dest(&_w); \ - fmt_print(w, __VA_ARGS__); \ - while (_w.cap <= _w.len) { \ - _w.cap = _w.len + 1; \ - _w.len = 0; \ - _w.dat = realloc(_w.dat, _w.cap); \ - fmt_print(w, __VA_ARGS__); \ - } \ - ((char *)_w.dat)[_w.len] = '\0'; \ - _w.dat; \ +#define fmt_asprint(...) ({ \ + struct fmt_buf _w = {}; \ + lo_interface fmt_dest w = LO_BOX(fmt_dest, &_w); \ + fmt_print(w, __VA_ARGS__); \ + while (_w.cap <= _w.len) { \ + _w.cap = _w.len + 1; \ + _w.len = 0; \ + _w.dat = realloc(_w.dat, _w.cap); \ + fmt_print(w, __VA_ARGS__); \ + } \ + ((char *)_w.dat)[_w.len] = '\0'; \ + _w.dat; \ }) /* justify ********************************************************************/ @@ -145,13 +145,13 @@ LO_IMPLEMENTATION_H(fmt_dest, struct fmt_buf, fmt_buf); fmt_print_byte(w, fillchar); \ } while (0) -#define fmt_print_rjust(w, width, fillchar, ...) do { \ - struct fmt_buf _discard = {}; \ - lo_interface fmt_dest discard = lo_box_fmt_buf_as_fmt_dest(&_discard); \ - fmt_print2(discard, __VA_ARGS__); \ - while (_discard.len++ < width) \ - fmt_print_byte(w, fillchar); \ - fmt_print2(w, __VA_ARGS__); \ +#define fmt_print_rjust(w, width, fillchar, ...) do { \ + struct fmt_buf _discard = {}; \ + lo_interface fmt_dest discard = LO_BOX2(fmt_dest, &_discard); \ + fmt_print2(discard, __VA_ARGS__); \ + while (_discard.len++ < width) \ + fmt_print_byte(w, fillchar); \ + fmt_print2(w, __VA_ARGS__); \ } while (0) void fmt_print_base16_u8_(lo_interface fmt_dest w, uint8_t x); diff --git a/libmisc/include/libmisc/macro.h b/libmisc/include/libmisc/macro.h index a2d4264..48f52e5 100644 --- a/libmisc/include/libmisc/macro.h +++ b/libmisc/include/libmisc/macro.h @@ -9,6 +9,8 @@ #include <libmisc/assert.h> +/* C: syntax ******************************************************************/ + #define LM_FORCE_SEMICOLON static_assert(1, "force semicolon") #define LM_PARTIAL_SWITCH(VAL) \ @@ -17,14 +19,14 @@ switch (VAL) \ _Pragma("GCC diagnostic pop") \ -/* for function definitions */ +/* C: function definitions ****************************************************/ #define LM_UNUSED(argname) #define LM_ALWAYS_INLINE [[gnu::always_inline]] inline #define LM_NEVER_INLINE [[gnu::noinline]] #define LM_FLATTEN [[gnu::flatten]] -/* types */ +/* C: types *******************************************************************/ /* If it's a pointer instead of an array, then typeof(&ptr[0]) == typeof(ptr) */ #define _LM_IS_ARRAY(ary) (!__builtin_types_compatible_p(typeof(&(ary)[0]), typeof(ary))) @@ -38,7 +40,7 @@ : NULL; \ }) -/* numeric */ +/* C: numeric *****************************************************************/ #define LM_CEILDIV(n, d) ( ((n)+(d)-1) / (d) ) /** Return ceil(n/d) */ #define LM_ROUND_UP(n, d) ( LM_CEILDIV(n, d) * (d) ) /** Return `n` rounded up to the nearest multiple of `d` */ @@ -46,12 +48,12 @@ #define LM_NEXT_POWER_OF_2(x) ( (x) ? 1ULL<<((sizeof(unsigned long long)*8)-__builtin_clzll(x)) : 1) /** Return the lowest power of 2 that is > x */ #define LM_FLOORLOG2(x) ((sizeof(unsigned long long)*8)-__builtin_clzll(x)-1) /** Return floor(log_2(x) */ -/* strings */ +/* CPP: strings ***************************************************************/ #define LM_STR(x) #x #define LM_STR_(x) LM_STR(x) -/* token pasting */ +/* CPP: token pasting *********************************************************/ #define LM_CAT2(a, b) a ## b #define LM_CAT3(a, b, c) a ## b ## c @@ -59,7 +61,7 @@ #define LM_CAT2_(a, b) LM_CAT2(a, b) #define LM_CAT3_(a, b, c) LM_CAT3(a, b, c) -/* macro arguments */ +/* CPP: macro arguments *******************************************************/ #define LM_FIRST(a, ...) a #define LM_SECOND(a, b, ...) b @@ -70,7 +72,7 @@ #define LM_EAT(...) #define LM_EXPAND(...) __VA_ARGS__ -/* conditionals */ +/* CPP: conditionals **********************************************************/ #define LM_T xxTxx #define LM_F xxFxx @@ -82,7 +84,7 @@ #define _LM_IF__xxTxx(...) __VA_ARGS__ LM_EAT #define _LM_IF__xxFxx(...) LM_EXPAND -/* tuples */ +/* CPP: tuples ****************************************************************/ #define LM_IS_TUPLE(x) LM_IS_SENTINEL(_LM_IS_TUPLE x) #define _LM_IS_TUPLE(...) LM_SENTINEL() @@ -112,7 +114,7 @@ #define LM_TUPLES_HEAD(tuples) LM_EXPAND(LM_FIRST LM_EAT() (_LM_TUPLES_COMMA tuples)) #define LM_TUPLES_TAIL(tuples) LM_EAT tuples -/* iteration */ +/* CPP: iteration *************************************************************/ /* The desire to support a high number of iterations is in competition * with the desire for short compile times. 16 is the lowest @@ -168,4 +170,18 @@ /** The same as LM_FOREACH_TUPLE(), but callable from inside of LM_FOREACH_TUPLE(). */ #define LM_FOREACH_TUPLE2(...) _LM_DEFER2(_LM_FOREACH_TUPLE_indirect)()(__VA_ARGS__) +/* CPP: wrap-cc extensions ****************************************************/ + +#ifdef __LIBMISC_ENHANCED_CPP__ +/** + * `LM_DEFAPPEND(macro, val)` is like `#define macro val`, but can (1) + * be used from inside of a macro, and (2) appends to a value if it is + * already defined with LM_DEFAPPEND. There are lots of edge-cases, + * don't get cute. + */ +#define LM_DEFAPPEND(macro, ...) __xx__LM_DEFAPPEND__xx__(#macro, #__VA_ARGS__) LM_FORCE_SEMICOLON +#define LM_DEFAPPEND_(macro, ...) _LM_DEFAPPEND_(#macro, __VA_ARGS__) +#define _LM_DEFAPPEND_(macrostr, ...) __xx__LM_DEFAPPEND__xx__(macrostr, #__VA_ARGS__) LM_FORCE_SEMICOLON +#endif + #endif /* _LIBMISC_MACRO_H_ */ diff --git a/libmisc/include/libmisc/obj.h b/libmisc/include/libmisc/obj.h index 6645db7..6afa391 100644 --- a/libmisc/include/libmisc/obj.h +++ b/libmisc/include/libmisc/obj.h @@ -30,10 +30,6 @@ * * Use `lo_interface {iface_name}` as the type of this interface; it * should not be a pointer type. - * - * If there are any LO_NEST interfaces, this will define a - * `lo_box_{iface_name}_as_{wrapped_iface_name}(obj)` function for - * each. */ #define LO_NEST(_ARG_child_iface_name) \ (lo_nest, _ARG_child_iface_name) @@ -50,28 +46,48 @@ }; \ LM_FOREACH_TUPLE(_ARG_iface_name##_LO_IFACE, \ _LO_IFACE_PROTO, _ARG_iface_name) \ - extern int LM_CAT2_(_HIDDEN_BOGUS_, __COUNTER__) + LM_FORCE_SEMICOLON -#define _LO_IFACE_VTABLE(_tuple_typ, ...) _LO_IFACE_VTABLE_##_tuple_typ(__VA_ARGS__) -#define _LO_IFACE_VTABLE_lo_nest(_ARG_child_iface_name) const struct _lo_##_ARG_child_iface_name##_vtable *_lo_##_ARG_child_iface_name##_vtable; LM_FOREACH_TUPLE2(_ARG_child_iface_name##_LO_IFACE, _LO_IFACE_VTABLE2) -#define _LO_IFACE_VTABLE_lo_func(_ARG_ret_type, _ARG_func_name, ...) _ARG_ret_type (*_ARG_func_name)(void * __VA_OPT__(,) __VA_ARGS__); +#define _LO_IFACE_VTABLE(_tuple_typ, ...) _LO_IFACE_VTABLE_##_tuple_typ(__VA_ARGS__) +#define _LO_IFACE_VTABLE_lo_nest(_ARG_child_iface_name) \ + const struct _lo_##_ARG_child_iface_name##_vtable *_lo_##_ARG_child_iface_name##_vtable; \ + LM_FOREACH_TUPLE2(_ARG_child_iface_name##_LO_IFACE, _LO_IFACE_VTABLE2) +#define _LO_IFACE_VTABLE_lo_func(_ARG_ret_type, _ARG_func_name, ...) \ + _ARG_ret_type (*_ARG_func_name)(void * __VA_OPT__(,) __VA_ARGS__); #define _LO_IFACE_VTABLE_indirect() _LO_IFACE_VTABLE #define _LO_IFACE_VTABLE2(...) _LM_DEFER2(_LO_IFACE_VTABLE_indirect)()(__VA_ARGS__) -#define _LO_IFACE_PROTO(_ARG_iface_name, _tuple_typ, ...) _LO_IFACE_PROTO_##_tuple_typ(_ARG_iface_name, __VA_ARGS__) -#define _LO_IFACE_PROTO_lo_nest(_ARG_iface_name, _ARG_child_iface_name) \ - LM_ALWAYS_INLINE static lo_interface _ARG_child_iface_name \ - box_##_ARG_iface_name##_as_##_ARG_child_iface_name(lo_interface _ARG_iface_name obj) { \ - return (lo_interface _ARG_child_iface_name){ \ - .self = obj.self, \ - .vtable = obj.vtable->_lo_##_ARG_child_iface_name##_vtable, \ - }; \ - } +#define _LO_IFACE_PROTO(_ARG_iface_name, _tuple_typ, ...) _LO_IFACE_PROTO_##_tuple_typ(_ARG_iface_name, __VA_ARGS__) +#define _LO_IFACE_PROTO_lo_nest(_ARG_iface_name, _ARG_child_iface_name) \ + LM_DEFAPPEND(_LO_REGISTRY_##_ARG_child_iface_name, \ + (lo_interface _ARG_iface_name, _LO_BOX_##_ARG_iface_name##_as_##_ARG_child_iface_name)); \ + LM_DEFAPPEND(_LO_BOX_##_ARG_iface_name##_as_##_ARG_child_iface_name(obj), \ + { .self = obj.self, .vtable = obj.vtable->_lo_##_ARG_child_iface_name##_vtable }); #define _LO_IFACE_PROTO_lo_func(_ARG_iface_name, _ARG_ret_type, _ARG_func_name, ...) \ /* empty */ /** + * `LO_BOX(iface_name, obj)` boxes `obj` as a `lo_interface + * iface_name`. `obj` must be one of: + * + * - A pointer to a value that implements `lo_interface iface_name` + * - An already-boxed instance of `lo_interface iface_name` + * - An already-boxed instance of another interface that + * `iface_name` inherits from. + */ +#define LO_BOX(_ARG_iface_name, obj) _Generic((obj), \ + lo_interface _ARG_iface_name: obj \ + LM_FOREACH_TUPLE(_LO_REGISTRY_##_ARG_iface_name, \ + _LO_BOX, _ARG_iface_name, obj)) +#define LO_BOX2(_ARG_iface_name, obj) _Generic((obj), \ + lo_interface _ARG_iface_name: obj \ + LM_FOREACH_TUPLE2(_LO_REGISTRY_##_ARG_iface_name, \ + _LO_BOX, _ARG_iface_name, obj)) +#define _LO_BOX(_ARG_iface_name, obj, typ, boxfn) \ + , typ: (lo_interface _ARG_iface_name)boxfn(obj) + +/** * `LO_NULL(iface_name)` is the null/nil/zero value for `lo_interface {iface_name}`. */ #define LO_NULL(_ARG_iface_name) ((lo_interface _ARG_iface_name){}) @@ -99,23 +115,17 @@ * file to declare that `{impl_type}` implements the `{iface_name}` * interface with functions named `{impl_name}_{method_name}`. * - * This will also define a `lo_box_{impl_name}_as_{iface_name}(obj)` - * function. - * * You must also call the LO_IMPLEMENTATION_C in a single .c file. */ -#define LO_IMPLEMENTATION_H(_ARG_iface_name, _ARG_impl_type, _ARG_impl_name) \ - /* Vtable. */ \ - extern const struct _lo_##_ARG_iface_name##_vtable \ - _lo_##_ARG_impl_name##_##_ARG_iface_name##_vtable; \ - /* Boxing. */ \ - LM_ALWAYS_INLINE static lo_interface _ARG_iface_name \ - lo_box_##_ARG_impl_name##_as_##_ARG_iface_name(_ARG_impl_type *self) { \ - return (lo_interface _ARG_iface_name){ \ - .self = self, \ - .vtable = &_lo_##_ARG_impl_name##_##_ARG_iface_name##_vtable, \ - }; \ - } \ +#define LO_IMPLEMENTATION_H(_ARG_iface_name, _ARG_impl_type, _ARG_impl_name) \ + /* Vtable. */ \ + extern const struct _lo_##_ARG_iface_name##_vtable \ + _lo_##_ARG_impl_name##_##_ARG_iface_name##_vtable; \ + /* Boxing. */ \ + LM_DEFAPPEND(_LO_REGISTRY_##_ARG_iface_name, \ + (_ARG_impl_type *, _LO_BOX_##_ARG_impl_name##_as_##_ARG_iface_name)); \ + LM_DEFAPPEND(_LO_BOX_##_ARG_impl_name##_as_##_ARG_iface_name(obj), \ + { .self = obj, .vtable = &_lo_##_ARG_impl_name##_##_ARG_iface_name##_vtable }); \ LM_FORCE_SEMICOLON /** diff --git a/libmisc/tests/test_assert.c b/libmisc/tests/test_assert.c index cdbc567..290b073 100644 --- a/libmisc/tests/test_assert.c +++ b/libmisc/tests/test_assert.c @@ -5,7 +5,6 @@ */ #include <setjmp.h> -#include <stdarg.h> /* for va_list, va_start(), va_end() */ #include <stdlib.h> #include <string.h> diff --git a/libmisc/tests/test_log.c b/libmisc/tests/test_log.c index ee762e2..6e7cdfd 100644 --- a/libmisc/tests/test_log.c +++ b/libmisc/tests/test_log.c @@ -4,7 +4,6 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -#include <stdarg.h> /* for va_list */ #include <stdio.h> /* for vsnprintf() */ #include <stdlib.h> /* for realloc(), free() */ #include <string.h> /* for strlen(), strcmp() */ diff --git a/libmisc/tests/test_macro.c b/libmisc/tests/test_macro.c index 5157820..6810005 100644 --- a/libmisc/tests/test_macro.c +++ b/libmisc/tests/test_macro.c @@ -178,5 +178,14 @@ int main() { free(act_suffix); } + printf("== LM_DEFAPPEND ===========================================\n"); + LM_DEFAPPEND(mylist, a); + LM_DEFAPPEND(mylist, + b); + { + const char *str = LM_STR_(mylist); + test_assert(strcmp(str, "a b") == 0); + } + return 0; } diff --git a/libmisc/tests/test_obj.c b/libmisc/tests/test_obj.c index 687ad4e..a13b8c9 100644 --- a/libmisc/tests/test_obj.c +++ b/libmisc/tests/test_obj.c @@ -53,7 +53,7 @@ int main() { struct myclass obj = { .a = MAGIC1, }; - lo_interface frobber iface = lo_box_myclass_as_frobber(&obj); + lo_interface frobber iface = LO_BOX(frobber, &obj); test_assert(LO_CALL(iface, frob) == MAGIC1); test_assert(LO_CALL(iface, frob1, MAGIC2) == MAGIC2); LO_CALL(iface, frob0); diff --git a/libmisc/tests/test_obj_nest.c b/libmisc/tests/test_obj_nest.c index d5e563e..ba5ac37 100644 --- a/libmisc/tests/test_obj_nest.c +++ b/libmisc/tests/test_obj_nest.c @@ -64,7 +64,7 @@ static ssize_t myclass_write(struct myclass *self, void *buf, size_t count) { int main() { struct myclass _obj = {}; - lo_interface read_writer obj = lo_box_myclass_as_read_writer(&_obj); + lo_interface read_writer obj = LO_BOX(read_writer, &_obj); test_assert(LO_CALL(obj, write, "Hello", 6) == 6); char buf[6] = {}; test_assert(LO_CALL(obj, read, buf, 3) == 3); diff --git a/libmisc/wrap-cc b/libmisc/wrap-cc new file mode 100755 index 0000000..e7a0b91 --- /dev/null +++ b/libmisc/wrap-cc @@ -0,0 +1,186 @@ +#!/usr/bin/env python3 +# libmisc/wrap-cc - Wrapper around GCC to enhance the preprocessor +# +# Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com> +# SPDX-License-Identifier: AGPL-3.0-or-later + +import os +import subprocess +import sys +import typing + + +def scan_tuple( + text: str, beg: int, on_part: typing.Callable[[str], None] | None = None +) -> int: + assert text[beg] == "(" + pos = beg + 1 + arg_start = pos + parens = 1 + instring = False + while parens: + c = text[pos] + if instring: + match c: + case "\\": + pos += 1 + case '"': + instring = False + else: + match c: + case "(": + parens += 1 + case ")": + parens -= 1 + if on_part and parens == 0 and text[beg + 1 : pos].strip(): + on_part(text[arg_start:pos]) + case ",": + if on_part and parens == 1: + on_part(text[arg_start:pos]) + arg_start = pos + 1 + case '"': + instring = True + pos += 1 + assert text[pos - 1] == ")" + return pos - 1 + + +def unquote(cstr: str) -> str: + assert len(cstr) >= 2 and cstr[0] == '"' and cstr[-1] == '"' + cstr = cstr[1:-1] + out = "" + while cstr: + if cstr[0] == "\\": + match cstr[1]: + case "n": + out += "\n" + cstr = cstr[2:] + case "\\": + out += "\\" + cstr = cstr[2:] + case '"': + out += '"' + cstr = cstr[2:] + else: + out += cstr[0] + cstr = cstr[1:] + return out + + +def preprocess(all_args: list[str]) -> typing.NoReturn: + # argparse ################################################################# + _args = all_args + + def shift(n: int) -> list[str]: + nonlocal _args + ret = _args[:n] + _args = _args[n:] + return ret + + arg0 = shift(1)[0] + common_flags: list[str] = [] + output_flags: list[str] = [] + positional: list[str] = [] + while _args: + if len(_args[0]) > 2 and _args[0][0] == "-" and _args[0][1] in "IDU": + _args = [_args[0][:2], _args[0][2:], *_args[1:]] + match _args[0]: + # Mode + case "-E" | "-quiet": + common_flags += shift(1) + case "-lang-asm": + os.execvp(all_args[0], all_args) + # Search path + case "-I" | "-imultilib" | "-isystem": + common_flags += shift(2) + # Define/Undefine + case "-D" | "-U": + common_flags += shift(2) + # Optimization + case "-O0" | "-O1" | "-O2" | "-O3" | "-Os" | "-Ofast" | "-Og" | "-Oz": + common_flags += shift(1) + case "-g": + common_flags += shift(1) + # Output files + case "-MD" | "-MF" | "-MT" | "-dumpbase" | "-dumpbase-ext": + output_flags += shift(2) + case "-o": + output_flags += shift(2) + # Other + case _: + if _args[0].startswith("-"): + if _args[0].startswith("-std="): + common_flags += shift(1) + elif _args[0].startswith("-m"): + common_flags += shift(1) + elif _args[0].startswith("-f"): + common_flags += shift(1) + elif _args[0].startswith("-W"): + common_flags += shift(1) + else: + raise ValueError(f"unknown flag: {_args!r}") + else: + positional += shift(1) + if len(positional) != 1: + raise ValueError("expected 1 input file") + infile = positional[0] + + # enhance ################################################################## + + common_flags += ["-D", "__LIBMISC_ENHANCED_CPP__"] + + text = subprocess.run( + [arg0, *common_flags, infile], + stdin=subprocess.DEVNULL, + stdout=subprocess.PIPE, + stderr=sys.stderr, + check=True, + text=True, + ).stdout + + macros: dict[str, str] = {} + + marker = "__xx__LM_DEFAPPEND__xx__" + pos = 0 + while (marker_beg := text.find(marker, pos)) >= 0: + args: list[str] = [] + + def add_arg(arg: str) -> None: + nonlocal args + args.append(arg) + + beg_paren = marker_beg + len(marker) + end_paren = scan_tuple(text, beg_paren, add_arg) + + before = text[:marker_beg] + # old = text[marker_beg : end_paren + 1] + after = text[end_paren + 1 :] + + assert len(args) == 2 + k = unquote(args[0].strip()) + v = unquote(args[1].strip()) + if k not in macros: + macros[k] = v + else: + macros[k] += " " + v + + text = before + after + pos = len(before) + + common_flags += ["-D", marker + "=LM_EAT"] + for k, v in macros.items(): + common_flags += ["-D", k + "=" + v] + + # Run, for-real ############################################################ + os.execvp(arg0, [arg0, *common_flags, *output_flags, infile]) + + +def main(all_args: list[str]) -> typing.NoReturn: + if len(all_args) >= 2 and all_args[0].endswith("cc1") and all_args[1] == "-E": + preprocess(all_args) + else: + os.execvp(all_args[0], all_args) + + +if __name__ == "__main__": + main(sys.argv[1:]) |