256 lines
8.5 KiB
Python
Executable File
256 lines
8.5 KiB
Python
Executable File
#!/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);
|
|
|
|
|