#!/usr/bin/env python """ uhub - A tiny ADC p2p connection hub Copyright (C) 2007-2013, Jan Vidar Krey """ from xml.dom import minidom, Node from datetime import datetime import argparse class OptionParseError(Exception): pass class Option(object): def _get(self, node, name): self.__dict__[name] = None if (node.getElementsByTagName(name)): self.__dict__[name] = node.getElementsByTagName(name)[0].firstChild.nodeValue def _attr(self, node, name, required = False): try: return node.attributes[name].value except Exception: pass if (required): raise OptionParseError("Option %s is required but not found!" % name) return None def __init__(self, node): self.otype = self._attr(node, 'type', True) # Verify that the type is known if not self.otype in ["int", "boolean", "string", "message", "file"]: raise OptionParseError("Option %s has unknown type" % self.name) self.name = self._attr(node, 'name', True) self.default = self._attr(node, 'default', True) self.advanced = self._attr(node, 'advanced', False) self.is_string = self.otype in ["string", "message", "file"] self._get(node, "short"); self._get(node, "description"); self._get(node, "syntax"); self._get(node, "since"); self._get(node, "example"); check = node.getElementsByTagName("check") if (check): check = node.getElementsByTagName("check")[0] self.check_min = self._attr(check, 'min', False) self.check_max = self._attr(check, 'max', False) self.check_regexp = self._attr(check, 'regexp', False) else: self.check_min = None self.check_max = None self.check_regexp = None def c_type(self): if self.otype == "boolean": return "int" elif self.is_string: return "char*" else: return self.otype def sql_type(self): if self.otype == "int": return "integer" return self.otype def c_comment(self): comment = "" if (self.otype == "message"): comment = self.formatted_default() elif len(self.short): comment = "%s (default: %s)" % (self.short, self.formatted_default()) return comment def formatted_default(self): if self.is_string: return "\"%s\"" % self.default return self.default class SourceGenerator(object): def __init__(self, filename, cppStyle = True): print ("Generating %s..." % filename) self.f = open(filename, 'w'); def write_header(self, Comment = True): if Comment: s = "/*\n * uhub - A tiny ADC p2p connection hub\n" s += " * Copyright (C) 2007-%s, Jan Vidar Krey\n *\n" % datetime.now().strftime("%Y") s += " * THIS FILE IS AUTOGENERATED - DO NOT MODIFY\n" s += " * Created %s, by config.py\n */\n\n" % datetime.now().strftime("%Y-%m-%d %H:%M") self.f.write(s) class CHeaderGenerator(SourceGenerator): def __init__(self, filename): super(CHeaderGenerator, self).__init__(filename) def _write_declaration(self, option): comment = ' ' * (32 - len(option.name)) + "/*<<< %s */" % option.c_comment() ptype = option.c_type() + (5 - len(option.c_type())) * ' ' self.f.write("\t%(type)s %(name)s;%(comment)s\n" % { "type": ptype, "name": option.name, "comment": comment}) def write(self, options): self.write_header() self.f.write("struct hub_config\n{\n") for option in options: self._write_declaration(option) self.f.write("};\n\n") class CSourceGenerator(SourceGenerator): def __init__(self, filename): super(CSourceGenerator, self).__init__(filename) def _write_default_impl(self, option): s = "\tconfig->%s = " % option.name if option.is_string: s += "hub_strdup(%s);\n" % option.formatted_default() else: s += option.formatted_default() + ";\n" self.f.write(s) def _write_apply_impl(self, option): s = "\tif (!strcmp(key, \"%s\"))\n\t{\n" % option.name if option.otype == "int": s_min = "0" s_max = "0" if (option.check_min): s += "\t\tmin = %s;\n" % option.check_min s_min = "&min" if (option.check_max): s += "\t\tmax = %s;\n" % option.check_max s_max = "&max" s+= "\t\tif (!apply_integer(key, data, &config->%s, %s, %s))\n" % (option.name, s_min, s_max) elif option.otype == "boolean": s += "\t\tif (!apply_boolean(key, data, &config->%s))\n" % option.name elif option.is_string: s += "\t\tif (!apply_string(key, data, &config->%s, (char*) \"\"))\n" % option.name s += "\t\t{\n\t\t\tLOG_ERROR(\"Configuration parse error on line %d\", line_count);\n\t\t\treturn -1;\n\t\t}\n\t\treturn 0;\n\t}\n\n" self.f.write(s) def _write_free_impl(self, option): if option.is_string: self.f.write("\thub_free(config->%s);\n\n" % option.name) def _write_dump_impl(self, option): s = "" fmt = "%s" val = "config->%s" % option.name test = "config->%s != %s" % (option.name, option.default) if (option.otype == "int"): fmt = "%d" elif (option.otype == "boolean"): val = "config->%s ? \"yes\" : \"no\"" % option.name elif (option.is_string): fmt = "\\\"%s\\\""; test = "strcmp(config->%s, %s) != 0" % (option.name, option.formatted_default()) s += "\tif (!ignore_defaults || %s)\n" % test; s += "\t\tfprintf(stdout, \"%s = %s\\n\", %s);\n\n" % (option.name, fmt, val) self.f.write(s) def write(self, options): self.write_header() self.f.write("void config_defaults(struct hub_config* config)\n{\n") for option in options: self._write_default_impl(option) self.f.write("}\n\n") self.f.write("static int apply_config(struct hub_config* config, char* key, char* data, int line_count)\n{\n\tint max = 0;\n\tint min = 0;\n\n") for option in options: self._write_apply_impl(option) self.f.write("\t/* Still here -- unknown directive */\n\tLOG_ERROR(\"Unknown configuration directive: '%s'\", key);\n\treturn -1;\n}\n\n") self.f.write("void free_config(struct hub_config* config)\n{\n") for option in options: self._write_free_impl(option) self.f.write("}\n\n") self.f.write("void dump_config(struct hub_config* config, int ignore_defaults)\n{\n") for option in options: self._write_dump_impl(option) self.f.write("}\n\n") class SqlWebsiteDocsGenerator(SourceGenerator): def __init__(self, filename, sqlite_support = False): self.sqlite_support = sqlite_support super(SqlWebsiteDocsGenerator, self).__init__(filename) def _sql_escape(self, s): if self.sqlite_support: return s.replace("\"", "\"\"") return s.replace("\"", "\\\"") def _write_or_null(self, s): if (not s or len(s) == 0): return "NULL" return "\"%s\"" % self._sql_escape(s) def write(self, options): self.write_header(False) table = "uhub_config" s = "" if not self.sqlite_support: s += "START TRANSACTION;\n\nDROP TABLE %(table)s IF EXISTS;" % { "table": table } s += "\n\nCREATE TABLE %(table)s (\n\tname VARCHAR(32) UNIQUE NOT NULL,\n\tdefaultValue TINYTEXT NOT NULL,\n\tdescription LONGTEXT NOT NULL,\n\ttype TINYTEXT NOT NULL,\n\tadvanced BOOLEAN,\n\texample LONGTEXT,\n\tsince TINYTEXT\n);\n\n" % { "table": table } self.f.write(s) for option in options: s = "INSERT INTO %(table)s VALUES(\"%(name)s\", \"%(default)s\", \"%(description)s\", \"%(type)s\", %(example)s, %(since)s, %(advanced)s);\n" % { "table": table, "name": self._sql_escape(option.name), "default": self._sql_escape(option.formatted_default()), "description": self._sql_escape(option.description), "type": option.sql_type(), "example": self._write_or_null(option.example), "since": self._write_or_null(option.since), "advanced": self._write_or_null(option.example), } self.f.write(s) if not self.sqlite_support: self.f.write("\n\nCOMMIT;\n\n") if __name__ == "__main__": # parser = argparse.ArgumentParser(description = "Configuration file parser and source generator") # parser.add_argument("--in", nargs=1, type=argparse.FileType('r'), default="config.xml", help="Input file (config.xml)", required = True) # parser.add_argument("--c-decl", nargs=1, type=argparse.FileType('w'), default="gen_config.h", help="Output file for C declarations (gen_config.h)") # parser.add_argument("--c-impl", nargs=1, type=argparse.FileType('w'), default="gen_config.c", help="Output file for C implementation (gen_config.c)") # parser.add_argument("--doc-sql", nargs=1, type=argparse.FileType('w'), help="Output file for SQL documentation") # args = parser.parse_args() xmldoc = minidom.parse("./config.xml") opt_tags = xmldoc.getElementsByTagName('option') options = [] for option in opt_tags: opt = Option(option) options.append(opt) header = CHeaderGenerator("./gen_config.h"); header.write(options); source = CSourceGenerator("./gen_config.c"); source.write(options); #sql = SqlWebsiteDocsGenerator("./gen_config.sql", True); #sql.write(options);