# lib9p/core_gen/c_fmt_print.py - Generate C pretty-print functions # # Copyright (C) 2024-2025 Luke T. Shumaker # 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}=");\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