summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build-aux/measurestack/app_main.py5
-rw-r--r--libmisc/CMakeLists.txt2
-rw-r--r--libmisc/fmt.c225
-rw-r--r--libmisc/include/libmisc/fmt.h156
-rw-r--r--libmisc/tests/test_fmt.c170
5 files changed, 558 insertions, 0 deletions
diff --git a/build-aux/measurestack/app_main.py b/build-aux/measurestack/app_main.py
index f705876..f933967 100644
--- a/build-aux/measurestack/app_main.py
+++ b/build-aux/measurestack/app_main.py
@@ -94,6 +94,11 @@ def main(
QName("fmt_vsnprintf"),
]:
return 1, False
+ for prefix in ["fmt_print_", "_fmt_print_"]:
+ if str(name.base()).startswith(prefix):
+ return 1, False
+ if str(name.base()).endswith("_putb"):
+ return 1, False
return 0, False
extra_includes: list[BaseName] = []
diff --git a/libmisc/CMakeLists.txt b/libmisc/CMakeLists.txt
index 8c9fa57..48407bd 100644
--- a/libmisc/CMakeLists.txt
+++ b/libmisc/CMakeLists.txt
@@ -7,6 +7,7 @@ add_library(libmisc INTERFACE)
target_include_directories(libmisc PUBLIC INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_sources(libmisc INTERFACE
assert.c
+ fmt.c
intercept.c
linkedlist.c
log.c
@@ -16,6 +17,7 @@ target_sources(libmisc INTERFACE
add_lib_test(libmisc test_assert)
add_lib_test(libmisc test_assert_min)
add_lib_test(libmisc test_endian)
+add_lib_test(libmisc test_fmt)
add_lib_test(libmisc test_hash)
add_lib_test(libmisc test_log)
add_lib_test(libmisc test_macro)
diff --git a/libmisc/fmt.c b/libmisc/fmt.c
new file mode 100644
index 0000000..33788b6
--- /dev/null
+++ b/libmisc/fmt.c
@@ -0,0 +1,225 @@
+/* libmisc/fmt.c - Write formatted text
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <string.h> /* for strnlen() */
+
+#include <libmisc/fmt.h>
+
+static const char *const hexdig = "0123456789ABCDEF";
+
+/* small/trivial formatters ***************************************************/
+
+void fmt_print_byte(lo_interface fmt_dest w, uint8_t b) {
+ LO_CALL(w, putb, b);
+}
+
+void fmt_print_bool(lo_interface fmt_dest w, bool b) {
+ fmt_print_str(w, b ? "true" : "false");
+}
+
+void fmt_print_base16_u8_(lo_interface fmt_dest w, uint8_t x) {
+ fmt_print(w, "0x", (rjust, 2, '0', (base16, x)));
+}
+void fmt_print_base16_u16_(lo_interface fmt_dest w, uint16_t x) {
+ fmt_print(w, "0x", (rjust, 4, '0', (base16, x)));
+}
+void fmt_print_base16_u32_(lo_interface fmt_dest w, uint32_t x) {
+ fmt_print(w, "0x", (rjust, 8, '0', (base16, x)));
+}
+void fmt_print_base16_u64_(lo_interface fmt_dest w, uint64_t x) {
+ fmt_print(w, "0x", (rjust, 16, '0', (base16, x)));
+}
+
+void fmt_print_ptr(lo_interface fmt_dest w, void *ptr) {
+ LM_CAT3_(fmt_print_base16_u, __INTPTR_WIDTH__, _)(w, (uintptr_t)ptr);
+}
+
+/* quote **********************************************************************/
+
+/**
+ * Quote a byte to ASCII-only C syntax.
+ */
+void fmt_print_qbyte(lo_interface fmt_dest w, uint8_t b) {
+ fmt_print_byte(w, '\'');
+ if (' ' <= b && b <= '~') {
+ if (b == '\'' || b == '\\')
+ fmt_print_byte(w, '\\');
+ fmt_print_byte(w, b);
+ } else {
+ fmt_print_byte(w, '\\');
+ fmt_print_byte(w, 'x');
+ fmt_print_byte(w, hexdig[(b >> 4) & 0xF]);
+ fmt_print_byte(w, hexdig[(b >> 0) & 0xF]);
+ }
+ fmt_print_byte(w, '\'');
+}
+
+/**
+ * Quote a region of memory to ASCII-only C string syntax. Valid
+ * UTF-8 is quoted as short C-escape characters, \uABCD, or
+ * \UABCDABCD; invalid UTF-8 is quoted as \xAB.
+ */
+void fmt_print_qmem(lo_interface fmt_dest w, const void *_str, size_t size) {
+ const uint8_t *str = _str;
+ fmt_print_byte(w, '"');
+ for (size_t pos = 0; pos < size;) {
+ uint32_t ch;
+ uint8_t chlen;
+ if ((str[pos] & 0b10000000) == 0b00000000) { ch = str[pos] & 0b01111111; chlen = 1; }
+ else if ((str[pos] & 0b11100000) == 0b11000000) { ch = str[pos] & 0b00011111; chlen = 2; }
+ else if ((str[pos] & 0b11110000) == 0b11100000) { ch = str[pos] & 0b00001111; chlen = 3; }
+ else if ((str[pos] & 0b11111000) == 0b11110000) { ch = str[pos] & 0b00000111; chlen = 4; }
+ else goto invalid_utf8;
+ if ((ch == 0 && chlen != 1) || pos + chlen > size) goto invalid_utf8;
+ for (uint8_t i = 1; i < chlen; i++) {
+ if ((str[pos+i] & 0b11000000) != 0b10000000) goto invalid_utf8;
+ ch = (ch << 6) | (str[pos+i] & 0b00111111);
+ }
+ if (ch > 0x10FFFF) goto invalid_utf8;
+
+ if (ch == '\0' ||
+ ch == '\b' ||
+ ch == '\f' ||
+ ch == '\n' ||
+ ch == '\r' ||
+ ch == '\t' ||
+ ch == '\v' ||
+ ch == '\\' ||
+ ch == '\'' ||
+ ch == '"' ||
+ ch == '?') {
+ /* short C-escape */
+ fmt_print_byte(w, '\\');
+ switch (ch) {
+ case '\0': fmt_print_byte(w, '0'); break;
+ case '\a': fmt_print_byte(w, 'a'); break;
+ case '\b': fmt_print_byte(w, 'b'); break;
+ case '\f': fmt_print_byte(w, 'f'); break;
+ case '\n': fmt_print_byte(w, 'n'); break;
+ case '\r': fmt_print_byte(w, 'r'); break;
+ case '\t': fmt_print_byte(w, 't'); break;
+ case '\v': fmt_print_byte(w, 'v'); break;
+ case '\\': fmt_print_byte(w, '\\'); break;
+ case '\'': fmt_print_byte(w, '\''); break;
+ case '"': fmt_print_byte(w, '"'); break;
+ case '?': fmt_print_byte(w, '?'); break;
+ }
+ } else if (' ' <= ch && ch <= '~') {
+ /* no escaping */
+ fmt_print_byte(w, ch);
+ } else if (ch < 0x10000) {
+ /* \uABCD */
+ fmt_print_byte(w, '\\');
+ fmt_print_byte(w, 'u');
+ fmt_print_byte(w, hexdig[(ch >> 12) & 0xF]);
+ fmt_print_byte(w, hexdig[(ch >> 8) & 0xF]);
+ fmt_print_byte(w, hexdig[(ch >> 4) & 0xF]);
+ fmt_print_byte(w, hexdig[(ch >> 0) & 0xF]);
+ } else {
+ /* \UABCDABCD */
+ fmt_print_byte(w, '\\');
+ fmt_print_byte(w, 'U');
+ fmt_print_byte(w, hexdig[(ch >> 28) & 0xF]);
+ fmt_print_byte(w, hexdig[(ch >> 24) & 0xF]);
+ fmt_print_byte(w, hexdig[(ch >> 20) & 0xF]);
+ fmt_print_byte(w, hexdig[(ch >> 16) & 0xF]);
+ fmt_print_byte(w, hexdig[(ch >> 12) & 0xF]);
+ fmt_print_byte(w, hexdig[(ch >> 8) & 0xF]);
+ fmt_print_byte(w, hexdig[(ch >> 4) & 0xF]);
+ fmt_print_byte(w, hexdig[(ch >> 0) & 0xF]);
+ }
+ pos += chlen;
+ continue;
+
+ invalid_utf8:
+ /* \xAB */
+ fmt_print_byte(w, '\\');
+ fmt_print_byte(w, 'x');
+ fmt_print_byte(w, hexdig[(str[pos] >> 4) & 0xF]);
+ fmt_print_byte(w, hexdig[(str[pos] >> 0) & 0xF]);
+ pos++;
+ }
+ fmt_print_byte(w, '"');
+}
+
+void fmt_print_qstr(lo_interface fmt_dest w, const char *str) {
+ fmt_print_qmem(w, str, strlen(str));
+}
+
+void fmt_print_qstrn(lo_interface fmt_dest w, const char *str, size_t n) {
+ fmt_print_qmem(w, str, strnlen(str, n));
+}
+
+/* int ************************************************************************/
+
+#define declare(BASE, BITS) \
+ void _fmt_print_base##BASE##_s##BITS(lo_interface fmt_dest w, \
+ int##BITS##_t val) { \
+ if (val < 0) { \
+ fmt_print_byte(w, '-'); \
+ val = -val; \
+ } \
+ _fmt_print_base##BASE##_u##BITS(w, (uint##BITS##_t)val); \
+ } \
+ \
+ void _fmt_print_base##BASE##_u##BITS(lo_interface fmt_dest w, \
+ uint##BITS##_t absval) { \
+ /* This digit-counting is O(log(absval)); there are \
+ * `__builtin_clz`-based O(1) ways to do this, but when I \
+ * tried them they bloated the code-size too much. And this \
+ * function as a whole is already O(log(absval)) anyway \
+ * because of actually printing the digits. */ \
+ unsigned ndigits = 1; \
+ uint##BITS##_t div = 1; \
+ while (absval / div >= BASE) { \
+ div *= BASE; \
+ ndigits++; \
+ } \
+ \
+ for (unsigned i = 0; i < ndigits; i++) { \
+ unsigned digit = (unsigned) (absval / div); \
+ absval %= div; \
+ div /= BASE; \
+ fmt_print_byte(w, hexdig[digit]); \
+ } \
+ } \
+ LM_FORCE_SEMICOLON
+
+declare(2, 8);
+declare(2, 16);
+declare(2, 32);
+declare(2, 64);
+
+declare(8, 8);
+declare(8, 16);
+declare(8, 32);
+declare(8, 64);
+
+declare(10, 8);
+declare(10, 16);
+declare(10, 32);
+declare(10, 64);
+
+declare(16, 8);
+declare(16, 16);
+declare(16, 32);
+declare(16, 64);
+
+#undef declare
+
+/* fmt_buf ********************************************************************/
+
+LO_IMPLEMENTATION_C(fmt_dest, struct fmt_buf, fmt_buf, static);
+
+static void fmt_buf_putb(struct fmt_buf *buf, uint8_t b) {
+ if (buf->len < buf->cap)
+ ((uint8_t *)(buf->dat))[buf->len] = b;
+ buf->len++;
+}
+
+static size_t fmt_buf_tell(struct fmt_buf *buf) {
+ return buf->len;
+}
diff --git a/libmisc/include/libmisc/fmt.h b/libmisc/include/libmisc/fmt.h
new file mode 100644
index 0000000..c29c085
--- /dev/null
+++ b/libmisc/include/libmisc/fmt.h
@@ -0,0 +1,156 @@
+/* libmisc/fmt.h - Write formatted text
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _LIBMISC_FMT_H_
+#define _LIBMISC_FMT_H_
+
+#include <stddef.h> /* for size_t */
+#include <stdint.h> /* for (u)int{n}_t */
+
+#include <libmisc/macro.h>
+#include <libmisc/obj.h>
+
+/* destination interface ******************************************************/
+
+#define fmt_dest_LO_IFACE \
+ LO_FUNC(void , putb, uint8_t b) \
+ LO_FUNC(size_t, tell)
+LO_INTERFACE(fmt_dest);
+
+/* type-specific fmt_print_() functions ***************************************/
+
+/* Simple bytes. */
+void fmt_print_byte(lo_interface fmt_dest w, uint8_t b);
+
+/* These are `static inline` so that the compiler can unroll the loops. */
+static inline void fmt_print_mem(lo_interface fmt_dest w, const void *_str, size_t size) {
+ const uint8_t *str = _str;
+ while (size--)
+ fmt_print_byte(w, *(str++));
+}
+static inline void fmt_print_str(lo_interface fmt_dest w, const char *str) {
+ while (*str)
+ fmt_print_byte(w, *(str++));
+}
+static inline void fmt_print_strn(lo_interface fmt_dest w, const char *str, size_t size) {
+ while (size-- && *str)
+ fmt_print_byte(w, *(str++));
+}
+
+/* Quoted bytes. */
+void fmt_print_qbyte(lo_interface fmt_dest w, uint8_t b);
+void fmt_print_qmem(lo_interface fmt_dest w, const void *str, size_t size);
+void fmt_print_qstr(lo_interface fmt_dest w, const char *str);
+void fmt_print_qstrn(lo_interface fmt_dest w, const char *str, size_t size);
+
+/* Integers. */
+#define _fmt_declare_base(base) \
+ void _fmt_print_base##base##_u8(lo_interface fmt_dest w, uint8_t val); \
+ void _fmt_print_base##base##_u16(lo_interface fmt_dest w, uint16_t val); \
+ void _fmt_print_base##base##_u32(lo_interface fmt_dest w, uint32_t val); \
+ void _fmt_print_base##base##_u64(lo_interface fmt_dest w, uint64_t val); \
+ void _fmt_print_base##base##_s8(lo_interface fmt_dest w, int8_t val); \
+ void _fmt_print_base##base##_s16(lo_interface fmt_dest w, int16_t val); \
+ void _fmt_print_base##base##_s32(lo_interface fmt_dest w, int32_t val); \
+ void _fmt_print_base##base##_s64(lo_interface fmt_dest w, int64_t val); \
+ LM_FORCE_SEMICOLON
+_fmt_declare_base(2);
+_fmt_declare_base(8);
+_fmt_declare_base(10);
+_fmt_declare_base(16);
+#undef _fmt_declare_base
+
+#define _fmt_intswitch(val, prefix) _Generic((val) , \
+ unsigned char : LM_CAT3_(prefix, u, __SCHAR_WIDTH__) , \
+ unsigned short : LM_CAT3_(prefix, u, __SHRT_WIDTH__) , \
+ unsigned int : LM_CAT3_(prefix, u, __INT_WIDTH__) , \
+ unsigned long : LM_CAT3_(prefix, u, __LONG_WIDTH__) , \
+ unsigned long long : LM_CAT3_(prefix, u, __LONG_LONG_WIDTH__) , \
+ signed char : LM_CAT3_(prefix, s, __SCHAR_WIDTH__) , \
+ signed short : LM_CAT3_(prefix, s, __SHRT_WIDTH__) , \
+ signed int : LM_CAT3_(prefix, s, __INT_WIDTH__) , \
+ signed long : LM_CAT3_(prefix, s, __LONG_WIDTH__) , \
+ signed long long : LM_CAT3_(prefix, s, __LONG_LONG_WIDTH__) )
+#define fmt_print_base2(w, val) (_fmt_intswitch((val), _fmt_print_base2_)((w), (val)))
+#define fmt_print_base8(w, val) (_fmt_intswitch((val), _fmt_print_base8_)((w), (val)))
+#define fmt_print_base10(w, val) (_fmt_intswitch((val), _fmt_print_base10_)((w), (val)))
+#define fmt_print_base16(w, val) (_fmt_intswitch((val), _fmt_print_base16_)((w), (val)))
+
+/* Booleans. */
+void fmt_print_bool(lo_interface fmt_dest w, bool b);
+
+/* The high-level fmt_print() interface ***************************************/
+
+#define fmt_print(w, ...) do { LM_FOREACH_PARAM_(_fmt_param, (w), __VA_ARGS__) } while (0)
+#define _fmt_param(w, arg) \
+ LM_IF(LM_IS_TUPLE(arg))( \
+ _fmt_param_tuple LM_EAT() (w, LM_EXPAND arg) \
+ )( \
+ _fmt_param_nontuple(w, arg) \
+ );
+#define _fmt_param_tuple(w, fn, ...) fmt_print_##fn(w __VA_OPT__(,) __VA_ARGS__)
+#define _fmt_param_nontuple(w, val) _Generic((val), \
+ unsigned char : LM_CAT2_(_fmt_print_base10_u, __SCHAR_WIDTH__) , \
+ unsigned short : LM_CAT2_(_fmt_print_base10_u, __SHRT_WIDTH__) , \
+ unsigned int : LM_CAT2_(_fmt_print_base10_u, __INT_WIDTH__) , \
+ unsigned long : LM_CAT2_(_fmt_print_base10_u, __LONG_WIDTH__) , \
+ unsigned long long : LM_CAT2_(_fmt_print_base10_u, __LONG_LONG_WIDTH__) , \
+ signed char : LM_CAT2_(_fmt_print_base10_s, __SCHAR_WIDTH__) , \
+ signed short : LM_CAT2_(_fmt_print_base10_s, __SHRT_WIDTH__) , \
+ signed int : LM_CAT2_(_fmt_print_base10_s, __INT_WIDTH__) , \
+ signed long : LM_CAT2_(_fmt_print_base10_s, __LONG_WIDTH__) , \
+ signed long long : LM_CAT2_(_fmt_print_base10_s, __LONG_LONG_WIDTH__) , \
+ char * : fmt_print_str , \
+ const char * : fmt_print_str , \
+ bool : fmt_print_bool )(w, val)
+
+/* print-to-memory ************************************************************/
+
+struct fmt_buf {
+ void *dat;
+ size_t len, cap;
+};
+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; \
+})
+
+/* justify ********************************************************************/
+
+/* *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__)
+
+#define fmt_print_ljust(w, width, fillchar, ...) do { \
+ size_t beg = LO_CALL(w, tell); \
+ _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__); \
+} while (0)
+
+void fmt_print_base16_u8_(lo_interface fmt_dest w, uint8_t x);
+void fmt_print_base16_u16_(lo_interface fmt_dest w, uint16_t x);
+void fmt_print_base16_u32_(lo_interface fmt_dest w, uint32_t x);
+void fmt_print_base16_u64_(lo_interface fmt_dest w, uint64_t x);
+void fmt_print_ptr(lo_interface fmt_dest w, void *ptr);
+
+#endif /* _LIBMISC_FMT_H_ */
diff --git a/libmisc/tests/test_fmt.c b/libmisc/tests/test_fmt.c
new file mode 100644
index 0000000..6a6eb7c
--- /dev/null
+++ b/libmisc/tests/test_fmt.c
@@ -0,0 +1,170 @@
+/* libmisc/tests/test_fmt.c - Tests for <libmisc/fmt.h>
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <string.h> /* for strcmp(), memcmp(), memset() */
+
+#include <libmisc/fmt.h>
+
+#include "test.h"
+
+int main() {
+ char str[128] = {};
+#define do_print(...) fmt_snprint(str, sizeof(str), __VA_ARGS__)
+
+ do_print("hello ", 9, " world!\n");
+ test_assert(strcmp(str, "hello 9 world!\n") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print("hello ", (base8, 9), " world!\n");
+ test_assert(strcmp(str, "hello 11 world!\n") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print("hello ", (base2, 9), (qstr, " world!\n"));
+ test_assert(strcmp(str, "hello 1001\" world!\\n\"") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print("hello ", (base16, 17), " world!\n");
+ test_assert(strcmp(str, "hello 11 world!\n") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((strn, "hello ", 4));
+ test_assert(strcmp(str, "hell") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((strn, "h\0ello ", 4));
+ test_assert(memcmp(str, "h\0\0", 3) == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((mem, "hello ", 4));
+ test_assert(strcmp(str, "hell") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((mem, "hello\0world", strlen("hello world")+1));
+ test_assert(memcmp(str, "hello\0world", strlen("hello world")+1) == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((qmem, "hello\0world", strlen("hello world")+1));
+ test_assert(strcmp(str, "\"hello\\0world\\0\"") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((qstr, "hello\0world"));
+ test_assert(strcmp(str, "\"hello\"") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((qstrn, "hello\0world", strlen("hello world")+1));
+ test_assert(strcmp(str, "\"hello\"") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((qstrn, "hello\0world", 4));
+ test_assert(strcmp(str, "\"hell\"") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((byte, 'h'), (byte, 'w'));
+ test_assert(strcmp(str, "hw") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((qbyte, 'h'), (qbyte, 'w'));
+ test_assert(strcmp(str, "'h''w'") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print("zero ", 0);
+ test_assert(strcmp(str, "zero 0") == 0);
+ memset(str, 0, sizeof(str));
+
+ const char *const_str = "hello";
+ do_print(const_str);
+ test_assert(strcmp(str, "hello") == 0);
+ memset(str, 0, sizeof(str));
+
+ bool t = true;
+ do_print(t);
+ test_assert(strcmp(str, "true") == 0);
+ memset(str, 0, sizeof(str));
+
+ bool f = false;
+ do_print(f);
+ test_assert(strcmp(str, "false") == 0);
+ memset(str, 0, sizeof(str));
+
+ /* Check that it accepts all primitive types of integer, not
+ * just all sizes of integer (e.g., on x86-64,
+ * sizeof(long)==sizeof(int), but they're different primitive
+ * types). */
+ {
+ signed char x = 42;
+ do_print("schar ", x);
+ test_assert(strcmp(str, "schar 42") == 0);
+ memset(str, 0, sizeof(str));
+ }
+ {
+ unsigned char x = 43;
+ do_print("uchar ", x);
+ test_assert(strcmp(str, "uchar 43") == 0);
+ memset(str, 0, sizeof(str));
+ }
+
+ {
+ short x = 44;
+ do_print("short ", x);
+ test_assert(strcmp(str, "short 44") == 0);
+ memset(str, 0, sizeof(str));
+ }
+ {
+ unsigned short x = 45;
+ do_print("ushort ", x);
+ test_assert(strcmp(str, "ushort 45") == 0);
+ memset(str, 0, sizeof(str));
+ }
+
+ {
+ int x = 46;
+ do_print("int ", x);
+ test_assert(strcmp(str, "int 46") == 0);
+ memset(str, 0, sizeof(str));
+ }
+ {
+ unsigned int x = 47;
+ do_print("uint ", x);
+ test_assert(strcmp(str, "uint 47") == 0);
+ memset(str, 0, sizeof(str));
+ }
+
+ {
+ long x = 48;
+ do_print("long ", x);
+ test_assert(strcmp(str, "long 48") == 0);
+ memset(str, 0, sizeof(str));
+ }
+ {
+ unsigned long x = 49;
+ do_print("ulong ", x);
+ test_assert(strcmp(str, "ulong 49") == 0);
+ memset(str, 0, sizeof(str));
+ }
+
+ {
+ long long x = 50;
+ do_print("long long ", x);
+ test_assert(strcmp(str, "long long 50") == 0);
+ memset(str, 0, sizeof(str));
+ }
+ {
+ unsigned long long x = 51;
+ do_print("ulong long ", x);
+ test_assert(strcmp(str, "ulong long 51") == 0);
+ memset(str, 0, sizeof(str));
+ }
+
+ do_print((ljust, 10, ' ', (base10, 1), "x"));
+ test_assert(strcmp(str, "1x ") == 0);
+ memset(str, 0, sizeof(str));
+
+ do_print((rjust, 10, ' ', (base10, 1), "x"));
+ test_assert(strcmp(str, " 1x") == 0);
+ memset(str, 0, sizeof(str));
+
+ return 0;
+}