From e7eab992b446dc68341e5e6a59c13b2e8181ea0b Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 9 Oct 2019 16:41:01 +0100 Subject: [PATCH] Implement rxgen Implement the rxgen program to process .xg interface files into C definitions. Signed-off-by: David Howells --- .gitignore | 3 + Makefile | 7 + lib/Makefile | 12 + lib/rxrpc.h | 215 +++++++++ rxgen/Makefile | 3 + rxgen/emit_c_struct.py | 375 ++++++++++++++++ rxgen/emit_c_sync_funcs.py | 877 +++++++++++++++++++++++++++++++++++++ rxgen/rxgen.py | 783 +++++++++++++++++++++++++++++++++ rxgen/rxgen_bits.py | 598 +++++++++++++++++++++++++ 9 files changed, 2873 insertions(+) create mode 100644 Makefile create mode 100644 lib/Makefile create mode 100644 lib/rxrpc.h create mode 100644 rxgen/Makefile create mode 100644 rxgen/emit_c_struct.py create mode 100644 rxgen/emit_c_sync_funcs.py create mode 100755 rxgen/rxgen.py create mode 100644 rxgen/rxgen_bits.py diff --git a/.gitignore b/.gitignore index b25c15b..d7c40ee 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ *~ +lib/afs_xg.* +rxgen/__pycache__/ +rxgen/parsetab.py diff --git a/Makefile b/Makefile new file mode 100644 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 index 0000000..a904339 --- /dev/null +++ b/lib/Makefile @@ -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 index 0000000..ad7ced8 --- /dev/null +++ b/lib/rxrpc.h @@ -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 +#include +#include +#include +#include +#include +#include +#include + +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 index 0000000..642cd4e --- /dev/null +++ b/rxgen/Makefile @@ -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 index 0000000..4112f34 --- /dev/null +++ b/rxgen/emit_c_struct.py @@ -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 index 0000000..847c894 --- /dev/null +++ b/rxgen/emit_c_sync_funcs.py @@ -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 index 0000000..632d5ce --- /dev/null +++ b/rxgen/rxgen.py @@ -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 + Copyright (C) 2004 University of Michigan, Center for + Information Technology Integration + Based on version written by Peter Astrand + 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_. +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_ functions are used by lex. They are called with t.value==, and t.type==. They expect a return value +# with attribute type= + +# 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="".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 \n") + o.rxhdr("#include \n") + o.rxhdr("#include \"rxrpc.h\"\n") + + o.rxsrc("/* AUTOGENERATED */\n") + o.rxsrc("#include \"afs_xg.h\"\n") + o.rxsrc("#include \n") + o.rxsrc("#include \n") + o.rxsrc("#include \n") + o.rxsrc("#include \n") + o.rxsrc("#include \n") + o.rxsrc("#include \n") + o.rxsrc("#include \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} *".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 index 0000000..4e52a33 --- /dev/null +++ b/rxgen/rxgen_bits.py @@ -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") -- 2.50.1