diff options
-rw-r--r-- | .gitmodules | 3 | ||||
m--------- | 3rd-party/pico-fmt | 0 | ||||
m--------- | 3rd-party/pico-sdk | 0 | ||||
-rw-r--r-- | CMakeLists.txt | 33 | ||||
-rwxr-xr-x | build-aux/lint-bin | 1 | ||||
-rw-r--r-- | build-aux/measurestack/app_main.py | 3 | ||||
-rw-r--r-- | build-aux/measurestack/app_plugins.py | 97 | ||||
-rw-r--r-- | build-aux/measurestack/test_app_plugins.py | 70 | ||||
-rw-r--r-- | cmd/sbc_harness/CMakeLists.txt | 6 | ||||
-rw-r--r-- | cmd/sbc_harness/config/tusb_config.h | 45 | ||||
-rw-r--r-- | cmd/sbc_harness/fs_harness_uptime_txt.c | 5 | ||||
-rw-r--r-- | cmd/sbc_harness/main.c | 3 | ||||
l--------- | cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/printf.mit.txt | 1 | ||||
-rw-r--r-- | cmd/sbc_harness/tusb_log.c | 9 | ||||
-rw-r--r-- | libfmt/CMakeLists.txt | 15 | ||||
-rw-r--r-- | libfmt/include/libfmt/fmt.h | 23 | ||||
-rw-r--r-- | libfmt/libmisc.c | 17 | ||||
-rw-r--r-- | libfmt/quote.c | 159 |
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); -} |