]> www.infradead.org Git - users/dhowells/kafs-utils.git/commitdiff
Implement rxgen
authorDavid Howells <dhowells@redhat.com>
Wed, 9 Oct 2019 15:41:01 +0000 (16:41 +0100)
committerDavid Howells <dhowells@redhat.com>
Fri, 5 May 2023 09:06:50 +0000 (10:06 +0100)
Implement the rxgen program to process .xg interface files into C
definitions.

Signed-off-by: David Howells <dhowells@redhat.com>
.gitignore
lib/Makefile
rxgen/Makefile [new file with mode: 0644]
rxgen/emit_c_struct.py [new file with mode: 0644]
rxgen/emit_c_sync_funcs.py [new file with mode: 0644]
rxgen/rxgen.py [new file with mode: 0755]
rxgen/rxgen_bits.py [new file with mode: 0644]

index bda489349fdcc941a4aeaa2d05d296a82101d5a9..0b36bab0b8310ea76e863db050c9fde3c565c6f1 100644 (file)
@@ -3,3 +3,6 @@
 .*.o.d
 *.so
 lib/libkafs_utils.*
+lib/afs_xg.*
+rxgen/__pycache__/
+rxgen/parsetab.py
index 6e81c71fa286b19de2dd3c71af5f6498f55b8a33..8228645f45a798143c8df6a56fa6c87addef5546 100644 (file)
@@ -1,6 +1,12 @@
 CFLAGS         := -g -Wall -Wformat -fpic -O2 -Wno-pointer-arith
 
-LIB_SRCS       := rxrpc_xdr.C af_rxrpc.C rxrpc_core.C
+RXGEN          := ../rxgen/rxgen.py
+RPC_HEADERS    := $(wildcard ../rpc-api/*.h)
+RPC_DEFS       := $(filter-out %test.xg, $(sort $(wildcard ../rpc-api/*.xg)))
+#RPC_HEADERS   := ../rpc-api/test.h
+#RPC_DEFS      := ../rpc-api/test.xg
+
+LIB_SRCS       := afs_xg.C rxrpc_xdr.C af_rxrpc.C rxrpc_core.C
 LIB_OBJS       := $(patsubst %.C,%.o,$(LIB_SRCS))
 
 %.o: %.C
@@ -11,10 +17,15 @@ all: libkafs_utils.a
 libkafs_utils.a: $(LIB_OBJS)
        $(AR) rcs -o $@ $(LIB_OBJS)
 
+afs_xg.o: afs_xg.C afs_xg.H rxrpc.H
+
+afs_xg.C afs_xg.H: $(RPC_HEADERS) $(RPC_DEFS) $(wildcard ../rxgen/*.py) Makefile
+       $(RXGEN) $(RPC_HEADERS) $(RPC_DEFS)
+
 DEPS           := $(wildcard .*.o.d)
 ifneq ($(DEPS),)
 include $(DEPS)
 endif
 
 clean:
-       $(RM) *~ *.o *.so *.a $(DEPS)
+       $(RM) *~ afs_xg.[CH] *.o *.so *.a $(DEPS)
diff --git a/rxgen/Makefile b/rxgen/Makefile
new file mode 100644 (file)
index 0000000..642cd4e
--- /dev/null
@@ -0,0 +1,3 @@
+
+clean:
+       $(RM) -r *~ parsetab.py __pycache__/
diff --git a/rxgen/emit_c_struct.py b/rxgen/emit_c_struct.py
new file mode 100644 (file)
index 0000000..43e7251
--- /dev/null
@@ -0,0 +1,366 @@
+#!/usr/bin/python3
+#
+# Emit C structure
+# -*- coding: utf-8 -*-
+
+__copyright__ = """
+Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
+Written by David Howells (dhowells@redhat.com)
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public Licence version 2 as
+published by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public Licence for more details.
+
+You should have received a copy of the GNU General Public Licence
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+"""
+
+from rxgen_bits import *
+
+###############################################################################
+#
+# Emit a structure member definition list
+#
+###############################################################################
+def emit_struct_member_list(o, struct, members):
+    for m in members:
+        o.where(struct.name + "::" + m.name)
+        if m.is_single():
+            if m.is_string():
+                o.rxhdr("\t", m.ty.c_name, "\t", m.name, ";\n")
+            elif m.is_opaque():
+                o.rxhdr("\t", m.ty.c_name, "\t", m.name, ";\n")
+            else:
+                o.rxhdr("\t", m.ty.c_name, "\t", m.name, ";\n")
+        elif m.is_int_array() or m.is_struct_array():
+            o.rxhdr("\t", m.ty.c_name, "\t", m.name, "[", m.ty.dim.name, "];\n")
+        elif m.is_bulk():
+            o.rxhdr("\tstd::vector<", m.ty.c_name, "> ", m.name, ";\n")
+        else:
+            raise RuntimeError("Unsupported type '" + str(m.ty) + "'\n")
+
+###############################################################################
+#
+# Emit a C union definition for this type
+#
+###############################################################################
+def emit_union_decl(o, struct):
+    selector = struct.selector_name
+    want_i = False
+
+    #
+    # Write out a C structure definition for this type
+    #
+    o.rxhdr("struct ", struct.name, " {\n")
+    o.rxhdr("\t", struct.selector_type, " ", selector, ";\n")
+
+    for case in struct.members:
+        for m in case.members:
+            if m.is_array():
+                want_i = True
+                break
+        o.rxhdr("\t\t/* case ", case.tag, " */\n")
+        emit_struct_member_list(o, struct, case.members)
+
+    o.rxhdr("};\n")
+
+###############################################################################
+#
+# Emit a C structure definition for this type
+#
+###############################################################################
+def emit_struct_decl(o, struct):
+    o.rxhdr("\n")
+    if struct.is_union():
+        emit_union_decl(o, struct)
+    else:
+        o.rxhdr("struct ", struct.name, " {\n")
+        emit_struct_member_list(o, struct, struct.members)
+        o.rxhdr("};\n")
+
+###############################################################################
+#
+# Emit code to calculate the size of a member list
+#
+###############################################################################
+def emit_struct_calc_members(o, members):
+    for m in members:
+        if m.is_single_int32():
+            o.rxsrc("\trxrpc::reserve(resv);\t// ", m.name, "\n")
+        elif m.is_single_int64():
+            o.rxsrc("\trxrpc::reserve_u64(resv);\t// ", m.name, "\n")
+        elif m.is_single_struct():
+            o.rxsrc("\treserve_", m.ty.name, "(resv, p.", m.name, ");\n")
+        elif m.is_single_string():
+            o.rxsrc("\trxrpc::reserve_string(resv, p.",
+                    m.name, ", ", m.what_max_obj(), ");\n");
+        elif m.is_single_opaque():
+            o.rxsrc("\trxrpc::reserve_opaque(resv, p.",
+                    m.name, ", ", m.what_max_obj(), ");\n");
+        elif m.is_array():
+            if m.is_int32_array():
+                o.rxsrc("\trxrpc::reserve_a32(resv, ", m.dim.name, ");\t// ", m.name, "[]\n")
+            elif m.is_int64_array():
+                o.rxsrc("\trxrpc::reserve_a64(resv, ", m.dim.name, ");\t// ", m.name, "[]\n")
+            elif m.is_struct_array():
+                o.rxsrc("\tfor (size_t i = 0; i < ", m.dim.name, "; i++)\n")
+                o.rxsrc("\t\treserve_", m.ty.name, "(resv, p.", m.name, "[i]);\n")
+            else:
+                raise RuntimeError("No enc-calc for array type '" + str(m.ty) + "'")
+        elif m.is_bulk():
+            o.rxsrc("\trxrpc::reserve(resv);\t// ", m.name, "__nr\n")
+            if m.is_int32():
+                o.rxsrc("\trxrpc::reserve_a32(resv, p.", m.name, ".size());\n")
+            else:
+                o.rxsrc("\tfor (size_t i = 0; i < p.", m.name, ".size(); i++)\n")
+                if m.is_string():
+                    o.rxsrc("\t\trxrpc::reserve_string(resv, p.",
+                            m.name, "[i], ", m.what_max_obj(), ");\n");
+                elif m.is_opaque():
+                    o.rxsrc("\t\trxrpc::reserve_opaque(resv, p.",
+                            m.name, "[i], ", m.what_max_obj(), ");\n");
+                else:
+                    o.rxsrc("\t\treserve_", m.ty.name, "(resv, p.", m.name, "[i]);\n")
+        elif str(m.ty) == "opr_uuid":
+            o.rxsrc("\t\trxrpc::reserve_", m.ty.name, "(resv, p.", m.name, ");\n")
+        else:
+            raise RuntimeError("No enc-calc for type '" + str(m.ty) + "'")
+
+###############################################################################
+#
+# Emit a structure member list encoder
+#
+###############################################################################
+def emit_struct_encode_members(o, struct, members):
+    for m in members:
+        o.where(struct.name + "::" + m.name)
+        if m.is_single_int32():
+            o.rxsrc("\trxrpc::enc     (z_buf, p.", m.name, ");\n")
+        elif m.is_single_int64():
+            o.rxsrc("\trxrpc::enc_u64 (z_buf, p.", m.name, ");\n")
+        elif m.is_single_struct():
+            o.rxsrc("\tencode_", m.ty.name, "(z_buf, p.", m.name, ");\n")
+        elif m.is_array():
+            o.rxsrc("\tfor (size_t i = 0; i < ", m.dim.name, "; i++)\n")
+            if m.is_int32_array():
+                o.rxsrc("\t\trxrpc::enc     (z_buf, p.", m.name, "[i]);\n")
+            elif m.is_int64_array():
+                o.rxsrc("\t\trxrpc::enc_u64 (z_buf, p.", m.name, "[i]);\n")
+            elif m.is_struct_array():
+                o.rxsrc("\t\tencode_", m.ty.name, "(z_buf, p.", m.name, "[i]);\n")
+            else:
+                raise RuntimeError("No encoding for array type '" + str(m.ty) + "'")
+        elif m.is_single_string():
+            o.rxsrc("\trxrpc::enc_string(z_buf, p.", m.name, ", ",
+                    m.what_max_obj(), ");\n")
+        elif m.is_single_opaque():
+            o.rxsrc("\trxrpc::enc_opaque(z_buf, p.", m.name, ", ", m.what_max_obj(), ");\n")
+        elif m.is_single_uuid():
+            o.rxsrc("\trxrpc::enc_uuid(z_buf, p.", m.name, ");\n")
+        elif m.is_bulk():
+            o.rxsrc("\trxrpc::enc     (z_buf, p.", m.name, ".size());\n")
+            o.rxsrc("\tfor (size_t i = 0; i < p.", m.name, ".size(); i++)\n")
+            if m.is_int32():
+                o.rxsrc("\t\trxrpc::enc(z_buf, p.", m.name, "[i]);\n")
+            elif m.is_string():
+                o.rxsrc("\t\trxrpc::enc_string(z_buf, p.", m.name, "[i], ",
+                        m.what_max_obj(), ");\n")
+            elif m.is_opaque():
+                o.rxsrc("\t\trxrpc::enc_opaque(z_buf, p.", m.name, "[i], ",
+                        m.what_max_obj(), ");\n")
+            else:
+                o.rxsrc("\t\tencode_", m.ty.name, "(z_buf, p.", m.name, "[i]);\n")
+        else:
+            raise RuntimeError("No encoding for type '" + str(m.ty) + "'")
+
+###############################################################################
+#
+# Emit a structure member list decoder
+#
+###############################################################################
+def emit_struct_decode_members(o, struct, members):
+    for m in members:
+        o.where(struct.name + "::" + m.name)
+        if m.is_single_int32():
+            o.rxsrc("\tp.", m.name, "\t= rxrpc::dec(z_rxq);\n")
+        elif m.is_single_int64():
+            o.rxsrc("\tp.", m.name, "\t= rxrpc::dec_u64(z_rxq);\n")
+        elif m.is_single_struct():
+            o.rxsrc("\tdecode_", m.ty.name, "(z_rxq, p.", m.name, ");\n")
+        elif m.is_array():
+            o.rxsrc("\tfor (size_t i = 0; i < ", m.dim.name, "; i++)\n")
+            if m.is_int32_array():
+                o.rxsrc("\t\tp.", m.name, "[i] = rxrpc::dec(z_rxq);\n")
+            elif m.is_int64_array():
+                o.rxsrc("\t\tp.", m.name, "[i] = rxrpc::dec_u64(z_rxq);\n")
+            elif m.is_struct_array():
+                o.rxsrc("\t\tdecode_", m.ty.name, "(z_rxq, p.", m.name, "[i]);\n")
+            else:
+                raise RuntimeError("No decoding for array type '" + str(m.ty) + "'")
+        elif m.is_single_string():
+            o.rxsrc("\trxrpc::dec_string(z_rxq, p.", m.name)
+            if m.max_obj != None:
+                o.rxsrc(", ", m.max_obj);
+            o.rxsrc(");\n")
+        elif m.is_single_opaque():
+            o.rxsrc("\trxrpc::dec_opaque(z_rxq, p.", m.name)
+            if m.max_obj != None:
+                o.rxsrc(", ", m.max_obj);
+            o.rxsrc(");\n")
+        elif m.is_bulk():
+            o.rxsrc("\tsize_t ", m.name, "__nr = rxrpc::dec(z_rxq);\n")
+            o.rxsrc("\tif (", m.name, "__nr > 0) {\n")
+            o.rxsrc("\t\tp.", m.name, ".resize(", m.name, "__nr);\n")
+            o.rxsrc("\t\tfor (size_t i = 0; i < ", m.name, "__nr; i++)\n")
+            if m.is_int32():
+                o.rxsrc("\t\t\tp.", m.name, "[i]\t= rxrpc::dec(z_rxq);\n")
+            elif m.is_string():
+                o.rxsrc("\t\t\trxrpc::dec_string(z_rxq, p.", m.name)
+                if m.max_obj != None:
+                    o.rxsrc("[i], ", m.max_obj);
+                o.rxsrc(");\n")
+            elif m.is_opaque():
+                o.rxsrc("\t\t\trxrpc::dec_opaque(z_rxq, p.", m.name)
+                if m.max_obj != None:
+                    o.rxsrc("[i], ", m.max_obj);
+                o.rxsrc(");\n")
+            else:
+                o.rxsrc("\t\t\tdecode_", m.ty.name, "(z_rxq, p.", m.name, "[i]);\n")
+            o.rxsrc("\t}\n")
+        elif str(m.ty) == "opr_uuid":
+            o.rxsrc("\tz_rxq.read(&p.", m.name, ", 16);\n")
+        else:
+            raise RuntimeError("No decoding for type '" + str(m.ty) + "'")
+
+###############################################################################
+#
+# Emit union encoders and decoders
+#
+###############################################################################
+def emit_union_encdec(o, struct):
+    selector = struct.selector_name
+    want_i = False
+
+    #
+    # Write a size calculation function
+    #
+    o.rxsrc("\n")
+    o.rxsrc("void reserve_", struct.name,
+            "(rxrpc::Resv &resv, const ", struct.c_name, " &p)\n")
+    o.rxsrc("{\n")
+    o.rxsrc("\trxrpc::reserve(resv); // selector - p.", selector, "\n")
+    o.rxsrc("\tswitch (p.", selector, ") {\n")
+    for case in struct.members:
+        for m in case.members:
+            if m.is_array() and m.is_struct_array():
+                o.rxsrc("\tint i;\n\n")
+                break
+    for case in struct.members:
+        o.rxsrc("\tcase ", case.tag, ":\n")
+        emit_struct_calc_members(o, case.members)
+        o.rxsrc("\tbreak;\n")
+    o.rxsrc("}\n")
+    o.rxsrc("}\n")
+
+    #
+    # Write an encoding function
+    #
+    o.rxsrc("\n")
+    o.rxsrc("void encode_", struct.name,
+            "(rxrpc::Enc_buffer *z_buf, const ", struct.c_name, " &p)\n")
+    o.rxsrc("{\n")
+
+    if want_i:
+        o.rxsrc("\tint i;\n\n")
+
+    o.rxsrc("\trxrpc::enc(z_buf, p.", selector, ");\n")
+    o.rxsrc("\tswitch (p.", selector, ") {\n")
+    for case in struct.members:
+        o.rxsrc("\tcase ", case.tag, ":\n")
+        emit_struct_encode_members(o, struct, case.members)
+        o.rxsrc("\tbreak;\n")
+    o.rxsrc("\t}\n")
+    o.rxsrc("}\n")
+
+    #
+    # Write a decoding function
+    #
+    o.rxsrc("\n")
+    #o.rxsrc("static __attribute__((unused))\n")
+    o.rxsrc("void decode_", struct.name,
+            "(rxrpc::Rx_queue &z_rxq, ", struct.c_name, " &p)\n")
+    o.rxsrc("{\n")
+
+    if want_i:
+        o.rxsrc("\tint i;\n\n")
+
+    o.rxsrc("\tp.", selector, " = rxrpc::dec(z_rxq);\n")
+    o.rxsrc("\tswitch (p.", selector, ") {\n")
+    for case in struct.members:
+        o.rxsrc("\tcase ", case.tag, ":\n")
+        emit_struct_decode_members(o, struct, case.members)
+        o.rxsrc("\tbreak;\n")
+    o.rxsrc("}\n")
+    o.rxsrc("}\n")
+
+###############################################################################
+#
+# Emit structure encoders and decoders
+#
+###############################################################################
+def emit_struct_encdec(o, struct):
+    if struct.is_union():
+        emit_union_encdec(o, struct)
+        return
+
+    #
+    # Write a size calculation function
+    #
+    o.rxsrc("\n")
+    o.rxsrc("void reserve_", struct.name,
+            "(rxrpc::Resv &resv, const struct ", struct.name, " &p)\n")
+    o.rxsrc("{\n")
+    emit_struct_calc_members(o, struct.members)
+    o.rxsrc("}\n")
+    o.rxsrc("\n")
+
+    #
+    # Write an encoding function
+    #
+    o.rxsrc("\n")
+    o.rxsrc("void encode_", struct.name,
+            "(rxrpc::Enc_buffer *z_buf, const struct ", struct.name, " &p)\n")
+    o.rxsrc("{\n")
+
+    emit_struct_encode_members(o, struct, struct.members)
+    o.rxsrc("}\n")
+
+    #
+    # Write a decoding function
+    #
+    o.rxsrc("\n")
+    o.rxsrc("void decode_", struct.name,
+            "(rxrpc::Rx_queue &z_rxq, struct ", struct.name, " &p)\n")
+    o.rxsrc("{\n")
+
+    emit_struct_decode_members(o, struct, struct.members)
+    o.rxsrc("}\n")
+
+###############################################################################
+#
+# Emit C func definitions for enc/dec routines
+#
+###############################################################################
+def emit_struct_encdec_decl(o, struct):
+    o.rxhdr("extern void reserve_", struct.name,
+            "(rxrpc::Resv &resv, const struct ", struct.name, " &p);\n")
+    o.rxhdr("extern void encode_", struct.name,
+            "(rxrpc::Enc_buffer *z_buf, const struct ", struct.name, " &p);\n")
+    o.rxhdr("extern void decode_", struct.name,
+            "(rxrpc::Rx_queue &z_rxq, struct ", struct.name, " &p);\n")
diff --git a/rxgen/emit_c_sync_funcs.py b/rxgen/emit_c_sync_funcs.py
new file mode 100644 (file)
index 0000000..1579508
--- /dev/null
@@ -0,0 +1,656 @@
+#!/usr/bin/python3
+#
+# Emit C synchronous functions
+# -*- coding: utf-8 -*-
+
+__copyright__ = """
+Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
+Written by David Howells (dhowells@redhat.com)
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public Licence version 2 as
+published by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public Licence for more details.
+
+You should have received a copy of the GNU General Public Licence
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+"""
+
+from rxgen_bits import *
+
+###############################################################################
+#
+# Calculate the C function prototypes
+#
+###############################################################################
+def emit_func_prototype(o, func):
+    # Function prototype lists (we add commas and the closing bracket later)
+    send_request_protos = list()
+    send_response_protos = list()
+
+    for p in func.params:
+        o.where(func.name + ":" + p.name)
+        s_req = list()
+        r_req = list()
+        s_rsp = list()
+        r_rsp = list()
+
+        t = p.ty.c_name
+        if p.is_array():
+            raise RuntimeError("Array arg not supported")
+        elif p.is_bulk():
+            s_req.append("const std::vector<" + p.ty.c_name + "> &" + p.name)
+            r_req.append("const std::vector<" + p.ty.c_name + "> &" + p.name)
+            s_rsp.append("const std::vector<" + p.ty.c_name + "> &" + p.name)
+            r_rsp.append("std::vector<" + p.ty.c_name + "> &_" + p.name)
+        elif p.is_single_string() or p.is_single_opaque() or p.is_single_struct() or \
+             p.is_single_uuid():
+            s_req.append("const " + t + " &" + p.name)
+            r_req.append("const " + t + " &" + p.name)
+            s_rsp.append("const " + t + " &" + p.name)
+            r_rsp.append(t + " &_" + p.name)
+        else:
+            enc_const = ""
+            if not p.is_single_int():
+                enc_const = "const "
+            proto = p.ty.c_name + " "
+            if not p.is_single_int():
+                proto += "&"
+            s_req.append(enc_const + proto + p.name)
+            r_rsp.append(proto + "*" + p.name)
+            r_req.append(enc_const + proto + p.name)
+            s_rsp.append(enc_const + proto + p.name)
+
+        if p.direction == xdr_direction.IN or p.direction == xdr_direction.INOUT:
+            send_request_protos += s_req
+        if p.direction == xdr_direction.OUT or p.direction == xdr_direction.INOUT:
+            send_response_protos += s_rsp
+
+    o.rxhdr("\n")
+    o.rxhdr("/*\n")
+    o.rxhdr(" * ", func.name, "\n")
+    o.rxhdr(" */\n")
+
+    o.rxhdr("extern rxrpc::ref<rxrpc::Enc_buffer> encode_", func.name, "(\n")
+    need_sep = False
+    for proto in send_request_protos:
+        if need_sep:
+            o.rxhdr(",\n")
+        o.rxhdr("\t", proto)
+        need_sep = True
+    o.rxhdr(");\n")
+    o.rxhdr("\n")
+
+    o.rxhdr("extern rxrpc::ref<rxrpc::Enc_buffer> encode_S_", func.name, "(\n")
+    need_sep = False
+    for proto in send_response_protos:
+        if need_sep:
+            o.rxhdr(",\n")
+        o.rxhdr("\t", proto)
+        need_sep = True
+    o.rxhdr(");\n")
+    o.rxhdr("\n")
+
+    o.rxhdr("extern rxrpc::ref<rxrpc::Call> begin_", func.name + "(\n")
+    o.rxhdr("\trxrpc::Call_params *z_params,\n")
+    o.rxhdr("\trxrpc::Enc_buffer *z_buf);\n")
+
+    o.rxhdr("\n")
+    o.rxhdr("extern void decode_", func.name, "(\n")
+    o.rxhdr("\trxrpc::Call *z_call")
+    for p in func.response:
+        o.rxhdr(",\n\t")
+        if p.is_bulk():
+            o.rxhdr("std::vector<", p.ty.c_name, "> &_", p.name)
+            need_i = True
+        else:
+            o.rxhdr(p.ty.c_name, " &_", p.name)
+    o.rxhdr(");\n")
+
+    o.rxhdr("\n")
+    o.rxhdr("extern int S_", func.name, "(\n")
+    o.rxhdr("\trxrpc::Call *z_call")
+    for p in func.request:
+        o.rxhdr(",\n\t")
+        if p.is_single_int():
+            o.rxhdr(p.ty.c_name, " ", p.name)
+        elif p.is_bulk():
+            o.rxhdr("std::vector<", p.ty.c_name, "> &a_", p.name)
+        else:
+            o.rxhdr(p.ty.c_name, " &a_", p.name)
+    o.rxhdr(");\n")
+
+    # Synchronous client call
+    o.rxhdr("\n")
+    o.rxhdr("extern void ", func.name, "(\n")
+    o.rxhdr("\trxrpc::Call_params *z_params")
+
+    # Request parameters
+    for proto in send_request_protos:
+        o.rxhdr(",\n\t", proto)
+
+    # Reply parameters
+    for p in func.response:
+        o.rxhdr(",\n\t")
+        if p.is_bulk():
+            o.rxhdr("std::vector<", p.ty.c_name, "> &_", p.name)
+        elif p.is_single_string():
+            o.rxhdr(p.ty.c_name, " &_", p.name)
+        elif p.is_single_opaque():
+            o.rxhdr(p.ty.c_name, " &_", p.name)
+        elif p.is_single_int():
+            o.rxhdr(p.ty.c_name, " &_", p.name)
+        elif p.is_single_struct():
+            o.rxhdr(p.ty.c_name, " &_", p.name)
+        elif str(p.ty) == "opr_uuid":
+            o.rxhdr(p.ty.c_name, " &_", p.name)
+        else:
+            print(p.name)
+            raise RuntimeError("No prototype for type '" + str(p.ty) + "'")
+    o.rxhdr(");\n")
+
+    func.send_request_protos = send_request_protos
+    func.send_response_protos = send_response_protos
+
+###############################################################################
+#
+# Emit a function to encode a request or a response into a buffer, not
+# including any split insertion.
+#
+###############################################################################
+def emit_func_encode(o, func, what):
+    # Function definition and arguments
+    if what == "request":
+        protos = func.send_request_protos
+        params = func.request
+    else:
+        protos = func.send_response_protos
+        params = func.response
+
+    o.rxsrc("\n")
+    if what == "request":
+        o.rxsrc("rxrpc::ref<rxrpc::Enc_buffer> encode_", func.name, "(")
+    else:
+        o.rxsrc("rxrpc::ref<rxrpc::Enc_buffer> encode_S_", func.name, "(")
+
+    need_sep = False
+    for proto in protos:
+        if need_sep:
+            o.rxsrc(",")
+        else:
+            need_sep = True
+        o.rxsrc("\n\t\t", proto)
+
+    o.rxsrc(")\n")
+    o.rxsrc("{\n")
+
+    blob_params = list()
+    bulk_params = list()
+    for p in params:
+        if p.is_single_string() or p.is_single_opaque():
+            blob_params.append(p)
+        if p.is_bulk():
+            bulk_params.append(p)
+
+    # Local variables
+    o.rxsrc("\trxrpc::ref<rxrpc::Enc_buffer> z_buf;\n")
+    o.rxsrc("\trxrpc::Resv z_resv = { .max_ioc = 1, };\n")
+    o.rxsrc("\n")
+
+    # Parameter checks
+    if blob_params or bulk_params:
+        for p in blob_params:
+            o.where(func.name + ":" + p.name)
+            if p.max_obj:
+                o.rxsrc("\tif (", p.name, ".size() > ", p.max_obj.name,
+                        ") throw std::invalid_argument(\"Size of ", p.name, " too large\");\n")
+
+        for p in bulk_params:
+            o.where(func.name + ":" + p.name)
+            if p.max_count:
+                o.rxsrc("\tif (", p.name, ".size() > ", p.max_count.name,
+                        ") throw std::invalid_argument(\"Size of ", p.name, " too large\");\n")
+        o.rxsrc("\n")
+
+    # Calculate the size of the parameter.
+    o.rxsrc("\t/* Determine size */\n")
+    o.rxsrc("\trxrpc::reserve(z_resv); // ", func.opcode.name, "\n");
+    for p in params:
+        if p.is_single_string():
+            o.rxsrc("\trxrpc::reserve_string(z_resv, ",
+                    p.name, ", ", p.what_max_obj(), ");\n")
+        elif p.is_single_opaque():
+            o.rxsrc("\trxrpc::reserve_opaque(z_resv, ",
+                    p.name, ", ", p.what_max_obj(), ");\n")
+        elif p.is_bulk():
+            o.rxsrc("\trxrpc::reserve(z_resv); // #", p.name, "\n")
+            if p.is_bulk_int32():
+                o.rxsrc("\trxrpc::reserve_a32(z_resv, ", p.name, ".size());\n")
+            elif p.is_bulk_int64():
+                o.rxsrc("\trxrpc::reserve_a64(z_resv, ", p.name, ".size());\n")
+            elif p.is_bulk():
+                o.rxsrc("\tfor (size_t i = 0; i < ", p.name, ".size(); i++)\n")
+                o.rxsrc("\t\txdr::reserve_", str(p.ty.name), "(z_resv, ", p.name, "[i]);\n")
+        elif p.is_int32():
+            o.rxsrc("\trxrpc::reserve(z_resv); // ", p.name, "\n")
+        elif p.is_int64():
+            o.rxsrc("\trxrpc::reserve_u64(z_resv); // ", p.name, "\n")
+        elif p.is_single_uuid():
+            o.rxsrc("\trxrpc::reserve_opr_uuid(z_resv, ", p.name, ");\n")
+        else:
+            o.rxsrc("\txdr::reserve_", p.ty.name, "(z_resv, ", p.name, ");\n")
+
+    # Allocate the buffer
+    o.rxsrc("\n")
+    o.rxsrc("\t/* Marshal */\n")
+    o.rxsrc("\tz_buf = rxrpc::alloc_enc_buffer(z_resv);\n")
+
+    # Marshal the data
+    if what == "request":
+        o.rxsrc("\trxrpc::enc(z_buf, ", func.opcode.name, ");\n")
+
+    for p in params:
+        o.where(func.name + ":" + p.name)
+        if p.is_single_int32():
+            o.rxsrc("\trxrpc::enc(z_buf, ", p.name, ");\n")
+        elif p.is_single_int64():
+            o.rxsrc("\trxrpc::enc_u64(z_buf, ", p.name, ");\n")
+        elif p.is_single_compound():
+            o.rxsrc("\txdr::encode_", p.ty.name, "(z_buf, ", p.name, ");\n")
+        elif p.is_single_string():
+            o.rxsrc("\trxrpc::enc_string(z_buf, ", p.name, ", ", p.what_max_obj(), ");\n")
+        elif p.is_single_opaque():
+            o.rxsrc("\trxrpc::enc_opaque(z_buf, ", p.name, ", ", p.what_max_obj(), ");\n")
+        elif p.is_bulk():
+            o.rxsrc("\trxrpc::enc(z_buf, ", p.name, ".size());\n")
+            o.rxsrc("\tfor (size_t i = 0; i < ", p.name, ".size(); i++)\n")
+            if p.is_bulk_int32():
+                if not p.ty.name.startswith("u"):
+                    o.rxsrc("\t\trxrpc::enc(z_buf, (u", p.ty.name, ")", p.name, "[i]);\n")
+                else:
+                    o.rxsrc("\t\trxrpc::enc(z_buf, ", p.name, "[i]);\n")
+            elif p.is_bulk_int64():
+                o.rxsrc("\t\trxrpc::enc_u64(z_buf, (uint32_t)", p.name, "[i]);\n")
+            elif p.is_bulk_struct():
+                o.rxsrc("\t\txdr::encode_", p.ty.name, "(z_buf, ", p.name, "[i]);\n")
+            else:
+                raise RuntimeError("No decoding for array type '" + str(p.ty) + "'")
+        elif p.is_single_uuid():
+            o.rxsrc("\trxrpc::enc_uuid(z_buf, ", p.name, ");\n")
+        else:
+            raise RuntimeError("Unsupported param encoding '" + str(p.ty) + "'")
+
+    o.rxsrc("\trxrpc::seal_buffer(z_buf);\n")
+
+    # Send the data
+    o.rxsrc("\treturn z_buf;\n")
+    o.rxsrc("}\n")
+
+###############################################################################
+#
+# Emit a function to encode and dispatch a request or a response
+#
+###############################################################################
+def emit_func_send(o, func, what):
+    # Function definition and arguments
+    if what == "request":
+        protos = func.send_request_protos
+        params = func.request
+    else:
+        protos = func.send_response_protos
+        params = func.response
+
+    o.rxsrc("\n")
+    if what == "request":
+        o.rxsrc("rxrpc::ref<rxrpc::Call> begin_", func.name, "(\n")
+        o.rxsrc("\trxrpc::Call_params *z_params,\n")
+        o.rxsrc("\trxrpc::Enc_buffer *z_buf)\n")
+    else:
+        o.rxsrc("void reply_to_", func.name, "(\n")
+        o.rxsrc("\trxrpc::Call *z_call,\n")
+        o.rxsrc("\trxrpc::Enc_buffer *z_buf)\n")
+
+    o.rxsrc("{\n")
+
+    # Local variables
+    if what == "request":
+        o.rxsrc("\trxrpc::ref<rxrpc::Call> z_call =\n")
+        o.rxsrc("\t\trxrpc::make_call(z_params, \"",
+                func.package.name, "::", func.name, "\", z_buf);\n")
+        o.rxsrc("\tz_call->throw_abort_func = afs_throw_abort;\n")
+    if func.split:
+        o.rxsrc("\tz_call->split = true;\n")
+
+    # Send the data
+    if what == "request":
+        o.rxsrc("\treturn z_call;\n")
+    o.rxsrc("}\n")
+
+###############################################################################
+#
+# Emit a weak stub function to implement a server call.
+#
+###############################################################################
+def emit_func_server_stub(o, func):
+    # Function definition and arguments
+    params = func.request
+
+    o.rxsrc("\n")
+    o.rxsrc("__attribute__((weak))\n")
+    o.rxsrc("int S_", func.name, "(\n")
+    o.rxsrc("\trxrpc::Call *z_call")
+    for p in params:
+        o.rxsrc(",\n\t")
+        if p.is_bulk():
+            o.rxsrc("std::vector<", p.ty.c_name, "> &_", p.name)
+        elif p.is_single_int():
+            o.rxsrc(p.ty.c_name, " ", p.name)
+        elif p.is_single_string():
+            o.rxsrc(p.ty.c_name, " &", p.name)
+        elif p.is_single_opaque():
+            o.rxsrc(p.ty.c_name, " &", p.name)
+        elif p.is_single_compound():
+            o.rxsrc(p.ty.c_name, " &", p.name)
+        elif str(p.ty) == "opr_uuid":
+            o.rxsrc(p.ty.c_name, " &", p.name)
+        else:
+            print(p.name)
+            raise RuntimeError("No prototype for type '" + str(p.ty) + "'")
+
+    o.rxsrc(")\n")
+    o.rxsrc("{\n")
+    o.rxsrc("\tthrow rxrpc::AbortRXGEN_OPCODE(z_call->op_name);\n")
+    o.rxsrc("}\n")
+
+###############################################################################
+#
+# Emit a function to decode a request and invoke the appropriate server
+# function.  The opcode is expected to have been removed from the incoming call
+# on the server side.
+#
+###############################################################################
+def emit_func_receive_request(o, func):
+    # Function definition and arguments
+    params = func.request
+    nomem = False
+    need_i = False
+
+    o.rxsrc("\n")
+    o.rxsrc("void decode_S_", func.name, "(rxrpc::Call *z_call)\n")
+    o.rxsrc("{\n")
+
+    # Local variables
+    for p in params:
+        if p.is_bulk():
+            o.rxsrc("\tstd::vector<", p.ty.c_name, "> a_", p.name, ";\n")
+            need_i = True
+        elif p.is_single_string():
+            o.rxsrc("\t", p.ty.c_name, " a_", p.name, ";\n")
+        elif p.is_single_opaque():
+            o.rxsrc("\t", p.ty.c_name, " a_", p.name, ";\n")
+        elif p.is_single_int() or p.is_single_struct():
+            o.rxsrc("\t", p.ty.c_name, " a_", p.name, ";\n")
+        elif p.is_single_union():
+            o.rxsrc("\t", p.ty.c_name, " a_", p.name, " = {};\n")
+        elif p.is_single_uuid():
+            o.rxsrc("\t", p.ty.c_name, " a_", p.name, ";\n")
+        else:
+            print(p.name)
+            raise RuntimeError("No local vars for type '" + str(p.ty) + "'")
+    if need_i:
+        o.rxsrc("\tsize_t count;\n")
+
+    o.rxsrc("\n");
+    o.rxsrc("\ttry {\n");
+
+    # Decode the data.
+    if params:
+        o.rxsrc("\t\trxrpc::Rx_queue &z_rxq = *z_call;\n")
+    for p in params:
+        if p.is_single_string():
+            o.rxsrc("\t\trxrpc::dec_string(z_rxq, a_", p.name)
+            if p.max_obj != None:
+                o.rxsrc(", ", p.max_obj)
+            o.rxsrc(");\n");
+        elif p.is_single_opaque():
+            o.rxsrc("\t\trxrpc::dec_opaque(z_rxq, a_", p.name)
+            if p.max_obj != None:
+                o.rxsrc(", ", p.max_obj)
+            o.rxsrc(");\n")
+        elif p.is_bulk():
+            o.rxsrc("\t\t/* ", str(p.ty), " a_", p.name, " */\n");
+            o.rxsrc("\t\tcount = rxrpc::dec(z_rxq")
+            if p.max_count != None:
+                o.rxsrc(", ", p.max_count)
+            o.rxsrc(");\n")
+
+            o.rxsrc("\t\ta_", p.name, ".resize(count);\n")
+            if p.is_bulk_int():
+                o.rxsrc("\t\tfor (size_t i = 0; i < count; i++)\n")
+                o.rxsrc("\t\t\ta_", p.name, "[i] = rxrpc::dec(z_rxq);\n")
+                nomem = True
+            elif p.is_bulk_struct():
+                o.rxsrc("\t\tfor (size_t i = 0; i < count; i++)\n")
+                o.rxsrc("\t\t\txdr::decode_", p.ty.name, "(z_rxq, a_", p.name, "[i]);\n")
+                nomem = True
+            else:
+                print(func.name, p.name, str(p.ty))
+                print("is_bulk: ", p.is_bulk())
+                print("is_bulk_int", p.is_bulk_int())
+                print("is_int:", p.is_int())
+                raise RuntimeError("No decode for bulk type '" + str(p.ty) + "'")
+        elif p.is_single_int():
+            o.rxsrc("\t\ta_", p.name, " = rxrpc::dec(z_rxq);\n")
+        elif p.is_single_compound():
+            o.rxsrc("\t\txdr::decode_", p.ty.name, "(z_rxq, a_", p.name, ");\n")
+        elif p.is_single_uuid():
+            o.rxsrc("\t\tz_rxq.read(&a_", p.name, ".uuid, sizeof(a_", p.name, ".uuid));\n")
+        else:
+            print(func.name, p.name, str(p.ty))
+            print("is_bulk: ", p.is_bulk())
+            print("is_bulk_int", p.is_bulk_int())
+            print("is_int:", p.is_int())
+            raise RuntimeError("No decode for type '" + str(p.ty) + "'")
+
+    # Check for decode errors
+    o.rxsrc("\n")
+    o.rxsrc("\t\tz_call->request_received();\n")
+
+    # Invoke the server stub
+    o.rxsrc("\n")
+    o.rxsrc("\t\tS_", func.name, "(\n")
+    o.rxsrc("\t\t\tz_call")
+    for p in params:
+        o.rxsrc(",\n\t\t\t")
+        o.rxsrc("a_", p.name)
+    o.rxsrc(");\n")
+
+    o.rxsrc("\t} catch (rxrpc::rx_abort &a) {\n")
+    o.rxsrc("\t\tz_call->end(a.get_abort_code());\n")
+    o.rxsrc("\t\tthrow;\n")
+    o.rxsrc("\t} catch (...) {\n")
+    o.rxsrc("\t\tz_call->end(RX_USER_ABORT);\n")
+    o.rxsrc("\t\tthrow;\n")
+    o.rxsrc("\t}\n")
+    o.rxsrc("}\n")
+
+###############################################################################
+#
+# Emit a function to decode a reply and return it to the caller on the client
+# side.
+#
+###############################################################################
+def emit_func_receive_reply(o, func):
+    # Function definition and arguments
+    params = func.response
+    need_i = False
+
+    o.rxsrc("void decode_", func.name, "(\n")
+    o.rxsrc("\trxrpc::Call *z_call")
+    for p in params:
+        o.rxsrc(",\n\t")
+        if p.is_single_string():
+            o.rxsrc(p.ty.c_name, " &_", p.name)
+        elif p.is_single_opaque():
+            o.rxsrc(p.ty.c_name, " &_", p.name)
+        elif p.is_bulk():
+            o.rxsrc("std::vector<", p.ty.c_name, "> &_", p.name)
+            need_i = True
+        elif p.is_single_int():
+            o.rxsrc(p.ty.c_name, " &_", p.name)
+        elif p.is_single_struct():
+            o.rxsrc(p.ty.c_name, " &_", p.name)
+        elif p.is_single_uuid():
+            o.rxsrc(p.ty.c_name, " &_", p.name)
+        else:
+            raise RuntimeError("No prototype for type '" + str(p.ty) + "'")
+    o.rxsrc(")\n")
+    o.rxsrc("{\n")
+
+    # Local variables
+    if need_i:
+        o.rxsrc("\tsize_t count;\n")
+
+    # Decode the data.
+    o.rxsrc("\n")
+    if params:
+        o.rxsrc("\t\trxrpc::Rx_queue &z_rxq = *z_call;\n")
+    for p in params:
+        if p.is_single_string():
+            o.rxsrc("\trxrpc::dec_string(z_rxq, _", p.name)
+            if p.max_obj != None:
+                o.rxsrc(", ", p.max_obj)
+            o.rxsrc(");\n");
+        elif p.is_single_opaque():
+            o.rxsrc("\trxrpc::dec_opaque(z_rxq, _", p.name)
+            if p.max_obj != None:
+                o.rxsrc(", ", p.max_obj)
+            o.rxsrc(");\n");
+        elif p.is_bulk():
+            o.rxsrc("\t/* ", str(p.ty), " ", p.name, " */\n");
+            o.rxsrc("\tcount = rxrpc::dec(z_rxq")
+            if p.max_count:
+                o.rxsrc(", ", p.max_count);
+            o.rxsrc(");\n")
+            o.rxsrc("\t_", p.name, ".resize(count);\n")
+            o.rxsrc("\tfor (size_t i = 0; i < count; i++)\n")
+
+            if p.is_bulk_int():
+                o.rxsrc("\t\t_", p.name, "[i] = rxrpc::dec(z_rxq);\n")
+            elif p.is_bulk_struct():
+                o.rxsrc("\t\txdr::decode_", p.ty.name, "(z_rxq, _", p.name, "[i]);\n")
+            else:
+                print(func.name, p.name, str(p.ty))
+                print("is_bulk: ", p.is_bulk())
+                print("is_bulk_int", p.is_bulk_int())
+                print("is_int:", p.is_int())
+                raise RuntimeError("No decoding for bulk type '" + str(p.ty) + "'")
+        elif p.is_single_int():
+            o.rxsrc("\t_", p.name, " = rxrpc::dec(z_rxq);\n")
+        elif p.is_single_struct():
+            o.rxsrc("\txdr::decode_", p.ty.name, "(z_rxq, _", p.name, ");\n")
+        elif p.is_single_uuid():
+            o.rxsrc("\tz_call->read(_", p.name, ".uuid, sizeof(_", p.name, ".uuid));\n")
+        else:
+            print(func.name, p.name, str(p.ty))
+            print("is_bulk: ", p.is_bulk())
+            print("is_bulk_int", p.is_bulk_int())
+            print("is_int:", p.is_int())
+            raise RuntimeError("No decoding for type '" + str(p.ty) + "'")
+    o.rxsrc("}\n")
+
+###############################################################################
+#
+# Emit a function to switch between service calls.
+#
+###############################################################################
+def emit_package_switch(o, package):
+    o.rxsrc("\n")
+    o.rxsrc("void ", package.name, "_service(rxrpc::Call *z_call, unsigned int opcode)\n")
+    o.rxsrc("{\n")
+    o.rxsrc("\tswitch (opcode) {\n")
+    for f in package.procs:
+        o.rxsrc("\tcase ", f.opcode, ":\treturn decode_S_", f.name, "(z_call);\n")
+    o.rxsrc("\tdefault:\tthrow rxrpc::AbortRXGEN_OPCODE();\n")
+    o.rxsrc("\t}\n")
+    o.rxsrc("}\n")
+
+###############################################################################
+#
+# Emit a function to do a complete synchronous client call
+#
+###############################################################################
+def emit_func_sync_client_call(o, func):
+    protos = func.send_request_protos
+    params = func.request
+    bad_ret = "NULL"
+
+    o.rxsrc("\n")
+    o.rxsrc("void ", func.name, "(\n")
+    o.rxsrc("\trxrpc::Call_params *z_params")
+
+    # Request parameters
+    for proto in protos:
+        o.rxsrc(",\n\t", proto)
+
+    # Reply parameters
+    for p in func.response:
+        o.rxsrc(",\n\t")
+        if p.is_bulk():
+            o.rxsrc("std::vector<", p.ty.c_name, "> &_", p.name)
+        else:
+            o.rxsrc(p.ty.c_name, " &_", p.name)
+
+    o.rxsrc(")\n")
+    o.rxsrc("{\n")
+
+    # Encode the parameters
+    o.rxsrc("\n")
+    o.rxsrc("\trxrpc::ref<rxrpc::Enc_buffer> z_buf =\n")
+    o.rxsrc("\t\t encode_", func.name, "(")
+    need_sep = False
+    for p in func.request:
+        if need_sep:
+            o.rxsrc(",")
+        else:
+            need_sep = True
+        o.rxsrc("\n\t\t")
+        if p.is_single_string():
+            o.rxsrc("\t", p.name)
+        elif p.is_single_opaque():
+            o.rxsrc("\t", p.name)
+        elif p.is_bulk():
+            o.rxsrc("\t", p.name)
+        else:
+            o.rxsrc("\t", p.name)
+
+    o.rxsrc(");\n")
+
+    # Begin the call
+    o.rxsrc("\n")
+    o.rxsrc("\trxrpc::ref<rxrpc::Call> z_call =\n")
+    o.rxsrc("\t\tbegin_", func.name, "(z_params, z_buf);\n")
+
+    o.rxsrc("\tz_call->send_data(z_buf);\n")
+
+    # Wait for the reply to come in
+    o.rxsrc("\n")
+    o.rxsrc("\twhile (z_call->state != rxrpc::Call::call_completed)\n")
+    o.rxsrc("\t\tz_call->endpoint->receive();\n")
+
+    # Decode the reply
+    o.rxsrc("\n")
+    o.rxsrc("\tdecode_", func.name, "(\n")
+    o.rxsrc("\t\tz_call")
+    for p in func.response:
+        o.rxsrc(",\n\t\t_", p.name)
+
+    o.rxsrc(");\n")
+    o.rxsrc("\tz_params->service_id_used = z_call->service_id;\n")
+    o.rxsrc("\tz_call->end(0);\n")
+    o.rxsrc("}\n")
diff --git a/rxgen/rxgen.py b/rxgen/rxgen.py
new file mode 100755 (executable)
index 0000000..8b8b3e5
--- /dev/null
@@ -0,0 +1,905 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+#
+# Tool for processing an RxRPC-based RPC API definition in a C header file to
+# produce (un)marshalling code and RPC functions to implement that API.
+#
+# It also produces a python module containing wrappers for the types, RPC
+# functions and constants in the API definition.
+
+__copyright__ = """
+Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
+Written by David Howells (dhowells@redhat.com)
+
+Yacc & Lex bits based on pynfs rpcgen.py code:
+
+    Written by Fred Isaman <iisaman@citi.umich.edu>
+    Copyright (C) 2004 University of Michigan, Center for
+                       Information Technology Integration
+    Based on version written by Peter Astrand <peter@cendio.se>
+    Copyright (C) 2001 Cendio Systems AB (http://www.cendio.se)
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public Licence version 2 as
+published by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public Licence for more details.
+
+You should have received a copy of the GNU General Public Licence
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+"""
+
+import sys
+import keyword
+import time
+import os
+from rxgen_bits import *
+from emit_c_struct import *
+from emit_c_sync_funcs import *
+
+xdr = None              # Current context
+
+##########################################################################
+#                                                                        #
+#                            Lexical analysis                            #
+#                                                                        #
+##########################################################################
+import ply.lex as lex
+
+basic_types = (
+    ( "char",      "char",          xdr_basic.int32  ),
+    ( "int8_t",    "int8_t",        xdr_basic.int32  ),
+    ( "int16_t",   "int16_t",       xdr_basic.int32  ),
+    ( "int32_t",   "int32_t",       xdr_basic.int32  ),
+    ( "int64_t",   "int64_t",       xdr_basic.int64  ),
+    ( "uint8_t",   "uint8_t",       xdr_basic.int32  ),
+    ( "uint16_t",  "uint16_t",      xdr_basic.int32  ),
+    ( "uint32_t",  "uint32_t",      xdr_basic.int32  ),
+    ( "uint64_t",  "uint64_t",      xdr_basic.int64  ),
+    ( "string",    "std::string",   xdr_basic.string ),
+    ( "opaque",    "rxrpc::Opaque", xdr_basic.opaque ),
+    ( "opr_uuid",  "rxrpc::Uuid",   xdr_basic.opr_uuid ),
+    )
+
+def load_basic_types(xdr):
+    global basic_types
+    for i in basic_types:
+        name = i[0]
+        t = xdr_type(xdr, name=name, c_name=i[1], basic=i[2])
+        xdr.types[name] = t
+
+keywords = (
+    "IN",
+    "INOUT",
+    "OUT",
+    "altonly",
+    "case",
+    "const",
+    "enum",
+    "multi",
+    "package",
+    "split",
+    "struct",
+    "switch",
+    "typedef",
+    "union",
+) + tuple([i[0] for i in basic_types])
+
+# Required by lex.  Each token also allows a function t_<token>.
+tokens = tuple([t.upper() for t in keywords]) + (
+    "ID", "CONST10", "CONST8", "CONST16",
+    # ( ) [ ] { }
+    "LPAREN", "RPAREN", "LSQUARE", "RSQUARE", "LBRACE", "RBRACE",
+    # ; : < > * = , :
+    "SEMI", "LT", "GT", "STAR", "EQUALS", "COMMA", "COLON",
+    "BEGIN_ERROR_CODES", "END_ERROR_CODES", "NEWFILE"
+)
+
+# t_<name> functions are used by lex.  They are called with t.value==<match
+# of rule in comment>, and t.type==<name>.  They expect a return value
+# with attribute type=<token>
+
+# Tell lexer to ignore Whitespace.
+t_ignore = " \t"
+
+def t_NEWFILE(t):
+    r'__NEWFILE__ [^\n]*'
+    t.lexer.source = t.value[12:]
+    t.lexer.lineno = 0
+    return t
+
+def t_ID(t):
+    r'[A-Za-z][A-Za-z0-9_]*'
+    if t.value in keywords:
+        t.type = t.value.upper()
+    return t
+
+def t_CONST16(t):
+    r'0x[0-9a-fA-F]+'
+    return t
+
+def t_CONST8(t):
+    r'0[0-7]+'
+    return t
+
+def t_CONST10(t):
+    r'-?(([1-9]\d*)|0)'
+    return t
+
+# Tokens
+t_LPAREN           = r'\('
+t_RPAREN           = r'\)'
+t_LSQUARE          = r'\['
+t_RSQUARE          = r'\]'
+t_LBRACE           = r'\{'
+t_RBRACE           = r'\}'
+t_SEMI             = r';'
+t_LT               = r'<'
+t_GT               = r'>'
+t_STAR             = r'\*'
+t_EQUALS           = r'='
+t_COMMA            = r','
+t_COLON            = r':'
+t_BEGIN_ERROR_CODES = r'__BEGIN_ERROR_CODES__'
+t_END_ERROR_CODES  = r'__END_ERROR_CODES__'
+
+def t_newline(t):
+    r'\n+'
+    t.lexer.lineno += t.value.count("\n")
+
+def t_error(t):
+    print("Illegal character {:s} at {:d} type {:s}".format(repr(t.value[0]),
+                                                            t.lineno, t.type))
+    t.lexer.skip(1)
+
+# Build the lexer
+lex.lex(debug=0)
+
+##########################################################################
+#                                                                        #
+#                          Yacc Parsing Info                             #
+#                                                                        #
+##########################################################################
+
+def p_specification(t):
+    '''specification : NEWFILE definition_list'''
+
+def p_definition_list(t):
+    '''definition_list : definition definition_list
+                       | empty'''
+
+def p_definition(t):
+    '''definition : package_def
+                  | constant_def
+                  | enum_def
+                  | type_def
+                  | error_code_list_def
+                  | proc_spec
+                  | ALTONLY proc_spec'''
+
+def p_package_def(t):
+    '''package_def : PACKAGE ID'''
+    prefix = t[2]
+    name = t[2]
+    if name.endswith("_"):
+        name = name[:-1]
+    global xdr
+    xdr.lineno = t.lineno(1)
+    xdr.add_package(name, prefix)
+
+###############################################################################
+#
+# Constants and values
+#
+def p_constant_def(t):
+    '''constant_def : CONST ID EQUALS constant SEMI'''
+    name = t[2]
+    value = t[4]
+    global xdr
+    xdr.lineno = t.lineno(2)
+    t[0] = xdr.add_constant(name, value)
+
+def p_constant(t):
+    '''constant : CONST10
+                | CONST8
+                | CONST16'''
+    value = t[1]
+    if len(value) > 9:
+        value = value + 'L'
+    global xdr
+    xdr.lineno = t.lineno(1)
+    t[0] = xdr.add_number(value)
+
+def p_value_1(t):
+    '''value : constant'''
+    t[0] = t[1]
+
+def p_value_2(t):
+    '''value : ID'''
+    name = t[1]
+    global xdr
+    xdr.lineno = t.lineno(1)
+    t[0] = xdr.get_constant(name)
+
+def p_optional_value(t):
+    '''optional_value : value
+                      | empty'''
+    # return value or None.
+    t[0] = t[1]
+
+def p_constant_list_1(t):
+    '''constant_list  : constant_def constant_list'''
+    t[0] = [t[1]] + t[2]
+
+def p_constant_list_2(t):
+    '''constant_list  : constant_def'''
+    t[0] = [t[1]]
+
+def p_error_code_list_def(t):
+    '''error_code_list_def : BEGIN_ERROR_CODES constant_list END_ERROR_CODES'''
+    global xdr
+    xdr.lineno = t.lineno(1)
+    xdr.add_error_codes(t[2])
+
+def p_enum_def(t):
+    '''enum_def       : ENUM ID LBRACE enum_list RBRACE SEMI'''
+    '''enum_def       : ENUM ID LBRACE enum_list COMMA RBRACE SEMI'''
+    name = t[2]
+    global xdr
+    xdr.lineno = t.lineno(2)
+    enum = xdr_type(xdr, base=xdr.get_type("int32_t"))
+    t[0] = xdr.add_type(name, enum)
+
+def p_enum_list_1(t):
+    '''enum_list      : enum_term'''
+    t[0] = [t[1]]
+
+def p_enum_list_2(t):
+    '''enum_list      : enum_term COMMA enum_list'''
+    t[0] = [t[1]] + t[3]
+
+def p_enum_term(t):
+    '''enum_term      : ID EQUALS constant'''
+    name = t[1]
+    value = t[3]
+    global xdr
+    xdr.lineno = t.lineno(1)
+    t[0] = xdr.add_constant(name, value)
+
+###############################################################################
+#
+# Type definition
+#
+def p_type_def_1(t):
+    '''type_def : TYPEDEF declaration SEMI'''
+    name = t[2].name
+    typespec = t[2].typespec
+    global xdr
+    xdr.lineno = t.lineno(1)
+    alias = xdr_type_alias(name, typespec, xdr)
+    xdr.add_type_alias(name, alias)
+    #xdr.debug("Typedef", name)
+
+def p_type_def_2(t):
+    '''type_def : STRUCT ID struct_body SEMI'''
+    name = t[2]
+    body = t[3]
+    global xdr
+    xdr.lineno = t.lineno(2)
+    if name in xdr.structs:
+        xdr.error("Structure {:s} already exists".format(name))
+    else:
+        s = xdr_type(xdr, name=name, c_name="struct " + name,
+                     basic=xdr_basic.struct, members=body)
+        xdr.structs[name] = s
+        xdr.all_structs.append(s)
+        xdr.add_type(name, s)
+    xdr.debug("New struct", name)
+
+def p_type_def_3(t):
+    '''type_def : UNION ID SWITCH LPAREN type_specifier ID RPAREN union_body SEMI'''
+    name = t[2]
+    body = t[8]
+    global xdr
+    xdr.lineno = t.lineno(2)
+    if name in xdr.structs:
+        xdr.error("Structure {:s} already exists".format(name))
+    else:
+        s = xdr_type(xdr, name=name, c_name="struct " + name,
+                     basic=xdr_basic.union, members=body)
+        s.selector_type = t[5]
+        s.selector_name = t[6]
+        xdr.structs[name] = s
+        xdr.all_structs.append(s)
+        xdr.add_type(name, s)
+    xdr.debug("New union", name)
+
+###############################################################################
+#
+# Type specification
+#
+def p_declaration_1(t):
+    '''declaration : type_specifier ID'''
+    typespec = t[1]
+    name = t[2]
+    global xdr
+    xdr.lineno = t.lineno(2)
+    t[0] = xdr_member(name, typespec, xdr);
+
+def p_declaration_2(t):
+    '''declaration : type_specifier STAR ID'''
+    # We ignore the pointer type marker
+    typespec = t[1]
+    name = t[3]
+    global xdr
+    xdr.lineno = t.lineno(3)
+    ty = xdr_type(xdr, base=typespec)
+    t[0] = xdr_member(name, ty, xdr)
+
+def p_declaration_3(t):
+    '''declaration : type_specifier ID LSQUARE value RSQUARE'''
+    typespec = t[1]
+    name = t[2]
+    array_size = t[4]
+    if not isinstance(typespec, xdr_type):
+        raise RuntimeError("Type is not type" + str(typespec))
+    global xdr
+    xdr.lineno = t.lineno(2)
+    ty = xdr_type(xdr, base=typespec, array=xdr_array.fixed, dim=array_size)
+    t[0] = xdr_member(name, ty, xdr)
+
+def p_declaration_4(t):
+    '''declaration : type_specifier ID LT optional_value GT'''
+    typespec = t[1]
+    name = t[2]
+    max_count = t[4]
+    global xdr
+    xdr.lineno = t.lineno(2)
+    ty = xdr_type(xdr, base=typespec, array=xdr_array.bulk, max_count=max_count)
+    t[0] = xdr_member(name, ty, xdr)
+
+def p_declaration_5(t):
+    '''declaration : type_specifier STAR ID LT optional_value GT'''
+    typespec = t[1]
+    name = t[3]
+    max_count = t[5]
+    global xdr
+    xdr.lineno = t.lineno(3)
+    ty = xdr_type(xdr, base=typespec, array=xdr_array.bulk, max_count=max_count)
+    t[0] = xdr_member(name, ty, xdr)
+
+def p_declaration_6(t):
+    '''declaration : type_specifier LT optional_value GT STAR ID'''
+    typespec = t[1]
+    max_count = t[3]
+    name = t[6]
+    global xdr
+    xdr.lineno = t.lineno(6)
+    ty = xdr_type(xdr, base=typespec, array=xdr_array.bulk, max_count=max_count)
+    t[0] = xdr_member(name, ty, xdr)
+
+def p_declaration_7(t):
+    '''declaration : type_blob ID'''
+    typespec = t[1]
+    name = t[2]
+    global xdr
+    xdr.lineno = t.lineno(2)
+    t[0] = xdr_member(name, typespec, xdr);
+
+def p_declaration_8(t):
+    '''declaration : type_blob STAR ID'''
+    # We ignore the pointer type marker
+    typespec = t[1]
+    name = t[3]
+    global xdr
+    xdr.lineno = t.lineno(3)
+    ty = xdr_type(xdr, base=typespec)
+    t[0] = xdr_member(name, ty, xdr)
+
+def p_declaration_9(t):
+    '''declaration : type_blob ID LSQUARE value RSQUARE'''
+    typespec = t[1]
+    name = t[2]
+    array_size = t[4]
+    if not isinstance(typespec, xdr_type):
+        raise RuntimeError("Type is not type" + str(typespec))
+    global xdr
+    xdr.lineno = t.lineno(2)
+    ty = xdr_type(xdr, base=typespec, array=xdr_array.fixed, dim=array_size)
+    t[0] = xdr_member(name, ty, xdr)
+
+def p_declaration_10(t):
+    '''declaration : type_blob ID LT optional_value GT'''
+    typespec = t[1]
+    name = t[2]
+    max_obj = t[4]
+    global xdr
+    xdr.lineno = t.lineno(2)
+    ty = xdr_type(xdr, base=typespec, max_obj=max_obj)
+    t[0] = xdr_member(name, ty, xdr)
+
+def p_declaration_11(t):
+    '''declaration : type_blob STAR ID LT optional_value GT'''
+    typespec = t[1]
+    name = t[3]
+    max_obj = t[5]
+    global xdr
+    xdr.lineno = t.lineno(3)
+    ty = xdr_type(xdr, base=typespec, max_obj=max_obj)
+    t[0] = xdr_member(name, ty, xdr)
+
+def p_declaration_12(t):
+    '''declaration : type_blob LT optional_value GT STAR ID'''
+    typespec = t[1]
+    max_count = t[3]
+    name = t[6]
+    global xdr
+    xdr.lineno = t.lineno(6)
+    ty = xdr_type(xdr, base=typespec, array=xdr_array.bulk, max_count=max_count)
+    t[0] = xdr_member(name, ty, xdr)
+
+def p_type_blob(t):
+    '''type_blob      : STRING
+                      | OPAQUE'''
+    name = t[1]
+    global xdr
+    xdr.lineno = t.lineno(1)
+    t[0] = xdr.types[name]
+    if not isinstance(t[0], xdr_type):
+        raise RuntimeError("Type is not type" + typespec);
+
+def p_type_specifier_1(t):
+    '''type_specifier : CHAR
+                      | INT8_T
+                      | INT16_T
+                      | INT32_T
+                      | INT64_T
+                      | UINT8_T
+                      | UINT16_T
+                      | UINT32_T
+                      | UINT64_T
+                      | OPR_UUID'''
+    name = t[1]
+    global xdr
+    xdr.lineno = t.lineno(1)
+    t[0] = xdr.types[name]
+    if not isinstance(t[0], xdr_type):
+        raise RuntimeError("Type is not type" + typespec);
+
+def p_type_specifier_2(t):
+    '''type_specifier : ID'''
+    name = t[1]
+    global xdr
+    xdr.lineno = t.lineno(1)
+    t[0] = xdr.get_type(t[1])
+
+def p_type_specifier_3(t):
+    '''type_specifier : struct_type_spec'''
+    t[0] = t[1]
+
+def p_type_specifier_4(t):
+    '''type_specifier : STRUCT ID'''
+    name = t[2]
+    global xdr
+    xdr.lineno = t.lineno(2)
+    t[0] = xdr.get_type(name)
+
+
+###############################################################################
+#
+# Structure specification
+#
+def p_struct_type_spec(t):
+    '''struct_type_spec : STRUCT struct_body'''
+    t[0] = xdr_type(xdr, name="<anon struct {:s}>".format(t.lineno), c_name="struct",
+                    compound=xdr_compound.struct, basic_type="struct", members=t[2])
+
+def p_struct_body(t):
+    '''struct_body : LBRACE member_list RBRACE'''
+    t[0] = t[2]
+
+def p_member_list_1(t):
+    '''member_list : declaration SEMI'''
+    t[0] = [t[1]]
+
+def p_member_list_2(t):
+    '''member_list : declaration SEMI member_list'''
+    t[0] = [t[1]] + t[3]
+
+def p_union_body(t):
+    '''union_body : LBRACE union_case_list RBRACE'''
+    t[0] = t[2]
+
+def p_union_case(t):
+    '''union_case : CASE ID COLON member_list'''
+    t[0] = xdr_union_case(xdr, tag=t[2], members=t[4])
+
+def p_union_case_list_1(t):
+    '''union_case_list : union_case'''
+    t[0] = [t[1]]
+
+def p_union_case_list_2(t):
+    '''union_case_list : union_case union_case_list'''
+    t[0] = [t[1]] + t[2]
+
+###############################################################################
+#
+# Procedure specification
+#
+def p_proc_spec_1(t):
+    '''proc_spec : ID LPAREN parameters RPAREN EQUALS value SEMI'''
+    name = t[1]
+    params = t[3]
+    op = t[6]
+    global xdr
+    proc = xdr_proc(name, xdr, params, op)
+    xdr.add_proc(proc)
+    t[0] = proc
+
+def p_proc_spec_2(t):
+    '''proc_spec : ID LPAREN parameters RPAREN SPLIT EQUALS value SEMI'''
+    name = t[1]
+    params = t[3]
+    op = t[7]
+    global xdr
+    proc = xdr_proc(name, xdr, params, op, split=True)
+    xdr.add_proc(proc)
+    t[0] = proc
+
+def p_proc_spec_3(t):
+    '''proc_spec : ID LPAREN parameters RPAREN MULTI EQUALS value SEMI'''
+    name = t[1]
+    params = t[3]
+    op = t[7]
+    global xdr
+    proc = xdr_proc(name, xdr, params, op, multi=True)
+    xdr.add_proc(proc)
+    t[0] = proc
+
+def p_parameters_0(t):
+    '''parameters : '''
+    t[0] = []
+
+def p_parameters_1(t):
+    '''parameters : parameter'''
+    t[0] = [t[1]]
+
+def p_parameters_2(t):
+    '''parameters : parameter COMMA parameters'''
+    t[0] = [t[1]] + t[3]
+
+def p_parameter_1(t):
+    '''parameter : IN declaration'''
+    t[2].direction = xdr_direction.IN
+    t[0] = t[2]
+
+def p_parameter_2(t):
+    '''parameter : INOUT declaration'''
+    t[2].direction = xdr_direction.INOUT
+    t[0] = t[2]
+
+def p_parameter_3(t):
+    '''parameter : OUT declaration'''
+    t[2].direction = xdr_direction.OUT
+    t[0] = t[2]
+
+###############################################################################
+#
+# Miscellany
+#
+def p_empty(t):
+    'empty :'
+
+def p_error(t):
+    global error_occurred
+    error_occurred = True
+    if t:
+        print(t)
+        print("Syntax error at '{:s}' (lineno {:d})".format(t.value, t.lineno))
+    else:
+        print("Syntax error: unexpectedly hit EOF")
+
+##########################################################################
+#                                                                        #
+#                          Global Variables                              #
+#                                                                        #
+##########################################################################
+
+error_occurred = False # Parsing of infile status
+
+
+
+##########################################################################
+#                                                                        #
+#                       C Preprocessor                                   #
+#                                                                        #
+##########################################################################
+def cpp(data, filename):
+    # Remove C comments
+    lines = data.splitlines()
+    inside_C_comment = False
+    inside_error_code_list = False
+
+    for i in range(0, len(lines)):
+        l = lines[i]
+        begin = None
+        if inside_C_comment:
+            begin = 0
+        cursor = 0
+
+        # Capture the error code list
+        #
+        if not inside_C_comment:
+            if l == "/* Error codes */" and not inside_error_code_list:
+                inside_error_code_list = True
+                lines[i] = "__BEGIN_ERROR_CODES__"
+                continue
+
+            if inside_error_code_list and l == "":
+                inside_error_code_list = False
+                lines[i] = "__END_ERROR_CODES__"
+                continue
+
+        while True:
+            if inside_C_comment:
+                p = l.find("*/", cursor)
+                if p == -1:
+                    l = l[:begin]
+                    lines[i] = l
+                    break
+                l = l[:begin] + l[p + 2:]
+                lines[i] = l
+                cursor = begin
+                begin = None
+                inside_C_comment = False
+            else:
+                p = l.find("/*");
+                if p == -1:
+                    break
+                inside_C_comment = True
+                begin = p
+                cursor += 2
+
+    if inside_error_code_list:
+        lines.append("__END_ERROR_CODES__")
+
+    # Remove C++ comments
+    for i in range(0, len(lines)):
+        l = lines[i]
+        p = l.find("//")
+        if p != -1:
+            lines[i] = l[:p]
+
+    # Remove directives
+    skipping = False
+    for i in range(0, len(lines)):
+        l = lines[i]
+        if skipping:
+            if l == "#endif":
+                skipping = False;
+            lines[i] = ""
+            continue
+        if l == "#if 0":
+            skipping = True;
+            lines[i] = ""
+            continue
+        if (l.startswith("#include") or
+            l.startswith("#define") or
+            l.startswith("%")):
+            lines[i] = ""
+            continue
+
+    lines.insert(0, "__NEWFILE__ {:s}".format(filename))
+    return "\n".join(lines)
+
+##########################################################################
+#                                                                        #
+#                          Main Loop                                     #
+#                                                                        #
+##########################################################################
+xdr = xdr_context()
+load_basic_types(xdr)
+#xdr.add_constant("RXRPC_SECURITY_PLAIN",   "0")
+#xdr.add_constant("RXRPC_SECURITY_AUTH",    "1")
+#xdr.add_constant("RXRPC_SECURITY_ENCRYPT", "2")
+
+# Parse the input data with yacc
+import ply.yacc as yacc
+yacc.yacc(debug=0)
+
+def parse(infile, debug=False):
+    global xdr
+    xdr.source = infile
+    xdr.lineno = 0
+
+    f = open(infile, encoding="utf-8")
+    data = f.read()
+    f.close()
+
+    data = cpp(data, infile)
+
+    print("Parsing", infile);
+    global yacc
+    yacc.parse(data, debug=debug)
+
+    if error_occurred:
+        print
+        print("Error occurred, did not write output files")
+        return False
+    return True
+
+def gen_abort_name(package, ac):
+    pname = package.name
+    acname = ac.name
+    #if acname.startswith(pname):
+    #    acname = acname[len(pname):]
+    #if acname.startswith("_"):
+    #    acname = acname[1:]
+    return acname
+
+#
+# Section: main
+#
+def run(files, out_file_prefix):
+    for f in files:
+        if not parse(f):
+            break
+
+    xdr.finished_parsing()
+
+    o = file_generator(xdr, out_file_prefix)
+    o.rxhdr("/* AUTOGENERATED */\n")
+    o.rxhdr("#ifndef _RXGEN_AFS_XG_H\n");
+    o.rxhdr("#define _RXGEN_AFS_XG_H\n");
+    o.rxhdr("\n")
+    #o.rxhdr("#define _XOPEN_SOURCE\n";
+    o.rxhdr("#include <stdint.h>\n")
+    o.rxhdr("#include <stdlib.h>\n")
+    o.rxhdr("#include \"rxrpc.H\"\n")
+    o.rxhdr("\n")
+    o.rxhdr("namespace kafs::afs {\n")
+
+    o.rxsrc("/* AUTOGENERATED */\n")
+    o.rxsrc("#include \"afs_xg.H\"\n")
+    o.rxsrc("\n")
+    o.rxsrc("namespace kafs::afs {\n")
+
+    # Declare constants
+    o.rxhdr("\n")
+    for name in xdr.all_constants:
+        c = xdr.constants[name]
+        o.rxhdr("constexpr int ", c.name, " = ", c.value, ";\n")
+
+    # Declare structure types
+    for s in xdr.all_structs:
+        emit_struct_decl(o, s)
+
+    o.rxhdr("\n")
+    o.rxhdr("namespace xdr {\n")
+    for s in xdr.all_structs:
+        emit_struct_encdec_decl(o, s);
+    o.rxhdr("\n")
+    o.rxhdr("} /* end namespace xdr */\n")
+
+    o.rxsrc("\n")
+    o.rxsrc("namespace xdr {\n")
+    for s in xdr.all_structs:
+        emit_struct_encdec(o, s);
+    o.rxsrc("\n")
+    o.rxsrc("} /* end namespace xdr */\n")
+
+    # Emit RPC call functions.  For this we need to classify parameters according
+    # to input and output usage and work out how big the RPC messages will be.
+    #
+    for package in xdr.packages.values():
+        o.rxhdr("\n")
+        o.rxhdr("namespace ", package.name, " {\n")
+        o.rxsrc("\n")
+        o.rxsrc("namespace ", package.name, " {\n")
+
+        for f in package.procs:
+            # Dump the banner comment block
+            o.rxsrc("\n")
+            o.rxsrc("/*\n")
+            o.rxsrc(" * RPC Call ", f.name, "\n")
+            o.rxsrc(" */\n")
+
+            # Find the Operation ID
+            if not f.opcode:
+                raise RuntimeError("Operation ID unspecified for " + f.name)
+
+            # Filter the parameters into request and response
+            f.request = list()
+            f.response = list()
+
+            for p in f.params:
+                o.where(f.name + ":" + p.name)
+                if p.is_single_basic():
+                    pass
+                elif p.is_compound():
+                    pass
+                elif p.is_single_string() or p.is_single_opaque():
+                    # Could validate max_obj attribute
+                    pass
+                elif p.is_bulk():
+                    pass
+                else:
+                    raise RuntimeError("Unsupported param type \"" + str(p.ty) + "\"")
+
+                if p.direction == xdr_direction.IN:
+                    f.request.append(p)
+                elif p.direction == xdr_direction.OUT:
+                    f.response.append(p)
+                elif p.direction == xdr_direction.INOUT:
+                    f.request.append(p)
+                    f.response.append(p)
+
+            emit_func_prototype(o, f)
+            emit_func_encode(o, f, "request")
+            emit_func_encode(o, f, "response")
+            emit_func_send(o, f, "request")
+            emit_func_server_stub(o, f)
+            emit_func_receive_request(o, f)
+            emit_func_receive_reply(o, f)
+            emit_func_sync_client_call(o, f)
+
+        # Emit RPC service switch functions.
+        if package.procs:
+                emit_package_switch(o, package)
+
+        o.rxhdr("\n")
+        o.rxhdr("} /* end namespace ", package.name, " */\n")
+        o.rxsrc("\n")
+        o.rxsrc("} /* end namespace ", package.name, " */\n")
+
+    # Emit an abort throwing function
+    o.rxhdr("extern void afs_throw_abort(int ac, const char *op=NULL);\n");
+    o.rxsrc("\n")
+    o.rxsrc("void afs_throw_abort(int ac, const char *op)\n");
+    o.rxsrc("{\n")
+    o.rxsrc("\tswitch (ac) {\n")
+    for package in xdr.packages.values():
+        if package.abort_codes:
+            pabort = "Abort" + package.name;
+            o.rxhdr("\n")
+            o.rxhdr("namespace ", package.name, " {\n")
+            o.rxhdr("\tclass ", pabort, " : public rxrpc::rx_abort { public:\n")
+            o.rxhdr("\t\t", pabort, "(int ac, const char *sym, const char *desc, const char *op)\n"),
+            o.rxhdr("\t\t\t: rx_abort(ac, sym, desc, op) {}\n")
+            o.rxhdr("\t};\n")
+            for ac in package.abort_codes:
+                acclass = "Abort" + gen_abort_name(package, ac)
+                o.rxhdr("\tclass ", acclass, " : public ", pabort, " { public:\n")
+                o.rxhdr("\t\t", acclass, "(const char *op = NULL)\n")
+                o.rxhdr("\t\t\t: ", pabort, "(", ac.name, ", \"", ac.name, "\", \"\", op) {}\n")
+                o.rxhdr("\t};\n")
+            o.rxhdr("}\n")
+
+        for ac in package.abort_codes:
+            acclass = "Abort" + gen_abort_name(package, ac)
+            o.rxsrc("\tcase ", ac.name, ":\tthrow ", package.name, "::", acclass, "(op);\n")
+
+    o.rxsrc("\tdefault: rxrpc::throw_abort(ac, op);\n")
+    o.rxsrc("\t}\n")
+    o.rxsrc("}\n")
+    o.rxsrc("\n")
+    o.rxsrc("} /* end namespace kafs::afs */\n")
+
+    o.rxhdr("\n")
+    o.rxhdr("} /* end namespace kafs::afs */\n")
+    o.rxhdr("\n")
+    o.rxhdr("#endif /* _RXGEN_AFS_XG_H */\n");
+
+#
+# Drive the program if executed as a standalone process
+#
+if __name__ == "__main__":
+    if len(sys.argv) < 2:
+        print("Usage: {:s} <filename>*".format(sys.argv[0]))
+        sys.exit(1)
+
+    run(sys.argv[1:], "")
diff --git a/rxgen/rxgen_bits.py b/rxgen/rxgen_bits.py
new file mode 100644 (file)
index 0000000..cf11cb4
--- /dev/null
@@ -0,0 +1,603 @@
+# Bits for rxgen implementation
+# -*- coding: utf-8 -*-
+
+__copyright__ = """
+Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
+Written by David Howells (dhowells@redhat.com)
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public Licence version 2 as
+published by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public Licence for more details.
+
+You should have received a copy of the GNU General Public Licence
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+"""
+
+import sys
+from enum import Enum
+
+class xdr_basic(Enum):
+    int32 = 1
+    int64 = 2
+    string = 3
+    opaque = 4
+    struct = 5
+    union = 6
+    opr_uuid = 7
+
+class xdr_array(Enum):
+    # single object is represented by None
+    fixed = 1
+    bulk = 2
+
+class xdr_direction(Enum):
+    IN = 1
+    OUT = 2
+    INOUT = 3
+
+class xdr_context:
+    def __init__(self):
+        self.source = None              # Current source file
+        self.lineno = None              # Current line number
+        self.types = dict()             # List of types
+        self.typedefs = dict()          # List of type aliases
+        self.structs = dict()           # Structure definitions
+        self.struct_sizes = dict()      # Structure sizes
+        self.all_structs = list()       # Structures in order of declaration
+        self.constants = dict()         # Constants
+        self.packages = dict()          # Packages
+        self.pkg = None                 # Current package
+        self.abort_syms = dict()        # Abort symbol to code map
+        self.abort_ids = dict()         # Abort code to symbol map
+        self.abort_count = 0            # Number of abort codes
+        self.error_codes = False        # True if parsing error code list
+        self.debug_level = False
+        self.zero = xdr_constant("0", "0", self)
+        self.typespecs = dict()
+
+    def debug(self, *s):
+        if (self.debug_level):
+            print(*s)
+
+    def error(self, *s):
+        print("{:s}:{:d}: Error".format(self.source, self.lineno), *s)
+
+    def add_package(self, name, prefix):
+        if name in self.packages:
+            self.error("Package {:s} already exists".format(name))
+        else:
+            pkg = xdr_package(name, prefix, self)
+            self.packages[name] = pkg
+            self.pkg = pkg
+            self.debug("New package", name)
+
+    def add_error_codes(self, codes):
+        self.abort_count += len(codes)
+        self.pkg.abort_codes += codes
+        for code in codes:
+            if not isinstance(code, xdr_constant):
+                raise TypeError("Expecting xdr_constant, got " + str(type(code)))
+            self.abort_syms[code.name] = code
+            value = code.value
+            suffix = ""
+            if value.endswith("L"):
+                value = value[0:-1]
+                suffix = "L"
+            if int(value, 0) < 0:
+                value = str(int(value, 0) + 0x100000000)
+            code.u32 = int(value, 0)
+            self.abort_ids[code.u32] = code
+
+    def add_constant(self, name, value):
+        if name in self.constants:
+            self.error("Constant {:s} already exists".format(name))
+        elif isinstance(value, xdr_constant):
+            self.constants[name] = xdr_constant(name, value.value, self)
+        else:
+            self.constants[name] = xdr_constant(name, value, self)
+            self.debug("New constant", name)
+        return self.constants[name]
+
+    def add_number(self, value):
+        if value == 0:
+            return self.zero
+        return xdr_constant(value, value, self)
+
+    def get_constant(self, name):
+        if name not in self.constants:
+            self.error("Constant {:s} undefined".format(name))
+            return self.zero
+        return self.constants[name]
+
+    def add_type(self, name, typespec):
+        if name in self.types:
+            self.error("Type {:s} already exists".format(name))
+        else:
+            self.types[name] = typespec
+            self.debug("New type", name)
+        return self.types[name]
+
+    def add_type_alias(self, name, alias):
+        if name in self.typedefs:
+            self.error("Type alias {:s} already exists".format(name))
+            return self.typedefs[name]
+        if name in self.structs:
+            self.error("struct {:s} already exists, cannot shadow with alias".format(name))
+            return alias
+
+        self.typedefs[name] = alias
+        self.debug("New typedef", name)
+
+    def get_type(self, name):
+        if name in self.types:
+            typespec = self.types[name]
+        elif name in self.typedefs:
+            typespec = self.typedefs[name].typespec
+        else:
+            raise RuntimeError("Undefined type requested '" + name + "'");
+        if not isinstance(typespec, xdr_type):
+            raise TypeError("Retrieved type object is not xdr_type " + name + str(typespec));
+        typespec.referenced = True
+        return typespec
+
+    def add_proc(self, proc):
+        if not isinstance(proc, xdr_proc):
+            raise KeyError("proc is not an xdr_proc ", name, proc);
+        name = proc.name
+        if name in self.pkg.proc_names:
+            self.error("Proc {:s} already exists".format(name))
+            return self.pkg.proc_names[name]
+        else:
+            self.pkg.proc_names[name] = proc
+            self.pkg.procs.append(proc)
+            self.debug("New proc", name)
+            return proc
+
+    def finished_parsing(self):
+        self.all_constants = list(self.constants.keys())
+        self.all_constants.sort()
+        self.all_types = list(self.types.keys())
+        self.all_types.sort()
+
+
+###############################################################################
+#
+# Token base class
+#
+###############################################################################
+class token_base(object):
+    def __init__(self):
+        self.source = None
+        self.lineno = None
+        self.name = None
+        self.type = None
+
+    def __str__(self):
+        return "{:s} {:s} at {:s}:{:d}".format(
+            self.type, self.name, self.source, self.lineno)
+
+###############################################################################
+#
+# XDR package
+#
+###############################################################################
+class xdr_package(token_base):
+    """The result of 'package ID'"""
+    def __init__(self, name, prefix, xdr):
+        self.type = 'package'
+        self.name = name
+        self.source = xdr.source
+        self.lineno = xdr.lineno
+        self.prefix = prefix
+        self.abort_codes = list()
+        self.procs = list()             # RPC procedures in declaration order
+        self.proc_names = dict()        # RPC procedure name uniquifier
+
+###############################################################################
+#
+# XDR constant
+#
+###############################################################################
+class xdr_constant(token_base):
+    """The result of 'CONST ID EQUALS constant SEMI'"""
+    def __init__(self, name, value, xdr):
+        self.type = 'const'
+        self.name = name
+        self.value = value
+        self.source = xdr.source
+        self.lineno = xdr.lineno
+        if not isinstance(value, str):
+            raise RuntimeError("Value should be a string");
+
+    def get_value(self):
+        if isinstance(self.value, xdr_constant):
+            return self.value.get_value()
+        return int(self.value)
+
+    def __str__(self):
+        return self.value
+
+    def __repr__(self):
+        return "constant {:s}={:s} at {:s}:{:d}".format(
+            self.name, self.value, self.source, self.lineno)
+
+###############################################################################
+#
+# XDR type definition
+#
+# Each type is specified by a hash of the following elements:
+#
+#       name            Name of non-anonymous type (char, {u,}int{8,16,32,64}_t, struct name)
+#       basic           Basic XDR type (int32, int64, string, opaque, struct)
+#       array           Array class (single, array, bulk)
+#       dim             Number of elements in fixed-size array (or None)
+#       max_obj         Max size of string/opaque
+#       max_count       Max elements in bulk array
+#       members         Members of struct
+#       source/lineno   Where defined in which file
+#
+# Members/parameters take a copy of their parent type's hash and add:
+#
+#       name            Member or parameter name (overrides type name)
+#       direction       Direction of parameter (IN, OUT or INOUT)
+#
+###############################################################################
+class xdr_type(token_base):
+    def __init__(self, xdr, base=None, name=None, c_name=None,
+                 basic=None, array=None, dim=None, max_obj=None, max_count=None,
+                 members=None):
+        if not isinstance(xdr, xdr_context):
+            raise TypeError("XDR context isn't")
+        if array:
+            if not isinstance(array, xdr_array):
+                raise TypeError("Invalid array class")
+            if array == xdr_array.fixed and not dim:
+                raise RuntimeError("Dimension required for fixed-size array")
+            if dim and max_count:
+                raise RuntimeError("Can't be both variable and fixed-size array")
+        elif basic == "string" or basic == "opaque" or \
+             base and (base.is_string() or base.is_opaque):
+            if dim:
+                raise RuntimeError("Can't specify fixed dimension limits on string/opaque")
+        else:
+            if dim or max_obj or max_count:
+                raise RuntimeError("Can't specify dimension limits on non-array")
+
+        self.source = xdr.source
+        self.lineno = xdr.lineno
+        self.referenced = False
+        self.flat = None
+
+        if base:
+            if name:
+               raise RuntimeError("Type basic name can't be changed on an extant type")
+            if c_name:
+               raise RuntimeError("Type C name can't be changed on an extant type")
+            if basic:
+               raise RuntimeError("Basic type can't be changed on an extant type")
+            if members:
+               raise RuntimeError("Members can't be added to an extant type")
+            if not isinstance(base, xdr_type):
+                raise TypeError("Base type is not a type", type(base))
+            base.referenced = True
+            self.referenced = True
+            self.name = base.name
+            self.c_name = base.c_name
+            self.basic = base.basic
+            self.array = base.array
+            self.dim = base.dim
+            self.max_count = base.max_count
+            self.max_obj = base.max_obj
+            self.members = base.members
+
+            if array:
+                if base.array:
+                    xdr.error("Array-of-array not supported")
+                self.array = array
+                self.dim = dim
+                self.max_count = max_count
+            elif (self.is_single_string() or self.is_single_opaque()) and max_obj:
+                if self.max_obj:
+                    xdr.error("Maximum size already set on string/opaque")
+                self.max_obj = max_obj
+        else:
+            if name and not isinstance(name, str):
+                raise TypeError("Type name is not a string")
+            if c_name and not isinstance(c_name, str):
+                raise TypeError("C type name is not a string")
+            if basic and not isinstance(basic, xdr_basic):
+                raise TypeError("Invalid basic XDR type")
+            self.name = name
+            self.c_name = c_name
+            self.basic = basic
+            self.array = array
+            self.array = array
+            self.dim = dim
+            self.max_obj = max_obj
+            self.max_count = max_count
+            self.members = members
+            if members:
+                if not isinstance(members, list):
+                    raise RuntimeError("Members should be a list")
+
+        if not self.basic:
+           raise RuntimeError("basic type unset")
+
+        typespec = str(self)
+        if typespec not in xdr.typespecs:
+            xdr.typespecs[typespec] = self
+
+
+    def is_int(self):
+        return self.basic == xdr_basic.int32 or self.basic == xdr_basic.int64
+
+    def is_int32(self):
+        return self.basic == xdr_basic.int32
+
+    def is_int64(self):
+        return self.basic == xdr_basic.int64
+
+    def is_string(self):
+        return self.basic == xdr_basic.string
+
+    def is_opaque(self):
+        return self.basic == xdr_basic.opaque
+
+    def is_struct(self):
+        return self.basic == xdr_basic.struct
+
+    def is_union(self):
+        return self.basic == xdr_basic.union
+
+    def is_compound(self):
+        return self.is_struct() or self.is_union()
+
+    def is_uuid(self):
+        return self.basic == xdr_basic.opr_uuid
+
+    def is_single_int(self):
+        return not self.array and self.is_int()
+
+    def is_single_int32(self):
+        return not self.array and self.is_int32()
+
+    def is_single_int64(self):
+        return not self.array and self.is_int64()
+
+    def is_single_string(self):
+        return not self.array and self.is_string()
+
+    def is_single_opaque(self):
+        return not self.array and self.is_opaque()
+
+    def is_single_basic(self):
+        return not self.array and not self.is_compound()
+
+    def is_single_struct(self):
+        return not self.array and self.is_struct()
+
+    def is_single_union(self):
+        return not self.array and self.is_union()
+
+    def is_single_compound(self):
+        return not self.array and self.is_compound()
+
+    def is_single_uuid(self):
+        return not self.array and self.is_uuid()
+
+    def is_single(self):
+        return not self.array
+
+    def is_array(self):
+        return self.array == xdr_array.fixed
+
+    def is_char_array(self):
+        return self.array == xdr_array.fixed and self.name == "char"
+
+    def is_int32_array(self):
+        return self.array == xdr_array.fixed and self.is_int32()
+
+    def is_int64_array(self):
+        return self.array == xdr_array.fixed and self.is_int64()
+
+    def is_int_array(self):
+        return self.array == xdr_array.fixed and self.is_int()
+
+    def is_struct_array(self):
+        return self.array == xdr_array.fixed and self.is_struct()
+
+    def is_bulk(self):
+        return self.array == xdr_array.bulk
+
+    def is_bulk_char(self):
+        return self.is_bulk() and self.name == "char"
+
+    def is_bulk_int32(self):
+        return self.is_bulk() and self.is_int32()
+
+    def is_bulk_int64(self):
+        return self.is_bulk() and self.is_int64()
+
+    def is_bulk_int(self):
+        return self.is_bulk() and self.is_int()
+
+    def is_bulk_struct(self):
+        return self.is_bulk() and self.is_struct()
+
+    def is_flat(self):
+        if self.flat != None:
+            return self.flat
+        if self.is_bulk() or self.is_string() or self.is_opaque():
+            self.flat = False
+            return False
+        if self.members:
+            for i in self.members:
+                if not i.is_flat():
+                    self.flat = False
+                    return False
+        self.flat = True
+        return True
+
+    def what_max_obj(self):
+        if self.max_obj != None:
+            return self.max_obj
+        return "UINT_MAX"
+
+    def what_max_count(self):
+        if self.max_count != None:
+            return self.max_count
+        return "UINT_MAX"
+
+    def __str__(self):
+        t = self.name
+        if self.is_array():
+            t += "[{:s}]".format(str(self.dim))
+        elif self.is_bulk() or self.is_string() or self.is_opaque():
+            if self.max_count:
+                t += "<{:s}>".format(str(self.max_count))
+            if self.max_obj:
+                t += "<{:s}>".format(str(self.max_obj))
+            else:
+                t += "<>"
+        return t
+
+###############################################################################
+#
+# XDR structure member definition
+#
+###############################################################################
+class xdr_member(token_base):
+    """A structure member"""
+    def __init__(self, name, typespec, xdr):
+        self.typespec = typespec
+        self.ty = typespec
+        self.dim = typespec.dim
+        self.max_obj = typespec.max_obj
+        self.max_count = typespec.max_count
+        self.name = name
+        self.source = xdr.source
+        self.lineno = xdr.lineno
+        self.special = None
+
+    def is_int(self):           return self.ty.is_int()
+    def is_int32(self):         return self.ty.is_int32()
+    def is_int64(self):         return self.ty.is_int64()
+    def is_string(self):        return self.ty.is_string()
+    def is_opaque(self):        return self.ty.is_opaque()
+    def is_struct(self):        return self.ty.is_struct()
+    def is_union(self):         return self.ty.is_union()
+    def is_compound(self):      return self.ty.is_compound()
+    def is_single_int(self):    return self.ty.is_single_int()
+    def is_single_int32(self):  return self.ty.is_single_int32()
+    def is_single_int64(self):  return self.ty.is_single_int64()
+    def is_single_string(self): return self.ty.is_single_string()
+    def is_single_opaque(self): return self.ty.is_single_opaque()
+    def is_single_basic(self):  return self.ty.is_single_basic()
+    def is_single_struct(self): return self.ty.is_single_struct()
+    def is_single_union(self):  return self.ty.is_single_union()
+    def is_single_uuid(self):   return self.ty.is_single_uuid()
+    def is_single_compound(self): return self.ty.is_single_compound()
+    def is_single(self):        return self.ty.is_single()
+    def is_array(self):         return self.ty.is_array()
+    def is_char_array(self):    return self.ty.is_char_array()
+    def is_int32_array(self):   return self.ty.is_int32_array()
+    def is_int64_array(self):   return self.ty.is_int64_array()
+    def is_int_array(self):     return self.ty.is_int_array()
+    def is_struct_array(self):  return self.ty.is_struct_array()
+    def is_bulk(self):          return self.ty.is_bulk()
+    def is_bulk_char(self):     return self.ty.is_bulk_char()
+    def is_bulk_int32(self):    return self.ty.is_bulk_int32()
+    def is_bulk_int64(self):    return self.ty.is_bulk_int64()
+    def is_bulk_int(self):      return self.ty.is_bulk_int()
+    def is_bulk_struct(self):   return self.ty.is_bulk_struct()
+    def is_flat(self):          return self.ty.is_flat()
+    def what_max_obj(self):     return self.ty.what_max_obj()
+    def what_max_count(self):   return self.ty.what_max_count()
+
+
+###############################################################################
+#
+# XDR type alias definition
+#
+###############################################################################
+class xdr_type_alias(token_base):
+    """A type alias"""
+    def __init__(self, name, typespec, xdr):
+        if not isinstance(typespec, xdr_type):
+            raise TypeError("Base type is not a type")
+        self.name = name
+        self.typespec = typespec
+        self.source = xdr.source
+        self.lineno = xdr.lineno
+
+###############################################################################
+#
+# XDR union case definition
+#
+###############################################################################
+class xdr_union_case(token_base):
+    """An XDR union case"""
+    def __init__(self, xdr, tag, members):
+        self.source = xdr.source
+        self.lineno = xdr.lineno
+        self.tag = tag
+        self.members = members
+
+###############################################################################
+#
+# XDR procedure member definition
+#
+###############################################################################
+class xdr_proc(token_base):
+    """An XDR procedure"""
+    def __init__(self, name, xdr, params, opcode, multi=False, split=False):
+        self.package = xdr.pkg
+        self.name = name
+        self.source = xdr.source
+        self.lineno = xdr.lineno
+        self.params = params
+        self.opcode = opcode
+        self.multi = multi
+        self.split = split
+
+###############################################################################
+#
+# Generated file writer class
+#
+###############################################################################
+class file_generator:
+    """File generator class"""
+    def __init__(self, xdr, prefix):
+        self.xdr = xdr
+        self._rxhdr = open(prefix + "afs_xg.H", "w", encoding="utf-8")
+        self._rxsrc = open(prefix + "afs_xg.C", "w", encoding="utf-8")
+
+    def rxhdr(self, *va):
+        for i in va:
+            self._rxhdr.write(str(i))
+
+    def rxsrc(self, *va):
+        for i in va:
+            self._rxsrc.write(str(i))
+
+    def rxhdrf(self, fmt, *va):
+        self._rxhdr.write(fmt.format(*va))
+
+    def rxsrcf(self, fmt, *va):
+        self._rxsrc.write(fmt.format(*va))
+
+    def where(self, loc):
+        self._where = loc + ": "
+
+    def error(self, *va):
+        if self._where:
+            sys.stdout.write(self._where)
+        for i in va:
+            sys.stdout.write(str(i))
+        sys.stdout.write("\n")