summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2025-05-15 01:59:17 -0600
committerLuke T. Shumaker <lukeshu@lukeshu.com>2025-05-15 14:59:02 -0600
commite38a2d29292ad3fad64729ef958ff07e3bca02cf (patch)
tree6b647d225665cd4fb2733a57bdfa5f0e96692995
parent3faaad9fe1f11cfe5699c6720c897bfddc7cf49a (diff)
Fully banish printf from the firmware
-rw-r--r--.gitmodules3
m---------3rd-party/pico-fmt0
m---------3rd-party/pico-sdk0
-rw-r--r--CMakeLists.txt33
-rwxr-xr-xbuild-aux/lint-bin1
-rw-r--r--build-aux/measurestack/app_main.py3
-rw-r--r--build-aux/measurestack/app_plugins.py97
-rw-r--r--build-aux/measurestack/test_app_plugins.py70
-rw-r--r--cmd/sbc_harness/CMakeLists.txt6
-rw-r--r--cmd/sbc_harness/config/tusb_config.h45
-rw-r--r--cmd/sbc_harness/fs_harness_uptime_txt.c5
-rw-r--r--cmd/sbc_harness/main.c3
l---------cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/printf.mit.txt1
-rw-r--r--cmd/sbc_harness/tusb_log.c9
-rw-r--r--libfmt/CMakeLists.txt15
-rw-r--r--libfmt/include/libfmt/fmt.h23
-rw-r--r--libfmt/libmisc.c17
-rw-r--r--libfmt/quote.c159
18 files changed, 61 insertions, 429 deletions
diff --git a/.gitmodules b/.gitmodules
index 17dec29..8a4c874 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -6,6 +6,3 @@
[submodule "3rd-party/pico-sdk"]
path = 3rd-party/pico-sdk
url = https://github.com/LukeShu/pico-sdk
-[submodule "3rd-party/pico-fmt"]
- path = 3rd-party/pico-fmt
- url = https://github.com/LukeShu/pico-fmt
diff --git a/3rd-party/pico-fmt b/3rd-party/pico-fmt
deleted file mode 160000
-Subproject a69f6e22e9bf3da849b128d3a0ed3e77da5997d
diff --git a/3rd-party/pico-sdk b/3rd-party/pico-sdk
-Subproject c8a16c00453e4db4b771d7f1281391057c7477d
+Subproject bbad146d79b42081d85b76e44f136ff8d42f1a1
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 659f530..5ce49ed 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -14,8 +14,6 @@ include("${PICO_SDK_PATH}/external/pico_sdk_import.cmake")
project(sbc_harness)
-add_subdirectory(3rd-party/pico-fmt/pico_fmt)
-add_subdirectory(3rd-party/pico-fmt/pico_printf)
pico_sdk_init()
if ((PICO_PLATFORM STREQUAL "host") AND (NOT PICO_NO_GC_SECTIONS))
@@ -36,7 +34,6 @@ string(TOUPPER "${CMAKE_BUILD_TYPE}" _upper_cmake_build_type)
string(REPLACE " " ";" _build_type_flags "${CMAKE_C_FLAGS_${_upper_cmake_build_type}}")
if ("-DNDEBUG" IN_LIST _build_type_flags)
add_compile_options(-Wno-unused-variable -Wno-unused-parameter -Wno-unused-but-set-variable)
- target_compile_definitions(pico_printf INTERFACE PICO_PRINTF_ALWAYS_INCLUDED=1)
endif()
function(_suppress_tinyusb_warnings)
@@ -123,8 +120,36 @@ function(add_lib_test arg_libname arg_testname)
endif()
endfunction()
+function(_apply_matrix_helper _m_depth _m_assignments)
+ list(LENGTH _m_arg_matrix _m_dimensions)
+ math(EXPR _m_dimensions ${_m_dimensions}/2)
+ if("${_m_depth}" EQUAL "${_m_dimensions}")
+ cmake_language(CALL "${_m_arg_action}" "${_m_n}" "${_m_assignments}")
+ math(EXPR _m_n "${_m_n}+1")
+ set(_m_n "${_m_n}" PARENT_SCOPE)
+ else()
+ math(EXPR _m_ik "${_m_depth}*2")
+ list(GET _m_arg_matrix "${_m_ik}" _m_tmp_key)
+
+ math(EXPR _m_iv "${_m_ik}+1")
+ list(GET _m_arg_matrix "${_m_iv}" _m_tmp_vals)
+ string(REGEX REPLACE "^\\[(.*)\\]$" "\\1" _m_tmp_vals "${_m_tmp_vals}")
+
+ foreach(_m_tmp_val IN LISTS _m_tmp_vals)
+ math(EXPR _m_tmp_depth "${_m_depth}+1")
+ set(_m_tmp_assignments "${_m_assignments}")
+ list(APPEND _m_tmp_assignments "${_m_tmp_key}=${_m_tmp_val}")
+ _apply_matrix_helper("${_m_tmp_depth}" "${_m_tmp_assignments}")
+ set(_m_n "${_m_n}" PARENT_SCOPE)
+ endforeach()
+ endif()
+endfunction()
+function(apply_matrix _m_arg_action _m_arg_matrix)
+ set(_m_n 0)
+ _apply_matrix_helper(0 "")
+endfunction()
+
add_subdirectory(libmisc)
-add_subdirectory(libfmt)
add_subdirectory(libcr)
add_subdirectory(libcr_ipc)
add_subdirectory(libhw_generic)
diff --git a/build-aux/lint-bin b/build-aux/lint-bin
index 91f1612..19d3a3b 100755
--- a/build-aux/lint-bin
+++ b/build-aux/lint-bin
@@ -114,6 +114,7 @@ lint_func_blocklist() {
local blocklist=(
gpio_default_irq_handler
+ {,__wrap,weak_raw_,stdio_,_}{,v}{,sn}printf
)
while read -r func; do
diff --git a/build-aux/measurestack/app_main.py b/build-aux/measurestack/app_main.py
index 463447e..4fdfd5c 100644
--- a/build-aux/measurestack/app_main.py
+++ b/build-aux/measurestack/app_main.py
@@ -48,7 +48,6 @@ def main(
plugins += [
app_plugins.CmdPlugin(),
libmisc_plugin,
- app_plugins.PicoFmtPlugin(arg_pico_platform, []),
app_plugins.LibHWPlugin(arg_pico_platform, libmisc_plugin),
app_plugins.LibCRPlugin(),
app_plugins.LibCRIPCPlugin(),
@@ -88,8 +87,6 @@ def main(
def misc_filter(name: QName) -> tuple[int, bool]:
if name in [
QName("__assert_msg_fail"),
- QName("fmt_vfctprintf"),
- QName("fmt_vsnprintf"),
]:
return 1, False
for prefix in ["fmt_print_", "_fmt_print_"]:
diff --git a/build-aux/measurestack/app_plugins.py b/build-aux/measurestack/app_plugins.py
index fbe137c..6fc81ec 100644
--- a/build-aux/measurestack/app_plugins.py
+++ b/build-aux/measurestack/app_plugins.py
@@ -19,7 +19,6 @@ __all__ = [
"LibCRIPCPlugin",
"Lib9PPlugin",
"LibMiscPlugin",
- "PicoFmtPlugin",
"PicoSDKPlugin",
"TinyUSBDevicePlugin",
"NewlibPlugin",
@@ -369,97 +368,6 @@ class Lib9PPlugin:
return {}
-class PicoFmtPlugin:
- known_fct: dict[BaseName, BaseName]
- wont_call_v: typing.Collection[BaseName]
-
- def __init__(
- self, arg_pico_platform: str, wont_call_v: typing.Collection[BaseName]
- ) -> None:
- self.known_fct = {
- # pico_fmt
- BaseName("fmt_vsnprintf"): BaseName("_out_buffer"),
- }
- match arg_pico_platform:
- case "rp2040":
- self.known_fct.update(
- {
- # pico_stdio
- BaseName("__wrap_vprintf"): BaseName("stdio_buffered_printer"),
- BaseName("stdio_vprintf"): BaseName("stdio_buffered_printer"),
- }
- )
- self.wont_call_v = set([*self.known_fct.values(), *wont_call_v])
-
- def is_intrhandler(self, name: QName) -> bool:
- return False
-
- def init_array(self) -> typing.Collection[QName]:
- return []
-
- def extra_includes(self) -> typing.Collection[BaseName]:
- return []
-
- def extra_nodes(self) -> typing.Collection[Node]:
- return []
-
- def indirect_callees(
- self, loc: str, line: str
- ) -> tuple[typing.Collection[QName], bool] | None:
- if "/3rd-party/pico-fmt/" not in loc:
- return None
- if "/printf.c:" in loc:
- m = util.re_call_other.fullmatch(line)
- call: str | None = m.group("func") if m else None
- if "->fct" in line:
- return [x.as_qname() for x in self.known_fct.values()], False
- if "specifier_table" in line:
- return [
- # pico-fmt
- QName("conv_sint"),
- QName("conv_uint"),
- # QName("conv_double"),
- QName("conv_char"),
- QName("conv_str"),
- QName("conv_ptr"),
- QName("conv_pct"),
- # libfmt
- QName("libfmt_conv_formatter"),
- QName("libfmt_conv_quote"),
- ], False
- return None
-
- def skipmodels(self) -> dict[BaseName, analyze.SkipModel]:
- ret: dict[BaseName, analyze.SkipModel] = {
- BaseName("fmt_state_putchar"): analyze.SkipModel(
- self.known_fct.keys(), self._skipmodel_fmt_state_putchar
- ),
- BaseName("_vfctprintf"): analyze.SkipModel(
- self.wont_call_v, self._skipmodel__vfctprintf
- ),
- }
- return ret
-
- def _skipmodel_fmt_state_putchar(
- self, chain: typing.Sequence[QName], node: Node, call: QName
- ) -> bool:
- if call.base() in self.known_fct.values():
- fct: BaseName | None = None
- for pcall in reversed(chain):
- if pcall.base() in self.known_fct:
- fct = self.known_fct[pcall.base()]
- return call.base() != fct
- return True
- return False
-
- def _skipmodel__vfctprintf(
- self, chain: typing.Sequence[QName], node: Node, call: QName
- ) -> bool:
- if call.base() == BaseName("libfmt_conv_formatter"):
- return any(c.base() in self.wont_call_v for c in chain)
- return False
-
-
class PicoSDKPlugin:
get_init_array: typing.Callable[[], typing.Collection[QName]]
app_init_array: typing.Collection[QName] | None
@@ -877,10 +785,7 @@ class LibGCCPlugin:
return False
def init_array(self) -> typing.Collection[QName]:
- return [
- QName("libfmt_install_formatter"),
- QName("libfmt_install_quote"),
- ]
+ return []
def extra_includes(self) -> typing.Collection[BaseName]:
return []
diff --git a/build-aux/measurestack/test_app_plugins.py b/build-aux/measurestack/test_app_plugins.py
deleted file mode 100644
index aed0bb4..0000000
--- a/build-aux/measurestack/test_app_plugins.py
+++ /dev/null
@@ -1,70 +0,0 @@
-# build-aux/measurestack/test_app_plugins.py - Tests for app_plugins.py
-#
-# Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
-# SPDX-License-Identifier: AGPL-3.0-or-later
-
-# pylint: disable=unused-variable
-
-import typing
-
-from . import analyze, app_plugins, testutil, util
-from .analyze import QName
-
-
-def test_fct() -> None:
- # 1. | a + | b + |
- # 2. | fmt_vsnprintf + | vprintf + |
- # 3. | fmt_vfctprintf + | fmt_vfctprintf + |
- # 4. | fmt_state_putchar + | fmt_state_putchar + |
- # 5. | _out_buffer + | stdio_buffered_printer + |
- max_call_depth = 5
- exp_a = ["a", "fmt_vsnprintf", "fmt_vfctprintf", "fmt_state_putchar", "_out_buffer"]
- exp_b = [
- "b",
- "__wrap_vprintf",
- "fmt_vfctprintf",
- "fmt_state_putchar",
- "stdio_buffered_printer",
- ]
- graph: typing.Sequence[tuple[str, typing.Collection[str]]] = [
- # main.c
- ("a", {"fmt_vsnprintf"}), # _out_buffer
- ("b", {"vprintf"}), # stdio_buffered_printer
- # wrappers
- ("fmt_vsnprintf", {"fmt_vfctprintf"}),
- ("__wrap_vprintf", {"fmt_vfctprintf"}),
- # printf.c
- ("fmt_vfctprintf", {"fmt_state_putchar"}),
- ("fmt_state_putchar", {"_out_buffer", "stdio_buffered_printer"}),
- # fcts
- ("_out_buffer", {}),
- ("stdio_buffered_printer", {}),
- ]
- graph_plugin = testutil.GraphProviderPlugin(max_call_depth, graph)
-
- plugins: list[util.Plugin] = [
- graph_plugin,
- app_plugins.LibMiscPlugin(arg_c_fnames=[]),
- # fmt_vsnprintf => fct=_out_buffer
- # if rp2040:
- # __wrap_vprintf => fct=stdio_buffered_printer
- # stdio_vprintf => fct=stdio_buffered_printer
- app_plugins.PicoFmtPlugin("rp2040", []),
- ]
-
- def test_filter(name: QName) -> tuple[int, bool]:
- if str(name.base()) in ["a", "b", "c"]:
- return 1, True
- return 0, False
-
- result = analyze.analyze(
- ci_fnames=[],
- app_func_filters={
- "Main": test_filter,
- },
- app=util.PluginApplication(testutil.nop_location_xform, plugins),
- cfg_max_call_depth=max_call_depth,
- )
-
- graph_plugin.assert_nstatic(result.groups["Main"].rows[QName("a")].nstatic, exp_a)
- graph_plugin.assert_nstatic(result.groups["Main"].rows[QName("b")].nstatic, exp_b)
diff --git a/cmd/sbc_harness/CMakeLists.txt b/cmd/sbc_harness/CMakeLists.txt
index 878e151..0e904ab 100644
--- a/cmd/sbc_harness/CMakeLists.txt
+++ b/cmd/sbc_harness/CMakeLists.txt
@@ -26,16 +26,13 @@ target_link_libraries(sbc_harness_objs
hardware_watchdog
libmisc
- libfmt
libusb
libdhcp
libhw_cr
lib9p_srv
lib9p_util
)
-pico_minimize_runtime(sbc_harness_objs
- INCLUDE PRINTF PRINTF_MINIMAL PRINTF_LONG_LONG PRINTF_PTRDIFF_T
-)
+pico_minimize_runtime(sbc_harness_objs)
target_compile_definitions(sbc_harness_objs PRIVATE
#PICO_USE_FASTEST_SUPPORTED_CLOCK=1
@@ -75,7 +72,6 @@ target_embed_sources(sbc_harness_objs sbc_harness static.h
static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/dhcp.bsd3-mit.txt
static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/newlib.txt
static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/pico-sdk.bsd3.txt
- static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/printf.mit.txt
static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/tinyusb.mit.txt
static/Documentation/harness_rom_bin.txt
static/Documentation/harness_flash_bin.txt
diff --git a/cmd/sbc_harness/config/tusb_config.h b/cmd/sbc_harness/config/tusb_config.h
index 5240311..2c7c02a 100644
--- a/cmd/sbc_harness/config/tusb_config.h
+++ b/cmd/sbc_harness/config/tusb_config.h
@@ -31,40 +31,31 @@
#ifndef _TUSB_CONFIG_H_
#define _TUSB_CONFIG_H_
+#include <stdint.h> /* for uint{n}_t */
+
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------
-// Override the default definition of TU_ASSERT() to use our logging
+// Override the default TU_MESS_FAILED() and tu_print_*() to use our logging
//--------------------------------------------------------------------
-// "magically" select between the 1-arg and 2-args variants, inject a
-// stringified version of `_cond`.
-//
-// Note: Use GNU-C `, ##__VA_ARGS__`, not standard C `__VA_OPT__(,)
-// __VA_ARGS__`; because __VA_OPT__ doesn't handle present-but-empty
-// arguments the way that we need.
-#define TU_ASSERT(_cond, ...) \
- _GET_3RD_ARG(_cond, ##__VA_ARGS__, \
- _LIBMISC_TU_ASSERT_2ARGS, _LIBMISC_TU_ASSERT_1ARGS, _dummy) \
- (_cond, #_cond, ##__VA_ARGS__)
-
-#define _LIBMISC_TU_ASSERT_1ARGS(_cond, _cond_str) \
- _LIBMISC_TU_ASSERT_2ARGS(_cond, _cond_str, false)
-
-#define _LIBMISC_TU_ASSERT_2ARGS(_cond, _cond_str, _ret) \
- do { \
- if ( !(_cond) ) { \
- _libmisc_tu_mess_failed(_cond_str, \
- __FILE__, __LINE__, __func__); \
- TU_BREAKPOINT(); \
- return _ret; \
- } \
- } while (0)
-
-void _libmisc_tu_mess_failed(const char *expr,
- const char *file, unsigned int line, const char *func);
+#define TU_MESS_FAILED(_cond_str) _libmisc_tu_mess_failed(_cond_str, __FILE__, __LINE__, __func__)
+#define tu_print_str _libmisc_tu_print_str
+#define tu_print_byte _libmisc_tu_print_byte
+#define tu_print_base10 _libmisc_tu_print_base10
+#define tu_print_base16 _libmisc_tu_print_base16
+#define tu_print_base16_u8 _libmisc_tu_print_base16_u8
+#define tu_print_base16_u16 _libmisc_tu_print_base16_u16
+
+void _libmisc_tu_mess_failed(const char *expr, const char *file, unsigned int line, const char *func);
+void _libmisc_tu_print_str(const char *);
+void _libmisc_tu_print_byte(uint8_t);
+void _libmisc_tu_print_base10(unsigned long);
+void _libmisc_tu_print_base16(unsigned long);
+void _libmisc_tu_print_base16_u8(uint8_t);
+void _libmisc_tu_print_base16_u16(uint16_t);
//--------------------------------------------------------------------
// Configuration: common
diff --git a/cmd/sbc_harness/fs_harness_uptime_txt.c b/cmd/sbc_harness/fs_harness_uptime_txt.c
index f7b755f..97246ea 100644
--- a/cmd/sbc_harness/fs_harness_uptime_txt.c
+++ b/cmd/sbc_harness/fs_harness_uptime_txt.c
@@ -4,11 +4,10 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
-#include <stdio.h> /* for snprintf() */
-
#include <libhw/generic/alarmclock.h>
#include <util9p/static.h>
#include <libmisc/alloc.h> /* for heap_alloc(), free() */
+#include <libmisc/fmt.h> /* for fmt_snprint() */
#include "fs_harness_uptime_txt.h"
@@ -120,7 +119,7 @@ static void uptime_fio_pread(struct uptime_fio *self, struct lib9p_srv_ctx *ctx,
if (byte_offset == 0 || self->buf_len == 0) {
uint64_t now = LO_CALL(bootclock, get_time_ns);
- self->buf_len = snprintf(self->buf, sizeof(self->buf), "%"PRIu64"ns\n", now);
+ self->buf_len = fmt_snprint(self->buf, sizeof(self->buf), now, "ns\n");
}
if (byte_offset > (uint64_t)self->buf_len) {
diff --git a/cmd/sbc_harness/main.c b/cmd/sbc_harness/main.c
index 0fb6e61..143bae0 100644
--- a/cmd/sbc_harness/main.c
+++ b/cmd/sbc_harness/main.c
@@ -80,9 +80,6 @@ static struct lib9p_srv_file root =
STATIC_FILE("pico-sdk.bsd3.txt",
.data_start = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_pico_sdk_bsd3_txt_start,
.data_end = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_pico_sdk_bsd3_txt_end),
- STATIC_FILE("printf.mit.txt",
- .data_start = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_printf_mit_txt_start,
- .data_end = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_printf_mit_txt_end),
STATIC_FILE("tinyusb.mit.txt",
.data_start = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_tinyusb_mit_txt_start,
.data_end = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_tinyusb_mit_txt_end),
diff --git a/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/printf.mit.txt b/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/printf.mit.txt
deleted file mode 120000
index 5a6e342..0000000
--- a/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/printf.mit.txt
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../3rd-party/pico-sdk/src/rp2_common/pico_printf/LICENSE \ No newline at end of file
diff --git a/cmd/sbc_harness/tusb_log.c b/cmd/sbc_harness/tusb_log.c
index 4629c04..09fe755 100644
--- a/cmd/sbc_harness/tusb_log.c
+++ b/cmd/sbc_harness/tusb_log.c
@@ -7,7 +7,16 @@
#define LOG_NAME TINY_USB
#include <libmisc/log.h>
+#include "tusb_config.h"
+
void _libmisc_tu_mess_failed(const char *expr,
const char *file, unsigned int line, const char *func) {
log_errorln(file, ":", line, ":", func, "(): assertion ", (qstr, expr), " failed");
}
+
+void _libmisc_tu_print_str(const char *x) { fmt_print_str(_log_dest, x); }
+void _libmisc_tu_print_byte(uint8_t x) { fmt_print_byte(_log_dest, x); }
+void _libmisc_tu_print_base10(unsigned long x) { fmt_print_base10(_log_dest, x); }
+void _libmisc_tu_print_base16(unsigned long x) { fmt_print(_log_dest, "0x", (base16, x)); }
+void _libmisc_tu_print_base16_u8(uint8_t x) { fmt_print_base16_u8_(_log_dest, x); }
+void _libmisc_tu_print_base16_u16(uint16_t x) { fmt_print_base16_u16_(_log_dest, x); }
diff --git a/libfmt/CMakeLists.txt b/libfmt/CMakeLists.txt
deleted file mode 100644
index dc626f7..0000000
--- a/libfmt/CMakeLists.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-# libfmt/CMakeLists.txt - Support for pico-fmt
-#
-# Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
-# SPDX-License-Identifier: AGPL-3.0-or-later
-
-add_library(libfmt INTERFACE)
-target_include_directories(libfmt PUBLIC INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
-target_sources(libfmt INTERFACE
- libmisc.c
- quote.c
-)
-target_link_libraries(libfmt INTERFACE
- pico_fmt
- libmisc
-)
diff --git a/libfmt/include/libfmt/fmt.h b/libfmt/include/libfmt/fmt.h
deleted file mode 100644
index 692abf2..0000000
--- a/libfmt/include/libfmt/fmt.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/* libfmt/fmt.h - Support for pico-fmt
- *
- * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#ifndef _LIBFMT_FMT_H_
-#define _LIBFMT_FMT_H_
-
-#include <pico/fmt_printf.h>
-#include <pico/fmt_install.h>
-
-#include <libmisc/obj.h>
-
-/**
- * An object that implements fmt_formatter can be printed using
- * `printf("%v", boxed_obj)`.
- */
-#define fmt_formatter_LO_IFACE \
- LO_FUNC(void, format, struct fmt_state *)
-LO_INTERFACE(fmt_formatter);
-
-#endif /* _LIBFMT_FMT_H_ */
diff --git a/libfmt/libmisc.c b/libfmt/libmisc.c
deleted file mode 100644
index 4d3724b..0000000
--- a/libfmt/libmisc.c
+++ /dev/null
@@ -1,17 +0,0 @@
-/* libfmt/libmisc.c - Integrate pico-fmt with libmisc
- *
- * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#include <libfmt/fmt.h> /* for fmt_vfctprintf() */
-
-static void libfmt_conv_formatter(struct fmt_state *state) {
- lo_interface fmt_formatter obj = va_arg(*state->args, lo_interface fmt_formatter);
- LO_CALL(obj, format, state);
-}
-
-[[gnu::constructor]]
-static void libfmt_install_formatter(void) {
- fmt_install('v', libfmt_conv_formatter);
-}
diff --git a/libfmt/quote.c b/libfmt/quote.c
deleted file mode 100644
index c91e0b0..0000000
--- a/libfmt/quote.c
+++ /dev/null
@@ -1,159 +0,0 @@
-/* libfmt/quote.c - C-string quoting for pico-fmt
- *
- * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#include <string.h> /* for strnlen() */
-#include <stdint.h> /* for uint{n}_t() */
-
-#include <libfmt/fmt.h>
-
-enum quote {
- QUOTE_NONE, /* c */
- QUOTE_SIMPLE, /* \c */
- QUOTE_U4, /* \uABCD */
- QUOTE_U8, /* \UABCDABCD */
-};
-
-static inline enum quote needs_quote(uint32_t ch) {
- if (ch == '\a' ||
- ch == '\b' ||
- ch == '\f' ||
- ch == '\n' ||
- ch == '\r' ||
- ch == '\t' ||
- ch == '\v' ||
- ch == '\\' ||
- ch == '\'' ||
- ch == '"' ||
- ch == '?')
- return QUOTE_SIMPLE;
- else if (' ' <= ch && ch <= '~')
- return QUOTE_NONE;
- else if (ch < 0x10000)
- return QUOTE_U4;
- else
- return QUOTE_U8;
-}
-
-/**
- * Quote a string to ASCII-only C syntax. Valid UTF-8 is quoted as
- * short C-escape characters, \uABCD or \UABCDABCD; invalid UTF-8 is
- * quoted as \xAB.
- */
-static void libfmt_conv_quote(struct fmt_state *state) {
- uint32_t ch;
- uint8_t chlen;
-
- const char *in = va_arg(*state->args, char*);
- size_t in_len = strnlen(in, (state->flags & FMT_FLAG_PRECISION) ? state->precision : (size_t)-1);
-
- size_t out_len = 2;
- for (size_t pos = 0; pos < in_len;) {
- if ((in[pos] & 0b10000000) == 0b00000000) { ch = in[pos] & 0b01111111; chlen = 1; }
- else if ((in[pos] & 0b11100000) == 0b11000000) { ch = in[pos] & 0b00011111; chlen = 2; }
- else if ((in[pos] & 0b11110000) == 0b11100000) { ch = in[pos] & 0b00001111; chlen = 3; }
- else if ((in[pos] & 0b11111000) == 0b11110000) { ch = in[pos] & 0b00000111; chlen = 4; }
- else goto measure_invalid_utf8;
- if ((ch == 0 && chlen != 1) || pos + chlen > in_len) goto measure_invalid_utf8;
- for (uint8_t i = 1; i < chlen; i++) {
- if ((in[pos+i] & 0b11000000) != 0b10000000) goto measure_invalid_utf8;
- ch = (ch << 6) | (in[pos+i] & 0b00111111);
- }
- if (ch > 0x10FFFF) goto measure_invalid_utf8;
- pos += chlen;
-
- switch (needs_quote(ch)) {
- case QUOTE_NONE : out_len += 1; break;
- case QUOTE_SIMPLE : out_len += 2; break;
- case QUOTE_U4 : out_len += 6; break;
- case QUOTE_U8 : out_len += 10; break;
- }
- continue;
- measure_invalid_utf8:
- pos++;
- out_len += 4; /* \xAB */
- }
-
- if (!(state->flags & FMT_FLAG_LEFT)) {
- for (size_t i = 0; i + out_len < state->width; i++) {
- fmt_state_putchar(state, ' ');
- }
- }
-
- fmt_state_putchar(state, '"');
- for (size_t pos = 0; pos < in_len;) {
- if ((in[pos] & 0b10000000) == 0b00000000) { ch = in[pos] & 0b01111111; chlen = 1; }
- else if ((in[pos] & 0b11100000) == 0b11000000) { ch = in[pos] & 0b00011111; chlen = 2; }
- else if ((in[pos] & 0b11110000) == 0b11100000) { ch = in[pos] & 0b00001111; chlen = 3; }
- else if ((in[pos] & 0b11111000) == 0b11110000) { ch = in[pos] & 0b00000111; chlen = 4; }
- else goto output_invalid_utf8;
- if ((ch == 0 && chlen != 1) || pos + chlen > in_len) goto output_invalid_utf8;
- for (uint8_t i = 1; i < chlen; i++) {
- if ((in[pos+i] & 0b11000000) != 0b10000000) goto output_invalid_utf8;
- ch = (ch << 6) | (in[pos+i] & 0b00111111);
- }
- if (ch > 0x10FFFF) goto output_invalid_utf8;
- pos += chlen;
-
- switch (needs_quote(ch)) {
- case QUOTE_NONE:
- fmt_state_putchar(state, ch);
- break;
- case QUOTE_SIMPLE:
- fmt_state_putchar(state, '\\');
- switch (ch) {
- case '\a': fmt_state_putchar(state, 'a'); break;
- case '\b': fmt_state_putchar(state, 'b'); break;
- case '\f': fmt_state_putchar(state, 'f'); break;
- case '\n': fmt_state_putchar(state, 'n'); break;
- case '\r': fmt_state_putchar(state, 'r'); break;
- case '\t': fmt_state_putchar(state, 't'); break;
- case '\v': fmt_state_putchar(state, 'v'); break;
- case '\\': fmt_state_putchar(state, '\\'); break;
- case '\'': fmt_state_putchar(state, '\''); break;
- case '"': fmt_state_putchar(state, '"'); break;
- case '?': fmt_state_putchar(state, '?'); break;
- }
- break;
- case QUOTE_U4:
- fmt_state_putchar(state, '\\');
- fmt_state_putchar(state, 'u');
- fmt_state_putchar(state, (ch >> 12) & 0xF);
- fmt_state_putchar(state, (ch >> 8) & 0xF);
- fmt_state_putchar(state, (ch >> 4) & 0xF);
- fmt_state_putchar(state, (ch >> 0) & 0xF);
- break;
- case QUOTE_U8:
- fmt_state_putchar(state, '\\');
- fmt_state_putchar(state, 'U');
- fmt_state_putchar(state, (ch >> 28) & 0xF);
- fmt_state_putchar(state, (ch >> 24) & 0xF);
- fmt_state_putchar(state, (ch >> 20) & 0xF);
- fmt_state_putchar(state, (ch >> 16) & 0xF);
- fmt_state_putchar(state, (ch >> 12) & 0xF);
- fmt_state_putchar(state, (ch >> 8) & 0xF);
- fmt_state_putchar(state, (ch >> 4) & 0xF);
- fmt_state_putchar(state, (ch >> 0) & 0xF);
- break;
- }
- continue;
- output_invalid_utf8:
- fmt_state_putchar(state, '\\');
- fmt_state_putchar(state, 'x');
- fmt_state_putchar(state, (in[pos] >> 4) & 0xF);
- fmt_state_putchar(state, (in[pos] >> 0) & 0xF);
- pos++;
- }
- fmt_state_putchar(state, '"');
-
- for (size_t i = 0; i + out_len < state->width; i++) {
- fmt_state_putchar(state, ' ');
- }
-}
-
-[[gnu::constructor]]
-static void libfmt_install_quote(void) {
- fmt_install('q', libfmt_conv_quote);
-}