diff options
-rw-r--r-- | .editorconfig | 7 | ||||
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | GNUmakefile | 5 | ||||
-rw-r--r-- | lib9p/9p.generated.c | 91 | ||||
-rwxr-xr-x | lib9p/idl.gen | 876 | ||||
-rw-r--r-- | lib9p/idl/0000-README.md | 2 | ||||
-rw-r--r-- | lib9p/idl/1992-9P0.9p.wip | 2 | ||||
-rw-r--r-- | lib9p/idl/1995-9P1.9p.wip | 2 | ||||
-rw-r--r-- | lib9p/idl/1996-Styx.9p.wip | 2 | ||||
-rw-r--r-- | lib9p/idl/2002-9P2000.9p | 4 | ||||
-rw-r--r-- | lib9p/idl/2005-9P2000.u.9p | 2 | ||||
-rw-r--r-- | lib9p/idl/2010-9P2000.L.9p.wip | 2 | ||||
-rw-r--r-- | lib9p/idl/2012-9P2000.e.9p | 2 | ||||
-rw-r--r-- | lib9p/idl/__init__.py | 491 | ||||
-rw-r--r-- | lib9p/include/lib9p/9p.generated.h | 90 |
15 files changed, 824 insertions, 755 deletions
diff --git a/.editorconfig b/.editorconfig index 62b7bb2..b95a6ff 100644 --- a/.editorconfig +++ b/.editorconfig @@ -36,7 +36,12 @@ _mode = sh [{build-aux/linux-errno.txt.gen,libusb/include/libusb/tusb_helpers.h.gen}] _mode = bash -[{lib9p/idl.gen,lib9p/include/lib9p/linux-errno.h.gen,build-aux/stack.c.gen,gdb-helpers/*.py}] +[{lib9p/idl.gen,lib9p/include/lib9p/linux-errno.h.gen,build-aux/stack.c.gen}] +_mode = python3 +indent_style = space +indent_size = 4 + +[*.py] _mode = python3 indent_style = space indent_size = 4 @@ -7,6 +7,7 @@ *.log *.tmp .mypy_cache/ +__pycache__/ .gdb_history /build/ diff --git a/GNUmakefile b/GNUmakefile index 9ebbe86..9f5ad61 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -34,8 +34,8 @@ lib9p/include/lib9p/linux-errno.h: %: %.gen 3rd-party/linux-errno.txt $^ >$@ generate/files += lib9p/9p.generated.c lib9p/include/lib9p/9p.generated.h -lib9p/9p.generated.c lib9p/include/lib9p/9p.generated.h &: lib9p/idl.gen lib9p/idl/*.9p - $^ +lib9p/9p.generated.c lib9p/include/lib9p/9p.generated.h &: lib9p/idl.gen lib9p/idl/__init__.py lib9p/idl lib9p/idl/*.9p + $< $(filter %.9p,$^) generate/files += libusb/include/libusb/tusb_helpers.h 3rd-party/MS-LCID.pdf 3rd-party/MS-LCID.txt libusb/include/libusb/tusb_helpers.h 3rd-party/MS-LCID.pdf 3rd-party/MS-LCID.txt &: libusb/include/libusb/tusb_helpers.h.gen @@ -133,7 +133,6 @@ lint/all: lint/%: -e 's,.*/config/,,' \ -e 's,.*/config\.h$$,config.h,' \ -e 's,.*include/,,' \ - -e 's,^lib9p/idl/,,' \ -e 's/\.wip$$//'); \ if [ "$$dscname_act" != "$$dscname_exp" ] && [ "cmd/$$dscname_act" != "$$dscname_exp" ]; then \ echo "$$filename self-identifies as $$dscname_act (expected $$dscname_exp)"; r=1; \ diff --git a/lib9p/9p.generated.c b/lib9p/9p.generated.c index d3dc36a..c260647 100644 --- a/lib9p/9p.generated.c +++ b/lib9p/9p.generated.c @@ -34,6 +34,46 @@ const char *lib9p_version_str(enum lib9p_version ver) { return version_strs[ver]; } +/* bitmasks *******************************************************************/ + +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_u +static const lib9p_dm_t dm_masks[LIB9P_VER_NUM] = { +#if CONFIG_9P_ENABLE_9P2000 + [LIB9P_VER_9P2000] = 0b11101100000000000000000111111111, +#endif /* CONFIG_9P_ENABLE_9P2000 */ +#if CONFIG_9P_ENABLE_9P2000_e + [LIB9P_VER_9P2000_e] = 0b11101100000000000000000111111111, +#endif /* CONFIG_9P_ENABLE_9P2000_e */ +#if CONFIG_9P_ENABLE_9P2000_u + [LIB9P_VER_9P2000_u] = 0b11101100101111000000000111111111, +#endif /* CONFIG_9P_ENABLE_9P2000_u */ +}; + +static const lib9p_qt_t qt_masks[LIB9P_VER_NUM] = { +#if CONFIG_9P_ENABLE_9P2000 + [LIB9P_VER_9P2000] = 0b11101100, +#endif /* CONFIG_9P_ENABLE_9P2000 */ +#if CONFIG_9P_ENABLE_9P2000_e + [LIB9P_VER_9P2000_e] = 0b11101100, +#endif /* CONFIG_9P_ENABLE_9P2000_e */ +#if CONFIG_9P_ENABLE_9P2000_u + [LIB9P_VER_9P2000_u] = 0b11101110, +#endif /* CONFIG_9P_ENABLE_9P2000_u */ +}; + +static const lib9p_o_t o_masks[LIB9P_VER_NUM] = { +#if CONFIG_9P_ENABLE_9P2000 + [LIB9P_VER_9P2000] = 0b01010011, +#endif /* CONFIG_9P_ENABLE_9P2000 */ +#if CONFIG_9P_ENABLE_9P2000_e + [LIB9P_VER_9P2000_e] = 0b01010011, +#endif /* CONFIG_9P_ENABLE_9P2000_e */ +#if CONFIG_9P_ENABLE_9P2000_u + [LIB9P_VER_9P2000_u] = 0b01010011, +#endif /* CONFIG_9P_ENABLE_9P2000_u */ +}; +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_u */ + /* validate_* *****************************************************************/ LM_ALWAYS_INLINE static bool _validate_size_net(struct _validate_ctx *ctx, uint32_t n) { @@ -99,17 +139,6 @@ LM_ALWAYS_INLINE static bool validate_s(struct _validate_ctx *ctx) { return false; } -static const lib9p_dm_t dm_masks[LIB9P_VER_NUM] = { -#if CONFIG_9P_ENABLE_9P2000 - [LIB9P_VER_9P2000] = 0b11101100000000000000000111111111, -#endif /* CONFIG_9P_ENABLE_9P2000 */ -#if CONFIG_9P_ENABLE_9P2000_e - [LIB9P_VER_9P2000_e] = 0b11101100000000000000000111111111, -#endif /* CONFIG_9P_ENABLE_9P2000_e */ -#if CONFIG_9P_ENABLE_9P2000_u - [LIB9P_VER_9P2000_u] = 0b11101100101111000000000111111111, -#endif /* CONFIG_9P_ENABLE_9P2000_u */ -}; LM_ALWAYS_INLINE static bool validate_dm(struct _validate_ctx *ctx) { if (validate_4(ctx)) return true; @@ -120,17 +149,6 @@ LM_ALWAYS_INLINE static bool validate_dm(struct _validate_ctx *ctx) { return false; } -static const lib9p_qt_t qt_masks[LIB9P_VER_NUM] = { -#if CONFIG_9P_ENABLE_9P2000 - [LIB9P_VER_9P2000] = 0b11101100, -#endif /* CONFIG_9P_ENABLE_9P2000 */ -#if CONFIG_9P_ENABLE_9P2000_e - [LIB9P_VER_9P2000_e] = 0b11101100, -#endif /* CONFIG_9P_ENABLE_9P2000_e */ -#if CONFIG_9P_ENABLE_9P2000_u - [LIB9P_VER_9P2000_u] = 0b11101110, -#endif /* CONFIG_9P_ENABLE_9P2000_u */ -}; LM_ALWAYS_INLINE static bool validate_qt(struct _validate_ctx *ctx) { if (validate_1(ctx)) return true; @@ -176,17 +194,6 @@ LM_ALWAYS_INLINE static bool validate_stat(struct _validate_ctx *ctx) { ; } -static const lib9p_o_t o_masks[LIB9P_VER_NUM] = { -#if CONFIG_9P_ENABLE_9P2000 - [LIB9P_VER_9P2000] = 0b01010011, -#endif /* CONFIG_9P_ENABLE_9P2000 */ -#if CONFIG_9P_ENABLE_9P2000_e - [LIB9P_VER_9P2000_e] = 0b01010011, -#endif /* CONFIG_9P_ENABLE_9P2000_e */ -#if CONFIG_9P_ENABLE_9P2000_u - [LIB9P_VER_9P2000_u] = 0b01010011, -#endif /* CONFIG_9P_ENABLE_9P2000_u */ -}; LM_ALWAYS_INLINE static bool validate_o(struct _validate_ctx *ctx) { if (validate_1(ctx)) return true; @@ -1784,15 +1791,15 @@ LM_FLATTEN static bool marshal_Rswrite(struct _marshal_ctx *ctx, struct lib9p_ms /* tables / exports ***********************************************************/ -#define _MSG(typ) [LIB9P_TYP_##typ] = { \ - .name = #typ, \ - .basesize = sizeof(struct lib9p_msg_##typ), \ - .validate = validate_##typ, \ - .unmarshal = (_unmarshal_fn_t)unmarshal_##typ, \ - .marshal = (_marshal_fn_t)marshal_##typ, \ +#define _MSG(typ) [LIB9P_TYP_##typ] = { \ + .name = #typ, \ + .basesize = sizeof(struct lib9p_msg_##typ), \ + .validate = validate_##typ, \ + .unmarshal = (_unmarshal_fn_t)unmarshal_##typ, \ + .marshal = (_marshal_fn_t)marshal_##typ, \ } -#define _NONMSG(num) [num] = { \ - .name = #num, \ +#define _NONMSG(num) [num] = { \ + .name = #num, \ } struct _table_version _lib9p_versions[LIB9P_VER_NUM] = { @@ -2843,5 +2850,5 @@ LM_FLATTEN void _lib9p_unmarshal_stat(struct _unmarshal_ctx *ctx, struct lib9p_s unmarshal_stat(ctx, out); } LM_FLATTEN bool _lib9p_marshal_stat(struct _marshal_ctx *ctx, struct lib9p_stat *val) { - return marshal_stat(ctx, val); + return marshal_stat(ctx, val); } diff --git a/lib9p/idl.gen b/lib9p/idl.gen index ae7f1a5..72efb7b 100755 --- a/lib9p/idl.gen +++ b/lib9p/idl.gen @@ -5,456 +5,47 @@ # Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> # SPDX-License-Identifier: AGPL-3.0-or-later -import enum import os.path -import re -from abc import ABC, abstractmethod -from typing import Callable, Final, Literal, TypeAlias, TypeVar, cast +import sys + +sys.path.insert(0, os.path.normpath(os.path.join(__file__, ".."))) + +import idl # 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". -# Types ######################################################################## - - -class Primitive(enum.Enum): - u8 = 1 - u16 = 2 - u32 = 4 - u64 = 8 - - @property - def in_versions(self) -> set[str]: - return set() - - @property - def name(self) -> str: - return str(self.value) - - @property - def static_size(self) -> int: - return self.value - - -class Number: - name: str - in_versions: set[str] - - prim: Primitive - - def __init__(self) -> None: - self.in_versions = set() - - @property - def static_size(self) -> int: - return self.prim.static_size - - -class BitfieldVal: - name: str - in_versions: set[str] - - val: str - - def __init__(self) -> None: - self.in_versions = set() - - -class Bitfield: - name: str - in_versions: set[str] - - prim: Primitive - - bits: list[str] # bitnames - names: dict[str, BitfieldVal] # bits *and* aliases - - def __init__(self) -> None: - self.in_versions = set() - self.names = {} - - @property - def static_size(self) -> int: - return self.prim.static_size - - def bit_is_valid(self, bit: str | int, ver: str | None = None) -> bool: - """Return whether the given bit is valid in the given protocol - version. - - """ - bitname = self.bits[bit] if isinstance(bit, int) else bit - assert bitname in self.bits - if not bitname: - return False - if bitname.startswith("_"): - return False - if ver and (ver not in self.names[bitname].in_versions): - return False - return True - - -class ExprLit: - val: int - - def __init__(self, val: int) -> None: - self.val = val - - -class ExprSym: - name: str - - def __init__(self, name: str) -> None: - self.name = name - - -class ExprOp: - op: Literal["-", "+"] - - def __init__(self, op: Literal["-", "+"]) -> None: - self.op = op - - -class Expr: - tokens: list[ExprLit | ExprSym | ExprOp] - - def __init__(self) -> None: - self.tokens = [] - - def __bool__(self) -> bool: - return len(self.tokens) > 0 - -class StructMember: - # from left-to-right when parsing - cnt: str | None = None - name: str - typ: "Type" - max: Expr - val: Expr +# Utilities #################################################################### - in_versions: set[str] - - @property - def static_size(self) -> int | None: - if self.cnt: - return None - return self.typ.static_size - - -class Struct: - name: str - in_versions: set[str] - - members: list[StructMember] - - def __init__(self) -> None: - self.in_versions = set() - - @property - def static_size(self) -> int | None: - size = 0 - for member in self.members: - msize = member.static_size - if msize is None: - return None - size += msize - return size - - -class Message(Struct): - @property - def msgid(self) -> int: - assert len(self.members) >= 3 - assert self.members[1].name == "typ" - assert self.members[1].static_size == 1 - assert self.members[1].val - assert len(self.members[1].val.tokens) == 1 - assert isinstance(self.members[1].val.tokens[0], ExprLit) - return self.members[1].val.tokens[0].val - - -Type: TypeAlias = Primitive | Number | Bitfield | Struct | Message -# type Type = Primitive | Number | Bitfield | Struct | Message # Change to this once we have Python 3.13 -T = TypeVar("T", Number, Bitfield, Struct, Message) - -# Parse *.9p ################################################################### - -re_priname = "(?:1|2|4|8)" # primitive names -re_symname = "(?:[a-zA-Z_][a-zA-Z_0-9]*)" # "symbol" names; most *.9p-defined names -re_impname = r"(?:\*|" + re_symname + ")" # names we can import -re_msgname = r"(?:[TR][a-zA-Z_0-9]*)" # names a message can be - -re_memtype = f"(?:{re_symname}|{re_priname})" # typenames that a struct member can be - -re_expr = f"(?:(?:-|\\+|[0-9]+|&?{re_symname})+)" - -re_bitspec_bit = f"(?P<bit>[0-9]+)\\s*=\\s*(?P<name>{re_symname})" -re_bitspec_alias = f"(?P<name>{re_symname})\\s*=\\s*(?P<val>\\S+)" - -re_memberspec = f"(?:(?P<cnt>{re_symname})\\*\\()?(?P<name>{re_symname})\\[(?P<typ>{re_memtype})(?:,max=(?P<max>{re_expr})|,val=(?P<val>{re_expr}))*\\]\\)?" - - -def parse_bitspec(ver: str, bf: Bitfield, spec: str) -> None: - spec = spec.strip() - - bit: int | None - val: BitfieldVal - if m := re.fullmatch(re_bitspec_bit, spec): - bit = int(m.group("bit")) - name = m.group("name") - - val = BitfieldVal() - val.name = name - val.val = f"1<<{bit}" - val.in_versions.add(ver) - - if bit < 0 or bit >= len(bf.bits): - raise ValueError(f"{bf.name}: bit {bit} is out-of-bounds") - if bf.bits[bit]: - raise ValueError(f"{bf.name}: bit {bit} already assigned") - bf.bits[bit] = val.name - elif m := re.fullmatch(re_bitspec_alias, spec): - name = m.group("name") - valstr = m.group("val") - - val = BitfieldVal() - val.name = name - val.val = valstr - val.in_versions.add(ver) - else: - raise SyntaxError(f"invalid bitfield spec {repr(spec)}") - - if val.name in bf.names: - raise ValueError(f"{bf.name}: name {val.name} already assigned") - bf.names[val.name] = val - - -def parse_expr(expr: str) -> Expr: - assert re.fullmatch(re_expr, expr) - ret = Expr() - for tok in re.split("([-+])", expr): - if tok == "-" or tok == "+": - # I, for the life of me, do not understand why I need this - # cast() to keep mypy happy. - ret.tokens += [ExprOp(cast(Literal["-", "+"], tok))] - elif re.fullmatch("[0-9]+", tok): - ret.tokens += [ExprLit(int(tok))] - else: - ret.tokens += [ExprSym(tok)] - return ret +idprefix = "lib9p_" -def parse_members(ver: str, env: dict[str, Type], struct: Struct, specs: str) -> None: - for spec in specs.split(): - m = re.fullmatch(re_memberspec, spec) - if not m: - raise SyntaxError(f"invalid member spec {repr(spec)}") - - member = StructMember() - member.in_versions = {ver} - - member.name = m.group("name") - if any(x.name == member.name for x in struct.members): - raise ValueError(f"duplicate member name {repr(member.name)}") - - if m.group("typ") not in env: - raise NameError(f"Unknown type {repr(m.group(2))}") - member.typ = env[m.group("typ")] - - if cnt := m.group("cnt"): - if len(struct.members) == 0 or struct.members[-1].name != cnt: - raise ValueError(f"list count must be previous item: {repr(cnt)}") - if not isinstance(struct.members[-1].typ, Primitive): - raise ValueError(f"list count must be an integer type: {repr(cnt)}") - member.cnt = cnt - - if maxstr := m.group("max"): - if (not isinstance(member.typ, Primitive)) or member.cnt: - raise ValueError("',max=' may only be specified on a non-repeated atom") - member.max = parse_expr(maxstr) - else: - member.max = Expr() - - if valstr := m.group("val"): - if (not isinstance(member.typ, Primitive)) or member.cnt: - raise ValueError("',val=' may only be specified on a non-repeated atom") - member.val = parse_expr(valstr) - else: - member.val = Expr() - - struct.members += [member] - - -def re_string(grpname: str) -> str: - return f'"(?P<{grpname}>[^"]*)"' - - -re_line_version = f"version\\s+{re_string('version')}" -re_line_import = f"from\\s+(?P<file>\\S+)\\s+import\\s+(?P<syms>{re_impname}(?:\\s*,\\s*{re_impname})*)" -re_line_num = f"num\\s+(?P<name>{re_symname})\\s*=\\s*(?P<prim>{re_priname})" -re_line_bitfield = f"bitfield\\s+(?P<name>{re_symname})\\s*=\\s*(?P<prim>{re_priname})" -re_line_bitfield_ = ( - f"bitfield\\s+(?P<name>{re_symname})\\s*\\+=\\s*{re_string('member')}" -) -re_line_struct = ( - f"struct\\s+(?P<name>{re_symname})\\s*(?P<op>\\+?=)\\s*{re_string('members')}" -) -re_line_msg = ( - f"msg\\s+(?P<name>{re_msgname})\\s*(?P<op>\\+?=)\\s*{re_string('members')}" -) -re_line_cont = f"\\s+{re_string('specs')}" # could be bitfield/struct/msg - - -def parse_file( - filename: str, get_include: Callable[[str], tuple[str, list[Type]]] -) -> tuple[str, list[Type]]: - version: str | None = None - env: dict[str, Type] = { - "1": Primitive.u8, - "2": Primitive.u16, - "4": Primitive.u32, - "8": Primitive.u64, - } - - def get_type(name: str, tc: type[T]) -> T: - nonlocal env - if name not in env: - raise NameError(f"Unknown type {repr(name)}") - ret = env[name] - if (not isinstance(ret, tc)) or (ret.__class__.__name__ != tc.__name__): - raise NameError(f"Type {repr(ret.name)} is not a {tc.__name__}") - return ret - - with open(filename, "r") as fh: - prev: Type | None = None - for line in fh: - line = line.split("#", 1)[0].rstrip() - if not line: - continue - if m := re.fullmatch(re_line_version, line): - if version: - raise SyntaxError("must have exactly 1 version line") - version = m.group("version") - continue - if not version: - raise SyntaxError("must have exactly 1 version line") - - if m := re.fullmatch(re_line_import, line): - other_version, other_typs = get_include(m.group("file")) - for symname in m.group("syms").split(sep=","): - symname = symname.strip() - for typ in other_typs: - if typ.name == symname or symname == "*": - match typ: - case Primitive(): - pass - case Number(): - typ.in_versions.add(version) - case Bitfield(): - typ.in_versions.add(version) - for val in typ.names.values(): - if other_version in val.in_versions: - val.in_versions.add(version) - case Struct(): # and Message() - typ.in_versions.add(version) - for member in typ.members: - if other_version in member.in_versions: - member.in_versions.add(version) - env[typ.name] = typ - elif m := re.fullmatch(re_line_num, line): - num = Number() - num.name = m.group("name") - num.in_versions.add(version) - - prim = env[m.group("prim")] - assert isinstance(prim, Primitive) - num.prim = prim - - env[num.name] = num - prev = num - elif m := re.fullmatch(re_line_bitfield, line): - bf = Bitfield() - bf.name = m.group("name") - bf.in_versions.add(version) - - prim = env[m.group("prim")] - assert isinstance(prim, Primitive) - bf.prim = prim - - bf.bits = (prim.static_size * 8) * [""] - - env[bf.name] = bf - prev = bf - elif m := re.fullmatch(re_line_bitfield_, line): - bf = get_type(m.group("name"), Bitfield) - parse_bitspec(version, bf, m.group("member")) - - prev = bf - elif m := re.fullmatch(re_line_struct, line): - match m.group("op"): - case "=": - struct = Struct() - struct.name = m.group("name") - struct.in_versions.add(version) - struct.members = [] - parse_members(version, env, struct, m.group("members")) - - env[struct.name] = struct - prev = struct - case "+=": - struct = get_type(m.group("name"), Struct) - parse_members(version, env, struct, m.group("members")) - - prev = struct - elif m := re.fullmatch(re_line_msg, line): - match m.group("op"): - case "=": - msg = Message() - msg.name = m.group("name") - msg.in_versions.add(version) - msg.members = [] - parse_members(version, env, msg, m.group("members")) - - env[msg.name] = msg - prev = msg - case "+=": - msg = get_type(m.group("name"), Message) - parse_members(version, env, msg, m.group("members")) - - prev = msg - elif m := re.fullmatch(re_line_cont, line): - match prev: - case Bitfield(): - parse_bitspec(version, prev, m.group("specs")) - case Struct(): # and Message() - parse_members(version, env, prev, m.group("specs")) - case _: - raise SyntaxError( - "continuation line must come after a bitfield, struct, or msg line" - ) - else: - raise SyntaxError(f"invalid line {repr(line)}") - if not version: - raise SyntaxError("must have exactly 1 version line") +def tab_ljust(s: str, width: int) -> str: + cur = len(s.expandtabs(tabsize=8)) + if cur >= width: + return s + return s + " " * (width - cur) - typs: list[Type] = [x for x in env.values() if not isinstance(x, Primitive)] - for typ in [typ for typ in typs if isinstance(typ, Struct)]: - valid_syms = ["end", *["&" + m.name for m in typ.members]] - for member in typ.members: - for tok in [*member.max.tokens, *member.val.tokens]: - if isinstance(tok, ExprSym) and tok.name not in valid_syms: - raise ValueError( - f"{typ.name}.{member.name}: invalid sym: {tok.name}" - ) +def add_prefix(p: str, s: str) -> str: + if s.startswith("_"): + return "_" + p + s[1:] + return p + s - return version, typs +def join_lines(*args: str) -> str: + return "\n".join([a.rstrip() for a in args]).rstrip() + "\n" -# Generate C ################################################################### -idprefix = "lib9p_" +def c_macro(*args: str) -> str: + full = join_lines(*args).rstrip() + assert "\n" in full + lines = [l.rstrip() for l in full.split("\n")] + width = max(len(l.expandtabs(tabsize=8)) for l in lines[:-1]) + lines = [tab_ljust(l, width) for l in lines] + return " \\\n".join(lines).rstrip() + "\n" def c_ver_enum(ver: str) -> str: @@ -473,22 +64,37 @@ def c_ver_cond(versions: set[str]) -> str: return "( " + (" || ".join(c_ver_cond({v}) for v in sorted(versions))) + " )" -def c_typename(typ: Type) -> str: +def c_typename(typ: idl.Type) -> str: match typ: - case Primitive(): + case idl.Primitive(): return f"uint{typ.value*8}_t" - case Number(): + case idl.Number(): return f"{idprefix}{typ.name}_t" - case Bitfield(): + case idl.Bitfield(): return f"{idprefix}{typ.name}_t" - case Message(): + case idl.Message(): return f"struct {idprefix}msg_{typ.name}" - case Struct(): + case idl.Struct(): return f"struct {idprefix}{typ.name}" case _: raise ValueError(f"not a type: {typ.__class__.__name__}") +def c_expr(expr: idl.Expr) -> str: + ret: list[str] = [] + for tok in expr.tokens: + match tok: + case idl.ExprOp(): + ret += [tok.op] + case idl.ExprLit(): + ret += [str(tok.val)] + case idl.ExprSym(name="end"): + ret += ["ctx->net_offset"] + case idl.ExprSym(): + ret += [f"_{tok.name[1:]}_offset"] + return " ".join(ret) + + _ifdef_stack: list[str | None] = [] @@ -531,14 +137,17 @@ def ifdef_pop(n: int) -> str: return ret -def gen_h(versions: set[str], typs: list[Type]) -> str: +# Generate .h ################################################################## + + +def gen_h(versions: set[str], typs: list[idl.Type]) -> str: global _ifdef_stack _ifdef_stack = [] ret = f"""/* Generated by `{' '.join(sys.argv)}`. DO NOT EDIT! */ #ifndef _LIB9P_9P_H_ - #error Do not include <lib9p/9p.generated.h> directly; include <lib9p/9p.h> instead +\t#error Do not include <lib9p/9p.generated.h> directly; include <lib9p/9p.h> instead #endif #include <stdint.h> /* for uint{{n}}_t types */ @@ -556,7 +165,7 @@ def gen_h(versions: set[str], typs: list[Type]) -> str: ret += "#endif\n" ret += f""" -/* versions *******************************************************************/ +/* enum version ***************************************************************/ enum {idprefix}version {{ """ @@ -574,15 +183,27 @@ enum {idprefix}version {{ ret += f"const char *{idprefix}version_str(enum {idprefix}version);\n" ret += """ -/* non-message types **********************************************************/ +/* enum msg_type **************************************************************/ + +""" + ret += f"enum {idprefix}msg_type {{ /* uint8_t */\n" + namewidth = max(len(msg.name) for msg in typs if isinstance(msg, idl.Message)) + for msg in [msg for msg in typs if isinstance(msg, idl.Message)]: + ret += ifdef_push(1, c_ver_ifdef(msg.in_versions)) + ret += f"\t{idprefix.upper()}TYP_{msg.name.ljust(namewidth)} = {msg.msgid},\n" + ret += ifdef_pop(0) + ret += "};\n" + + ret += """ +/* payload types **************************************************************/ """ - for typ in [typ for typ in typs if not isinstance(typ, Message)]: + for typ in typs: ret += "\n" ret += ifdef_push(1, c_ver_ifdef(typ.in_versions)) match typ: - case Number(): + case idl.Number(): ret += f"typedef {c_typename(typ.prim)} {c_typename(typ)};\n" - case Bitfield(): + case idl.Bitfield(): ret += f"typedef {c_typename(typ.prim)} {c_typename(typ)};\n" names = [ *reversed( @@ -591,39 +212,48 @@ enum {idprefix}version {{ "", *[k for k in typ.names if k not in typ.bits], ] - namewidth = max(len(name) for name in names) + prefix = f"{idprefix.upper()}{typ.name.upper()}_" + namewidth = max(len(add_prefix(prefix, name)) for name in names) ret += "\n" for name in names: if name == "": ret += "\n" - elif name.startswith(" "): - ret += ifdef_push(2, c_ver_ifdef(typ.in_versions)) - sp = " " * ( - len("# define ") - + len(idprefix) - + len(typ.name) - + 1 - + namewidth - + 2 - - len("/* unused") - ) - ret += f"/* unused{sp}(({c_typename(typ)})(1<<{name[1:]})) */\n" + continue + + if name.startswith(" "): + vers = typ.in_versions + c_name = "" + c_val = f"1<<{name[1:]}" else: - ret += ifdef_push(2, c_ver_ifdef(typ.names[name].in_versions)) - if name.startswith("_"): - c_name = f"_{idprefix.upper()}{typ.name.upper()}_{name[1:]}" - else: - c_name = f"{idprefix.upper()}{typ.name.upper()}_{name}" - sp1 = " " if _ifdef_stack[-1] else "" - sp2 = " " if _ifdef_stack[-1] else " " - sp3 = " " * (2 + namewidth - len(name)) - ret += f"#{sp1}define{sp2}{c_name}{sp3}(({c_typename(typ)})({typ.names[name].val}))\n" + vers = typ.names[name].in_versions + c_name = add_prefix(prefix, name) + c_val = f"{typ.names[name].val}" + + ret += ifdef_push(2, c_ver_ifdef(vers)) + + # It is important all of the `beg` strings have + # the same length. + end = "" + if name.startswith(" "): + beg = "/* unused" + end = " */" + elif _ifdef_stack[-1]: + beg = "# define" + else: + beg = "#define " + + ret += f"{beg} {c_name.ljust(namewidth)} (({c_typename(typ)})({c_val})){end}\n" ret += ifdef_pop(1) - case Struct(): + case idl.Struct(): # and idl.Message(): + ret += c_typename(typ) + " {" + if not typ.members: + ret += "};\n" + continue + ret += "\n" + typewidth = max(len(c_typename(m.typ)) for m in typ.members) - ret += c_typename(typ) + " {\n" for member in typ.members: if member.val: continue @@ -636,57 +266,13 @@ enum {idprefix}version {{ ret += "};\n" ret += ifdef_pop(0) - ret += """ -/* messages *******************************************************************/ - -""" - ret += f"enum {idprefix}msg_type {{ /* uint8_t */\n" - namewidth = max(len(msg.name) for msg in typs if isinstance(msg, Message)) - for msg in [msg for msg in typs if isinstance(msg, Message)]: - ret += ifdef_push(1, c_ver_ifdef(msg.in_versions)) - ret += f"\t{idprefix.upper()}TYP_{msg.name.ljust(namewidth)} = {msg.msgid},\n" - ret += ifdef_pop(0) - ret += "};\n" - - for msg in [msg for msg in typs if isinstance(msg, Message)]: - ret += "\n" - ret += ifdef_push(1, c_ver_ifdef(msg.in_versions)) - ret += c_typename(msg) + " {" - if not msg.members: - ret += "};\n" - continue - ret += "\n" - - typewidth = max(len(c_typename(m.typ)) for m in msg.members) - - for member in msg.members: - if member.val: - continue - ret += ifdef_push(2, c_ver_ifdef(member.in_versions)) - ret += f"\t{c_typename(member.typ).ljust(typewidth)} {'*' if member.cnt else ' '}{member.name};\n" - ret += ifdef_pop(1) - ret += "};\n" - ret += ifdef_pop(0) - return ret -def c_expr(expr: Expr) -> str: - ret: list[str] = [] - for tok in expr.tokens: - match tok: - case ExprOp(): - ret += [tok.op] - case ExprLit(): - ret += [str(tok.val)] - case ExprSym(name="end"): - ret += ["ctx->net_offset"] - case ExprSym(): - ret += [f"_{tok.name[1:]}_offset"] - return " ".join(ret) +# Generate .c ################################################################## -def gen_c(versions: set[str], typs: list[Type]) -> str: +def gen_c(versions: set[str], typs: list[idl.Type]) -> str: global _ifdef_stack _ifdef_stack = [] @@ -732,37 +318,62 @@ const char *{idprefix}version_str(enum {idprefix}version ver) {{ }} """ + # bitmasks ################################################################# + ret += f""" +/* bitmasks *******************************************************************/ +""" + for typ in typs: + if not isinstance(typ, idl.Bitfield): + continue + ret += "\n" + ret += ifdef_push(1, c_ver_ifdef(typ.in_versions)) + ret += f"static const {c_typename(typ)} {typ.name}_masks[{c_ver_enum('NUM')}] = {{\n" + verwidth = max(len(ver) for ver in versions) + for ver in sorted(versions): + ret += ifdef_push(2, c_ver_ifdef({ver})) + ret += ( + f"\t[{c_ver_enum(ver)}]{' '*(verwidth-len(ver))} = 0b" + + "".join( + "1" if typ.bit_is_valid(bitname, ver) else "0" + for bitname in reversed(typ.bits) + ) + + ",\n" + ) + ret += ifdef_pop(1) + ret += "};\n" + ret += ifdef_pop(0) + # validate_* ############################################################### ret += """ /* validate_* *****************************************************************/ LM_ALWAYS_INLINE static bool _validate_size_net(struct _validate_ctx *ctx, uint32_t n) { - if (__builtin_add_overflow(ctx->net_offset, n, &ctx->net_offset)) - /* If needed-net-size overflowed uint32_t, then - * there's no way that actual-net-size will live up to - * that. */ - return lib9p_error(ctx->ctx, LINUX_EBADMSG, "message is too short for content"); - if (ctx->net_offset > ctx->net_size) - return lib9p_error(ctx->ctx, LINUX_EBADMSG, "message is too short for content"); - return false; +\tif (__builtin_add_overflow(ctx->net_offset, n, &ctx->net_offset)) +\t\t/* If needed-net-size overflowed uint32_t, then +\t\t * there's no way that actual-net-size will live up to +\t\t * that. */ +\t\treturn lib9p_error(ctx->ctx, LINUX_EBADMSG, "message is too short for content"); +\tif (ctx->net_offset > ctx->net_size) +\t\treturn lib9p_error(ctx->ctx, LINUX_EBADMSG, "message is too short for content"); +\treturn false; } LM_ALWAYS_INLINE static bool _validate_size_host(struct _validate_ctx *ctx, size_t n) { - if (__builtin_add_overflow(ctx->host_extra, n, &ctx->host_extra)) - /* If needed-host-size overflowed size_t, then there's - * no way that actual-net-size will live up to - * that. */ - return lib9p_error(ctx->ctx, LINUX_EBADMSG, "message is too short for content"); - return false; +\tif (__builtin_add_overflow(ctx->host_extra, n, &ctx->host_extra)) +\t\t/* If needed-host-size overflowed size_t, then there's +\t\t * no way that actual-net-size will live up to +\t\t * that. */ +\t\treturn lib9p_error(ctx->ctx, LINUX_EBADMSG, "message is too short for content"); +\treturn false; } LM_ALWAYS_INLINE static bool _validate_list(struct _validate_ctx *ctx, size_t cnt, _validate_fn_t item_fn, size_t item_host_size) { - for (size_t i = 0; i < cnt; i++) - if (_validate_size_host(ctx, item_host_size) || item_fn(ctx)) - return true; - return false; +\tfor (size_t i = 0; i < cnt; i++) +\t\tif (_validate_size_host(ctx, item_host_size) || item_fn(ctx)) +\t\t\treturn true; +\treturn false; } #define validate_1(ctx) _validate_size_net(ctx, 1) @@ -771,27 +382,10 @@ LM_ALWAYS_INLINE static bool _validate_list(struct _validate_ctx *ctx, #define validate_8(ctx) _validate_size_net(ctx, 8) """ for typ in typs: - inline = "LM_FLATTEN" if isinstance(typ, Message) else "LM_ALWAYS_INLINE" - argfn = unused if (isinstance(typ, Struct) and not typ.members) else used + inline = "LM_FLATTEN" if isinstance(typ, idl.Message) else "LM_ALWAYS_INLINE" + argfn = unused if (isinstance(typ, idl.Struct) and not typ.members) else used ret += "\n" ret += ifdef_push(1, c_ver_ifdef(typ.in_versions)) - - if isinstance(typ, Bitfield): - ret += f"static const {c_typename(typ)} {typ.name}_masks[{c_ver_enum('NUM')}] = {{\n" - verwidth = max(len(ver) for ver in versions) - for ver in sorted(versions): - ret += ifdef_push(2, c_ver_ifdef({ver})) - ret += ( - f"\t[{c_ver_enum(ver)}]{' '*(verwidth-len(ver))} = 0b" - + "".join( - "1" if typ.bit_is_valid(bitname, ver) else "0" - for bitname in reversed(typ.bits) - ) - + ",\n" - ) - ret += ifdef_pop(1) - ret += "};\n" - ret += f"{inline} static bool validate_{typ.name}(struct _validate_ctx *{argfn('ctx')}) {{\n" if typ.name == "d": # SPECIAL @@ -820,9 +414,9 @@ LM_ALWAYS_INLINE static bool _validate_list(struct _validate_ctx *ctx, continue match typ: - case Number(): + case idl.Number(): ret += f"\treturn validate_{typ.prim.name}(ctx);\n" - case Bitfield(): + case idl.Bitfield(): ret += f"\t if (validate_{typ.static_size}(ctx))\n" ret += "\t\treturn true;\n" ret += ( @@ -832,7 +426,7 @@ LM_ALWAYS_INLINE static bool _validate_list(struct _validate_ctx *ctx, ret += f"\tif (val & ~mask)\n" ret += f'\t\treturn lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "unknown bits in {typ.name} bitfield: %#0{typ.static_size}"PRIx{typ.static_size*8}, val & ~mask);\n' ret += "\treturn false;\n" - case Struct(): # and Message() + case idl.Struct(): # and idl.Message() if len(typ.members) == 0: ret += "\treturn false;\n" ret += "}\n" @@ -849,7 +443,7 @@ LM_ALWAYS_INLINE static bool _validate_list(struct _validate_ctx *ctx, mark_offset: set[str] = set() for member in typ.members: for tok in [*member.max.tokens, *member.val.tokens]: - if isinstance(tok, ExprSym) and tok.name.startswith("&"): + if isinstance(tok, idl.ExprSym) and tok.name.startswith("&"): if tok.name[1:] not in mark_offset: ret += f"\tuint32_t _{tok.name[1:]}_offset;\n" mark_offset.add(tok.name[1:]) @@ -904,37 +498,37 @@ LM_ALWAYS_INLINE static bool _validate_list(struct _validate_ctx *ctx, /* unmarshal_* ****************************************************************/ LM_ALWAYS_INLINE static void unmarshal_1(struct _unmarshal_ctx *ctx, uint8_t *out) { - *out = decode_u8le(&ctx->net_bytes[ctx->net_offset]); - ctx->net_offset += 1; +\t*out = decode_u8le(&ctx->net_bytes[ctx->net_offset]); +\tctx->net_offset += 1; } LM_ALWAYS_INLINE static void unmarshal_2(struct _unmarshal_ctx *ctx, uint16_t *out) { - *out = decode_u16le(&ctx->net_bytes[ctx->net_offset]); - ctx->net_offset += 2; +\t*out = decode_u16le(&ctx->net_bytes[ctx->net_offset]); +\tctx->net_offset += 2; } LM_ALWAYS_INLINE static void unmarshal_4(struct _unmarshal_ctx *ctx, uint32_t *out) { - *out = decode_u32le(&ctx->net_bytes[ctx->net_offset]); - ctx->net_offset += 4; +\t*out = decode_u32le(&ctx->net_bytes[ctx->net_offset]); +\tctx->net_offset += 4; } LM_ALWAYS_INLINE static void unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *out) { - *out = decode_u64le(&ctx->net_bytes[ctx->net_offset]); - ctx->net_offset += 8; +\t*out = decode_u64le(&ctx->net_bytes[ctx->net_offset]); +\tctx->net_offset += 8; } """ for typ in typs: - inline = "LM_FLATTEN" if isinstance(typ, Message) else "LM_ALWAYS_INLINE" - argfn = unused if (isinstance(typ, Struct) and not typ.members) else used + inline = "LM_FLATTEN" if isinstance(typ, idl.Message) else "LM_ALWAYS_INLINE" + argfn = unused if (isinstance(typ, idl.Struct) and not typ.members) else used ret += "\n" ret += ifdef_push(1, c_ver_ifdef(typ.in_versions)) ret += f"{inline} static void unmarshal_{typ.name}(struct _unmarshal_ctx *{argfn('ctx')}, {c_typename(typ)} *out) {{\n" match typ: - case Number(): + case idl.Number(): ret += f"\tunmarshal_{typ.prim.name}(ctx, ({c_typename(typ.prim)} *)out);\n" - case Bitfield(): + case idl.Bitfield(): ret += f"\tunmarshal_{typ.prim.name}(ctx, ({c_typename(typ.prim)} *)out);\n" - case Struct(): + case idl.Struct(): ret += "\tmemset(out, 0, sizeof(*out));\n" for member in typ.members: @@ -977,58 +571,58 @@ LM_ALWAYS_INLINE static void unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *o /* marshal_* ******************************************************************/ LM_ALWAYS_INLINE static bool _marshal_too_large(struct _marshal_ctx *ctx) { - lib9p_errorf(ctx->ctx, LINUX_ERANGE, "%s too large to marshal into %s limit (limit=%"PRIu32")", - (ctx->net_bytes[4] % 2 == 0) ? "T-message" : "R-message", - ctx->ctx->version ? "negotiated" : ((ctx->net_bytes[4] % 2 == 0) ? "client" : "server"), - ctx->ctx->max_msg_size); - return true; +\tlib9p_errorf(ctx->ctx, LINUX_ERANGE, "%s too large to marshal into %s limit (limit=%"PRIu32")", +\t\t(ctx->net_bytes[4] % 2 == 0) ? "T-message" : "R-message", +\t\tctx->ctx->version ? "negotiated" : ((ctx->net_bytes[4] % 2 == 0) ? "client" : "server"), +\t\tctx->ctx->max_msg_size); +\treturn true; } LM_ALWAYS_INLINE static bool marshal_1(struct _marshal_ctx *ctx, uint8_t *val) { - if (ctx->net_offset + 1 > ctx->ctx->max_msg_size) - return _marshal_too_large(ctx); - ctx->net_bytes[ctx->net_offset] = *val; - ctx->net_offset += 1; - return false; +\tif (ctx->net_offset + 1 > ctx->ctx->max_msg_size) +\t\treturn _marshal_too_large(ctx); +\tctx->net_bytes[ctx->net_offset] = *val; +\tctx->net_offset += 1; +\treturn false; } LM_ALWAYS_INLINE static bool marshal_2(struct _marshal_ctx *ctx, uint16_t *val) { - if (ctx->net_offset + 2 > ctx->ctx->max_msg_size) - return _marshal_too_large(ctx); - encode_u16le(*val, &ctx->net_bytes[ctx->net_offset]); - ctx->net_offset += 2; - return false; +\tif (ctx->net_offset + 2 > ctx->ctx->max_msg_size) +\t\treturn _marshal_too_large(ctx); +\tencode_u16le(*val, &ctx->net_bytes[ctx->net_offset]); +\tctx->net_offset += 2; +\treturn false; } LM_ALWAYS_INLINE static bool marshal_4(struct _marshal_ctx *ctx, uint32_t *val) { - if (ctx->net_offset + 4 > ctx->ctx->max_msg_size) - return true; - encode_u32le(*val, &ctx->net_bytes[ctx->net_offset]); - ctx->net_offset += 4; - return false; +\tif (ctx->net_offset + 4 > ctx->ctx->max_msg_size) +\t\treturn true; +\tencode_u32le(*val, &ctx->net_bytes[ctx->net_offset]); +\tctx->net_offset += 4; +\treturn false; } LM_ALWAYS_INLINE static bool marshal_8(struct _marshal_ctx *ctx, uint64_t *val) { - if (ctx->net_offset + 8 > ctx->ctx->max_msg_size) - return true; - encode_u64le(*val, &ctx->net_bytes[ctx->net_offset]); - ctx->net_offset += 8; - return false; +\tif (ctx->net_offset + 8 > ctx->ctx->max_msg_size) +\t\treturn true; +\tencode_u64le(*val, &ctx->net_bytes[ctx->net_offset]); +\tctx->net_offset += 8; +\treturn false; } """ for typ in typs: - inline = "LM_FLATTEN" if isinstance(typ, Message) else "LM_ALWAYS_INLINE" - argfn = unused if (isinstance(typ, Struct) and not typ.members) else used + inline = "LM_FLATTEN" if isinstance(typ, idl.Message) else "LM_ALWAYS_INLINE" + argfn = unused if (isinstance(typ, idl.Struct) and not typ.members) else used ret += "\n" ret += ifdef_push(1, c_ver_ifdef(typ.in_versions)) ret += f"{inline} static bool marshal_{typ.name}(struct _marshal_ctx *{argfn('ctx')}, {c_typename(typ)} *{argfn('val')}) {{\n" match typ: - case Number(): + case idl.Number(): ret += f"\treturn marshal_{typ.prim.name}(ctx, ({c_typename(typ.prim)} *)val);\n" - case Bitfield(): + case idl.Bitfield(): ret += f"\t{c_typename(typ)} masked_val = *val & {typ.name}_masks[ctx->ctx->version];\n" ret += f"\treturn marshal_{typ.prim.name}(ctx, ({c_typename(typ.prim)} *)&masked_val);\n" - case Struct(): + case idl.Struct(): if len(typ.members) == 0: ret += "\treturn false;\n" ret += "}\n" @@ -1042,7 +636,7 @@ LM_ALWAYS_INLINE static bool marshal_8(struct _marshal_ctx *ctx, uint64_t *val) ret += f"\tuint32_t _{member.name}_offset;\n" mark_offset.add(member.name) for tok in member.val.tokens: - if isinstance(tok, ExprSym) and tok.name.startswith("&"): + if isinstance(tok, idl.ExprSym) and tok.name.startswith("&"): if tok.name[1:] not in mark_offset: ret += f"\tuint32_t _{tok.name[1:]}_offset;\n" mark_offset.add(tok.name[1:]) @@ -1067,7 +661,7 @@ LM_ALWAYS_INLINE static bool marshal_8(struct _marshal_ctx *ctx, uint64_t *val) ret += f"marshal_{member.typ.name}(ctx, &val->{member.name}[i]);\n" ret += f"\t err; }})" elif member.val: - # Just increment net_offset, don't actually marsha anything (yet). + # Just increment net_offset, don't actually marshal anything (yet). assert member.static_size ret += ( f"({{ ctx->net_offset += {member.static_size}; false; }})" @@ -1093,33 +687,36 @@ LM_ALWAYS_INLINE static bool marshal_8(struct _marshal_ctx *ctx, uint64_t *val) ret += ifdef_pop(0) # tables / exports ######################################################### - ret += f""" + ret += """ /* tables / exports ***********************************************************/ - -#define _MSG(typ) [{idprefix.upper()}TYP_##typ] = {{ \\ - .name = #typ, \\ - .basesize = sizeof(struct {idprefix}msg_##typ), \\ - .validate = validate_##typ, \\ - .unmarshal = (_unmarshal_fn_t)unmarshal_##typ, \\ - .marshal = (_marshal_fn_t)marshal_##typ, \\ - }} -#define _NONMSG(num) [num] = {{ \\ - .name = #num, \\ - }} - -struct _table_version _{idprefix}versions[{c_ver_enum('NUM')}] = {{ """ - id2typ: dict[int, Message] = {} - for msg in [msg for msg in typs if isinstance(msg, Message)]: + id2typ: dict[int, idl.Message] = {} + for msg in [msg for msg in typs if isinstance(msg, idl.Message)]: id2typ[msg.msgid] = msg + ret += "\n" + ret += c_macro( + f"#define _MSG(typ) [{idprefix.upper()}TYP_##typ] = {{", + f"\t\t.name = #typ,", + f"\t\t.basesize = sizeof(struct {idprefix}msg_##typ),", + f"\t\t.validate = validate_##typ,", + f"\t\t.unmarshal = (_unmarshal_fn_t)unmarshal_##typ,", + f"\t\t.marshal = (_marshal_fn_t)marshal_##typ,", + f"\t}}", + ) + ret += c_macro( + f"#define _NONMSG(num) [num] = {{", f"\t\t.name = #num,", f"\t}}" + ) + + ret += "\n" + ret += f"struct _table_version _{idprefix}versions[{c_ver_enum('NUM')}] = {{\n" for ver in ["unknown", *sorted(versions)]: if ver != "unknown": ret += ifdef_push(1, c_ver_ifdef({ver})) ret += f"\t[{c_ver_enum(ver)}] = {{ .msgs = {{\n" for n in range(0, 0x100): - xmsg: Message | None = id2typ.get(n, None) + xmsg: idl.Message | None = id2typ.get(n, None) if xmsg: if ver == "unknown": # SPECIAL if xmsg.name not in ["Tversion", "Rversion", "Rerror"]: @@ -1137,13 +734,13 @@ struct _table_version _{idprefix}versions[{c_ver_enum('NUM')}] = {{ ret += f""" LM_FLATTEN bool _{idprefix}validate_stat(struct _validate_ctx *ctx) {{ - return validate_stat(ctx); +\treturn validate_stat(ctx); }} LM_FLATTEN void _{idprefix}unmarshal_stat(struct _unmarshal_ctx *ctx, struct lib9p_stat *out) {{ - unmarshal_stat(ctx, out); +\tunmarshal_stat(ctx, out); }} LM_FLATTEN bool _{idprefix}marshal_stat(struct _marshal_ctx *ctx, struct lib9p_stat *val) {{ - return marshal_stat(ctx, val); +\treturn marshal_stat(ctx, val); }} """ @@ -1151,36 +748,7 @@ LM_FLATTEN bool _{idprefix}marshal_stat(struct _marshal_ctx *ctx, struct lib9p_s return ret -################################################################################ - - -class Parser: - cache: dict[str, tuple[str, list[Type]]] = {} - - def parse_file(self, filename: str) -> tuple[str, list[Type]]: - filename = os.path.normpath(filename) - if filename not in self.cache: - - def get_include(other_filename: str) -> tuple[str, list[Type]]: - return self.parse_file(os.path.join(filename, "..", other_filename)) - - self.cache[filename] = parse_file(filename, get_include) - return self.cache[filename] - - def all(self) -> tuple[set[str], list[Type]]: - ret_versions: set[str] = set() - ret_typs: dict[str, Type] = {} - for version, typs in self.cache.values(): - if version in ret_versions: - raise ValueError(f"duplicate protocol version {repr(version)}") - ret_versions.add(version) - for typ in typs: - if typ.name in ret_typs: - if typ != ret_typs[typ.name]: - raise ValueError(f"duplicate type name {repr(typ.name)}") - else: - ret_typs[typ.name] = typ - return ret_versions, list(ret_typs.values()) +# Main ######################################################################### if __name__ == "__main__": @@ -1188,7 +756,7 @@ if __name__ == "__main__": if len(sys.argv) < 2: raise ValueError("requires at least 1 .9p filename") - parser = Parser() + parser = idl.Parser() for txtname in sys.argv[1:]: parser.parse_file(txtname) versions, typs = parser.all() diff --git a/lib9p/idl/0000-README.md b/lib9p/idl/0000-README.md index cec27e2..86862b7 100644 --- a/lib9p/idl/0000-README.md +++ b/lib9p/idl/0000-README.md @@ -1,5 +1,5 @@ <!-- - 0000-README.md - Overview of 9P protocol definitions + lib9p/idl/0000-README.md - Overview of 9P protocol definitions Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/lib9p/idl/1992-9P0.9p.wip b/lib9p/idl/1992-9P0.9p.wip index 4278fa3..27d6b33 100644 --- a/lib9p/idl/1992-9P0.9p.wip +++ b/lib9p/idl/1992-9P0.9p.wip @@ -1,4 +1,4 @@ -# 1992-9P0.9p - Definitions of 9P0 (Plan 9 1st ed) messages +# lib9p/idl/1992-9P0.9p - Definitions of 9P0 (Plan 9 1st ed) messages # # Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> # SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/lib9p/idl/1995-9P1.9p.wip b/lib9p/idl/1995-9P1.9p.wip index 55814d4..30b9112 100644 --- a/lib9p/idl/1995-9P1.9p.wip +++ b/lib9p/idl/1995-9P1.9p.wip @@ -1,4 +1,4 @@ -# 1995-9P1.9p - Definitions of 9P1 (Plan 9 2nd ed and 3rd ed) messages +# lib9p/idl/1995-9P1.9p - Definitions of 9P1 (Plan 9 2nd ed and 3rd ed) messages # # Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> # SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/lib9p/idl/1996-Styx.9p.wip b/lib9p/idl/1996-Styx.9p.wip index 2feb24f..d9d3399 100644 --- a/lib9p/idl/1996-Styx.9p.wip +++ b/lib9p/idl/1996-Styx.9p.wip @@ -1,4 +1,4 @@ -# 1996-Styx.9p - Definitions of Styx messages +# lib9p/idl/1996-Styx.9p - Definitions of Styx messages # # Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> # SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/lib9p/idl/2002-9P2000.9p b/lib9p/idl/2002-9P2000.9p index 47d402a..c83f439 100644 --- a/lib9p/idl/2002-9P2000.9p +++ b/lib9p/idl/2002-9P2000.9p @@ -1,4 +1,4 @@ -# 2002-9P2000.9p - Definitions of 9P2000 messages +# lib9p/idl/2002-9P2000.9p - Definitions of 9P2000 messages # # Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> # SPDX-License-Identifier: AGPL-3.0-or-later @@ -104,7 +104,7 @@ struct stat = "stat_size[2,val=end-&kern_type]" # "O"pen flags (flags to pass to Topen and Tcreate) bitfield o = 1 "0=mode_0" # low bit of the 2-bit READ/WRITE/RDWR/EXEC enum - "1=mode_1" # high bit of the 2-bit READ/WRITE/RDWR/EXEC enum" + "1=mode_1" # high bit of the 2-bit READ/WRITE/RDWR/EXEC enum #"2=unused" #"3=unused" "4=TRUNC" diff --git a/lib9p/idl/2005-9P2000.u.9p b/lib9p/idl/2005-9P2000.u.9p index 8b59efa..3eab4ad 100644 --- a/lib9p/idl/2005-9P2000.u.9p +++ b/lib9p/idl/2005-9P2000.u.9p @@ -1,4 +1,4 @@ -# 2005-9P2000.u.9p - Definitions of 9P2000.u messages +# lib9p/idl/2005-9P2000.u.9p - Definitions of 9P2000.u messages # # Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> # SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/lib9p/idl/2010-9P2000.L.9p.wip b/lib9p/idl/2010-9P2000.L.9p.wip index 5261f7e..f5e79c5 100644 --- a/lib9p/idl/2010-9P2000.L.9p.wip +++ b/lib9p/idl/2010-9P2000.L.9p.wip @@ -1,4 +1,4 @@ -# 2010-9P2000.L.9p - Definitions of 9P2000.L messages +# lib9p/idl/2010-9P2000.L.9p - Definitions of 9P2000.L messages # # Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> # SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/lib9p/idl/2012-9P2000.e.9p b/lib9p/idl/2012-9P2000.e.9p index 27db50f..c113618 100644 --- a/lib9p/idl/2012-9P2000.e.9p +++ b/lib9p/idl/2012-9P2000.e.9p @@ -1,4 +1,4 @@ -# 2012-9P2000.e.9p - Definitions of 9P2000.e messages +# lib9p/idl/2012-9P2000.e.9p - Definitions of 9P2000.e messages # # Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> # SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/lib9p/idl/__init__.py b/lib9p/idl/__init__.py new file mode 100644 index 0000000..920d02d --- /dev/null +++ b/lib9p/idl/__init__.py @@ -0,0 +1,491 @@ +# lib9p/idl/__init__.py - A parser for .9p specification files. +# +# Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> +# SPDX-License-Identifier: AGPL-3.0-or-later + +import enum +import os.path +import re +from typing import Callable, Literal, TypeAlias, TypeVar, cast + +__all__ = [ + # entrypoint + "Parser", + # types + "Type", + "Primitive", + "Number", + *["Bitfield", "BitfieldVal"], + *["Struct", "StructMember", "Expr", "ExprOp", "ExprSym", "ExprLit"], + "Message", +] + +# The syntax that this parses is described in `./0000-README.md`. + +# Types ######################################################################## + + +class Primitive(enum.Enum): + u8 = 1 + u16 = 2 + u32 = 4 + u64 = 8 + + @property + def in_versions(self) -> set[str]: + return set() + + @property + def name(self) -> str: + return str(self.value) + + @property + def static_size(self) -> int: + return self.value + + +class Number: + name: str + in_versions: set[str] + + prim: Primitive + + def __init__(self) -> None: + self.in_versions = set() + + @property + def static_size(self) -> int: + return self.prim.static_size + + +class BitfieldVal: + name: str + in_versions: set[str] + + val: str + + def __init__(self) -> None: + self.in_versions = set() + + +class Bitfield: + name: str + in_versions: set[str] + + prim: Primitive + + bits: list[str] # bitnames + names: dict[str, BitfieldVal] # bits *and* aliases + + def __init__(self) -> None: + self.in_versions = set() + self.names = {} + + @property + def static_size(self) -> int: + return self.prim.static_size + + def bit_is_valid(self, bit: str | int, ver: str | None = None) -> bool: + """Return whether the given bit is valid in the given protocol + version. + + """ + bitname = self.bits[bit] if isinstance(bit, int) else bit + assert bitname in self.bits + if not bitname: + return False + if bitname.startswith("_"): + return False + if ver and (ver not in self.names[bitname].in_versions): + return False + return True + + +class ExprLit: + val: int + + def __init__(self, val: int) -> None: + self.val = val + + +class ExprSym: + name: str + + def __init__(self, name: str) -> None: + self.name = name + + +class ExprOp: + op: Literal["-", "+"] + + def __init__(self, op: Literal["-", "+"]) -> None: + self.op = op + + +class Expr: + tokens: list[ExprLit | ExprSym | ExprOp] + + def __init__(self) -> None: + self.tokens = [] + + def __bool__(self) -> bool: + return len(self.tokens) > 0 + + +class StructMember: + # from left-to-right when parsing + cnt: str | None = None + name: str + typ: "Type" + max: Expr + val: Expr + + in_versions: set[str] + + @property + def static_size(self) -> int | None: + if self.cnt: + return None + return self.typ.static_size + + +class Struct: + name: str + in_versions: set[str] + + members: list[StructMember] + + def __init__(self) -> None: + self.in_versions = set() + + @property + def static_size(self) -> int | None: + size = 0 + for member in self.members: + msize = member.static_size + if msize is None: + return None + size += msize + return size + + +class Message(Struct): + @property + def msgid(self) -> int: + assert len(self.members) >= 3 + assert self.members[1].name == "typ" + assert self.members[1].static_size == 1 + assert self.members[1].val + assert len(self.members[1].val.tokens) == 1 + assert isinstance(self.members[1].val.tokens[0], ExprLit) + return self.members[1].val.tokens[0].val + + +Type: TypeAlias = Primitive | Number | Bitfield | Struct | Message +# type Type = Primitive | Number | Bitfield | Struct | Message # Change to this once we have Python 3.13 +T = TypeVar("T", Number, Bitfield, Struct, Message) + +# Parse ######################################################################## + +re_priname = "(?:1|2|4|8)" # primitive names +re_symname = "(?:[a-zA-Z_][a-zA-Z_0-9]*)" # "symbol" names; most *.9p-defined names +re_impname = r"(?:\*|" + re_symname + ")" # names we can import +re_msgname = r"(?:[TR][a-zA-Z_0-9]*)" # names a message can be + +re_memtype = f"(?:{re_symname}|{re_priname})" # typenames that a struct member can be + +re_expr = f"(?:(?:-|\\+|[0-9]+|&?{re_symname})+)" + +re_bitspec_bit = f"(?P<bit>[0-9]+)\\s*=\\s*(?P<name>{re_symname})" +re_bitspec_alias = f"(?P<name>{re_symname})\\s*=\\s*(?P<val>\\S+)" + +re_memberspec = f"(?:(?P<cnt>{re_symname})\\*\\()?(?P<name>{re_symname})\\[(?P<typ>{re_memtype})(?:,max=(?P<max>{re_expr})|,val=(?P<val>{re_expr}))*\\]\\)?" + + +def parse_bitspec(ver: str, bf: Bitfield, spec: str) -> None: + spec = spec.strip() + + bit: int | None + val: BitfieldVal + if m := re.fullmatch(re_bitspec_bit, spec): + bit = int(m.group("bit")) + name = m.group("name") + + val = BitfieldVal() + val.name = name + val.val = f"1<<{bit}" + val.in_versions.add(ver) + + if bit < 0 or bit >= len(bf.bits): + raise ValueError(f"{bf.name}: bit {bit} is out-of-bounds") + if bf.bits[bit]: + raise ValueError(f"{bf.name}: bit {bit} already assigned") + bf.bits[bit] = val.name + elif m := re.fullmatch(re_bitspec_alias, spec): + name = m.group("name") + valstr = m.group("val") + + val = BitfieldVal() + val.name = name + val.val = valstr + val.in_versions.add(ver) + else: + raise SyntaxError(f"invalid bitfield spec {repr(spec)}") + + if val.name in bf.names: + raise ValueError(f"{bf.name}: name {val.name} already assigned") + bf.names[val.name] = val + + +def parse_expr(expr: str) -> Expr: + assert re.fullmatch(re_expr, expr) + ret = Expr() + for tok in re.split("([-+])", expr): + if tok == "-" or tok == "+": + # I, for the life of me, do not understand why I need this + # cast() to keep mypy happy. + ret.tokens += [ExprOp(cast(Literal["-", "+"], tok))] + elif re.fullmatch("[0-9]+", tok): + ret.tokens += [ExprLit(int(tok))] + else: + ret.tokens += [ExprSym(tok)] + return ret + + +def parse_members(ver: str, env: dict[str, Type], struct: Struct, specs: str) -> None: + for spec in specs.split(): + m = re.fullmatch(re_memberspec, spec) + if not m: + raise SyntaxError(f"invalid member spec {repr(spec)}") + + member = StructMember() + member.in_versions = {ver} + + member.name = m.group("name") + if any(x.name == member.name for x in struct.members): + raise ValueError(f"duplicate member name {repr(member.name)}") + + if m.group("typ") not in env: + raise NameError(f"Unknown type {repr(m.group(2))}") + member.typ = env[m.group("typ")] + + if cnt := m.group("cnt"): + if len(struct.members) == 0 or struct.members[-1].name != cnt: + raise ValueError(f"list count must be previous item: {repr(cnt)}") + if not isinstance(struct.members[-1].typ, Primitive): + raise ValueError(f"list count must be an integer type: {repr(cnt)}") + member.cnt = cnt + + if maxstr := m.group("max"): + if (not isinstance(member.typ, Primitive)) or member.cnt: + raise ValueError("',max=' may only be specified on a non-repeated atom") + member.max = parse_expr(maxstr) + else: + member.max = Expr() + + if valstr := m.group("val"): + if (not isinstance(member.typ, Primitive)) or member.cnt: + raise ValueError("',val=' may only be specified on a non-repeated atom") + member.val = parse_expr(valstr) + else: + member.val = Expr() + + struct.members += [member] + + +def re_string(grpname: str) -> str: + return f'"(?P<{grpname}>[^"]*)"' + + +re_line_version = f"version\\s+{re_string('version')}" +re_line_import = f"from\\s+(?P<file>\\S+)\\s+import\\s+(?P<syms>{re_impname}(?:\\s*,\\s*{re_impname})*)" +re_line_num = f"num\\s+(?P<name>{re_symname})\\s*=\\s*(?P<prim>{re_priname})" +re_line_bitfield = f"bitfield\\s+(?P<name>{re_symname})\\s*=\\s*(?P<prim>{re_priname})" +re_line_bitfield_ = ( + f"bitfield\\s+(?P<name>{re_symname})\\s*\\+=\\s*{re_string('member')}" +) +re_line_struct = ( + f"struct\\s+(?P<name>{re_symname})\\s*(?P<op>\\+?=)\\s*{re_string('members')}" +) +re_line_msg = ( + f"msg\\s+(?P<name>{re_msgname})\\s*(?P<op>\\+?=)\\s*{re_string('members')}" +) +re_line_cont = f"\\s+{re_string('specs')}" # could be bitfield/struct/msg + + +def parse_file( + filename: str, get_include: Callable[[str], tuple[str, list[Type]]] +) -> tuple[str, list[Type]]: + version: str | None = None + env: dict[str, Type] = { + "1": Primitive.u8, + "2": Primitive.u16, + "4": Primitive.u32, + "8": Primitive.u64, + } + + def get_type(name: str, tc: type[T]) -> T: + nonlocal env + if name not in env: + raise NameError(f"Unknown type {repr(name)}") + ret = env[name] + if (not isinstance(ret, tc)) or (ret.__class__.__name__ != tc.__name__): + raise NameError(f"Type {repr(ret.name)} is not a {tc.__name__}") + return ret + + with open(filename, "r") as fh: + prev: Type | None = None + for line in fh: + line = line.split("#", 1)[0].rstrip() + if not line: + continue + if m := re.fullmatch(re_line_version, line): + if version: + raise SyntaxError("must have exactly 1 version line") + version = m.group("version") + continue + if not version: + raise SyntaxError("must have exactly 1 version line") + + if m := re.fullmatch(re_line_import, line): + other_version, other_typs = get_include(m.group("file")) + for symname in m.group("syms").split(sep=","): + symname = symname.strip() + for typ in other_typs: + if typ.name == symname or symname == "*": + match typ: + case Primitive(): + pass + case Number(): + typ.in_versions.add(version) + case Bitfield(): + typ.in_versions.add(version) + for val in typ.names.values(): + if other_version in val.in_versions: + val.in_versions.add(version) + case Struct(): # and Message() + typ.in_versions.add(version) + for member in typ.members: + if other_version in member.in_versions: + member.in_versions.add(version) + env[typ.name] = typ + elif m := re.fullmatch(re_line_num, line): + num = Number() + num.name = m.group("name") + num.in_versions.add(version) + + prim = env[m.group("prim")] + assert isinstance(prim, Primitive) + num.prim = prim + + env[num.name] = num + prev = num + elif m := re.fullmatch(re_line_bitfield, line): + bf = Bitfield() + bf.name = m.group("name") + bf.in_versions.add(version) + + prim = env[m.group("prim")] + assert isinstance(prim, Primitive) + bf.prim = prim + + bf.bits = (prim.static_size * 8) * [""] + + env[bf.name] = bf + prev = bf + elif m := re.fullmatch(re_line_bitfield_, line): + bf = get_type(m.group("name"), Bitfield) + parse_bitspec(version, bf, m.group("member")) + + prev = bf + elif m := re.fullmatch(re_line_struct, line): + match m.group("op"): + case "=": + struct = Struct() + struct.name = m.group("name") + struct.in_versions.add(version) + struct.members = [] + parse_members(version, env, struct, m.group("members")) + + env[struct.name] = struct + prev = struct + case "+=": + struct = get_type(m.group("name"), Struct) + parse_members(version, env, struct, m.group("members")) + + prev = struct + elif m := re.fullmatch(re_line_msg, line): + match m.group("op"): + case "=": + msg = Message() + msg.name = m.group("name") + msg.in_versions.add(version) + msg.members = [] + parse_members(version, env, msg, m.group("members")) + + env[msg.name] = msg + prev = msg + case "+=": + msg = get_type(m.group("name"), Message) + parse_members(version, env, msg, m.group("members")) + + prev = msg + elif m := re.fullmatch(re_line_cont, line): + match prev: + case Bitfield(): + parse_bitspec(version, prev, m.group("specs")) + case Struct(): # and Message() + parse_members(version, env, prev, m.group("specs")) + case _: + raise SyntaxError( + "continuation line must come after a bitfield, struct, or msg line" + ) + else: + raise SyntaxError(f"invalid line {repr(line)}") + if not version: + raise SyntaxError("must have exactly 1 version line") + + typs: list[Type] = [x for x in env.values() if not isinstance(x, Primitive)] + + for typ in [typ for typ in typs if isinstance(typ, Struct)]: + valid_syms = ["end", *["&" + m.name for m in typ.members]] + for member in typ.members: + for tok in [*member.max.tokens, *member.val.tokens]: + if isinstance(tok, ExprSym) and tok.name not in valid_syms: + raise ValueError( + f"{typ.name}.{member.name}: invalid sym: {tok.name}" + ) + + return version, typs + + +# Filesystem ################################################################### + + +class Parser: + cache: dict[str, tuple[str, list[Type]]] = {} + + def parse_file(self, filename: str) -> tuple[str, list[Type]]: + filename = os.path.normpath(filename) + if filename not in self.cache: + + def get_include(other_filename: str) -> tuple[str, list[Type]]: + return self.parse_file(os.path.join(filename, "..", other_filename)) + + self.cache[filename] = parse_file(filename, get_include) + return self.cache[filename] + + def all(self) -> tuple[set[str], list[Type]]: + ret_versions: set[str] = set() + ret_typs: dict[str, Type] = {} + for version, typs in self.cache.values(): + if version in ret_versions: + raise ValueError(f"duplicate protocol version {repr(version)}") + ret_versions.add(version) + for typ in typs: + if typ.name in ret_typs: + if typ != ret_typs[typ.name]: + raise ValueError(f"duplicate type name {repr(typ.name)}") + else: + ret_typs[typ.name] = typ + return ret_versions, list(ret_typs.values()) diff --git a/lib9p/include/lib9p/9p.generated.h b/lib9p/include/lib9p/9p.generated.h index 25aacfe..a6dc13c 100644 --- a/lib9p/include/lib9p/9p.generated.h +++ b/lib9p/include/lib9p/9p.generated.h @@ -22,7 +22,7 @@ #error config.h must define CONFIG_9P_ENABLE_9P2000_u #endif -/* versions *******************************************************************/ +/* enum version ***************************************************************/ enum lib9p_version { LIB9P_VER_unknown = 0, /* "unknown" */ @@ -40,7 +40,49 @@ enum lib9p_version { const char *lib9p_version_str(enum lib9p_version); -/* non-message types **********************************************************/ +/* enum msg_type **************************************************************/ + +enum lib9p_msg_type { /* uint8_t */ +#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_u + LIB9P_TYP_Tversion = 100, + LIB9P_TYP_Rversion = 101, + LIB9P_TYP_Tauth = 102, + LIB9P_TYP_Rauth = 103, + LIB9P_TYP_Tattach = 104, + LIB9P_TYP_Rattach = 105, + LIB9P_TYP_Rerror = 107, + LIB9P_TYP_Tflush = 108, + LIB9P_TYP_Rflush = 109, + LIB9P_TYP_Twalk = 110, + LIB9P_TYP_Rwalk = 111, + LIB9P_TYP_Topen = 112, + LIB9P_TYP_Ropen = 113, + LIB9P_TYP_Tcreate = 114, + LIB9P_TYP_Rcreate = 115, + LIB9P_TYP_Tread = 116, + LIB9P_TYP_Rread = 117, + LIB9P_TYP_Twrite = 118, + LIB9P_TYP_Rwrite = 119, + LIB9P_TYP_Tclunk = 120, + LIB9P_TYP_Rclunk = 121, + LIB9P_TYP_Tremove = 122, + LIB9P_TYP_Rremove = 123, + LIB9P_TYP_Tstat = 124, + LIB9P_TYP_Rstat = 125, + LIB9P_TYP_Twstat = 126, + LIB9P_TYP_Rwstat = 127, +#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_u */ +#if CONFIG_9P_ENABLE_9P2000_e + LIB9P_TYP_Tsession = 150, + LIB9P_TYP_Rsession = 151, + LIB9P_TYP_Tsread = 152, + LIB9P_TYP_Rsread = 153, + LIB9P_TYP_Tswrite = 154, + LIB9P_TYP_Rswrite = 155, +#endif /* CONFIG_9P_ENABLE_9P2000_e */ +}; + +/* payload types **************************************************************/ #if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_u typedef uint16_t lib9p_tag_t; @@ -156,51 +198,7 @@ typedef uint8_t lib9p_o_t; #define LIB9P_O_EXEC ((lib9p_o_t)(3)) #define LIB9P_O_MODE_MASK ((lib9p_o_t)(0b00000011)) #define LIB9P_O_FLAG_MASK ((lib9p_o_t)(0b11111100)) -#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_u */ -/* messages *******************************************************************/ - -enum lib9p_msg_type { /* uint8_t */ -#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_u - LIB9P_TYP_Tversion = 100, - LIB9P_TYP_Rversion = 101, - LIB9P_TYP_Tauth = 102, - LIB9P_TYP_Rauth = 103, - LIB9P_TYP_Tattach = 104, - LIB9P_TYP_Rattach = 105, - LIB9P_TYP_Rerror = 107, - LIB9P_TYP_Tflush = 108, - LIB9P_TYP_Rflush = 109, - LIB9P_TYP_Twalk = 110, - LIB9P_TYP_Rwalk = 111, - LIB9P_TYP_Topen = 112, - LIB9P_TYP_Ropen = 113, - LIB9P_TYP_Tcreate = 114, - LIB9P_TYP_Rcreate = 115, - LIB9P_TYP_Tread = 116, - LIB9P_TYP_Rread = 117, - LIB9P_TYP_Twrite = 118, - LIB9P_TYP_Rwrite = 119, - LIB9P_TYP_Tclunk = 120, - LIB9P_TYP_Rclunk = 121, - LIB9P_TYP_Tremove = 122, - LIB9P_TYP_Rremove = 123, - LIB9P_TYP_Tstat = 124, - LIB9P_TYP_Rstat = 125, - LIB9P_TYP_Twstat = 126, - LIB9P_TYP_Rwstat = 127, -#endif /* CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_u */ -#if CONFIG_9P_ENABLE_9P2000_e - LIB9P_TYP_Tsession = 150, - LIB9P_TYP_Rsession = 151, - LIB9P_TYP_Tsread = 152, - LIB9P_TYP_Rsread = 153, - LIB9P_TYP_Tswrite = 154, - LIB9P_TYP_Rswrite = 155, -#endif /* CONFIG_9P_ENABLE_9P2000_e */ -}; - -#if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_u struct lib9p_msg_Tversion { lib9p_tag_t tag; uint32_t max_msg_size; |