summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.editorconfig6
-rw-r--r--GNUmakefile38
-rwxr-xr-xbuild-aux/get-dscname36
-rwxr-xr-xbuild-aux/lint-generic62
-rwxr-xr-xbuild-aux/lint-h27
-rwxr-xr-xbuild-aux/lint-src145
-rwxr-xr-xbuild-aux/lint-unknown19
-rwxr-xr-xbuild-aux/valgrind2
-rw-r--r--cmd/sbc_harness/fs_harness_flash_bin.c2
-rw-r--r--cmd/sbc_harness/fs_harness_flash_bin.h1
-rw-r--r--cmd/sbc_harness/fs_harness_uptime_txt.c2
-rw-r--r--cmd/sbc_harness/fs_harness_uptime_txt.h1
-rw-r--r--cmd/sbc_harness/main.c16
-rw-r--r--lib9p/core_gen/c_validate.py2
-rw-r--r--lib9p/core_generated.c12
-rw-r--r--lib9p/core_include/lib9p/core.h38
-rw-r--r--lib9p/srv_include/lib9p/srv.h2
-rw-r--r--lib9p/tests/test_server/fs_flush.c2
-rw-r--r--lib9p/tests/test_server/fs_flush.h1
-rw-r--r--lib9p/tests/test_server/fs_shutdown.c4
-rw-r--r--lib9p/tests/test_server/fs_shutdown.h1
-rw-r--r--lib9p/tests/test_server/fs_whoami.c2
-rw-r--r--lib9p/tests/test_server/fs_whoami.h1
-rw-r--r--lib9p/tests/test_server/main.c14
-rw-r--r--lib9p_util/include/util9p/static.h24
-rw-r--r--lib9p_util/static.c4
-rw-r--r--libdhcp/tests/test_client.c6
-rw-r--r--libhw_cr/host_net.c2
-rw-r--r--libhw_cr/rp2040_hwtimer.c2
-rw-r--r--libhw_cr/w5500.c10
-rw-r--r--libmisc/CMakeLists.txt5
-rw-r--r--libmisc/include/libmisc/fmt.h55
-rw-r--r--libmisc/include/libmisc/macro.h34
-rw-r--r--libmisc/include/libmisc/obj.h74
-rw-r--r--libmisc/tests/test_fmt.c9
-rw-r--r--libmisc/tests/test_macro.c9
-rw-r--r--libmisc/tests/test_obj.c2
-rw-r--r--libmisc/tests/test_obj_nest.c2
-rwxr-xr-xlibmisc/wrap-cc186
39 files changed, 539 insertions, 321 deletions
diff --git a/.editorconfig b/.editorconfig
index 9540302..b7ad057 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -45,13 +45,13 @@ _mode = gitignore
# By specific filename (non-lib9p) #############################################
-[{build-aux/lint-unknown,build-aux/embed-sources.h.gen}]
+[{build-aux/embed-sources.h.gen,build-aux/valgrind}]
_mode = sh
-[{build-aux/lint-{bin,h,generic},build-aux/get-dscname,build-aux/valgrind,build-aux/gcov-prune,libusb/include/libusb/tusb_helpers.h.gen}]
+[{build-aux/lint-{src,bin},build-aux/gcov-prune,libusb/include/libusb/tusb_helpers.h.gen}]
_mode = bash
-[build-aux/stack.c.gen]
+[{build-aux/stack.c.gen,libmisc/wrap-cc}]
_mode = python3
indent_style = space
indent_size = 4
diff --git a/GNUmakefile b/GNUmakefile
index 758c5aa..fa76266 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -125,33 +125,17 @@ build-aux/venv: build-aux/requirements.txt
touch --no-create $@
# `lint` ###############################
-# generic ##########
lint:
- $(MAKE) -k INNER=t $(patsubst sources_%,lint/%,$(filter sources_%,$(.VARIABLES)))
# Only lint binaries if the build scripts pass lint.
- $(MAKE) INNER=t lint/bin
+ $(MAKE) -k INNER=t lint/src
+ $(MAKE) INNER=t lint/bin
+lint/src: $(patsubst sources_%,lint/%,$(filter-out sources_all,$(filter sources_%,$(.VARIABLES))))
lint/bin: build build-aux/lint-bin
./build-aux/lint-bin $(foreach t,$(build_types),build/rp2040-$t/cmd/sbc_harness/sbc_harness.elf)
-lint/all: lint/%: build-aux/lint-generic build-aux/get-dscname
- ./build-aux/lint-generic $(sources_$*)
-lint/unknown: lint/%: build-aux/lint-unknown
- ./build-aux/lint-unknown $(sources_$*)
+lint/unknown lint/c lint/sh lint/bash lint/python3 lint/make lint/cmake lint/gitignore lint/ini lint/9p-idl lint/9p-log lint/markdown lint/pip lint/man-cat: build-aux/lint-src
+ ./build-aux/lint-src $(@F) $(sources_$(@F))
+lint/python3: build-aux/venv
.PHONY: lint lint/%
-# specific #########
-lint/c: lint/%: build-aux/lint-h build-aux/get-dscname
- ./build-aux/lint-h $(filter %.h,$(sources_$*))
-lint/sh lint/bash: lint/%:
- shellcheck $(sources_$*)
- shfmt --diff --case-indent --simplify $(sources_$*)
-lint/python3: lint/%: build-aux/venv
- ./build-aux/venv/bin/mypy --strict --scripts-are-modules $(sources_$*)
- ./build-aux/venv/bin/black --check $(sources_$*)
- ./build-aux/venv/bin/isort --check $(sources_$*)
- ./build-aux/venv/bin/pylint $(sources_$*)
- ! grep -nh 'SPECIAL$$' -- lib9p/core.gen lib9p/core_gen/*.py
- ./build-aux/venv/bin/pytest $(foreach f,$(sources_python3),$(if $(filter test_%.py,$(notdir $f)),$f))
-lint/make lint/cmake lint/gitignore lint/ini lint/9p-idl lint/9p-log lint/markdown lint/pip lint/man-cat: lint/%:
- @: TODO: Write/adopt linters for these file types
# `format` #############################
# generic ##########
@@ -162,9 +146,9 @@ format:
format/c: format/%:
@: TODO: Adopt a C code-formatter
format/sh format/bash: format/%:
- shfmt --write --case-indent --simplify $(sources_$*)
-format/python3: format/%: ./build-aux/venv
- ./build-aux/venv/bin/black $(sources_$*)
- ./build-aux/venv/bin/isort $(sources_$*)
-format/make format/cmake format/gitignore format/ini format/9p-idl format/9p-log format/markdown format/pip format/man-cat: format/%:
+ shfmt --write --case-indent --simplify $(sources_$(@F))
+format/python3: ./build-aux/venv
+ ./build-aux/venv/bin/black $(sources_$(@F))
+ ./build-aux/venv/bin/isort $(sources_$(@F))
+format/make format/cmake format/gitignore format/ini format/9p-idl format/9p-log format/markdown format/pip format/man-cat:
@: TODO: Write/adopt formatters for these file types
diff --git a/build-aux/get-dscname b/build-aux/get-dscname
deleted file mode 100755
index 34a1b08..0000000
--- a/build-aux/get-dscname
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/usr/bin/env bash
-# build-aux/get-dscname - Get a file's self-described filename
-#
-# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
-# SPDX-License-Identifier: AGPL-3.0-or-later
-
-if [ $# -ne 1 ]; then
- echo "$0: expected exactly 1 argument"
- exit 2
-fi
-
-if [[ $1 == */Documentation/* ]] && [[ "$(sed 1q -- "$1")" == 'NAME' ]]; then
- sed -n '
- 2{
- s,[/.],_,g;
- s,^\s*_,Documentation/,;
- s,$,.txt,;
-
- p;
- q;
- }
- ' -- "$1"
-else
- sed -n '
- 1,3{
- /^\#!/d;
- /^<!--$/d;
- /-\*- .* -\*-/d;
- s,[/*\# ]*,,;
- s/ - .*//;
-
- p;
- q;
- }
- ' -- "$1"
-fi
diff --git a/build-aux/lint-generic b/build-aux/lint-generic
deleted file mode 100755
index 9bf88dd..0000000
--- a/build-aux/lint-generic
+++ /dev/null
@@ -1,62 +0,0 @@
-#!/usr/bin/env bash
-# build-aux/lint-generic - Non-language-specific lint checks
-#
-# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
-# SPDX-License-Identifier: AGPL-3.0-or-later
-
-RED=$(tput setaf 1)
-RESET=$(tput sgr0)
-
-err() {
- printf "${RED}%s${RESET}: %s\n" "$1" "$2" >&2
- r=1
-}
-
-r=0
-for filename in "$@"; do
- # File header ##########################################################
-
- shebang="$(sed -n '1{/^#!/p;}' "$filename")"
- if [[ -x $filename && (-z $shebang || $shebang == '#!/hint/'*) ]]; then
- err "$filename" 'is executable but does not have a shebang'
- elif [[ (-n $shebang && $shebang != '#!/hint/'*) && ! -x $filename ]]; then
- err "$filename" 'has a shebang but is not executable'
- fi
- case "$shebang" in
- '') : ;;
- '#!/bin/sh') : ;;
- '#!/usr/bin/env bash') : ;;
- '#!/usr/bin/env python3') : ;;
- *) err "$filename" 'has an unrecognized shebang' ;;
- esac
-
- if ! grep -E -q 'Copyright \(C\) 202[4-9]((-|, )202[5-9])* Luke T. Shumaker' "$filename"; then
- err "$filename" 'is missing a copyright statement'
- fi
- if test -e .git && ! git diff --quiet milestone/2025-01-01 HEAD -- "$filename"; then
- if ! grep -E -q 'Copyright \(C\) .*2025 Luke T. Shumaker' "$filename"; then
- err "$filename" 'has an outdated copyright statement'
- fi
- fi
- if ! grep -q '\sSPDX-License-Identifier[:] ' "$filename"; then
- err "$filename" 'is missing an SPDX-License-Identifier'
- fi
-
- dscname_act=$(./build-aux/get-dscname "$filename")
- dscname_exp=$(echo "$filename" | sed \
- -e 's,.*/config/,,' \
- -e 's,.*/config\.h$,config.h,' \
- -e 's,.*include/,,' \
- -e 's,.*static/,,' \
- -e 's/\.wip$//')
- if [ "$dscname_act" != "$dscname_exp" ] && [ "cmd/$dscname_act" != "$dscname_exp" ]; then
- err "$filename" "self-identifies as $dscname_act (expected $dscname_exp)"
- fi
-
- # File body ############################################################
-
- if grep -n --color=auto "$(printf '\\S\t')" "$filename"; then
- err "$filename" 'uses tabs for alignment'
- fi
-done
-exit $r
diff --git a/build-aux/lint-h b/build-aux/lint-h
deleted file mode 100755
index 7459032..0000000
--- a/build-aux/lint-h
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/usr/bin/env bash
-# build-aux/lint-h - Lint checks for C header files
-#
-# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
-# SPDX-License-Identifier: AGPL-3.0-or-later
-
-RED=$(tput setaf 1)
-RESET=$(tput sgr0)
-
-err() {
- printf "${RED}%s${RESET}: %s\n" "$1" "$2" >&2
- r=1
-}
-
-r=0
-for filename in "$@"; do
- dscname=$(./build-aux/get-dscname "$filename")
- guard=${dscname//'/'/'_'}
- guard=${guard//'.'/'_'}
- guard="_${guard^^}_"
- if ! { grep -Fxq "#ifndef ${guard}" "$filename" &&
- grep -Fxq "#define ${guard}" "$filename" &&
- grep -Fxq "#endif /* ${guard} */" "$filename"; }; then
- err "$filename" "does not have ${guard} guard"
- fi
-done
-exit $r
diff --git a/build-aux/lint-src b/build-aux/lint-src
new file mode 100755
index 0000000..033340d
--- /dev/null
+++ b/build-aux/lint-src
@@ -0,0 +1,145 @@
+#!/usr/bin/env bash
+# build-aux/lint-src - Lint checks for source files
+#
+# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+RED=$(tput setaf 1)
+RESET=$(tput sgr0)
+
+err() {
+ printf "${RED}%s${RESET}: %s\n" "$1" "$2" >&2
+ r=1
+}
+
+get-dscname() {
+ if [[ $1 == */Documentation/* ]] && [[ "$(sed 1q -- "$1")" == 'NAME' ]]; then
+ sed -n '
+ 2{
+ s,[/.],_,g;
+ s,^\s*_,Documentation/,;
+ s,$,.txt,;
+
+ p;
+ q;
+ }
+ ' -- "$1"
+ else
+ sed -n '
+ 1,3{
+ /^\#!/d;
+ /^<!--$/d;
+ /-\*- .* -\*-/d;
+ s,[/*\# ]*,,;
+ s/ - .*//;
+
+ p;
+ q;
+ }
+ ' -- "$1"
+ fi
+}
+
+{
+ filetype=$1
+ filenames=("${@:2}")
+
+ r=0
+ for filename in "${filenames[@]}"; do
+ # File header ##########################################################
+
+ shebang="$(sed -n '1{/^#!/p;}' "$filename")"
+ if [[ -x $filename && (-z $shebang || $shebang == '#!/hint/'*) ]]; then
+ err "$filename" 'is executable but does not have a shebang'
+ elif [[ (-n $shebang && $shebang != '#!/hint/'*) && ! -x $filename ]]; then
+ err "$filename" 'has a shebang but is not executable'
+ fi
+ case "$shebang" in
+ '') : ;;
+ '#!/bin/sh') : ;;
+ '#!/usr/bin/env bash') : ;;
+ '#!/usr/bin/env python3') : ;;
+ *) err "$filename" 'has an unrecognized shebang' ;;
+ esac
+ if [[ -n $shebang && $shebang != */"$filetype" && $shebang != *' '"$filetype" ]]; then
+ err "$filename" "wrong shebang for $filetype"
+ fi
+
+ if ! grep -E -q 'Copyright \(C\) 202[4-9]((-|, )202[5-9])* Luke T. Shumaker' "$filename"; then
+ err "$filename" 'is missing a copyright statement'
+ fi
+ if test -e .git && ! git diff --quiet milestone/2025-01-01 HEAD -- "$filename"; then
+ if ! grep -E -q 'Copyright \(C\) .*2025 Luke T. Shumaker' "$filename"; then
+ err "$filename" 'has an outdated copyright statement'
+ fi
+ fi
+ if ! grep -q '\sSPDX-License-Identifier[:] ' "$filename"; then
+ err "$filename" 'is missing an SPDX-License-Identifier'
+ fi
+
+ dscname_act=$(get-dscname "$filename")
+ dscname_exp=$(echo "$filename" | sed \
+ -e 's,.*/config/,,' \
+ -e 's,.*/config\.h$,config.h,' \
+ -e 's,.*include/,,' \
+ -e 's,.*static/,,' \
+ -e 's/\.wip$//')
+ if [ "$dscname_act" != "$dscname_exp" ] && [ "cmd/$dscname_act" != "$dscname_exp" ]; then
+ err "$filename" "self-identifies as $dscname_act (expected $dscname_exp)"
+ fi
+
+ # File body ############################################################
+
+ if grep -n --color=auto "$(printf '\\S\t')" "$filename"; then
+ err "$filename" 'uses tabs for alignment'
+ fi
+ done
+ case "$filetype" in
+ unknown)
+ for filename in "${filenames[@]}"; do
+ err "$filename" 'cannot lint unknown file type'
+ done
+ ;;
+ c)
+ for filename in "${filenames[@]}"; do
+ if [[ $filename == *.h ]]; then
+ dscname=$(get-dscname "$filename")
+ guard=${dscname//'/'/'_'}
+ guard=${guard//'.'/'_'}
+ guard="_${guard^^}_"
+ if ! { grep -Fxq "#ifndef ${guard}" "$filename" &&
+ grep -Fxq "#define ${guard}" "$filename" &&
+ grep -Fxq "#endif /* ${guard} */" "$filename"; }; then
+ err "$filename" "does not have ${guard} guard"
+ fi
+ fi
+ done
+ ;;
+ sh | bash)
+ shellcheck "${filenames[@]}" || exit $?
+ shfmt --diff --case-indent --simplify "${filenames[@]}" || exit $?
+ ;;
+ python3)
+ ./build-aux/venv/bin/mypy --strict --scripts-are-modules "${filenames[@]}" || exit $?
+ ./build-aux/venv/bin/black --check "${filenames[@]}" || exit $?
+ ./build-aux/venv/bin/isort --check "${filenames[@]}" || exit $?
+ ./build-aux/venv/bin/pylint "${filenames[@]}" || exit $?
+ if grep -nh 'SPECIAL$$' -- lib9p/core.gen lib9p/core_gen/*.py; then exit 1; fi
+ testfiles=()
+ for filename in "${filenames[@]}"; do
+ if [[ ${filename##*/} == test_*.py ]]; then
+ testfiles+=("$filename")
+ fi
+ done
+ ./build-aux/venv/bin/pytest "${testfiles[@]}" || exit $?
+ ;;
+ make | cmake | gitignore | ini | 9p-idl | 9p-log | markdown | pip | man-cat)
+ # TODO: Write/adopt linters for these file types
+ :
+ ;;
+ *)
+ err "$0" "unknown filetype: ${filetype}"
+ ;;
+ esac
+ exit $r
+}
diff --git a/build-aux/lint-unknown b/build-aux/lint-unknown
deleted file mode 100755
index bc23a81..0000000
--- a/build-aux/lint-unknown
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/sh
-# build-aux/lint-unknown - Lint checks for unknown files
-#
-# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
-# SPDX-License-Identifier: AGPL-3.0-or-later
-
-RED=$(tput setaf 1)
-RESET=$(tput sgr0)
-
-err() {
- printf "${RED}%s${RESET}: %s\n" "$1" "$2" >&2
- r=1
-}
-
-r=0
-for filename in "$@"; do
- err "$filename" 'cannot lint unknown file type'
-done
-exit $r
diff --git a/build-aux/valgrind b/build-aux/valgrind
index 8fe7c6e..0700e4d 100755
--- a/build-aux/valgrind
+++ b/build-aux/valgrind
@@ -1,4 +1,4 @@
-#!/usr/bin/env bash
+#!/bin/sh
# build-aux/valgrind - Wrapper around valgrind to keep flags consistent
#
# Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
diff --git a/cmd/sbc_harness/fs_harness_flash_bin.c b/cmd/sbc_harness/fs_harness_flash_bin.c
index 5e6f49c..8bd144e 100644
--- a/cmd/sbc_harness/fs_harness_flash_bin.c
+++ b/cmd/sbc_harness/fs_harness_flash_bin.c
@@ -201,7 +201,7 @@ static lo_interface lib9p_srv_fio flash_file_fopen(struct flash_file *self, stru
self->wbuf.ok = false;
}
- return lo_box_flash_file_as_lib9p_srv_fio(self);
+ return LO_BOX(lib9p_srv_fio, self);
}
/* srv_fio ********************************************************************/
diff --git a/cmd/sbc_harness/fs_harness_flash_bin.h b/cmd/sbc_harness/fs_harness_flash_bin.h
index 36382be..148a446 100644
--- a/cmd/sbc_harness/fs_harness_flash_bin.h
+++ b/cmd/sbc_harness/fs_harness_flash_bin.h
@@ -25,6 +25,5 @@ struct flash_file {
END_PRIVATE(FS_HARNESS_FLASH_BIN);
};
LO_IMPLEMENTATION_H(lib9p_srv_file, struct flash_file, flash_file);
-#define lo_box_flash_file_as_lib9p_srv_file(obj) util9p_box(flash_file, obj)
#endif /* _SBC_HARNESS_FS_HARNESS_FLASH_BIN_H_ */
diff --git a/cmd/sbc_harness/fs_harness_uptime_txt.c b/cmd/sbc_harness/fs_harness_uptime_txt.c
index 339b410..23ac9d4 100644
--- a/cmd/sbc_harness/fs_harness_uptime_txt.c
+++ b/cmd/sbc_harness/fs_harness_uptime_txt.c
@@ -89,7 +89,7 @@ static lo_interface lib9p_srv_fio uptime_file_fopen(struct uptime_file *self, st
ret->parent = self;
ret->buf_len = 0;
- return lo_box_uptime_fio_as_lib9p_srv_fio(ret);
+ return LO_BOX(lib9p_srv_fio, ret);
}
/* srv_fio ********************************************************************/
diff --git a/cmd/sbc_harness/fs_harness_uptime_txt.h b/cmd/sbc_harness/fs_harness_uptime_txt.h
index 7bf2945..c575580 100644
--- a/cmd/sbc_harness/fs_harness_uptime_txt.h
+++ b/cmd/sbc_harness/fs_harness_uptime_txt.h
@@ -14,6 +14,5 @@ struct uptime_file {
uint64_t pathnum;
};
LO_IMPLEMENTATION_H(lib9p_srv_file, struct uptime_file, uptime_file);
-#define lo_box_uptime_file_as_lib9p_srv_file(obj) util9p_box(uptime_file, obj)
#endif /* _SBC_HARNESS_FS_HARNESS_UPTIME_TXT_H_ */
diff --git a/cmd/sbc_harness/main.c b/cmd/sbc_harness/main.c
index 71c39b2..d32b775 100644
--- a/cmd/sbc_harness/main.c
+++ b/cmd/sbc_harness/main.c
@@ -54,11 +54,11 @@ enum { PATH_BASE = __COUNTER__ };
#define STATIC_FILE(STRNAME, ...) UTIL9P_STATIC_FILE(PATH_COUNTER, STRNAME, __VA_ARGS__)
#define STATIC_DIR(STRNAME, ...) UTIL9P_STATIC_DIR(PATH_COUNTER, STRNAME, __VA_ARGS__)
-#define API_FILE(STRNAME, SYMNAME, ...) \
- lo_box_##SYMNAME##_file_as_lib9p_srv_file(&((struct SYMNAME##_file){ \
- .name = STRNAME, \
- .pathnum = PATH_COUNTER \
- __VA_OPT__(,) __VA_ARGS__ \
+#define API_FILE(STRNAME, SYMNAME, ...) \
+ LO_BOX(lib9p_srv_file, &((struct SYMNAME##_file){ \
+ .name = STRNAME, \
+ .pathnum = PATH_COUNTER \
+ __VA_OPT__(,) __VA_ARGS__ \
}))
static struct lib9p_srv_file root =
@@ -149,7 +149,7 @@ struct {
static COROUTINE dhcp_cr(void *) {
cr_begin();
- dhcp_client_main(lo_box_w5500_if_as_net_iface(&globals.dev_w5500), "harness");
+ dhcp_client_main(LO_BOX(net_iface, &globals.dev_w5500), "harness");
cr_end();
}
@@ -157,7 +157,7 @@ static COROUTINE dhcp_cr(void *) {
static COROUTINE read9p_cr(void *) {
cr_begin();
- lo_interface net_iface iface = lo_box_w5500_if_as_net_iface(&globals.dev_w5500);
+ lo_interface net_iface iface = LO_BOX(net_iface, &globals.dev_w5500);
lo_interface net_stream_listener listener = LO_CALL(iface, tcp_listen, LIB9P_DEFAULT_PORT_9FS);
lib9p_srv_accept_and_read_loop(&globals.srv, listener);
@@ -204,7 +204,7 @@ COROUTINE init_cr(void *) {
17, /* PIN_CS */
0, 1, 2, 3); /* DMA channels */
w5500_init(&globals.dev_w5500, "W5500",
- lo_box_rp2040_hwspi_as_spi(&globals.dev_spi),
+ LO_BOX(spi, &globals.dev_spi),
21, /* PIN_INTR */
20, /* PIN_RESET */
((struct net_eth_addr){{
diff --git a/lib9p/core_gen/c_validate.py b/lib9p/core_gen/c_validate.py
index e7a4017..8997237 100644
--- a/lib9p/core_gen/c_validate.py
+++ b/lib9p/core_gen/c_validate.py
@@ -67,7 +67,7 @@ def gen_c_validate(versions: set[str], typs: list[idl.UserType]) -> str:
"\t\tsize_t len = n;\n"
"\t\tVALIDATE_NET_BYTES(len);\n"
"\t\tif (!utf8_is_valid_without_nul(&net_bytes[net_offset-len], len))\n"
- f'\t\t\treturn lib9p_error(ctx, {c9util.IDENT("ERRNO_L_EBADMSG")}, "message contains invalid UTF-8");\n'
+ f'\t\t\treturn lib9p_error(ctx, {c9util.IDENT("ERRNO_L_EILSEQ")}, "message contains invalid UTF-8");\n'
"\t}\n"
)
ret += cutil.macro(
diff --git a/lib9p/core_generated.c b/lib9p/core_generated.c
index 6e3633f..ad7b210 100644
--- a/lib9p/core_generated.c
+++ b/lib9p/core_generated.c
@@ -229,12 +229,12 @@ static const lib9p_lock_flags_t lock_flags_masks[LIB9P_VER_NUM] = {
return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "message is too short for content"); \
if (net_offset > net_size) \
return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "message is too short for content (", net_offset, " > ", net_size, ")");
-#define VALIDATE_NET_UTF8(n) \
- { \
- size_t len = n; \
- VALIDATE_NET_BYTES(len); \
- if (!utf8_is_valid_without_nul(&net_bytes[net_offset-len], len)) \
- return lib9p_error(ctx, LIB9P_ERRNO_L_EBADMSG, "message contains invalid UTF-8"); \
+#define VALIDATE_NET_UTF8(n) \
+ { \
+ size_t len = n; \
+ VALIDATE_NET_BYTES(len); \
+ if (!utf8_is_valid_without_nul(&net_bytes[net_offset-len], len)) \
+ return lib9p_error(ctx, LIB9P_ERRNO_L_EILSEQ, "message contains invalid UTF-8"); \
}
#define RESERVE_HOST_BYTES(n) \
if (__builtin_add_overflow(host_size, n, &host_size)) \
diff --git a/lib9p/core_include/lib9p/core.h b/lib9p/core_include/lib9p/core.h
index 4941220..1fb71f1 100644
--- a/lib9p/core_include/lib9p/core.h
+++ b/lib9p/core_include/lib9p/core.h
@@ -66,19 +66,19 @@ bool lib9p_ctx_has_error(struct lib9p_ctx *ctx);
#endif
/** Write a <libmisc/fmt.h>-style error into ctx, return -1. */
-#define lib9p_error(ctx, linux_errno, ...) ({ \
- if (!lib9p_ctx_has_error(ctx)) { \
- _lib9p_set_err_num(ctx, linux_errno); \
- struct fmt_buf _w = { \
- .dat = (ctx)->err_msg, \
- .cap = sizeof((ctx)->err_msg), \
- }; \
- lo_interface fmt_dest w = lo_box_fmt_buf_as_fmt_dest(&_w); \
- fmt_print(w, __VA_ARGS__); \
- if (_w.len < _w.cap) \
- memset(_w.dat + _w.len, 0, _w.cap - _w.len); \
- } \
- -1; \
+#define lib9p_error(ctx, linux_errno, ...) ({ \
+ if (!lib9p_ctx_has_error(ctx)) { \
+ _lib9p_set_err_num(ctx, linux_errno); \
+ struct fmt_buf _w = { \
+ .dat = (ctx)->err_msg, \
+ .cap = sizeof((ctx)->err_msg), \
+ }; \
+ lo_interface fmt_dest w = LO_BOX(fmt_dest, &_w); \
+ fmt_print(w, __VA_ARGS__); \
+ if (_w.len < _w.cap) \
+ memset(_w.dat + _w.len, 0, _w.cap - _w.len); \
+ } \
+ -1; \
})
/* misc utilities *************************************************************/
@@ -98,11 +98,7 @@ void fmt_print_lib9p_msg(lo_interface fmt_dest w, struct lib9p_ctx *ctx, enum li
* number may be larger than net_bytes due to (1) struct padding, (2)
* array pointers.
*
- * Emits an error (return -1, set ctx->err_num and ctx->err_msg) if
- * either the message type is unknown, or if net_bytes is too short
- * for that message type, or if an invalid string (invalid UTF-8,
- * contains a nul-byte) is encountered.
- *
+ * @param ctx : negotiated protocol parameters, where to record errors
* @param net_bytes : the complete request, starting with the "size[4]"
*
* @return required size, or -1 on error
@@ -110,7 +106,7 @@ void fmt_print_lib9p_msg(lo_interface fmt_dest w, struct lib9p_ctx *ctx, enum li
* @errno L_EOPNOTSUPP: message is an R-message
* @errno L_EOPNOTSUPP: message has unknown type
* @errno L_EBADMSG: message is wrong size for content
- * @errno L_EBADMSG: message contains invalid UTF-8
+ * @errno L_EILSEQ: message contains invalid UTF-8, or the UTF-8 contains a nul-byte
* @errno L_EBADMSG: message contains a bitfield with unknown bits
* @errno L_EMSGSIZE: would-be return value overflows SSIZE_MAX
*/
@@ -142,9 +138,9 @@ void lib9p_Tmsg_unmarshal(struct lib9p_ctx *ctx, uint8_t *net_bytes,
*
* @param ctx : negotiated protocol parameters, where to record errors
* @param typ : the message type
- * @param msg : the message to encode
+ * @param msg : the message to encode (`struct lib9p_msg_XXXX` according to `typ`)
*
- * @return ret_bytes : the buffer to encode to, must be at be at least ctx->max_msg_size bytes
+ * @return ret : the buffer to encode to
* @return whether there was an error (false=success, true=error)
*
* @errno L_ERANGE: reply does not fit in ctx->max_msg_size
diff --git a/lib9p/srv_include/lib9p/srv.h b/lib9p/srv_include/lib9p/srv.h
index eb87d6f..89dc986 100644
--- a/lib9p/srv_include/lib9p/srv.h
+++ b/lib9p/srv_include/lib9p/srv.h
@@ -185,7 +185,7 @@ LO_INTERFACE(lib9p_srv_fio); /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
LO_FUNC(void , iofree ) \
/** \
* Return the idx-th dirent. idx will always be either 0 or \
- * prev_idx+1. A dirrent with an empty name signals EOF. The string \
+ * prev_idx+1. A dirent with an empty name signals EOF. The string \
* must remain valid until the next dread() call or iofree(). \
*/ \
LO_FUNC(struct lib9p_srv_dirent , dread , struct lib9p_srv_ctx *, \
diff --git a/lib9p/tests/test_server/fs_flush.c b/lib9p/tests/test_server/fs_flush.c
index e6408d7..fade0a1 100644
--- a/lib9p/tests/test_server/fs_flush.c
+++ b/lib9p/tests/test_server/fs_flush.c
@@ -67,7 +67,7 @@ static lo_interface lib9p_srv_fio flush_file_fopen(struct flush_file *self, stru
struct flush_fio *ret = heap_alloc(1, struct flush_fio);
ret->parent = self;
- return lo_box_flush_fio_as_lib9p_srv_fio(ret);
+ return LO_BOX(lib9p_srv_fio, ret);
}
/* srv_fio ********************************************************************/
diff --git a/lib9p/tests/test_server/fs_flush.h b/lib9p/tests/test_server/fs_flush.h
index 2b08850..023434b 100644
--- a/lib9p/tests/test_server/fs_flush.h
+++ b/lib9p/tests/test_server/fs_flush.h
@@ -22,6 +22,5 @@ struct flush_file {
} flush_behavior;
};
LO_IMPLEMENTATION_H(lib9p_srv_file, struct flush_file, flush_file);
-#define lo_box_flush_file_as_lib9p_srv_file(obj) util9p_box(flush_file, obj)
#endif /* _LIB9P_TESTS_TEST_SERVER_FS_FLUSH_H_ */
diff --git a/lib9p/tests/test_server/fs_shutdown.c b/lib9p/tests/test_server/fs_shutdown.c
index d4ae67e..0dd473d 100644
--- a/lib9p/tests/test_server/fs_shutdown.c
+++ b/lib9p/tests/test_server/fs_shutdown.c
@@ -66,7 +66,7 @@ static lo_interface lib9p_srv_fio shutdown_file_fopen(struct shutdown_file *self
struct shutdown_fio *ret = heap_alloc(1, struct shutdown_fio);
ret->parent = self;
- return lo_box_shutdown_fio_as_lib9p_srv_fio(ret);
+ return LO_BOX(lib9p_srv_fio, ret);
}
/* srv_fio ********************************************************************/
@@ -94,7 +94,7 @@ static uint32_t shutdown_fio_pwrite(struct shutdown_fio *self, struct lib9p_srv_
if (byte_count == 0)
return 0;
for (size_t i = 0; i < self->parent->nlisteners; i++)
- LO_CALL(lo_box_hostnet_tcplist_as_net_stream_listener(&self->parent->listeners[i]), close);
+ LO_CALL(LO_BOX(net_stream_listener, &self->parent->listeners[i]), close);
return byte_count;
}
static void shutdown_fio_pread(struct shutdown_fio *LM_UNUSED(self), struct lib9p_srv_ctx *LM_UNUSED(ctx),
diff --git a/lib9p/tests/test_server/fs_shutdown.h b/lib9p/tests/test_server/fs_shutdown.h
index 6b8683c..7b8d327 100644
--- a/lib9p/tests/test_server/fs_shutdown.h
+++ b/lib9p/tests/test_server/fs_shutdown.h
@@ -18,6 +18,5 @@ struct shutdown_file {
size_t nlisteners;
};
LO_IMPLEMENTATION_H(lib9p_srv_file, struct shutdown_file, shutdown_file);
-#define lo_box_shutdown_file_as_lib9p_srv_file(obj) util9p_box(shutdown_file, obj)
#endif /* _LIB9P_TESTS_TEST_SERVER_FS_SHUTDOWN_H_ */
diff --git a/lib9p/tests/test_server/fs_whoami.c b/lib9p/tests/test_server/fs_whoami.c
index 7e1d635..6cc46ac 100644
--- a/lib9p/tests/test_server/fs_whoami.c
+++ b/lib9p/tests/test_server/fs_whoami.c
@@ -92,7 +92,7 @@ static lo_interface lib9p_srv_fio whoami_file_fopen(struct whoami_file *self, st
ret->buf_len = 0;
ret->buf = NULL;
- return lo_box_whoami_fio_as_lib9p_srv_fio(ret);
+ return LO_BOX(lib9p_srv_fio, ret);
}
/* srv_fio ********************************************************************/
diff --git a/lib9p/tests/test_server/fs_whoami.h b/lib9p/tests/test_server/fs_whoami.h
index 5e1aee9..518e11d 100644
--- a/lib9p/tests/test_server/fs_whoami.h
+++ b/lib9p/tests/test_server/fs_whoami.h
@@ -15,6 +15,5 @@ struct whoami_file {
uint64_t pathnum;
};
LO_IMPLEMENTATION_H(lib9p_srv_file, struct whoami_file, whoami_file);
-#define lo_box_whoami_file_as_lib9p_srv_file(obj) util9p_box(whoami_file, obj)
#endif /* _LIB9P_TESTS_TEST_SERVER_FS_WHOAMI_H_ */
diff --git a/lib9p/tests/test_server/main.c b/lib9p/tests/test_server/main.c
index f2a73bf..052d180 100644
--- a/lib9p/tests/test_server/main.c
+++ b/lib9p/tests/test_server/main.c
@@ -60,11 +60,11 @@ static struct {
#define STATIC_DIR(N, STRNAME, ...) \
UTIL9P_STATIC_DIR(N, STRNAME, __VA_ARGS__)
-#define API_FILE(N, STRNAME, SYMNAME, ...) \
- lo_box_##SYMNAME##_file_as_lib9p_srv_file(&((struct SYMNAME##_file){ \
- .name = STRNAME, \
- .pathnum = N \
- __VA_OPT__(,) __VA_ARGS__ \
+#define API_FILE(N, STRNAME, SYMNAME, ...) \
+ LO_BOX(lib9p_srv_file, &((struct SYMNAME##_file){ \
+ .name = STRNAME, \
+ .pathnum = N \
+ __VA_OPT__(,) __VA_ARGS__ \
}))
static struct lib9p_srv_file root =
@@ -96,7 +96,7 @@ static COROUTINE read_cr(void *_i) {
hostnet_tcp_listener_init(&globals.listeners[i], globals.port);
- lib9p_srv_accept_and_read_loop(&globals.srv, lo_box_hostnet_tcplist_as_net_stream_listener(&globals.listeners[i]));
+ lib9p_srv_accept_and_read_loop(&globals.srv, LO_BOX(net_stream_listener, &globals.listeners[i]));
cr_end();
}
@@ -164,7 +164,7 @@ int main(int argc, char *argv[]) {
struct hostclock clock_monotonic = {
.clock_id = CLOCK_MONOTONIC,
};
- bootclock = lo_box_hostclock_as_alarmclock(&clock_monotonic);
+ bootclock = LO_BOX(alarmclock, &clock_monotonic);
coroutine_add("init", init_cr, NULL);
coroutine_main();
fclose(globals.logstream);
diff --git a/lib9p_util/include/util9p/static.h b/lib9p_util/include/util9p/static.h
index 5454c24..c204607 100644
--- a/lib9p_util/include/util9p/static.h
+++ b/lib9p_util/include/util9p/static.h
@@ -9,12 +9,6 @@
#include <lib9p/srv.h>
-#define util9p_box(nam, obj) \
- ((struct lib9p_srv_file){ \
- .self = obj, \
- .vtable = (void*)&_lo_##nam##_lib9p_srv_file_vtable, \
- })
-
#define UTIL9P_ATIME 1728337905
#define UTIL9P_MTIME 1728337904
@@ -56,12 +50,11 @@ struct util9p_static_dir {
lo_interface lib9p_srv_file members[];
};
LO_IMPLEMENTATION_H(lib9p_srv_file, struct util9p_static_dir, util9p_static_dir);
-#define lo_box_util9p_static_dir_as_lib9p_srv_file(obj) util9p_box(util9p_static_dir, obj)
-#define UTIL9P_STATIC_DIR(PATH, STRNAME, ...) \
- lo_box_util9p_static_dir_as_lib9p_srv_file(&((struct util9p_static_dir){ \
- .c = UTIL9P_STATIC_COMMON(PATH, STRNAME, 0555), \
- .members = { __VA_ARGS__ LO_NULL(lib9p_srv_file) }, \
+#define UTIL9P_STATIC_DIR(PATH, STRNAME, ...) \
+ LO_BOX(lib9p_srv_file, &((struct util9p_static_dir){ \
+ .c = UTIL9P_STATIC_COMMON(PATH, STRNAME, 0555), \
+ .members = { __VA_ARGS__ LO_NULL(lib9p_srv_file) }, \
}))
/* File ***********************************************************************/
@@ -74,12 +67,11 @@ struct util9p_static_file {
size_t data_size; /* only used if .data_end==NULL */
};
LO_IMPLEMENTATION_H(lib9p_srv_file, struct util9p_static_file, util9p_static_file);
-#define lo_box_util9p_static_file_as_lib9p_srv_file(obj) util9p_box(util9p_static_file, obj)
-#define UTIL9P_STATIC_FILE(PATH, STRNAME, ...) \
- lo_box_util9p_static_file_as_lib9p_srv_file(&((struct util9p_static_file){ \
- .c = UTIL9P_STATIC_COMMON(PATH, STRNAME, 0444), \
- __VA_ARGS__ \
+#define UTIL9P_STATIC_FILE(PATH, STRNAME, ...) \
+ LO_BOX(lib9p_srv_file, &((struct util9p_static_file){ \
+ .c = UTIL9P_STATIC_COMMON(PATH, STRNAME, 0444), \
+ __VA_ARGS__ \
}))
#endif /* _UTIL9P_STATIC_H_ */
diff --git a/lib9p_util/static.c b/lib9p_util/static.c
index c35d28c..40810b5 100644
--- a/lib9p_util/static.c
+++ b/lib9p_util/static.c
@@ -100,7 +100,7 @@ LIB9P_SRV_NOTFILE(struct util9p_static_dir, util9p_static_dir);
static lo_interface lib9p_srv_dio util9p_static_dir_dopen(struct util9p_static_dir *self, struct lib9p_srv_ctx *ctx) {
assert(self);
assert(ctx);
- return lo_box_util9p_static_dir_as_lib9p_srv_dio(self);
+ return LO_BOX(lib9p_srv_dio, self);
}
static void util9p_static_dir_iofree(struct util9p_static_dir *self) {
assert(self);
@@ -191,7 +191,7 @@ static lo_interface lib9p_srv_fio util9p_static_file_fopen(struct util9p_static_
assert(rd);
assert(!wr);
assert(!trunc);
- return lo_box_util9p_static_file_as_lib9p_srv_fio(self);
+ return LO_BOX(lib9p_srv_fio, self);
}
static void util9p_static_file_iofree(struct util9p_static_file *self) {
assert(self);
diff --git a/libdhcp/tests/test_client.c b/libdhcp/tests/test_client.c
index 24b3af6..09557e5 100644
--- a/libdhcp/tests/test_client.c
+++ b/libdhcp/tests/test_client.c
@@ -99,7 +99,7 @@ static lo_interface net_packet_conn test_udp_conn(struct test_iface *self, uint1
test_assert(local_port == 68);
test_assert(!once);
once = true;
- return lo_box_test_udp_as_net_packet_conn(&self->conn);
+ return LO_BOX(net_packet_conn, &self->conn);
}
/******************************************************************************/
@@ -107,7 +107,7 @@ static lo_interface net_packet_conn test_udp_conn(struct test_iface *self, uint1
COROUTINE dhcp_cr(void *) {
cr_begin();
struct test_iface iface = {};
- dhcp_client_main(lo_box_test_as_net_iface(&iface), "test-client");
+ dhcp_client_main(LO_BOX(net_iface, &iface), "test-client");
cr_end();
}
@@ -115,7 +115,7 @@ int main() {
struct hostclock clock_monotonic = {
.clock_id = CLOCK_MONOTONIC,
};
- bootclock = lo_box_hostclock_as_alarmclock(&clock_monotonic);
+ bootclock = LO_BOX(alarmclock, &clock_monotonic);
coroutine_add("dhcp", dhcp_cr, NULL);
coroutine_main();
diff --git a/libhw_cr/host_net.c b/libhw_cr/host_net.c
index 8016787..c8d4472 100644
--- a/libhw_cr/host_net.c
+++ b/libhw_cr/host_net.c
@@ -187,7 +187,7 @@ static lo_interface net_stream_conn hostnet_tcplist_accept(struct hostnet_tcp_li
listener->active_conn.fd = ret_connfd;
listener->active_conn.read_deadline_ns = 0;
- return lo_box_hostnet_tcp_as_net_stream_conn(&listener->active_conn);
+ return LO_BOX(net_stream_conn, &listener->active_conn);
}
/* TCP listener close() *******************************************************/
diff --git a/libhw_cr/rp2040_hwtimer.c b/libhw_cr/rp2040_hwtimer.c
index 8227abb..d9f0a24 100644
--- a/libhw_cr/rp2040_hwtimer.c
+++ b/libhw_cr/rp2040_hwtimer.c
@@ -44,7 +44,7 @@ static_assert(sizeof(hwtimers)/sizeof(hwtimers[0]) == _RP2040_HWALARM_NUM);
lo_interface alarmclock rp2040_hwtimer(enum rp2040_hwalarm_instance alarm_num) {
assert(alarm_num < _RP2040_HWALARM_NUM);
- return lo_box_rp2040_hwtimer_as_alarmclock(&hwtimers[alarm_num]);
+ return LO_BOX(alarmclock, &hwtimers[alarm_num]);
}
diff --git a/libhw_cr/w5500.c b/libhw_cr/w5500.c
index c318819..58715c9 100644
--- a/libhw_cr/w5500.c
+++ b/libhw_cr/w5500.c
@@ -459,7 +459,7 @@ static lo_interface net_stream_listener w5500_if_tcp_listen(struct w5500 *chip,
sock->read_deadline_ns = 0;
sock->list_open = true;
- return lo_box_w5500_tcplist_as_net_stream_listener(sock);
+ return LO_BOX(net_stream_listener, sock);
}
static lo_interface net_stream_conn w5500_if_tcp_dial(struct w5500 *chip,
@@ -509,7 +509,7 @@ static lo_interface net_stream_conn w5500_if_tcp_dial(struct w5500 *chip,
cr_yield();
break;
case STATE_TCP_ESTABLISHED:
- return lo_box_w5500_tcp_as_net_stream_conn(socket);
+ return LO_BOX(net_stream_conn, socket);
default:
goto restart;
}
@@ -545,7 +545,7 @@ static lo_interface net_packet_conn w5500_if_udp_conn(struct w5500 *chip, uint16
cr_yield();
cr_mutex_unlock(&chip->mu);
- return lo_box_w5500_udp_as_net_packet_conn(socket);
+ return LO_BOX(net_packet_conn, socket);
}
static bool w5500_if_arp_ping(struct w5500 *chip, struct net_ip4_addr addr) {
@@ -598,10 +598,10 @@ static lo_interface net_stream_conn w5500_tcplist_accept(struct _w5500_socket *s
break;
case STATE_TCP_ESTABLISHED:
socket->read_open = true;
- /* fall-through */
+ [[fallthrough]];
case STATE_TCP_CLOSE_WAIT:
socket->write_open = true;
- return lo_box_w5500_tcp_as_net_stream_conn(socket);
+ return LO_BOX(net_stream_conn, socket);
default:
goto restart;
}
diff --git a/libmisc/CMakeLists.txt b/libmisc/CMakeLists.txt
index c6405ad..07f154b 100644
--- a/libmisc/CMakeLists.txt
+++ b/libmisc/CMakeLists.txt
@@ -18,6 +18,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/include/libmisc/fmt.h b/libmisc/include/libmisc/fmt.h
index c0743ff..135e48b 100644
--- a/libmisc/include/libmisc/fmt.h
+++ b/libmisc/include/libmisc/fmt.h
@@ -9,6 +9,7 @@
#include <stddef.h> /* for size_t */
#include <stdint.h> /* for (u)int{n}_t */
+#include <stdlib.h> /* for realloc() */
#include <libmisc/macro.h>
#include <libmisc/obj.h>
@@ -99,6 +100,11 @@ void fmt_print_bool(lo_interface fmt_dest w, bool b);
const char * : fmt_print_str , \
bool : fmt_print_bool )(w, val)
+/** Same as fmt_print(), but usable from inside of fmt_print(). */
+#define fmt_print2(w, ...) do { LM_FOREACH_PARAM2_(_fmt_param2, (w), __VA_ARGS__) } while (0)
+#define _fmt_param2(...) _LM_DEFER2(_fmt_param_indirect)()(__VA_ARGS__)
+#define _fmt_param_indirect() _fmt_param
+
/* print-to-memory ************************************************************/
struct fmt_buf {
@@ -107,36 +113,45 @@ 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; \
})
-/* justify ********************************************************************/
+#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; \
+})
-/* *grubles about not being allowed to nest things* */
-#define _fmt_param_indirect() _fmt_param
-#define _fmt_print2(w, ...) do { LM_FOREACH_PARAM2_(_fmt_param2, (w), __VA_ARGS__) } while (0)
-#define _fmt_param2(...) _LM_DEFER2(_fmt_param_indirect)()(__VA_ARGS__)
+/* justify ********************************************************************/
#define fmt_print_ljust(w, width, fillchar, ...) do { \
size_t beg = LO_CALL(w, tell); \
- _fmt_print2(w, __VA_ARGS__); \
+ fmt_print2(w, __VA_ARGS__); \
while ((LO_CALL(w, tell) - beg) < width) \
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_fmt.c b/libmisc/tests/test_fmt.c
index a9157d6..64b3b8a 100644
--- a/libmisc/tests/test_fmt.c
+++ b/libmisc/tests/test_fmt.c
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
+#include <stdlib.h> /* for free() */
#include <string.h> /* for strcmp(), memcmp(), memset() */
#include <libmisc/fmt.h>
@@ -230,5 +231,13 @@ int main() {
test_assert(strcmp(str, "{0x68,0x65,0x6C,0x6C,0x6F,0x00}") == 0);
memset(str, 0, sizeof(str));
+ char *astr = fmt_asprint("");
+ test_assert(astr != NULL && astr[0] == '\0');
+ free(astr);
+
+ astr = fmt_asprint("hello ", (base2, 9), (qstr, " world!\n"));
+ test_assert(strcmp(astr, "hello 1001\" world!\\n\"") == 0);
+ free(astr);
+
return 0;
}
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:])