]> 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, 10 Jul 2020 20:24:57 +0000 (21:24 +0100)
Implement the rxgen program to process .xg interface files into C
definitions.

Signed-off-by: David Howells <dhowells@redhat.com>
.gitignore
Makefile [new file with mode: 0644]
lib/Makefile [new file with mode: 0644]
lib/rxrpc.h [new file with mode: 0644]
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 b25c15b81fae06e1c55946ac6270bfdb293870e8..d7c40eee1e5065ac94ca2b48b1c5529584320952 100644 (file)
@@ -1 +1,4 @@
 *~
+lib/afs_xg.*
+rxgen/__pycache__/
+rxgen/parsetab.py
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..38fda8f
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,7 @@
+all:
+       $(MAKE) -C lib
+
+clean:
+       $(RM) *~
+       $(MAKE) -C lib clean
+       $(MAKE) -C rxgen clean
diff --git a/lib/Makefile b/lib/Makefile
new file mode 100644 (file)
index 0000000..a904339
--- /dev/null
@@ -0,0 +1,12 @@
+RXGEN  := ../rxgen/rxgen.py
+
+RPC_HEADERS    := $(wildcard ../rpc-api/*.h)
+RPC_DEFS       := $(filter-out %test.xg, $(sort $(wildcard ../rpc-api/*.xg)))
+CFLAGS         := -g -Wall -Wformat -fpic
+
+#RPC_DEFS      := ../rpc-api/test.xg
+
+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)
diff --git a/lib/rxrpc.h b/lib/rxrpc.h
new file mode 100644 (file)
index 0000000..ad7ced8
--- /dev/null
@@ -0,0 +1,215 @@
+/* Core definitions.
+ *
+ * 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
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#ifndef RXRPC_H
+#define RXRPC_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <arpa/inet.h>
+#include <uuid/uuid.h>
+#include <linux/rxrpc.h>
+
+typedef unsigned int net_xdr_t;
+
+struct xdr_u64 { net_xdr_t hi, lo; };
+
+struct rxrpc_result {
+};
+
+/*
+ * Call parameters.
+ */
+struct rxrpc_call_params {
+       bool                            exclusive;
+       bool                            upgrade_service;
+};
+
+enum rx_call_state {
+       rx_call_completed,
+};
+
+struct rxrpc_resv {
+       unsigned int            max_ioc;        /* Size of msg_iov[] */
+       size_t                  alloc;          /* Size of buffer allocated */
+       size_t                  additional;     /* Size of additional content */
+};
+
+/*
+ * RxRPC encoded data buffer.
+ */
+struct rxrpc_buffer {
+       net_xdr_t               *enc_buf;       /* Encoding buffer */
+       net_xdr_t               *enc_buf_p;     /* Running pointer into encoding buffer */
+       net_xdr_t               *enc_buf_end;   /* Encoding buffer limit */
+       struct iovec            *msg_iov;       /* For sendmsg()'s msghdr */
+       unsigned int            ioc;            /* Occupancy of msg_iov[] */
+       size_t                  size;           /* Size of content, including additional iovecs */
+};
+
+/*
+ * RxRPC call.
+ */
+struct rxrpc_call {
+       struct rxrpc_local_endpoint *endpoint;
+       enum rx_call_state      state;
+
+       bool                    split;          /* True if split-mode call */
+
+       /* Transmission phase state */
+       uint32_t                encode_error;
+
+       /* Receive phase state */
+       bool                    dec_oom;        /* True if we got ENOMEM during decode */
+       uint32_t                decode_error;
+};
+
+extern bool rxrpc_receive(struct rxrpc_local_endpoint *endpoint, bool nowait,
+                         struct rxrpc_result *result);
+
+extern struct rxrpc_buffer *rxrpc_alloc_enc_buffer(struct rxrpc_resv *resv);
+extern void rxrpc_free_buffer(struct rxrpc_buffer *buf);
+extern struct rxrpc_call *rxrpc_make_call(struct rxrpc_call_params *z_params,
+                                         struct rxrpc_buffer *z_request,
+                                         const char *op_name,
+                                         struct rxrpc_result *_result);
+extern void rxrpc_receive_reply(struct rxrpc_call *call);
+extern bool rxrpc_request_received(struct rxrpc_call *call);
+extern bool rxrpc_read(struct rxrpc_call *z_call, void *buffer, size_t buffer_size);
+extern bool rxrpc_send_data(struct rxrpc_call *call, struct rxrpc_buffer *buf, bool more,
+                           struct rxrpc_result *_result);
+extern void rxrpc_abort_call(struct rxrpc_call *call, uint32_t abort_code);
+extern bool rxrpc_end_call(struct rxrpc_call *call, uint32_t abort_code);
+extern bool rxrpc_call_failed(struct rxrpc_call *call);
+
+/*
+ * XDR encoding size calculation routines
+ */
+static inline void rxrpc_resv(struct rxrpc_resv *resv)
+{
+       resv->alloc += 4;
+}
+
+static inline void rxrpc_resv_a32(struct rxrpc_resv *resv, unsigned int n)
+{
+       resv->alloc += 4 * n;
+}
+
+static inline void rxrpc_resv_u64(struct rxrpc_resv *resv)
+{
+       resv->alloc += 8;
+}
+
+static inline void rxrpc_resv_a64(struct rxrpc_resv *resv, unsigned int n)
+{
+       resv->alloc += 8 * n;
+}
+
+static inline size_t xdr_round_up(unsigned int size)
+{
+       return (size + sizeof(net_xdr_t) - 1) & ~(sizeof(net_xdr_t) - 1);
+}
+
+static inline bool rxrpc_resv_opaque_inline(unsigned int size, unsigned int max)
+{
+       return max <= 1024 && size <= 1024;
+}
+
+static inline void rxrpc_resv_opaque(struct rxrpc_resv *resv, unsigned int size, unsigned int max)
+{
+       size_t rs = xdr_round_up(size);
+
+       rxrpc_resv(resv);
+       if (rxrpc_resv_opaque_inline(size, max)) {
+               resv->alloc += rs;
+       } else {
+               if ((size & 3) == 0) {
+                       resv->additional = rs;
+               } else {
+                       resv->alloc += 4;
+                       resv->additional = rs - 4;
+               }
+               resv->max_ioc += 2;
+       }
+}
+
+static inline void rxrpc_resv_string(struct rxrpc_resv *resv, unsigned int size, unsigned int max)
+{
+       rxrpc_resv_opaque(resv, size, max);
+}
+
+static inline void rxgen_calc_encode_opr_uuid(struct rxrpc_resv *resv, const uuid_t *uuid)
+{
+       resv->alloc += 16;
+}
+
+/*
+ * XDR encoding routines
+ */
+extern void rxrpc_enc_opaque(struct rxrpc_buffer *buf, const void *blob,
+                            unsigned int size, unsigned int max);
+
+static inline void rxrpc_enc(struct rxrpc_buffer *z_buf, uint32_t val)
+{
+       *z_buf->enc_buf_p++ = htonl(val);
+}
+
+static inline void rxrpc_enc_u64(struct rxrpc_buffer *z_buf, uint64_t x)
+{
+       rxrpc_enc(z_buf, x >> 32);
+       rxrpc_enc(z_buf, x);
+}
+
+static inline void rxrpc_enc_string(struct rxrpc_buffer *buf, const char *s,
+                                   unsigned int size, unsigned int max)
+{
+       rxrpc_enc_opaque(buf, s, size, max);
+}
+
+static inline void rxrpc_enc_block(struct rxrpc_buffer *z_buf, const void *p, size_t s)
+{
+       memcpy(z_buf->enc_buf_p, p, s);
+       z_buf->enc_buf_p += s / 4;
+}
+
+static inline void rxrpc_seal_buffer(struct rxrpc_buffer *buf)
+{
+       unsigned int len;
+
+       /* call->ioc points to the iovec being filled, so we need to advance it
+        * if that has anything in it.
+        */
+       len = (void *)buf->enc_buf_p - (void *)buf->msg_iov[buf->ioc].iov_base;
+       if (len) {
+               buf->msg_iov[buf->ioc].iov_len = len;
+               buf->ioc++;
+       }
+}
+
+/*
+ * XDR decoding routines
+ */
+extern uint32_t rxrpc_dec(struct rxrpc_call *z_call);
+extern uint32_t rxrpc_dec_limit(struct rxrpc_call *z_call, size_t limit);
+extern void *rxrpc_dec_blob(struct rxrpc_call *z_call, size_t size);
+
+static inline uint64_t rxrpc_dec_u64(struct rxrpc_call *call)
+{
+       uint64_t x;
+       x  = (uint64_t)rxrpc_dec(call) << 32;
+       x |= (uint64_t)rxrpc_dec(call);
+       return x;
+}
+
+#endif /* RXRPC_H */
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..4112f34
--- /dev/null
@@ -0,0 +1,375 @@
+#!/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 structure encoders and decoders predeclarations
+#
+###############################################################################
+def emit_struct_encdec_decl(o, struct):
+    o.rxsrc("/* ", struct.c_name, " */\n")
+
+###############################################################################
+#
+# 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 not m.is_blob():
+                o.rxhdr("\t", m.ty.c_name, "\t", m.name, ";\n")
+            else:
+                o.rxhdr("\t", m.ty.c_name, "\t*", m.name, ";\n")
+                o.rxhdr("\tunsigned int\t", m.name, "__len;\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("\tunsigned int\t", m.name, "__nr;\n")
+            o.rxhdr("\t", m.ty.c_name, "\t*", m.name, ";\n")
+        else:
+            raise RuntimeError("Unsupported type '" + str(m.ty) + "'\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_resv(resv);\t// ", m.name, "\n")
+        elif m.is_single_int64():
+            o.rxsrc("\trxrpc_resv_u64(resv);\t// ", m.name, "\n")
+        elif m.is_single_struct():
+            o.rxsrc("\trxgen_calc_encode_", m.ty.name, "(resv, &p->", m.name, ");\n")
+        elif m.is_single_string():
+            o.rxsrc("\trxrpc_resv_string(resv, p->",
+                    m.name, "__len, ", m.what_max_size(), ");\n");
+        elif m.is_single_opaque():
+            o.rxsrc("\trxrpc_resv_opaque(resv, p->",
+                    m.name, "__len, ", m.what_max_size(), ");\n");
+        elif m.is_array():
+            if m.is_int32_array():
+                o.rxsrc("\trxrpc_resv_a32(resv, ", m.dim.name, ");\t// ", m.name, "[]\n")
+            elif m.is_int64_array():
+                o.rxsrc("\trxrpc_resv_a64(resv, ", m.dim.name, ");\t// ", m.name, "[]\n")
+            elif m.is_struct_array():
+                o.rxsrc("\tfor (i = 0; i < ", m.dim.name, "; i++)\n")
+                o.rxsrc("\t\trxgen_calc_encode_", 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_resv(resv);\t// ", m.name, "__nr\n")
+            o.rxsrc("\tfor (i = 0; i < p->", m.name, "__nr; i++)\n")
+            o.rxsrc("\t\trxgen_calc_encode_", m.ty.name, "(resv, &p->", m.name, "[i]);\n")
+        elif str(m.ty) == "opr_uuid":
+            o.rxsrc("\t\trxgen_calc_encode_", 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, " >> 32);\n")
+        elif m.is_single_struct():
+            o.rxsrc("\trxgen_encode_", m.ty.name, "(z_buf, &p->", m.name, ");\n")
+        elif m.is_array():
+            o.rxsrc("\tfor (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] >> 32);\n")
+            elif m.is_struct_array():
+                o.rxsrc("\t\trxgen_encode_", m.ty.name, "(z_buf, &p->", m.name, "[i]);\n")
+            else:
+                raise RuntimeError("No encoding for array type '" + str(m.ty) + "'")
+        elif m.is_string():
+            o.rxsrc("\trxrpc_enc_string(z_buf, p->", m.name, ", p->", m.name, "__len, ",
+                    m.what_max_size(), ");\n")
+        elif m.is_opaque():
+            o.rxsrc("\trxrpc_enc_opaque(z_buf, p->", m.name, ", p->", m.name, "__len, ",
+                    m.what_max_size(), ");\n")
+        elif m.is_bulk():
+            o.rxsrc("\trxrpc_enc     (z_buf, p->", m.name, "__nr);\n")
+            o.rxsrc("\tfor (i = 0; i < p->", m.name, "__nr; i++)\n")
+            o.rxsrc("\t\trxgen_encode_", m.ty.name, "(z_buf, &p->", m.name, "[i]);\n")
+        elif str(m.ty) == "opr_uuid":
+            o.rxsrc("\trxrpc_enc_block(z_buf, p->", m.name, ", 16);\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_call);\n")
+        elif m.is_single_int64():
+            o.rxsrc("\tp->", m.name, "\t= rxrpc_dec_u64(z_call);\n")
+        elif m.is_single_struct():
+            o.rxsrc("\trxgen_decode_", m.ty.name, "(z_call, &p->", m.name, ");\n")
+        elif m.is_array():
+            o.rxsrc("\tfor (i = 0; i < ", m.dim.name, "; i++)\n")
+            if m.is_int32_array():
+                o.rxsrc("\t\tp->", m.name, "[i] = rxrpc_dec(z_call);\n")
+            elif m.is_int64_array():
+                o.rxsrc("\t\tp->", m.name, "[i] = rxrpc_dec_u64(z_call);\n")
+            elif m.is_struct_array():
+                o.rxsrc("\t\trxgen_decode_", m.ty.name, "(z_call, &p->", m.name, "[i]);\n")
+            else:
+                raise RuntimeError("No decoding for array type '" + str(m.ty) + "'")
+        elif m.is_bulk():
+            o.rxsrc("\tp->", m.name, "__nr = rxrpc_dec(z_call);\n")
+            o.rxsrc("\tif (p->", m.name, "__nr > 0) {\n")
+            o.rxsrc("\t\t", m.ty.c_name, " *b = calloc(p->", m.name, "__nr, sizeof(*b));\n")
+            o.rxsrc("\t\tif (!b) {\n")
+            o.rxsrc("\t\t\tp->", m.name, "__nr = 0;\n")
+            o.rxsrc("\t\t\tz_call->dec_oom = true;\n")
+            o.rxsrc("\t\t\treturn;\n")
+            o.rxsrc("\t\t}\n")
+            o.rxsrc("\t\tfor (i = 0; i < p->", m.name, "__nr; i++)\n")
+            o.rxsrc("\t\t\trxgen_decode_", m.ty.name, "(z_call, &b[i]);\n")
+            o.rxsrc("\t\tp->", m.name, " = b;\n")
+            o.rxsrc("\t}\n")
+        elif m.is_blob():
+            if m.max_size == None:
+                o.rxsrc("\tp->", m.name, "__len = rxrpc_dec_limit(z_call, UINT_MAX);\n")
+            else:
+                o.rxsrc("\tp->", m.name, "__len = rxrpc_dec_limit(z_call, ", m.max_size, ");\n")
+            o.rxsrc("\tp->", m.name, " = rxrpc_dec_blob(z_call, p->", m.name, "__len);\n")
+        elif str(m.ty) == "opr_uuid":
+            o.rxsrc("\trxrpc_read(z_call, &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 out a C structure definition for this type
+    #
+    o.rxhdr("union ", struct.name, " {\n")
+    o.rxhdr("struct {\n")
+    o.rxhdr("\t", struct.selector_type, " ", selector, ";\n")
+    o.rxhdr("\tunion {\n")
+
+    for case in struct.members:
+        for m in case.members:
+            if m.is_array():
+                want_i = True
+                break
+        o.rxhdr("\t\tstruct { /* case ", case.tag, " */\n")
+        emit_struct_member_list(o, struct, case.members)
+        o.rxhdr("\t\t};\n")
+
+    o.rxhdr("\t};\n")
+    o.rxhdr("};\n")
+    o.rxhdr("};\n")
+    o.rxhdr("\n")
+
+    #
+    # Write a bulk free function
+    #
+    o.rxhdr("\n")
+    o.rxhdr("static inline\n")
+    o.rxhdr("void rxgen_bulk_free_", struct.name,
+            "(", struct.c_name, " **p, unsigned int nr)\n")
+    o.rxhdr("{\n")
+    o.rxhdr("\tif (p) {\n")
+    o.rxhdr("\t\tint i;\n")
+    o.rxhdr("\t\tfor (i = 0; i < nr; i++)\n")
+    o.rxhdr("\t\t\tfree(p[i]);\n")
+    o.rxhdr("\t\tfree(p);\n")
+    o.rxhdr("\t}\n")
+    o.rxhdr("}\n")
+    o.rxhdr("\n")
+
+    #
+    # Write a size calculation function
+    #
+    o.rxsrc("\n")
+    o.rxsrc("static void rxgen_calc_encode_", struct.name,
+            "(struct rxrpc_resv *resv, const ", struct.c_name, " *p)\n")
+    o.rxsrc("{\n")
+    o.rxsrc("\trxrpc_resv(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 rxgen_encode_", struct.name,
+            "(struct rxrpc_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 rxgen_decode_", struct.name,
+            "(struct rxrpc_call *z_call, ", struct.c_name, " *p)\n")
+    o.rxsrc("{\n")
+
+    if want_i:
+        o.rxsrc("\tint i;\n\n")
+
+    o.rxsrc("\tp->", selector, " = rxrpc_dec(z_call);\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):
+    want_i = False
+
+    if struct.is_union():
+        emit_union_encdec(o, struct)
+        return
+
+    for m in struct.members:
+        if m.is_array() or m.is_bulk():
+            want_i = True
+            break
+
+    #
+    # Write out a C structure definition for this type
+    #
+    o.rxhdr("struct ", struct.name, " {\n")
+    emit_struct_member_list(o, struct, struct.members)
+    o.rxhdr("};\n")
+    o.rxhdr("\n")
+
+    #
+    # Write a bulk free function
+    #
+    o.rxhdr("\n")
+    o.rxhdr("static inline\n")
+    o.rxhdr("void rxgen_bulk_free_", struct.name,
+            "(struct ", struct.name, " **p, unsigned int nr)\n")
+    o.rxhdr("{\n")
+    o.rxhdr("\tif (p) {\n")
+    o.rxhdr("\t\tunsigned int i;\n")
+    o.rxhdr("\t\tfor (i = 0; i < nr; i++)\n")
+    o.rxhdr("\t\t\tfree(p[i]);\n")
+    o.rxhdr("\t\tfree(p);\n")
+    o.rxhdr("\t}\n")
+    o.rxhdr("}\n")
+    o.rxhdr("\n")
+
+    #
+    # Write a size calculation function
+    #
+    o.rxsrc("\n")
+    o.rxsrc("static void rxgen_calc_encode_", struct.name,
+            "(struct rxrpc_resv *resv, const struct ", struct.name, " *p)\n")
+    o.rxsrc("{\n")
+    for m in struct.members:
+        if (m.is_array() and m.is_struct_array()) or m.is_bulk():
+            o.rxsrc("\tunsigned int i;\n\n")
+            break
+    emit_struct_calc_members(o, struct.members)
+    o.rxsrc("}\n")
+    o.rxsrc("\n")
+
+    #
+    # Write an encoding function
+    #
+    o.rxsrc("\n")
+    o.rxsrc("void rxgen_encode_", struct.name,
+            "(struct rxrpc_buffer *z_buf, const struct ", struct.name, " *p)\n")
+    o.rxsrc("{\n")
+
+    if want_i:
+        o.rxsrc("\tunsigned int i;\n\n")
+
+    emit_struct_encode_members(o, struct, struct.members)
+    o.rxsrc("}\n")
+
+    #
+    # Write a decoding function
+    #
+    o.rxsrc("\n")
+    #o.rxsrc("static __attribute__((unused))\n")
+    o.rxsrc("void rxgen_decode_", struct.name,
+            "(struct rxrpc_call *z_call, struct ", struct.name, " *p)\n")
+    o.rxsrc("{\n")
+
+    if want_i:
+        o.rxsrc("\tint i;\n\n")
+
+    emit_struct_decode_members(o, struct, struct.members)
+    o.rxsrc("}\n")
diff --git a/rxgen/emit_c_sync_funcs.py b/rxgen/emit_c_sync_funcs.py
new file mode 100644 (file)
index 0000000..847c894
--- /dev/null
@@ -0,0 +1,877 @@
+#!/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()
+
+        if p.is_array():
+            raise RuntimeError("Array arg not supported")
+        elif p.is_bulk_int():
+            s_req.append("const " + p.ty.c_name + " *" + p.name)
+            s_req.append("unsigned int " + p.name + "__nr")
+            r_rsp.append(p.ty.c_name + " **_" + p.name)
+            r_rsp.append("unsigned int *_" + p.name + "__nr")
+            r_req.append("const " + p.ty.c_name + " *" + p.name)
+            r_req.append("unsigned int " + p.name + "__nr")
+            s_rsp.append("const " + p.ty.c_name + " *" + p.name)
+            s_rsp.append("unsigned int " + p.name + "__nr")
+        elif p.is_bulk():
+            s_req.append("const " + p.ty.c_name + " **" + p.name)
+            s_req.append("unsigned int " + p.name + "__nr")
+            r_rsp.append(p.ty.c_name + " ***_" + p.name)
+            r_rsp.append("unsigned int *_" + p.name + "__nr")
+            r_req.append("const " + p.ty.c_name + " **" + p.name)
+            r_req.append("unsigned int " + p.name + "__nr")
+            s_rsp.append("const " + p.ty.c_name + " **" + p.name)
+            s_rsp.append("unsigned int " + p.name + "__nr")
+        elif p.is_single_string():
+            s_req.append("const char *" + p.name)
+            r_rsp.append("char **_" + p.name)
+            r_req.append("const char *" + p.name)
+            s_rsp.append("const char *" + p.name)
+        elif p.is_single_opaque():
+            # Do we want to insert a "*" in the following?
+            s_req.append("const void *" + p.name)
+            s_req.append("unsigned int " + p.name + "__len")
+            r_rsp.append("void **_" + p.name)
+            r_rsp.append("unsigned int _" + p.name + "__len")
+            r_req.append("const void *" + p.name)
+            r_req.append("unsigned int " + p.name + "__len")
+            s_rsp.append("const void *" + p.name)
+            s_rsp.append("unsigned int " + p.name + "__len")
+        elif p.is_single_struct():
+            t = p.ty.c_name
+            s_req.append("const " + t + " *" + p.name)
+            r_rsp.append(t + " **_" + p.name)
+            r_req.append("const " + t + " *" + p.name)
+            s_rsp.append("const " + 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 struct rxrpc_buffer *rxgen_encode_req_", func.name, "(")
+    o.rxhdr("\n\t\tstruct rxrpc_result *_result")
+    for proto in send_request_protos:
+        o.rxhdr(",\n\t\t", proto)
+    o.rxhdr(");\n")
+    o.rxhdr("\n")
+
+    o.rxhdr("extern struct rxrpc_buffer *rxgen_encode_reply_", func.name, "(")
+    o.rxhdr("\n\t\tstruct rxrpc_result *_result")
+    for proto in send_response_protos:
+        o.rxhdr(",\n\t\t", proto)
+    o.rxhdr(");\n")
+    o.rxhdr("\n")
+
+    o.rxhdr("extern struct rxrpc_call *rxgen_begin_", func.name + "(\n")
+    o.rxhdr("\tstruct rxrpc_call_params *z_params,\n")
+    o.rxhdr("\tstruct rxrpc_buffer *z_buf,\n")
+    o.rxhdr("\tstruct rxrpc_result *_result);\n")
+
+    o.rxhdr("\n")
+    o.rxhdr("extern bool rxgen_", func.name, "_get_reply(\n")
+    o.rxhdr("\tstruct rxrpc_call *z_call")
+    for p in func.response:
+        o.rxhdr(",\n\t")
+        if p.is_single_blob():
+            o.rxhdr(p.ty.c_name, " **_", p.name)
+            if not p.is_single_string():
+                o.rxhdr(", unsigned int *_", p.name, "__len")
+        elif p.is_bulk_int():
+            o.rxhdr(p.ty.c_name, " **_", p.name, ", ")
+            o.rxhdr("unsigned int *_", p.name, "__nr")
+            need_i = True
+        elif p.is_bulk():
+            o.rxhdr(p.ty.c_name, " ***_", p.name, ", ")
+            o.rxhdr("unsigned int *_", p.name, "__nr")
+            need_i = True
+        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:
+            raise RuntimeError("No prototype for type '" + str(p.ty) + "'")
+    o.rxhdr(",\n\tstruct rxrpc_result *z_result")
+    o.rxhdr(");\n")
+
+    o.rxhdr("\n")
+    o.rxhdr("extern int S", func.name, "(\n")
+    o.rxhdr("\tstruct rxrpc_call *z_call")
+    for p in func.request:
+        o.rxhdr(",\n\t")
+        if p.is_single_blob():
+            o.rxhdr(p.ty.c_name, " *", p.name)
+            if not p.is_single_string():
+                o.rxhdr(", unsigned int ", p.name, "__len")
+        elif p.is_bulk_int():
+            o.rxhdr(p.ty.c_name, " *", p.name, ", ")
+            o.rxhdr("unsigned int ", p.name, "__nr")
+        elif p.is_bulk():
+            o.rxhdr(p.ty.c_name, " **", p.name, ", ")
+            o.rxhdr("unsigned int ", p.name, "__nr")
+        elif p.is_single_int():
+            o.rxhdr(p.ty.c_name, " ", p.name)
+        elif p.is_single_compound():
+            o.rxhdr(p.ty.c_name, " *", p.name)
+        elif str(p.ty) == "opr_uuid":
+            o.rxhdr(p.ty.c_name, " *", p.name)
+        else:
+            raise RuntimeError("No prototype for '" + str(p.ty) + " " + p.name + "'")
+    o.rxhdr(");\n")
+
+    # Synchronous client call
+    o.rxhdr("\n")
+    o.rxhdr("extern bool ", func.name, "(\n")
+    o.rxhdr("\tstruct rxrpc_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_single_blob():
+            o.rxhdr(p.ty.c_name, " **_", p.name)
+            if not p.is_single_string():
+                o.rxhdr(", unsigned int *_", p.name, "__len")
+        elif p.is_bulk_int():
+            o.rxhdr(p.ty.c_name, " **_", p.name, ", ")
+            o.rxhdr("unsigned int *_", p.name, "__nr")
+        elif p.is_bulk():
+            o.rxhdr(p.ty.c_name, " ***_", p.name, ", ")
+            o.rxhdr("unsigned int *_", p.name, "__nr")
+        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\tstruct rxrpc_result *z_result")
+    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):
+    need_i = False
+
+    # 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("struct rxrpc_buffer *rxgen_encode_req_", func.name, "(")
+    else:
+        o.rxsrc("struct rxrpc_buffer *rxgen_encode_reply_", func.name, "(")
+
+    o.rxsrc("\n\t\tstruct rxrpc_result *_result")
+    for proto in protos:
+        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_blob():
+            blob_params.append(p)
+        if p.is_bulk():
+            bulk_params.append(p)
+            need_i = True
+
+    # Local variables
+    o.rxsrc("\tstruct rxrpc_buffer *z_buf;\n")
+    o.rxsrc("\tstruct rxrpc_resv z_resv = { .max_ioc = 1, };\n")
+    for p in params:
+        if p.is_single_string():
+            o.rxsrc("\tunsigned int ", p.name, "__len;\n")
+    if need_i:
+        o.rxsrc("\tint i;\n")
+
+    # Pointer checks
+    if blob_params or bulk_params:
+        o.rxsrc("\n")
+        for p in blob_params:
+            o.where(func.name + ":" + p.name)
+            o.rxsrc("\tif (!", p.name, ") goto einval;\n")
+
+        for p in bulk_params:
+            o.where(func.name + ":" + p.name)
+            o.rxsrc("\tif (!", p.name, ") goto einval;\n")
+
+    # Parameter checks
+    if blob_params or bulk_params:
+        for p in blob_params:
+            o.where(func.name + ":" + p.name)
+            if p.is_single_string():
+                o.rxsrc("\t", p.name, "__len = strlen(", p.name, ");\n")
+            if p.max_size:
+                o.rxsrc("\tif (", p.name, "__len > ", p.max_size.name, ") goto einval;\n")
+
+        for p in bulk_params:
+            o.where(func.name + ":" + p.name)
+            if p.max_size:
+                o.rxsrc("\tif (", p.name, "__nr > ", p.max_size.name, ") goto einval;\n")
+
+    # Calculate the size of the parameter.
+    o.rxsrc("\n")
+    o.rxsrc("\trxrpc_resv(&z_resv); // ", func.opcode.name, "\n");
+    for p in params:
+        if p.is_single_string():
+            o.rxsrc("\trxrpc_resv_string(&z_resv, ",
+                    p.name, "__len, ", p.what_max_size(), ");\n");
+        elif p.is_single_opaque():
+            o.rxsrc("\trxrpc_resv_opaque(&z_resv, ",
+                    p.name, "__len, ", p.what_max_size(), ");\n");
+        elif p.is_bulk_int32():
+            o.rxsrc("\trxrpc_resv(&z_resv); // #", p.name, "\n")
+            o.rxsrc("\trxrpc_resv_a32(&z_resv, ", p.name, "__nr);\n")
+        elif p.is_bulk_int64():
+            o.rxsrc("\trxrpc_resv(&z_resv); // #", p.name, "\n")
+            o.rxsrc("\trxrpc_resv_a64(&z_resv, ", p.name, "__nr);\n")
+        elif p.is_bulk():
+            o.rxsrc("\trxrpc_resv(&z_resv); // #", p.name, "\n")
+            o.rxsrc("\tfor (i = 0; i < ", p.name, "__nr; i++)\n")
+            o.rxsrc("\t\trxgen_calc_encode_", str(p.ty.name), "(&z_resv, ", p.name, "[i]);\n");
+        elif p.is_int32():
+            o.rxsrc("\trxrpc_resv(&z_resv); // ", p.name, "\n")
+        elif p.is_int64():
+            o.rxsrc("\trxrpc_resv_u64(&z_resv); // ", p.name, "\n")
+        else:
+            o.rxsrc("\trxgen_calc_encode_", p.ty.name, "(&z_resv, ", p.name, ");\n")
+
+    # Allocate the buffer
+    o.rxsrc("\n")
+    o.rxsrc("\tz_buf = rxrpc_alloc_enc_buffer(&z_resv);\n")
+    o.rxsrc("\tif (!z_buf)\n")
+    o.rxsrc("\t\treturn NULL;\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("\trxgen_encode_", p.ty.name, "(z_buf, ", p.name, ");\n")
+        elif p.is_single_string():
+            o.rxsrc("\trxrpc_enc_string(z_buf, ", p.name, ", ", p.name, "__len, ",
+                    p.what_max_size(), ");\n")
+        elif p.is_single_opaque():
+            o.rxsrc("\trxrpc_enc_opaque(z_buf, ", p.name, ", ", p.name, "__len, ",
+                    p.what_max_size(), ");\n")
+        elif p.is_bulk():
+            o.rxsrc("\trxrpc_enc(z_buf, ", p.name, "__nr);\n")
+            o.rxsrc("\tfor (i = 0; i < ", p.name, "__nr; 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\trxgen_encode_", p.ty.name, "(z_buf, ", p.name, "[i]);\n")
+            else:
+                raise RuntimeError("No decoding for array type '" + str(p.ty) + "'")
+        elif str(p.ty) == "opr_uuid":
+            o.rxsrc("\trxrpc_enc_block(z_buf, ", p.name, ", 16);\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")
+    if blob_params or bulk_params:
+        o.rxsrc("einval:\n")
+        o.rxsrc("\terrno = EINVAL;\n")
+        o.rxsrc("\treturn NULL;\n")
+    o.rxsrc("}\n")
+
+###############################################################################
+#
+# Emit a function to encode and dispatch a request or a response
+#
+###############################################################################
+def emit_func_send(o, func, what):
+    need_i = False
+
+    # Function definition and arguments
+    if what == "request":
+        protos = func.send_request_protos
+        params = func.request
+        bad_ret = "NULL"
+    else:
+        protos = func.send_response_protos
+        params = func.response
+        bad_ret = "-1"
+
+    o.rxsrc("\n")
+    if what == "request":
+        o.rxsrc("struct rxrpc_call *rxgen_begin_", func.name, "(\n")
+        o.rxsrc("\tstruct rxrpc_call_params *z_params,\n")
+        o.rxsrc("\tstruct rxrpc_buffer *z_buf,\n")
+        o.rxsrc("\tstruct rxrpc_result *_result)\n")
+    else:
+        o.rxsrc("int rxgen_reply_to_", func.name, "(\n")
+        o.rxsrc("\tstruct rxrpc_call *z_call,\n")
+        o.rxsrc("\tstruct rxrpc_buffer *z_buf)\n")
+
+    o.rxsrc("{\n")
+
+    # Local variables
+    if what == "request":
+        o.rxsrc("\tstruct rxrpc_call *z_call;\n")
+
+    # Allocate call
+    if what == "request":
+        o.rxsrc("\n")
+        o.rxsrc("\tz_call = rxrpc_make_call(z_params, z_buf, \"", func.name, "\", _result);\n")
+        o.rxsrc("\tif (!z_call)\n")
+        o.rxsrc("\t\treturn ", bad_ret, ";\n")
+
+    if func.split:
+        o.rxsrc("\tz_call->split = true;\n")
+
+    # Send the data
+    if what == "request":
+        o.rxsrc("\treturn z_call;\n")
+    else:
+        o.rxsrc("\treturn 0;\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("\tstruct rxrpc_call *z_call")
+    for p in params:
+        o.rxsrc(",\n\t")
+        if p.is_single_blob():
+            o.rxsrc(p.ty.c_name, " *", p.name)
+            if not p.is_single_string():
+                o.rxsrc(", unsigned int ", p.name, "__len")
+        elif p.is_bulk_int():
+            o.rxsrc(p.ty.c_name, " *", p.name, ", ")
+            o.rxsrc("unsigned int ", p.name, "__nr")
+        elif p.is_bulk():
+            o.rxsrc(p.ty.c_name, " **", p.name, ", ")
+            o.rxsrc("unsigned int ", p.name, "__nr")
+        elif p.is_single_int():
+            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("\treturn rxrpc_end_call(z_call, RXGEN_OPCODE);\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
+    need_i = False
+    nomem = False
+
+    o.rxsrc("\n")
+    o.rxsrc("int rxgen_decode_S", func.name, "(struct rxrpc_call *z_call)\n")
+    o.rxsrc("{\n")
+
+    # Local variables
+    for p in params:
+        if p.is_single_blob():
+            o.rxsrc("\tunsigned int ", p.name, "__len;\n")
+            o.rxsrc("\t", p.ty.c_name, " *", p.name, " = NULL;\n")
+        elif p.is_bulk_int():
+            o.rxsrc("\tunsigned int ", p.name, "__nr;\n")
+            o.rxsrc("\t", p.ty.c_name, " *", p.name, " = NULL;\n")
+            need_i = True
+        elif p.is_bulk():
+            o.rxsrc("\tunsigned int ", p.name, "__nr;\n")
+            o.rxsrc("\t", p.ty.c_name, " **", p.name, " = NULL;\n")
+            need_i = True
+        elif p.is_single_int() or p.is_single_struct():
+            o.rxsrc("\t", p.ty.c_name, " ", p.name, ";\n")
+        elif p.is_single_union():
+            o.rxsrc("\t", p.ty.c_name, " ", p.name, " = {};\n")
+        elif p.is_single_uuid():
+            o.rxsrc("\t", p.ty.c_name, " ", p.name, ";\n")
+        else:
+            print(p.name)
+            raise RuntimeError("No local vars for type '" + str(p.ty) + "'")
+    o.rxsrc("\tint ret = -1;\n")
+    if need_i:
+        o.rxsrc("\tint i;\n")
+
+    # Decode the data.
+    o.rxsrc("\n")
+    for p in params:
+        if p.is_single_blob():
+            o.rxsrc("\t", p.name, "__len = rxrpc_dec(z_call);\n")
+            if p.max_size != None:
+                o.rxsrc("\tif (", p.name, "__len > ", p.max_size, ") goto error;\n")
+            o.rxsrc("\t", p.name, " = rxrpc_dec_blob(z_call, ", p.name, "__len);\n");
+        elif p.is_bulk():
+            o.rxsrc("\t/* ", str(p.ty), " ", p.name, " */\n");
+            o.rxsrc("\t", p.name, "__nr = rxrpc_dec(z_call);\n")
+            if p.max_size != None:
+                o.rxsrc("\tif (", p.name, "__nr > ", p.max_size, ") goto error;\n")
+
+            if p.is_bulk_int():
+                o.rxsrc("\t", p.name, " = calloc(", p.name, "__nr, sizeof(", p.name, "[0]));\n")
+                o.rxsrc("\tif (!", p.name, ") goto nomem;\n")
+                o.rxsrc("\tfor (i = 0; i < ", p.name, "__nr; i++)\n")
+                o.rxsrc("\t\t", p.name, "[i] = rxrpc_dec(z_call);\n")
+                nomem = True
+            elif p.is_bulk_struct():
+                o.rxsrc("\t", p.name, " = calloc(", p.name, "__nr, sizeof(", p.name, "[0]));\n")
+                o.rxsrc("\tif (!", p.name, ") goto nomem;\n")
+                o.rxsrc("\tfor (i = 0; i < ", p.name, "__nr; i++) {\n")
+                o.rxsrc("\t\t", p.name, "[i] = malloc(sizeof(", p.name, "[0][0]));\n")
+                o.rxsrc("\t\tif (!", p.name, "[i]) goto nomem;\n")
+                o.rxsrc("\t\trxgen_decode_", p.ty.name, "(z_call, ", p.name, "[i]);\n")
+                o.rxsrc("\t}\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", p.name, " = rxrpc_dec(z_call);\n")
+        elif p.is_single_compound():
+            o.rxsrc("\trxgen_decode_", p.ty.name, "(z_call, &", p.name, ");\n")
+        elif p.is_single_uuid():
+            o.rxsrc("\trxrpc_read(z_call, &", p.name, ", sizeof(", p.ty.c_name, "));\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("\tif (!rxrpc_request_received(z_call))\n")
+    o.rxsrc("\t\tgoto error;\n")
+
+    # Invoke the server stub
+    o.rxsrc("\n")
+    o.rxsrc("\tret = S", func.name, "(\n")
+    o.rxsrc("\t\tz_call")
+    for p in params:
+        o.rxsrc(",\n\t\t")
+        if p.is_single_string():
+            o.rxsrc(p.name)
+        elif p.is_single_blob():
+            o.rxsrc(p.name, ", ", p.name, "__len")
+        elif p.is_bulk():
+            o.rxsrc(p.name, ", ", p.name, "__nr")
+        elif p.is_single_int():
+            o.rxsrc(p.name)
+        elif p.is_single_compound():
+            o.rxsrc("&", p.name)
+        elif p.is_single_uuid():
+            o.rxsrc("&", p.name)
+        else:
+            print(p.name)
+            raise RuntimeError("No stub variable for type '" + str(p.ty) + "'")
+    o.rxsrc(");\n")
+    o.rxsrc("clear:\n")
+
+    for p in params:
+        if p.is_single_blob() or p.is_bulk_int():
+            o.rxsrc("\tfree(", p.name, ");\n")
+        elif p.is_bulk():
+            o.rxsrc("\trxgen_bulk_free_", p.ty.name, "(", p.name, ", ", p.name, "__nr);\n")
+
+    o.rxsrc("\treturn ret;\n")
+
+    o.rxsrc("\n")
+    if nomem:
+        o.rxsrc("nomem:\n")
+        o.rxsrc("\tz_call->decode_error = UAENOMEM;\n")
+    o.rxsrc("error:\n")
+    o.rxsrc("\tret = rxrpc_end_call(z_call, z_call->decode_error);\n")
+    o.rxsrc("\tgoto clear;\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
+    nomem = False
+    error = False
+    too_big = False
+
+    o.rxsrc("\n")
+    o.rxsrc("bool rxgen_", func.name, "_get_reply(\n")
+    o.rxsrc("\tstruct rxrpc_call *z_call")
+    for p in params:
+        o.rxsrc(",\n\t")
+        if p.is_single_blob():
+            o.rxsrc(p.ty.c_name, " **_", p.name)
+            if not p.is_single_string():
+                o.rxsrc(", unsigned int *_", p.name, "__len")
+        elif p.is_bulk_int():
+            o.rxsrc(p.ty.c_name, " **_", p.name, ", ")
+            o.rxsrc("unsigned int *_", p.name, "__nr")
+            need_i = True
+        elif p.is_bulk():
+            o.rxsrc(p.ty.c_name, " ***_", p.name, ", ")
+            o.rxsrc("unsigned int *_", p.name, "__nr")
+            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\tstruct rxrpc_result *z_result")
+    o.rxsrc(")\n")
+    o.rxsrc("{\n")
+
+    # Local variables
+    for p in params:
+        if p.is_single_blob():
+            o.rxsrc("\tunsigned int ", p.name, "__len;\n")
+            o.rxsrc("\t", p.ty.c_name, " *", p.name, " = NULL;\n")
+        elif p.is_bulk_int():
+            o.rxsrc("\tunsigned int ", p.name, "__nr;\n")
+            o.rxsrc("\t", p.ty.c_name, " *", p.name, " = NULL;\n")
+        elif p.is_bulk():
+            o.rxsrc("\tunsigned int ", p.name, "__nr;\n")
+            o.rxsrc("\t", p.ty.c_name, " **", p.name, " = NULL;\n")
+        elif p.is_single_int() or p.is_single_struct():
+            pass
+        elif p.is_single_uuid():
+            pass
+        else:
+            raise RuntimeError("No local var for type '" + str(p.ty) + "'")
+    if need_i:
+        o.rxsrc("\tint i;\n")
+
+    # Decode the data.
+    o.rxsrc("\n")
+    for p in params:
+        if p.is_single_blob():
+            o.rxsrc("\t", p.name, "__len = rxrpc_dec(z_call);\n")
+            if p.max_size:
+                o.rxsrc("\tif (", p.name, "__len > ", p.max_size, ") goto too_big;\n");
+                too_big = True
+            o.rxsrc("\t", p.name, " = rxrpc_dec_blob(z_call, ", p.name, "__len);\n");
+        elif p.is_bulk():
+            o.rxsrc("\t/* ", str(p.ty), " ", p.name, " */\n");
+            o.rxsrc("\t", p.name, "__nr = rxrpc_dec(z_call);\n")
+            if p.max_size:
+                o.rxsrc("\tif (", p.name, "__nr > ", p.max_size, ") goto too_big;\n");
+                too_big = True
+
+            if p.is_bulk_int():
+                o.rxsrc("\t", p.name, " = calloc(", p.name, "__nr, sizeof(", p.name, "[0]));\n")
+                o.rxsrc("\tif (!", p.name, ") goto nomem;\n")
+                o.rxsrc("\tfor (i = 0; i < ", p.name, "__nr; i++)\n")
+                o.rxsrc("\t\t", p.name, "[i] = rxrpc_dec(z_call);\n")
+                nomem = True
+            elif p.is_bulk_struct():
+                o.rxsrc("\t", p.name, " = calloc(", p.name, "__nr, sizeof(", p.name, "[0]));\n")
+                o.rxsrc("\tif (!", p.name, ") goto nomem;\n")
+                o.rxsrc("\tfor (i = 0; i < ", p.name, "__nr; i++) {\n")
+                o.rxsrc("\t\t", p.name, "[i] = malloc(sizeof(", p.name, "[0][0]));\n")
+                o.rxsrc("\t\tif (!", p.name, "[i]) goto nomem;\n")
+                o.rxsrc("\t\trxgen_decode_", p.ty.name, "(z_call, ", p.name, "[i]);\n")
+                o.rxsrc("\t}\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 decoding for bulk type '" + str(p.ty) + "'")
+        elif p.is_single_int():
+            o.rxsrc("\t*_", p.name, " = rxrpc_dec(z_call);\n")
+        elif p.is_single_struct():
+            o.rxsrc("\trxgen_decode_", p.ty.name, "(z_call, _", p.name, ");\n")
+        elif p.is_single_uuid():
+            o.rxsrc("\trxrpc_read(z_call, _", p.name, ", sizeof(", p.ty.c_name, "));\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) + "'")
+
+    # Pass back any allocated arguments
+    o.rxsrc("\n")
+    for p in params:
+        if p.is_single_string():
+            o.rxsrc("\t*_", p.name, " = ", p.name, ";\n")
+        elif p.is_single_blob():
+            o.rxsrc("\t*_", p.name, " = ", p.name, ";\n")
+            o.rxsrc("\t*_", p.name, "__len = ", p.name, "__len;\n")
+        elif p.is_bulk():
+            o.rxsrc("\t*_", p.name, " = ", p.name, ";\n")
+            o.rxsrc("\t*_", p.name, "__nr = ", p.name, "__nr;\n")
+        else:
+            pass
+    o.rxsrc("\tif (!rxrpc_end_call(z_call, 0))\n")
+    o.rxsrc("\t\tgoto clear;\n")
+    o.rxsrc("\treturn true;\n")
+
+    o.rxsrc("\n")
+    if too_big:
+        o.rxsrc("too_big:\n")
+        o.rxsrc("\tz_call->decode_error = RXGEN_CC_UNMARSHAL;\n")
+        o.rxsrc("\tgoto error;\n")
+        error = True
+    if nomem:
+        o.rxsrc("nomem:\n")
+        o.rxsrc("\tz_call->decode_error = UAENOMEM;\n")
+        o.rxsrc("\tgoto error;\n")
+        error = True
+    if error:
+        o.rxsrc("error:\n")
+        o.rxsrc("\trxrpc_end_call(z_call, z_call->decode_error);\n")
+    o.rxsrc("clear:\n")
+    for p in params:
+        if p.is_single_blob() or p.is_bulk_int():
+            o.rxsrc("\tfree(", p.name, ");\n")
+        elif p.is_bulk():
+            o.rxsrc("\trxgen_bulk_free_", p.ty.name, "(", p.name, ", ", p.name, "__nr);\n")
+    o.rxsrc("\treturn false;\n")
+    o.rxsrc("}\n")
+
+###############################################################################
+#
+# Emit a function to switch between service calls.
+#
+###############################################################################
+def emit_package_switch(o, package):
+    o.rxsrc("\n")
+    o.rxsrc("int ", package.name, "_service(struct 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 rxgen_decode_S", f.name, "(z_call);\n")
+    o.rxsrc("\tdefault:\n")
+    o.rxsrc("\t\tz_call->decode_error = RXGEN_OPCODE;\n")
+    o.rxsrc("\t\treturn -1;\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("bool ", func.name, "(\n")
+    o.rxsrc("\tstruct rxrpc_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_single_blob():
+            o.rxsrc(p.ty.c_name, " **_", p.name)
+            if not p.is_single_string():
+                o.rxsrc(", unsigned int *_", p.name, "__len")
+        elif p.is_bulk_int():
+            o.rxsrc(p.ty.c_name, " **_", p.name, ", ")
+            o.rxsrc("unsigned int *_", p.name, "__nr")
+        elif p.is_bulk():
+            o.rxsrc(p.ty.c_name, " ***_", p.name, ", ")
+            o.rxsrc("unsigned int *_", p.name, "__nr")
+        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\tstruct rxrpc_result *z_result")
+
+    o.rxsrc(")\n")
+    o.rxsrc("{\n")
+
+    # Local variables
+    o.rxsrc("\tstruct rxrpc_buffer *z_buf;\n")
+    o.rxsrc("\tstruct rxrpc_call *z_call;\n")
+    o.rxsrc("\tbool ret;\n")
+
+    # Encode the parameters
+    o.rxsrc("\n")
+    o.rxsrc("\tz_buf = rxgen_encode_req_", func.name, "(\n")
+    o.rxsrc("\t\t\tz_result")
+    for p in func.request:
+        o.rxsrc(",\n\t\t")
+        if p.is_single_string():
+            o.rxsrc("\t", p.name)
+        elif p.is_single_blob():
+            o.rxsrc("\t", p.name, ", ", p.name, "__len")
+        elif p.is_bulk_int():
+            o.rxsrc("\t", p.name, ", ", p.name, "__nr")
+        elif p.is_bulk():
+            o.rxsrc("\t", p.name, ", ", p.name, "__nr")
+        else:
+            o.rxsrc("\t", p.name)
+
+    o.rxsrc(");\n")
+    o.rxsrc("\tif (!z_buf)\n")
+    o.rxsrc("\t\treturn false;\n")
+
+    # Begin the call
+    o.rxsrc("\n")
+    o.rxsrc("\tz_call = rxgen_begin_", func.name, "(z_params, z_buf, z_result);\n")
+    o.rxsrc("\tif (!z_call) {\n")
+    o.rxsrc("\t\trxrpc_free_buffer(z_buf);\n")
+    o.rxsrc("\t\treturn false;\n")
+    o.rxsrc("\t}\n")
+
+    o.rxsrc("\tret = rxrpc_send_data(z_call, z_buf, false, z_result);\n")
+    o.rxsrc("\trxrpc_free_buffer(z_buf);\n")
+    o.rxsrc("\tif (!ret) {\n")
+    o.rxsrc("\t\treturn rxrpc_end_call(z_call, RX_USER_ABORT);\n")
+    o.rxsrc("\t}\n")
+
+    # Wait for the reply to come in
+    o.rxsrc("\n")
+    o.rxsrc("\twhile (z_call->state != rx_call_completed) {\n")
+    o.rxsrc("\t\tif (!rxrpc_receive(z_call->endpoint, false, z_result))\n")
+    o.rxsrc("\t\t\treturn rxrpc_end_call(z_call, RX_USER_ABORT);\n")
+    o.rxsrc("\t}\n")
+
+    # Decode the reply
+    o.rxsrc("\n")
+    o.rxsrc("\treturn rxgen_", func.name, "_get_reply(\n")
+    o.rxsrc("\t\tz_call,\n")
+    for p in func.response:
+        if p.is_single_string():
+            o.rxsrc("\t\t_", p.name, ",\n")
+        elif p.is_single_blob():
+            o.rxsrc("\t\t_", p.name, ", _", p.name, "__len,\n")
+        elif p.is_bulk_int():
+            o.rxsrc("\t\t_", p.name, ", _", p.name, "__nr,\n")
+        elif p.is_bulk():
+            o.rxsrc("\t\t_", p.name, ", _", p.name, "__nr,\n")
+        else:
+            o.rxsrc("\t\t_", p.name, ",\n")
+
+    o.rxsrc("\t\tz_result);\n")
+    o.rxsrc("}\n")
diff --git a/rxgen/rxgen.py b/rxgen/rxgen.py
new file mode 100755 (executable)
index 0000000..632d5ce
--- /dev/null
@@ -0,0 +1,783 @@
+#!/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",    "char",      xdr_basic.string ),
+    ( "opaque",    "void",      xdr_basic.opaque ),
+    ( "opr_uuid",  "uuid_t",    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(1)
+    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(1)
+    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(1)
+    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(1)
+    if name in xdr.structs:
+        xdr.error("Structure {:s} already exists".format(name))
+    else:
+        s = xdr_type(xdr, name=name, c_name="union " + 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(1)
+    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(1)
+    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(1)
+    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_size = t[4]
+    global xdr
+    xdr.lineno = t.lineno(1)
+    if typespec.is_single_blob():
+        ty = xdr_type(xdr, base=typespec, max_size=max_size)
+    else:
+        ty = xdr_type(xdr, base=typespec, array=xdr_array.bulk, max_size=max_size)
+    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_size = t[5]
+    global xdr
+    xdr.lineno = t.lineno(1)
+    if typespec.is_single_blob():
+        ty = xdr_type(xdr, base=typespec, max_size=max_size)
+    else:
+        ty = xdr_type(xdr, base=typespec, array=xdr_array.bulk, max_size=max_size)
+    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_size = t[3]
+    name = t[6]
+    global xdr
+    xdr.lineno = t.lineno(1)
+    ty = xdr_type(xdr, base=typespec, array=xdr_array.bulk, max_size=max_size)
+    t[0] = xdr_member(name, ty, xdr)
+
+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
+                      | STRING
+                      | OPAQUE
+                      | 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(1)
+    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
+
+#
+# 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.rxsrc("/* AUTOGENERATED */\n")
+    o.rxsrc("#include \"afs_xg.h\"\n")
+    o.rxsrc("#include <stdio.h>\n")
+    o.rxsrc("#include <stdlib.h>\n")
+    o.rxsrc("#include <string.h>\n")
+    o.rxsrc("#include <unistd.h>\n")
+    o.rxsrc("#include <errno.h>\n")
+    o.rxsrc("#include <sys/socket.h>\n")
+    o.rxsrc("#include <sys/param.h>\n")
+    o.rxsrc("\n")
+
+    # Declare constants
+    o.rxhdr("\n")
+    for name in xdr.all_constants:
+        c = xdr.constants[name]
+        o.rxhdr("#define ", c.name, " ", c.value, "\n")
+
+    # Declare structure types
+    for s in xdr.all_structs:
+        emit_struct_encdec_decl(o, s)
+
+    o.rxhdr("\n")
+    for s in xdr.all_structs:
+        emit_struct_encdec(o, s);
+
+    # 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 f in xdr.funcs:
+        # 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_blob():
+                # Could validate max_size 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.
+    for package in xdr.packages.values():
+        if package.procs:
+            emit_package_switch(o, package)
+
+    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..4e52a33
--- /dev/null
@@ -0,0 +1,598 @@
+# 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.funcs = list()             # Functions in declaration order
+        self.func_names = dict()        # Function name uniquifier
+        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.func_names:
+            self.error("Proc {:s} already exists".format(name))
+        else:
+            self.func_names[name] = proc
+            self.funcs.append(proc)
+            self.debug("New proc", name)
+        return self.func_names[name]
+
+    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()
+
+###############################################################################
+#
+# 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_size        Max elements in bulk array (if array/bulk)
+#       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_size=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_size:
+                raise RuntimeError("Can't be both variable and fixed-size array")
+        elif basic == "string" or basic == "opaque" or base and base.is_blob():
+            if dim:
+                raise RuntimeError("Can't specify fixed dimension limits on string/opaque")
+        else:
+            if dim or max_size:
+                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_size = base.max_size
+            self.members = base.members
+
+            if array:
+                if base.array:
+                    xdr.error("Array-of-array not supported")
+                self.array = array
+                self.dim = dim
+                self.max_size = max_size
+            elif self.is_single_blob() and max_size:
+                if self.max_size:
+                    xdr.error("Maximum size already set on string/opaque")
+                self.max_size = max_size
+        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_size = max_size
+            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_blob(self):
+        return self.is_string() or self.is_opaque()
+
+    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_blob(self):
+        return not self.array and self.is_blob()
+
+    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_blob():
+            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_size(self):
+        if self.max_size != None:
+            return self.max_size
+        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_blob():
+            if self.max_size:
+                t += "<{:s}>".format(str(self.max_size))
+            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_size = typespec.max_size
+        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_blob(self):          return self.ty.is_blob()
+    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_blob(self):   return self.ty.is_single_blob()
+    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_size(self):    return self.ty.what_max_size()
+
+
+###############################################################################
+#
+# 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.name = xdr.pkg.name + "_" + name
+        self.source = xdr.source
+        self.lineno = xdr.lineno
+        self.params = params
+        self.opcode = opcode
+        self.multi = multi
+        self.split = split
+        xdr.pkg.procs.append(self)
+
+###############################################################################
+#
+# 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")