--- /dev/null
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+#
+# Parse a comerr table into a series of throwable objects and a function that
+# throws the appropriate one for an abort code.
+#
+
+__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
+import keyword
+import time
+import os
+
+#
+# Convert the error table name to the base value. The name is a base-64
+# encoding, but without a 0.
+#
+def calc_base_from_name(name):
+ charmap = " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"
+ mod_base = 1000000
+ table_number = 0
+
+ for i in range(0, len(name)):
+ c = name[i]
+ v = charmap.find(c)
+ if v == -1:
+ print("Can't map name char ", c)
+ exit(1)
+ table_number *= 64
+ table_number += v
+ return table_number * 256
+
+
+class error:
+ def __init__(self, name, string):
+ self.source = None # Current source file
+ self.lineno = None # Current line number
+ self.symbol = name # Symbolic name
+ self.text = string # Display string
+ self.value = None # Abort code
+
+class error_table:
+ def __init__(self, name, errors):
+ self.name = name # Name of error table
+ self.base = calc_base_from_name(name) # Base error number
+ self.errors = errors # List of errors
+ val = self.base
+ for ec in errors:
+ ec.value = val
+ val += 1
+
+##########################################################################
+# #
+# Lexical analysis #
+# #
+##########################################################################
+import ply.lex as lex
+
+keywords = (
+ "error_table",
+ "ec",
+ "end",
+)
+
+# Required by lex. Each token also allows a function t_<token>.
+tokens = tuple([t.upper() for t in keywords]) + (
+ "ID", "STRING",
+ # ,
+ "COMMA", "NEWFILE"
+)
+
+# t_<name> functions are used by lex. They are called with t.value==<match
+# of rule in comment>, and t.type==<name>. They expect a return value
+# with attribute type=<token>
+
+# Tell lexer to ignore Whitespace.
+t_ignore = " \t"
+
+def t_NEWFILE(t):
+ r'__NEWFILE__ [^\n]*'
+ t.lexer.source = t.value[12:]
+ t.lexer.lineno = 0
+ return t
+
+def t_ID(t):
+ r'[A-Za-z][A-Za-z0-9_]*'
+ if t.value in keywords:
+ t.type = t.value.upper()
+ return t
+
+def t_STRING(t):
+ r'\"(\\.|[^"])*\"'
+ return t
+
+# Tokens
+t_COMMA = r','
+
+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'''
+ t[0] = t[2]
+
+def p_definition(t):
+ '''definition : ERROR_TABLE ID error_list END'''
+ t[0] = error_table(t[2], t[3])
+
+def p_error_list_1(t):
+ '''error_list : ec'''
+ t[0] = [ t[1] ]
+
+def p_error_list_2(t):
+ '''error_list : error_list ec'''
+ t[1].append(t[2])
+ t[0] = t[1]
+
+def p_ec(t):
+ '''ec : EC ID COMMA STRING'''
+ t[0] = error(t[2], t[4])
+
+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")
+
+# Parse the input data with yacc
+import ply.yacc as yacc
+yacc.yacc(debug=0)
+
+##########################################################################
+# #
+# Global Variables #
+# #
+##########################################################################
+
+error_occurred = False # Parsing of infile status
+
+##########################################################################
+# #
+# Preprocessor #
+# #
+##########################################################################
+def preprocess(data, filename):
+ lines = data.splitlines()
+
+ # Remove comments
+ for i in range(0, len(lines)):
+ l = lines[i]
+ p = l.find("#")
+ if p != -1:
+ lines[i] = l[:p]
+
+ lines.insert(0, "__NEWFILE__ {:s}".format(filename))
+ return "\n".join(lines)
+
+##########################################################################
+# #
+# Main Loop #
+# #
+##########################################################################
+def parse(infile, debug=False):
+ f = open(infile, encoding="utf-8")
+ data = f.read()
+ f.close()
+
+ data = preprocess(data, infile)
+
+ print("Parsing", infile);
+ global yacc
+ p = yacc.parse(data, debug=debug)
+
+ if error_occurred:
+ print
+ print("Error occurred, did not write output files")
+ return False
+ return p
+
+#
+# Render the result
+#
+def rxhdr(*va):
+ for i in va:
+ sys.stdout.write(str(i))
+
+def rxsrc(*va):
+ for i in va:
+ sys.stdout.write(str(i))
+
+def rxsrcf(*va):
+ sys.stdout.write(fmt.format(*va))
+
+def render(tables):
+ rxhdr("namespace comerr {\n")
+ for et in tables:
+ rxhdr("\n")
+ rxhdr("namespace comerr::", et.name, " {\n")
+ for ec in et.errors:
+ ecclass = ec.symbol
+ rxhdr("class ", ecclass, " : public rxrpc::rx_abort {\n");
+ rxhdr("\tpublic: ", ecclass, "(int ac, const char *op)\n")
+ rxhdr("\t: rx_abort(", ec.value, ", \"", ec.symbol, "\", ", ec.text, ", op) {}\n")
+ rxhdr("};\n")
+ rxhdr("} /* namespace comerr::", et.name, " */\n")
+ rxhdr("extern void comerr_throw_abort(int ac, const char *op=NULL);\n");
+ rxhdr("} /* namespace comerr */\n")
+
+ # Emit an abort throwing function
+ rxsrc("\n")
+ rxsrc("namespace comerr {\n")
+ rxsrc("\n")
+ rxsrc("void comerr_throw_abort(int ac, const char *op)\n");
+ rxsrc("{\n")
+ rxsrc("\tswitch (ac) {\n")
+ for et in tables:
+ for ec in et.errors:
+ ecclass = ec.symbol
+ rxsrc("\tcase ", ec.symbol, ":\tthrow ", et.name, "::", ecclass, "(op);\n")
+
+ rxsrc("\tdefault: rxrpc::throw_abort(ac, op);\n")
+ rxsrc("\t}\n")
+ rxsrc("}\n")
+ rxsrc("\n")
+ rxsrc("} /* end namespace comerr */\n")
+
+#
+# Section: main
+#
+def run(files, out_file_prefix):
+ tables = []
+
+ for f in files:
+ et = parse(f)
+ if not et:
+ break
+ tables.append(et)
+ render(tables)
+
+#
+# Drive the program if executed as a standalone process
+#
+if __name__ == "__main__":
+ if len(sys.argv) < 2:
+ print("Usage: {:s} <filename>*".format(sys.argv[0]))
+ sys.exit(1)
+
+ run(sys.argv[1:], "")