summaryrefslogtreecommitdiff
path: root/lib9p/core_gen/c_fmt_print.py
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2025-05-14 16:37:27 -0600
committerLuke T. Shumaker <lukeshu@lukeshu.com>2025-05-15 14:56:35 -0600
commit9c0338b1b4495457659157e1e9f47d422dcefc2e (patch)
tree9102ac3a979f082e1c39f9d4223df5f98a7be0c2 /lib9p/core_gen/c_fmt_print.py
parent67cec6d2770aa14a13c89247612f16c628ebd54c (diff)
lib9p: Remove uses of printf
Diffstat (limited to 'lib9p/core_gen/c_fmt_print.py')
-rw-r--r--lib9p/core_gen/c_fmt_print.py146
1 files changed, 146 insertions, 0 deletions
diff --git a/lib9p/core_gen/c_fmt_print.py b/lib9p/core_gen/c_fmt_print.py
new file mode 100644
index 0000000..eaacddb
--- /dev/null
+++ b/lib9p/core_gen/c_fmt_print.py
@@ -0,0 +1,146 @@
+# lib9p/core_gen/c_fmt_print.py - Generate C pretty-print functions
+#
+# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+
+import idl
+
+from . import c9util, cutil, idlutil
+
+# This strives to be "general-purpose" in that it just acts on the
+# *.9p inputs; but (unfortunately?) there are a few special-cases in
+# this script, marked with "SPECIAL".
+
+# pylint: disable=unused-variable
+__all__ = ["gen_c_fmt_print"]
+
+
+def bf_numname(typ: idl.Bitfield, num: idl.BitNum, base: str) -> str:
+ prefix = f"{typ.typname}_{num.numname}_".upper()
+ return c9util.Ident(c9util.add_prefix(prefix, base))
+
+
+def gen_c_fmt_print(versions: set[str], typs: list[idl.UserType]) -> str:
+ ret = """
+/* fmt_print_* ****************************************************************/
+"""
+ for typ in idlutil.topo_sorted(typs):
+ ret += "\n"
+ ret += cutil.ifdef_push(1, c9util.ver_ifdef(typ.in_versions))
+ storage = "" if typ.typname == "stat" else "static " # SPECIAL (stat is public)
+ ret += f"[[maybe_unused]] {storage}void fmt_print_{typ.typname}(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, {c9util.typename(typ)} *self) {{\n"
+ match typ:
+ case idl.Number():
+ if typ.vals:
+ ret += "\tswitch (*self) {\n"
+ for name in typ.vals:
+ ret += f"\tcase {c9util.Ident(c9util.add_prefix(f'{typ.typname}_'.upper(), name))}:\n"
+ ret += f'\t\tfmt_print_str(w, "{name}");\n'
+ ret += "\t\tbreak;\n"
+ ret += "\tdefault:\n"
+ ret += "\t\tfmt_print_base10(w, *self);\n"
+ ret += "\t}\n"
+ else:
+ ret += "\t\tfmt_print_base10(w, *self);\n"
+ case idl.Bitfield():
+ val = "*self"
+ if typ.typname == "dm": # SPECIAL (pretty file permissions)
+ val = f"(*self & ~(({c9util.typename(typ)})0777))"
+ ret += "\tbool empty = true;\n"
+ ret += "\tfmt_print_byte(w, '(');\n"
+ nums: set[str] = set()
+
+ for bit in reversed(typ.bits):
+ match bit.cat:
+ case "UNUSED" | "USED" | "RESERVED":
+ if bit.cat == "UNUSED":
+ bitname = f"1<<{bit.num}"
+ else:
+ bitname = bit.bitname
+ ret += f"\tif ({val} & (UINT{typ.static_size*8}_C(1)<<{bit.num})) {{\n"
+ ret += "\t\tif (!empty)\n"
+ ret += "\t\t\tfmt_print_byte(w, '|');\n"
+ ret += f'\t\tfmt_print_str(w, "{bitname}");\n'
+ ret += "\t\tempty = false;\n"
+ ret += "\t}\n"
+ case idl.BitNum():
+ if bit.cat.numname in nums:
+ continue
+ ret += f"\tswitch ({val} & {bf_numname(typ, bit.cat, 'MASK')}) {{\n"
+ for name in bit.cat.vals:
+ ret += f"\tcase {bf_numname(typ, bit.cat, name)}:\n"
+ bitname = c9util.add_prefix(
+ f"{bit.cat.numname}_".upper(), name
+ )
+ ret += "\t\tif (!empty)\n"
+ ret += "\t\t\tfmt_print_byte(w, '|');\n"
+ ret += f'\t\tfmt_print_str(w, "{bitname}");\n'
+ ret += "\t\tempty = false;\n"
+ ret += "\t\tbreak;\n"
+ ret += "\tdefault:\n"
+ ret += "\t\tif (!empty)\n"
+ ret += "\t\t\tfmt_print_byte(w, '|');\n"
+ ret += f"\t\tfmt_print_base10(w, {val} & {bf_numname(typ, bit.cat, 'MASK')});\n"
+ ret += "\t\tempty = false;\n"
+ ret += "\t}\n"
+ nums.add(bit.cat.numname)
+ if typ.typname == "dm": # SPECIAL (pretty file permissions)
+ ret += "\tif (!empty)\n"
+ ret += "\t\tfmt_print_byte(w, '|');\n"
+ ret += "\tfmt_print(w, (rjust, 4, '0', (base8, *self & 0777)));\n"
+ else:
+ ret += "\tif (empty)\n"
+ ret += "\t\tfmt_print_byte(w, '0');\n"
+ ret += "\tfmt_print_byte(w, ')');\n"
+ case idl.Struct(typname="s"): # SPECIAL (string)
+ ret += "\tfmt_print_qmem(w, self->utf8, self->len);\n"
+ case idl.Struct(): # and idl.Message():
+ if isinstance(typ, idl.Message):
+ ret += f'\tfmt_print_str(w, "{typ.typname} {{");\n'
+ else:
+ ret += "\tfmt_print_byte(w, '{');\n"
+ for member in typ.members:
+ if member.val:
+ continue
+ ret += cutil.ifdef_push(2, c9util.ver_ifdef(member.in_versions))
+ if member.cnt:
+ if isinstance(member.cnt, int):
+ cnt_str = str(member.cnt)
+ cnt_typ = "size_t"
+ else:
+ cnt_str = f"self->{member.cnt.membname}"
+ cnt_typ = c9util.typename(member.cnt.typ)
+ if member.typ.static_size == 1: # SPECIAL (data)
+ ret += f"\tif (is_valid_utf8_without_nul((uint8_t *)self->{member.membname}, (size_t){cnt_str})) {{\n"
+ ret += f'\t\tfmt_print_str(w, " {member.membname}=");\n'
+ ret += f"\t\tfmt_print_qmem(w, self->{member.membname}, {cnt_str} < 50 ? {cnt_str} : 50);\n"
+ ret += f"\t\tif ({cnt_str} > 50)\n"
+ ret += '\t\t\tfmt_print_str(w, "...");\n'
+ ret += "\t} else {\n"
+ ret += f'\t\tfmt_print_str(w, " {member.membname}=<bytedata>");\n'
+ ret += "\t}\n"
+ continue
+ ret += f'\tfmt_print_str(w, " {member.membname}=[");\n'
+ ret += f"\tfor ({cnt_typ} i = 0; i < {cnt_str}; i++) {{\n"
+ ret += "\t\tif (i)\n"
+ ret += "\t\t\tfmt_print_byte(w, ',');\n"
+ ret += "\t\tfmt_print_byte(w, ' ');\n"
+ if isinstance(member.typ, idl.Primitive):
+ ret += f"\t\tfmt_print_base10(w, self->{member.membname}[i]);\n"
+ else:
+ ret += f"\t\tfmt_print_{member.typ.typname}(w, ctx, &self->{member.membname}[i]);\n"
+ ret += "\t}\n"
+ ret += '\tfmt_print_str(w, " ]");\n'
+ else:
+ ret += f'\tfmt_print_str(w, " {member.membname}=");\n'
+ if isinstance(member.typ, idl.Primitive):
+ ret += f"\tfmt_print_base10(w, self->{member.membname});\n"
+ else:
+ ret += f"\tfmt_print_{member.typ.typname}(w, ctx, &self->{member.membname});\n"
+ ret += cutil.ifdef_pop(1)
+ ret += '\tfmt_print_str(w, " }");\n'
+ ret += "}\n"
+ ret += cutil.ifdef_pop(0)
+
+ return ret