From cf9be754aaaec049da9f2f802cf88c8058768771 Mon Sep 17 00:00:00 2001 From: Jan Vidar Krey Date: Mon, 22 Apr 2013 21:58:06 +0200 Subject: [PATCH] Rewrote the configuration file parser generator. Converted from Perl to Python for a better and cleaner object oriented design. --- src/core/config.pl | 289 ------------------------------------------ src/core/config.py | 255 +++++++++++++++++++++++++++++++++++++ src/core/gen_config.c | 8 +- src/core/gen_config.h | 8 +- 4 files changed, 269 insertions(+), 291 deletions(-) delete mode 100755 src/core/config.pl create mode 100755 src/core/config.py diff --git a/src/core/config.pl b/src/core/config.pl deleted file mode 100755 index cf88449..0000000 --- a/src/core/config.pl +++ /dev/null @@ -1,289 +0,0 @@ -#!/usr/bin/perl -w - -use strict; -use XML::DOM; - -sub write_c_header(@); -sub write_sql_dump(@); -sub get_data($); - -my $dump_to_sql = 0; - -# initialize parser and read the file -my $input = "./config.xml"; -my $parser = new XML::DOM::Parser; -my $tree = $parser->parsefile($input) || die "Unable to parse XML file."; - -# Get data -my $nodes = $tree->getElementsByTagName("option"); -my @options = (); -for (my $i = 0; $i < $nodes->getLength; $i++) -{ - my @data = get_data($nodes->item($i)); - push @options, \@data; -} - -write_c_header(@options); -write_sql_dump(@options) if ($dump_to_sql); - -my $config_defaults = "void config_defaults(struct hub_config* config)\n{\n"; -my $config_apply = "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"; -my $config_free = "void free_config(struct hub_config* config)\n{\n"; -my $config_dump = "void dump_config(struct hub_config* config, int ignore_defaults)\n{\n"; - -foreach my $option (@options) -{ - my ($type, $name, $default, $advanced, $short, $desc, $since, $example, $check, $ifdef) = @$option; - my $string = ($type =~ /(string|file|message)/); - my $min = undef; - my $max = undef; - my $regexp = undef; - - if (defined $check) - { - $min = $check->getAttribute("min"); - $max = $check->getAttribute("max"); - $regexp = $check->getAttribute("regexp"); - - $max = undef if ($max eq ""); - $min = undef if ($min eq ""); - $regexp = undef if ($regexp eq ""); - } - - $config_defaults .= "#ifdef $ifdef\n" if ($ifdef ne ""); - $config_defaults .= "\tconfig->$name = "; - $config_defaults .= "hub_strdup(\"" if ($string); - $config_defaults .= $default; - $config_defaults .= "\")" if ($string); - $config_defaults .= ";\n"; - $config_defaults .= "#endif /* $ifdef */\n" if ($ifdef ne ""); - - $config_apply .= "#ifdef $ifdef\n" if ($ifdef ne ""); - $config_apply .= "\tif (!strcmp(key, \"" . $name . "\"))\n\t{\n"; - - if ($type eq "int") - { - $config_apply .= "\t\tmin = $min;\n" if (defined $min); - $config_apply .= "\t\tmax = $max;\n" if (defined $max); - $config_apply .= "\t\tif (!apply_integer(key, data, &config->$name, "; - if (defined $min) { $config_apply .= "&min"; } else { $config_apply .= "0"; } - $config_apply .= ", "; - if (defined $max) { $config_apply .= "&max"; } else { $config_apply .= "0"; } - $config_apply .= "))\n"; - } - elsif ($type eq "boolean") - { - $config_apply .= "\t\tif (!apply_boolean(key, data, &config->$name))\n"; - } - elsif ($string) - { - $config_apply .="\t\tif (!apply_string(key, data, &config->$name, (char*) \"\"))\n"; - } - - $config_apply .= "\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"; - $config_apply .= "#endif /* $ifdef */\n" if ($ifdef ne ""); - $config_apply .= "\n"; - - if ($string) - { - $config_free .= "#ifdef $ifdef\n" if ($ifdef ne ""); - $config_free .= "\thub_free(config->" . $name . ");\n"; - $config_free .= "#endif /* $ifdef */\n" if ($ifdef ne ""); - $config_free .= "\n"; - } - - my $out = "%s"; - my $val = "config->$name"; - my $test = "config->$name != $default"; - - $out = "%d" if ($type eq "int"); - $val = "config->$name ? \"yes\" : \"no\"" if ($type eq "boolean"); - - if ($string) - { - $out = "\\\"%s\\\""; - $test = "strcmp(config->$name, \"$default\") != 0"; - } - - $config_dump .= "#ifdef $ifdef\n" if ($ifdef ne ""); - $config_dump .= "\tif (!ignore_defaults || $test)\n"; - $config_dump .= "\t\tfprintf(stdout, \"$name = $out\\n\", $val);\n"; - $config_dump .= "#endif /* $ifdef */\n" if ($ifdef ne ""); - $config_dump .= "\n"; -} - -$config_apply .= "\t/* Still here -- unknown directive */\n"; -$config_apply .= "\tLOG_ERROR(\"Unknown configuration directive: '%s'\", key);\n"; -$config_apply .= "\treturn -1;\n"; -$config_apply .= "}\n\n"; -$config_defaults .= "}\n\n"; -$config_free .= "}\n\n"; -$config_dump .= "}\n\n"; - -open GENIMPL, ">gen_config.c" || die "Unable to write source file"; -print GENIMPL "/* THIS FILE IS AUTOGENERATED - DO NOT CHANGE IT! */\n\n"; -print GENIMPL $config_defaults; -print GENIMPL $config_apply; -print GENIMPL $config_free; -print GENIMPL $config_dump; - - -sub get_data($) -{ - my $p = shift; - - my $short = ""; - my $example = ""; - my $description = ""; - my $since = ""; - my $ifdef = ""; - - $short = $p->getElementsByTagName("short")->item(0)->getFirstChild()->getData() if ($p->getElementsByTagName("short")->getLength()); - $since = $p->getElementsByTagName("since")->item(0)->getFirstChild()->getData() if ($p->getElementsByTagName("since")->getLength()); - $example = $p->getElementsByTagName("example")->item(0)->getFirstChild()->getData() if ($p->getElementsByTagName("example")->getLength()); - $description = $p->getElementsByTagName("description")->item(0)->getFirstChild()->getData() if ($p->getElementsByTagName("description")->getLength()); - my $check = $p->getElementsByTagName("check")->item(0); - $ifdef = $p->getElementsByTagName("ifdef")->item(0)->getFirstChild()->getData() if ($p->getElementsByTagName("ifdef")->getLength()); - - my @data = ( - $p->getAttribute("type"), - $p->getAttribute("name"), - $p->getAttribute("default"), - $p->getAttribute("advanced"), - $short, - $description, - $since, - $example, - $check, - $ifdef - ); - return @data; -} - -# Write header file -sub write_c_header(@) -{ - my @data = @_; - - open GENHEAD, ">gen_config.h" || die "Unable to write header file"; - print GENHEAD "/* THIS FILE IS AUTOGENERATED - DO NOT CHANGE IT! */\n\n"; - print GENHEAD "struct hub_config\n{\n"; - - foreach my $option (@data) - { - my ($type, $name, $default, $advanced, $short, $desc, $since, $example, $check, $ifdef) = @$option; - - my $string = ($type =~ /(string|file|message)/); - - print GENHEAD "#ifdef $ifdef\n" if ($ifdef ne ""); - - print GENHEAD "\t"; - print GENHEAD "int " if ($type eq "int"); - print GENHEAD "int " if ($type eq "boolean"); - print GENHEAD "char*" if ($string); - print GENHEAD " " . $name . ";"; - - my $comment = ""; - if ($type eq "message") - { - $comment = "\"" . $default . "\""; - } - elsif (defined $short && length $short > 0) - { - $comment = $short; - if (defined $default) - { - $comment .= " (default: "; - $comment .= "\"" if ($string); - $comment .= $default; - $comment .= "\"" if ($string); - $comment .= ")"; - } - } - - if (length $comment > 0) - { - my $pad = ""; - for (my $i = length $name; $i < 32; $i++) - { - $pad .= " "; - } - $comment = $pad . "/*<<< " . $comment . " */"; - } - print GENHEAD $comment . "\n"; - print GENHEAD "#endif /* $ifdef */\n" if ($ifdef ne ""); - } - - print GENHEAD "};\n\n"; -} - - -sub write_sql_dump(@) -{ - my @data = @_; - - # Write SQL dump code - open GENSQL, ">gen_config.sql" || die "Unable to write SQL dump"; - print GENSQL "START TRANSACTION;\n\n - DROP TABLE uhub_config IF EXISTS;\n\n - CREATE TABLE uhub_config ( - name VARCHAR(32) UNIQUE NOT NULL, - defaultValue TINYTEXT NOT NULL, - description LONGTEXT NOT NULL, - type TINYTEXT NOT NULL, - advanced BOOLEAN, - example LONGTEXT, - since TINYTEXT - );\n\n"; - - foreach my $option (@data) - { - my ($type, $name, $default, $advanced, $short, $desc, $since, $example, $check, $ifdef) = @$option; - - if ($type =~ /(string|file|message)/ ) - { - $default = "\\\"$default\\\""; - } - - $desc =~ s/\"/\\\"/g; - $type =~ s/^int$/integer/; - - my $stmt = "INSERT INTO uhub_config VALUES("; - $stmt .= "\"$name\", "; - $stmt .= "\"$default\", "; - $stmt .= "\"$desc\", "; - $stmt .= "\"$type\", "; - - if (defined $example) - { - my $example_str = $example; - $example_str =~ s/\\/\\\\/g; - $example_str =~ s/\"/\\\"/g; - $stmt .= "\"$example_str\", "; - } else { - $stmt .= "NULL, "; - } - - if (defined $since) { - $stmt .= "\"$since\", "; - } else { - $stmt .= "NULL, "; - } - - if (defined $advanced) { - $stmt .= "\"$advanced\""; - } else { - $stmt .= "NULL"; - } - - $stmt .= ");\n"; - - print GENSQL $stmt; - } - print GENSQL "\n\nCOMMIT;\n\n"; -} diff --git a/src/core/config.py b/src/core/config.py new file mode 100755 index 0000000..733e23e --- /dev/null +++ b/src/core/config.py @@ -0,0 +1,255 @@ +#!/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); + + diff --git a/src/core/gen_config.c b/src/core/gen_config.c index 063db97..8372a7c 100644 --- a/src/core/gen_config.c +++ b/src/core/gen_config.c @@ -1,4 +1,10 @@ -/* THIS FILE IS AUTOGENERATED - DO NOT CHANGE IT! */ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2013, Jan Vidar Krey + * + * THIS FILE IS AUTOGENERATED - DO NOT MODIFY + * Created 2013-04-22 21:57, by config.py + */ void config_defaults(struct hub_config* config) { diff --git a/src/core/gen_config.h b/src/core/gen_config.h index a6effe1..26ed499 100644 --- a/src/core/gen_config.h +++ b/src/core/gen_config.h @@ -1,4 +1,10 @@ -/* THIS FILE IS AUTOGENERATED - DO NOT CHANGE IT! */ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2013, Jan Vidar Krey + * + * THIS FILE IS AUTOGENERATED - DO NOT MODIFY + * Created 2013-04-22 21:57, by config.py + */ struct hub_config {