#! /usr/bin/python3

import argparse
from copy import copy
from errno import ENOENT
from functools import partial
from os import path
import re
import sys
import operator

import xml.etree.ElementTree as ET
from functools import reduce

def c_str(s):
    # FIXME
    return '"%s"' % s

bol = re.compile("^(?!$)", re.MULTILINE)
def indent(n, s):
    return bol.sub(" " * n, s)

class XMLObjectifier(object):
    @classmethod
    def objectify(cls, elem, *args):
        return cls.tagmap[elem.tag](elem, *args)

class FormatError(Exception):
    pass

class TypeNotFoundError(LookupError):
    pass

class HeaderNotFoundError(Exception):
    pass

class Header(object):
    def __init__(self, file, include_dir):
        self.include_dir = include_dir
        root = ET.parse(file).getroot()
        if root.tag != "xcb":
            raise FormatError("Not an xcb/proto file")
        self.header_name = root.attrib["header"]
        self.headers = { self.header_name: self,
                         "__primitive_types__": PrimitiveTypes() }
        self.extension_name = ""
        if "extension-name" in root.attrib:
            self.extension_name = root.attrib["extension-name"]
            self.c_tag = "EXT_" + self.extension_name.upper()
            self.xname = root.attrib["extension-xname"]
        self.import_header("xproto")
        self.types = { }
        retryqueue = { }
        def load_with_retry(elem):
            try:
                t = self.load_elem(elem)
                if t == None: return
                self.add_type(t)
            except TypeNotFoundError as e:
                missingtype = e.args[0]
                retryqueue.setdefault(missingtype, []).append(elem)
                return
            for elem in retryqueue.get((t.namespace, t.name), []):
                load_with_retry(elem)
            try: del retryqueue[(t.namespace, t.name)]
            except KeyError: pass
        for elem in root:
            load_with_retry(elem)
        for typename in list(retryqueue.keys()):
            raise TypeNotFoundError(typename)

    def load_elem(self, elem):
        if elem.tag == "import":
            self.import_header(elem.text)
            return None
        else:
            return TypeObjectifier.objectify(elem, self)

    def import_header(self, other_header):
        if other_header in self.headers: return
        for dir in self.include_dir:
            try:
                fn = path.join(dir, other_header + ".xml")
                self.headers[other_header] = Header(fn, self.include_dir)
                return
            except IOError as e:
                if e.errno != ENOENT: raise
        raise HeaderNotFoundError("Header '%s' not found on include path" %
                                    other_header)
    def add_type(self, newtype):
        self.types[(newtype.namespace, newtype.name)] = newtype
    def get_type(self, namespace, wanttype, allow_ambig=False):
        if ":" in wanttype:
            headername, typename = wanttype.split(":")
            return self.headers[headername].types[(namespace, typename)]
        gottypes = [ ]
        key = (namespace, wanttype)
        for header in sorted(list(self.headers.values()), key=(lambda x:x != self)):
            #print sorted(header.types.keys())
            if key in header.types:
                gottypes.append(header.types[key])
        if len(gottypes) == 0:
            raise TypeNotFoundError(key,)
        # The xcb-proto spec says that most uses of type names aren't allowed
        # to be ambiguous.  But some of them are anyway, so until we have
        # a nice way to emit non-fatal warnings, this check is disabled.
        if len(gottypes) > 1 and not allow_ambig and False:
            raise FormatError("Type '%s' is ambiguous" % (key,))
        return gottypes[0]

    @classmethod
    def parse_expression(self, elem, container=None):
        return ExpressionObjectifier.objectify(elem, container)

    def xtruss_known_extension(self):
        return "X(%s, %s) \\\n" % (self.c_tag, c_str(self.xname))
    def xtruss_requestparser(self):
        requests = [r for r in list(self.types.values()) if isinstance(r, Request)]
        requests.sort(key=lambda x: x.opcode)
        return "".join([r.xtruss_requestparser() for r in requests])
    def xtruss_replyparser(self):
        requests = [r for r in list(self.types.values())
                    if isinstance(r, Request) and r.reply]
        requests.sort(key=lambda x: x.opcode)
        return "".join([r.reply.xtruss_replyparser() for r in requests])
    def xtruss_eventtranslator(self):
        events = [e for e in list(self.types.values()) if isinstance(e, Event)]
        events.sort(key=lambda x: (x.is_genericevent, x.number))
        return "".join([e.xtruss_elephanttranslator() for e in events])
    def xtruss_eventparser(self):
        events = [e for e in list(self.types.values()) if isinstance(e, Event)]
        events.sort(key=lambda x: (x.is_genericevent, x.number))
        return "".join([e.xtruss_elephantparser() for e in events])
    def xtruss_errortranslator(self):
        errors = [e for e in list(self.types.values()) if isinstance(e, Error)]
        errors.sort(key=lambda x: x.number)
        return "".join([e.xtruss_elephanttranslator() for e in errors])
    def xtruss_errorparser(self):
        errors = [e for e in list(self.types.values()) if isinstance(e, Error)]
        errors.sort(key=lambda x: x.number)
        return "".join([e.xtruss_elephantparser() for e in errors])

class PrimitiveTypes(Header):
    def __init__(self):
        self.header_name = "__primitive_types__"
        self.headers = { self.header_name: self }
        self.types = { }
        for t in [
            PrimitiveType("BOOL", xtruss_format="BOOLEAN", size=1),
            PrimitiveType("BYTE", xtruss_format="HEX8", size=1),
            PrimitiveType("CARD8", xtruss_format="DECU", size=1),
            PrimitiveType("CARD16", xtruss_format="DECU", size=2),
            PrimitiveType("CARD32", xtruss_format="DECU", size=4),
            PrimitiveType("INT8", xtruss_format="DEC8", size=1),
            PrimitiveType("INT16", xtruss_format="DEC16", size=2),
            PrimitiveType("INT32", xtruss_format="DEC32", size=4),
            PrimitiveType("char", xtruss_format="HEX8", size=1),
            PrimitiveType("void", xtruss_format="HEX8", size=1),
            PrimitiveType("float", xtruss_format="FLOAT32", size=4),
            PrimitiveType("double", xtruss_format="FLOAT64", size=8),
        ]:
            self.add_type(t)

class Type(object):
    def __init__(self, elem, header):
        self.header = header
        self.setname(elem)
    def setname(self, elem):
        self.name = elem.attrib["name"]
    def __str__(self):
        return self.name
    namespace = 'type'

class ScalarType(Type):
    def xtruss_fieldparser(self, name, offset, structbase,
                           enum=None, altenum=None, mask=None):
        format = self.xtruss_format
        if enum != None:
            format = "ENUM"
        elif mask != None:
            format = "GENMASK"
        enum = enum or altenum
        if enum: format += " | SPECVAL"
        return "xlog_param(xl, %s, %s, %s%s);\n" % (
            name, format, self.xtruss_fetcher(offset, structbase),
            enum.xtruss_enum() if enum else mask.xtruss_enum() if mask else "")
    def xtruss_fetcher(self, offset, structbase):
        return "FETCH%d(data, %s)" % (
            self.size * 8, xtruss_expression(offset, structbase))

class PrimitiveType(ScalarType):
    def __init__(self, name, xtruss_format=None, size=0):
        self.name = name
        self.xtruss_format = xtruss_format
        self.size = size

class Struct(Type):
    base = 0
    def __init__(self, elem, header):
        super().__init__(elem, header)
        self.contents = [ ]
        self.fieldnames = { }
        pos = self.base
        for subelem in elem:
            member = StructObjectifier.objectify(subelem, header, self, pos)
            if member == None:
                continue
            self.contents.append(member)
            pos += member.size()
        self.size = pos
    def register_field(self, field):
        self.fieldnames[field.name] = field
    def xtruss_structparser(self, offset = None):
        if offset == None: offset = self.base
        ret = ""
        structbase = offset
        for c in self.contents:
            ret += c.xtruss_fieldparser(offset, structbase)
            offset += c.size()
        return ret
    def xtruss_fieldparser(self, name, offset, structbase,
                           enum=None, altenum=None, mask=None):
        return ("xlog_param(xl, %s, SETBEGIN);\n" % (name,) +
                self.xtruss_structparser(offset) +
                "xlog_set_end(xl);\n")
    def get_field(self, fieldname):
        return self.fieldnames[fieldname]

class Union(Struct):
    pass

class XIDType(ScalarType):
    def __init__(self, elem, header):
        super().__init__(elem, header)
        self.xtruss_format = self.name
    size = 4

class XIDUnion(XIDType):
    def __init__(self, elem, header):
        super().__init__(elem, header)
        self.members = [ ]
        for subelem in elem:
            if subelem.tag == "type":
                self.members.append(header.get_type('type', subelem.text))

class Enum(Type):
    namespace = 'enum'
    def __init__(self, elem, header):
        super().__init__(elem, header)
        self.values = { }
        for item in elem:
            if item.tag == "item":
                self.values[item.attrib["name"]] = (
                    header.parse_expression(item[0]))
    def xtruss_enum(self):
        return "".join([", %s, %s" % (c_str(n), v)
                          for n, v in list(self.values.items())]) + ", (char *)NULL"

# This is called (and named) as though it were a constructor.
def TypeDef(elem, header):
    t = copy(header.get_type('type', elem.attrib["oldname"]))
    t.name = elem.attrib["newname"]
    return t

class Request(Struct):
    namespace = 'request'
    base = 4
    def __init__(self, elem, header):
        super().__init__(elem, header)
        self.opcode = int(elem.attrib["opcode"])
        self.reply = None
        replyelem = elem.find("./reply")
        self.displayname = header.extension_name + self.name
        self.header = header
        self.reply = None
        if replyelem != None:
            self.reply = Reply(replyelem, header, self)
    def xtruss_requestparser(self):
        ret = ("case %s | %s:\n" %
               (self.header.c_tag, str(self.opcode)))
        ret += ("  xlog_request_name(xl, req, %s, true);\n" %
                c_str(self.displayname))
        ret += indent(2, self.xtruss_structparser())
        if self.reply:
            ret += "  req->replies = 1;\n"
        ret += "  break;\n"
        return ret

class Reply(Struct):
    class ReplyOffset(int):
        def __add__(self, other):
            return 8 - 1 + other
    base = ReplyOffset(1)
    def __init__(self, elem, header, request):
        super().__init__(elem, header)
        self.request = request
        self.header = header
    def __str__(self):
        return "%s/reply" % (self.request,)
    def setname(self, elem):
        pass
    def xtruss_replyparser(self):
        ret = ("case %s | %s:\n" %
               (self.header.c_tag, str(self.request.opcode)))
        ret += ("  /* %s */\n" % self.request.displayname)
        ret += indent(2, self.xtruss_structparser())
        ret += "  break;\n"
        return ret

class Elephant(Struct):
    def __init__(self, elem, header):
        super().__init__(elem, header)
        self.number = int(elem.attrib["number"])
        self.displayname = header.extension_name + self.name
        self.header = header
    def xtruss_caselabel(self):
        return ("case %s | %s:\n" %
                (self.header.c_tag, str(self.number)))
    def xtruss_elephanttranslator(self):
        ret = self.xtruss_caselabel()
        ret += "  return %s;\n" % c_str(self.displayname)
        return ret
    def xtruss_elephantparser(self):
        ret = self.xtruss_caselabel()
        ret += ("  /* %s */\n" % self.displayname)
        ret += "  strbuf_catf(xl->textbuf, \"(\");\n"
        ret += indent(2, self.xtruss_structparser())
        ret += "  strbuf_catf(xl->textbuf, \")\");\n"
        ret += "  break;\n"
        return ret

class Event(Elephant):
    namespace = 'event'
    def __init__(self, elem, header):
        super().__init__(elem, header)
        self.is_genericevent = "xge" in elem.attrib
        class EventOffset(int):
            def __add__(self, other):
                return 4 - 1 + other
        if self.is_genericevent:
            self.base = CVariable("pos") + 10
        else:
            self.base = CVariable("pos") + EventOffset(1)
    def xtruss_caselabel(self):
        if self.is_genericevent:
            return ("case %s | GENERICEVENT | %s:\n" %
                    (self.header.c_tag, str(self.number)))
        else:
            return ("case %s | %s:\n" %
                    (self.header.c_tag, str(self.number)))

def EventCopy(elem, header, namespace='event'):
    e = copy(header.get_type(namespace, elem.attrib["ref"], allow_ambig=True))
    e.name = elem.attrib["name"]
    e.number = int(elem.attrib["number"])
    e.displayname = header.extension_name + e.name
    return e

class Error(Elephant):
    namespace = 'error'
    base = 4

def ErrorCopy(elem, header):
    return EventCopy(elem, header, namespace='error')

class TypeObjectifier(XMLObjectifier):
    tagmap = {
        "struct": Struct,
        "union": Union,
        "xidtype": XIDType,
        "xidunion": XIDUnion,
        "enum": Enum,
        "typedef": TypeDef,
        "request": Request,
        "event": Event,
        "error": Error,
        "eventcopy": EventCopy,
        "errorcopy": ErrorCopy,
    }

class Pad(object):
    def __init__(self, elem, header, container, offset):
        self.align = int(elem.attrib.get("align", 1))
        self.bytes = int(elem.attrib.get("bytes", 0))
    def xtruss_fieldparser(self, offset, structbase, enum=None, altenum=None,
                           mask=None):
        return ""
    def size(self):
        return self.bytes

class Field(object):
    def __init__(self, elem, header, container, offset):
        self.name = elem.attrib["name"]
        self.container = container
        self.enum = None
        self.altenum = None
        self.mask = None
        self.altmask = None
        self.type = header.get_type('type', elem.attrib["type"])
        if "enum" in elem.attrib:
            self.enum = header.get_type('enum', elem.attrib["enum"])
        if "altenum" in elem.attrib:
            self.altenum = header.get_type('enum', elem.attrib["altenum"])
        if "mask" in elem.attrib:
            self.mask = header.get_type('enum', elem.attrib["mask"])
        if "altmask" in elem.attrib:
            self.altmask = header.get_type('enum', elem.attrib["altmask"])
        self.offset = offset
        container.register_field(self)
    def xtruss_fieldparser(self, offset, structbase):
        return self.type.xtruss_fieldparser(c_str(self.name), offset,
                                            structbase,
                                            self.enum, self.altenum, self.mask)
    def xtruss_fetcher(self, structbase):
        return self.type.xtruss_fetcher(self.offset, structbase)
    def size(self):
        return self.type.size

class FD(object):
    def __init__(self, elem, header, container, offset):
        self.name = elem.attrib["name"]

class List(object):
    def __init__(self, elem, header, container, offset):
        self.name = elem.attrib["name"]
        self.type = header.get_type('type', elem.attrib["type"])
        if len(elem):
            self.nmembers = header.parse_expression(elem[0], container)
        else:
            self.nmembers = None
    def xtruss_fieldparser(self, offset, structbase):
        if str(self.type) == "char":
            if self.nmembers == None:
                nm = xtruss_expression(CVariable("len") - offset, structbase)
            else:
                nm = xtruss_expression(self.nmembers,structbase)
            return ("xlog_param(xl, %s, STRING, %s, STRING(data, %s, %s));\n"
                    % (c_str(self.name),
                       nm, xtruss_expression(offset, structbase), nm))
        ret =  "/* LIST of %s %s @ %s */\n" % (
            xtruss_expression(self.nmembers, structbase)
            if self.nmembers != None else "", self.type,
            xtruss_expression(offset, structbase))
        ret += "{\n"
        ret += "    int lpos = %s;\n" % xtruss_expression(offset, structbase)
        ret += "    int i = 0;\n"
        if self.nmembers != None:
            ret += "    int n = %s;\n" % (
                xtruss_expression(self.nmembers, structbase),)
        ret += "    char buf[64];\n"
        if self.nmembers != None:
            ret += "    for (i = 0; i < n; i++) {\n"
        else:
            ret += "    for (i = 0; lpos + %s <= len; i++) {\n" % (
                xtruss_expression(self.type.size, structbase),)
        ret += "        sprintf(buf, \"%%s[%%d]\", %s, i);\n" % (
            c_str(self.name),)
        ret += indent(8,
            self.type.xtruss_fieldparser("buf", CVariable("lpos"), structbase))
        ret += "        lpos += %s;\n" % (
            xtruss_expression(self.type.size, structbase),)
        if self.nmembers == None:
            ret += "        if (lpos + %s < len && xlog_check_list_length(xl))\n" % (xtruss_expression(self.type.size, structbase),)
        else:
            ret += "        if (i+1 < n && xlog_check_list_length(xl))\n"
        ret += "            break;\n"
        ret += "    }\n"
        ret += "}\n"
        return ret
    def size(self):
        if self.nmembers == None:
            return CVariable("unknown_length")
        return self.nmembers * self.type.size

class ValueParam(object):
    # A <valueparam> doesn't really give us enough information to
    # properly decode what it's all about, so our decoding will
    # necessarily be a bit rudimentary.
    def __init__(self, elem, header, container, offset):
        self.value_mask_type = header.get_type('type',
                                               elem.attrib["value-mask-type"])
        self.value_mask_name = elem.attrib["value-mask-name"]
        self.value_list_name = elem.attrib["value-list-name"]
    def size(self):
        # FIXME
        return CVariable("unknown_length")
    def xtruss_fieldparser(self, offset, structbase):
        ret = self.value_mask_type.xtruss_fieldparser(
            c_str(self.value_mask_name), offset, structbase)
        ret += ("xlog_param(xl, %s, HEXSTRING4, %s, "
                "STRING(data, %s, %s));\n" % (
            c_str(self.value_list_name),
            xtruss_expression((CVariable("len") - offset)/4, structbase),
            xtruss_expression(offset, structbase),
            xtruss_expression(CVariable("len") - offset, structbase)))
        return ret

class BaseCase(object):
    def __init__(self, elem, header, container, offset, key):
        self.key = key
        self.tests = []
        self.members = []
        self.offset = offset
        pos = 0
        for subelem in elem:
            if subelem.tag == "enumref":
                self.tests.append(header.parse_expression(subelem, container))
            else:
                member = StructObjectifier.objectify(subelem, header, container,
                                                offset + pos)
                self.members.append(member)
                pos += member.size()
        self.fullsize = pos
    def xtruss_fieldparser(self, offset, structbase):
        ret = ("if (%s) {\n" % xtruss_expression(self.condition(), structbase))
        for m in self.members:
            ret += indent(4, m.xtruss_fieldparser(offset, structbase))
            offset += m.size()
        ret += "}\n"
        return ret
    def size(self):
        return GenericExpression("%s ? %s : 0", self.condition(), self.fullsize)

class BitCase(BaseCase):
    def condition(self):
        return self.key & (reduce(operator.__or__, self.tests))

class Case(BaseCase):
    def condition(self):
        return reduce(partial(Op, '||'),
                      [Op("==", self.key, t) for t in self.tests])

class Switch(object):
    def __init__(self, elem, header, container, offset):
        self.key = header.parse_expression(elem[0], container)
        self.cases = []
        pos = offset
        for subelem in elem[1:]:
            case = SwitchObjectifier.objectify(subelem, header,
                                               container, pos, self.key)
            self.cases.append(case)
            pos += case.size()
    def xtruss_fieldparser(self, offset, structbase):
        ret = ""
        for case in self.cases:
            ret += case.xtruss_fieldparser(offset, structbase);
            offset += case.size()
        return ret
    def size(self):
        return sum([case.size() for case in self.cases])

class SwitchObjectifier(XMLObjectifier):
    tagmap = {
        "bitcase": BitCase,
        "case": Case,
    }

class StructObjectifier(XMLObjectifier):
    tagmap = {
        "pad": Pad,
        "field": Field,
        "fd": FD,
        "list": List,
        "exprfield": Field,
        "valueparam": ValueParam,
        "switch": Switch,
        "doc": lambda *x: None,
        "reply": lambda *x: None,
    }

class Expression(object):
    def __add__(self, other):
        return Op("+", self, other)
    def __radd__(self, other):
        return Op("+", other, self)
    def __sub__(self, other):
        return Op("-", self, other)
    def __mul__(self, other):
        return Op("*", self, other)
    def __div__(self, other):
        return Op("/", self, other)
    def __and__(self, other):
        return Op("&", self, other)

class Op(Expression):
    def __init__(self, *args):
        if len(args) == 3:
            self.op = args[0]
            self.args = args[1:]
        else:
            elem, container = args
            self.op = elem.attrib["op"]
            self.args = [Header.parse_expression(subelem, container)
                         for subelem in elem]
    def __add__(self, other):
        # A little bit of constant folding
        if (self.op == "+" and
            isinstance(self.args[1], int) and isinstance(other, int)):
            return self.args[0] + (self.args[1] + other)
        return super().__add__(other)
    def xtruss_expression(self, structbase):
        return "(%s %s %s)" % (xtruss_expression(self.args[0], structbase),
                               self.op,
                               xtruss_expression(self.args[1], structbase))

class FieldRef(Expression):
    def __new__(cls, elem, container):
        # There's something magic about the name "length"
        if (elem.text == "length" and
            (isinstance(container, Request) or isinstance(container, Reply))):
            return LengthRef(container)
        return super().__new__(cls)
    def __init__(self, elem, container):
        self.target = container.get_field(elem.text)
    def xtruss_expression(self, structbase):
        return self.target.xtruss_fetcher(structbase)

class LengthRef(Expression):
    def __init__(self, container):
        pass
    def xtruss_expression(self, structbase):
        return "FETCH32(data, 4)"

def EnumRef(elem, container):
    e = container.header.get_type("enum", elem.attrib["ref"])
    return e.values[elem.text]

class UnOp(Expression):
    def __init__(self, elem, container):
        self.op = elem.attrib["op"]
        self.args = [Header.parse_expression(elem[0], container)]
    def xtruss_expression(self, structbase):
        return "(%s %s)" % (self.op,
                            xtruss_expression(self.args[0], structbase))

class PopCount(Expression):
    def __init__(self, elem, container):
        self.args = [Header.parse_expression(elem[0], container)]
    def xtruss_expression(self, structbase):
        return "popcount(%s)" % ( xtruss_expression(self.args[0], structbase),)

class CVariable(Expression):
    def __init__(self, var):
        self.var =  var
    def xtruss_expression(self, structbase):
        return self.var

class GenericExpression(Expression):
    def __init__(self, format, *args):
        self.format = format
        self.args = args
    def xtruss_expression(self, structbase):
        return (self.format %
                tuple([xtruss_expression(arg, structbase)
                       for arg in self.args]))

class ExpressionObjectifier(XMLObjectifier):
    tagmap = {
        "op": Op,
        "fieldref": FieldRef,
        "value": lambda elem, container: int(elem.text),
        "bit": lambda elem, container: 1 << int(elem.text),
        "enumref": EnumRef,
        "unop": UnOp,
        "popcount": PopCount,
    }

def closure(e, structbase):
    if hasattr(e, 'closure'):
        return e.closure(structbase)
    if isinstance(e, int):
        return e
    return e.closure(structbase)

def xtruss_expression(e, structbase):
    if hasattr(e, 'xtruss_expression'):
        return e.xtruss_expression(structbase)
    if isinstance(e, int):
        return str(e)
    return e.xtruss_expression(structbase)


def main():
    parser = argparse.ArgumentParser(description="xtruss code generator")
    parser.add_argument("source", nargs="+")
    parser.add_argument("-I", dest="include_dir", action='append', default=[])

    global cfg
    cfg = parser.parse_args()

    hdrs = [Header(s, [path.dirname(s)] + cfg.include_dir) for s in cfg.source]

    print("/* Make sure this is in KNOWNEXTENSIONS:")
    print(indent(4, "".join([hdr.xtruss_known_extension() for hdr in hdrs])))
    print(" */")
    print("")
    print("static int xtrgen_do_request(struct xlog *xl,")
    print("    const unsigned char *data, int len, struct request *req)")
    print("{")
    print("    switch (req->opcode) {")
    print(indent(6, "".join([hdr.xtruss_requestparser() for hdr in hdrs])), end=' ')
    print("      default: return 0;")
    print("    }")
    print("    return 1;")
    print("}")
    print("")
    print("static int xtrgen_do_reply(struct xlog *xl,")
    print("    const unsigned char *data, int len, struct request *req)")
    print("{")
    print("    switch (req->opcode) {")
    print(indent(6, "".join([hdr.xtruss_replyparser() for hdr in hdrs])), end=' ')
    print("      default: return 0;")
    print("    }")
    print("    return 1;")
    print("}")
    print("")
    print("static const char *xtrgen_translate_event(int eventtype)")
    print("{")
    print("    switch (eventtype & ~0x80) {")
    print(indent(6, "".join([hdr.xtruss_eventtranslator() for hdr in hdrs])), end=' ')
    print("      default:")
    print("        return NULL;")
    print("    }")
    print("}")
    print("")
    print("static void xtrgen_event(struct xlog *xl, int event,")
    print("    const unsigned char *data, int len, int pos)")
    print("{")
    print("    switch (event) {")
    print(indent(6, "".join([hdr.xtruss_eventparser() for hdr in hdrs])), end=' ')
    print("    }")
    print("}")
    print("")
    print("static const char *xtrgen_translate_error(int errcode)")
    print("{")
    print("    switch (errcode) {")
    print(indent(6, "".join([hdr.xtruss_errortranslator() for hdr in hdrs])), end=' ')
    print("      default:")
    print("        return NULL;")
    print("    }")
    print("}")
    print("")
    print("static void xtrgen_do_error(struct xlog *xl, int errcode,")
    print("    const unsigned char *data, int len)")
    print("{")
    print("    switch (errcode) {")
    print(indent(6, "".join([hdr.xtruss_errorparser() for hdr in hdrs])), end=' ')
    print("    }")
    print("}")

try:
    main()
#except Exception as e:
#    exit(e)
except KeyboardInterrupt:
    exit(129)
