diff options
-rw-r--r-- | src/pythonmap.xml | 16 | ||||
-rw-r--r-- | src/setup.py | 3 | ||||
-rw-r--r-- | src/xmlpythonizer.c | 522 | ||||
-rw-r--r-- | src/xmlpythonizer.h | 50 |
4 files changed, 590 insertions, 1 deletions
diff --git a/src/pythonmap.xml b/src/pythonmap.xml new file mode 100644 index 0000000..a49a8ab --- /dev/null +++ b/src/pythonmap.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<dmidecode_fieldmap version="1"> + <Mapping name="BIOSLanguage"> + <Map keytype="string" key="/BIOSlanguage/@handle" valuetype="dict"> + <Map keytype="constant" key="data" valuetype="dict"> + <Map keytype="constant" key="Currently Installed Language" + valuetype="list:string" value="/BIOSlanguage/Installed/Language"/> + <Map keytype="constant" key="Installed Languages" + valuetype="integer" value="/BIOSlanguage/@installable_languages"/> + </Map> + <Map keytype="constant" key="dmi_type" valuetype="integer" value="/BIOSlanguage/@type"/> + <Map keytype="constant" key="dmi_handle" valuetype="string" value="/BIOSlanguage/@handle"/> + <Map keytype="constant" key="dmi_size" valuetype="integer" value="/BIOSlanguage/@size"/> + </Map> + </Mapping> +</dmidecode_fieldmap> diff --git a/src/setup.py b/src/setup.py index 6272e85..ec66c3f 100644 --- a/src/setup.py +++ b/src/setup.py @@ -16,7 +16,8 @@ setup( "src/util.c", "src/dmioem.c", "src/dmidecode.c", - "src/dmixml.c" + "src/dmixml.c", + "src/xmlpythonizer.c" ], include_dirs = [ "/usr/include/libxml2" ], library_dirs = [ "/home/nima/dev-room/projects/dmidecode" ], diff --git a/src/xmlpythonizer.c b/src/xmlpythonizer.c new file mode 100644 index 0000000..92e0d7f --- /dev/null +++ b/src/xmlpythonizer.c @@ -0,0 +1,522 @@ +/* Converts XML docs and nodes to Python dicts and lists by + * using an XML file which describes the Python dict layout + * + * Copyright 2009 David Sommerseth <davids@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * For the avoidance of doubt the "preferred form" of this code is one which + * is in an open unpatent encumbered format. Where cryptographic key signing + * forms part of the process of creating an executable the information + * including keys needed to generate an equivalently functional executable + * are deemed to be part of the source code. + */ + +#include <Python.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include <libxml/tree.h> +#include <libxml/xpath.h> + +#include "dmixml.h" +#include "xmlpythonizer.h" + +ptzMAP *ptzmap_Add(const ptzMAP *chain, + ptzTYPES ktyp, const char *key, + ptzTYPES vtyp, const char *value, + ptzMAP *child) +{ + ptzMAP *ret = NULL; + + assert( (ktyp == ptzCONST) || (ktyp == ptzSTR) || (ktyp == ptzINT) || (ktyp == ptzFLOAT) ); + assert( key != NULL ); + // Make sure that value and child are not used together + assert( ((value == NULL) && child != NULL) || ((value != NULL) && (child == NULL)) ); + + ret = (ptzMAP *) malloc(sizeof(ptzMAP)+2); + assert( ret != NULL ); + memset(ret, 0, sizeof(ptzMAP)+2); + + ret->type_key = ktyp; + ret->key = strdup(key); + + ret->type_value = vtyp; + if( value != NULL ) { + ret->value = strdup(value); + ret->child = NULL; + } else if( child != NULL ) { + ret->value = NULL; + ret->child = child; + } + + if( chain != NULL ) { + ret->next = (ptzMAP *) chain; + } + return ret; +}; + +#define ptzmap_Free(ptr) { ptzmap_Free_func(ptr); ptr = NULL; } +void ptzmap_Free_func(ptzMAP *ptr) +{ + if( ptr == NULL ) { + return; + } + + free(ptr->key); + ptr->key = NULL; + + if( ptr->value != NULL ) { + free(ptr->value); + ptr->value = NULL; + } + + if( ptr->child != NULL ) { + ptzmap_Free(ptr->child); + } + if( ptr->next != NULL ) { + ptzmap_Free(ptr->next); + } + free(ptr); +} + + +#if 1 +// DEBUG FUNCTIONS +static const char *ptzTYPESstr[] = { "ptzCONST", "ptzSTR", "ptzINT", "ptzFLOAT", + "ptzLIST_STR", "ptzLIST_INT", "ptzLIST_FLOAT", + "ptzDICT", NULL }; + +void indent(int lvl) +{ + int i = 0; + if( lvl == 0 ) { + return; + } + + for( i = 0; i < (lvl * 3); i++ ) { + printf(" "); + } +} + +#define ptzmap_Dump(ptr) { ptzmap_Dump_func(ptr, 0); } +void ptzmap_Dump_func(const ptzMAP *ptr, int level) +{ + if( ptr == NULL ) { + return; + } + + indent(level); printf("key type: (%i) %-13.13s - key: %s\n", + ptr->type_key, ptzTYPESstr[ptr->type_key], ptr->key); + indent(level); printf("value type: (%i) %-13.13s - value: %s\n", + ptr->type_value, ptzTYPESstr[ptr->type_value], ptr->value); + if( ptr->child != NULL ) { + indent(level); printf(" ** CHILD\n"); + ptzmap_Dump_func(ptr->child, level + 1); + indent(level); printf(" ** ---------\n"); + } + if( ptr->next != NULL ) { + printf("\n"); + ptzmap_Dump_func(ptr->next, level); + } +} +#endif // END OF DEBUG FUNCTIONS + +// +// Parser for the XML -> Python mapping XML file +// +// This mappipng XML file describes how the Python result +// should look like and where it should pick the data from +// when later on parsing the dmidecode XML data. +// + +// Valid key and value types for the mapping file +inline ptzTYPES _convert_maptype(const char *str) { + if( strcmp(str, "string") == 0 ) { + return ptzSTR; + } else if( strcmp(str, "constant") == 0 ) { + return ptzCONST; + } else if( strcmp(str, "integer") == 0 ) { + return ptzINT; + } else if( strcmp(str, "float") == 0 ) { + return ptzFLOAT; + } else if( strcmp(str, "list:string") == 0 ) { + return ptzLIST_STR; + } else if( strcmp(str, "list:integer") == 0 ) { + return ptzLIST_INT; + } else if( strcmp(str, "list:float") == 0 ) { + return ptzLIST_FLOAT; + } else if( strcmp(str, "dict") == 0 ) { + return ptzDICT; + } else { + fprintf(stderr, "Unknown field type: %s - defaulting to 'string'\n", str); + return ptzSTR; + } +} + +// Internal parser +ptzMAP *_do_dmimap_parsing(xmlNode *node) { + ptzMAP *retmap = NULL; + xmlNode *ptr_n = NULL, *map_n = NULL;; + + // Go to the next XML_ELEMENT_NODE + for( map_n = node; map_n != NULL; map_n = map_n->next ) { + if( map_n->type == XML_ELEMENT_NODE ) { + break; + } + } + if( map_n == NULL ) { + return NULL; + } + + // Go to the first <Map> node + if( xmlStrcmp(node->name, (xmlChar *) "Map") != 0 ) { + map_n = dmixml_FindNode(node, "Map"); + if( map_n == NULL ) { + return NULL; + } + } + + // Loop through it's children + for( ptr_n = map_n ; ptr_n != NULL; ptr_n = ptr_n->next ) { + ptzTYPES type_key, type_value; + char *key = NULL, *value = NULL; + + if( ptr_n->type != XML_ELEMENT_NODE ) { + continue; + } + + // Get the attributes defining key, keytype, value and valuetype + key = dmixml_GetAttrValue(ptr_n, "key"); + type_key = _convert_maptype(dmixml_GetAttrValue(ptr_n, "keytype")); + + value = dmixml_GetAttrValue(ptr_n, "value"); + type_value = _convert_maptype(dmixml_GetAttrValue(ptr_n, "valuetype")); + + if( type_value == ptzDICT ) { + // When value type is ptzDICT, traverse the children nodes + // - should contain another Map set instead of a value attribute + if( ptr_n->children == NULL ) { + continue; + } + // Recursion + retmap = ptzmap_Add(retmap, type_key, key, type_value, NULL, + _do_dmimap_parsing(ptr_n->children->next)); + } else { + // Append the value as a normal value when the + // value type is not a Python Dict + retmap = ptzmap_Add(retmap, type_key, key, type_value, value, NULL); + } + value = NULL; + key = NULL; + } + return retmap; +} + +// Main parser function for the mapping XML +ptzMAP *dmiMAP_ParseMappingXML(xmlDoc *xmlmap, const char *mapname) { + ptzMAP *map = NULL; + xmlNode *node = NULL; + + // Find the root tag and locate our mapping + node = xmlDocGetRootElement(xmlmap); + assert( node != NULL ); + + // Verify that the root node got the right name + if( (node == NULL) + || (xmlStrcmp(node->name, (xmlChar *) "dmidecode_fieldmap") != 0 )) { + fprintf(stderr, "Invalid XML-Python mapping file\n"); + return NULL; + } + + // Verify that it's of a version we support + if( strcmp(dmixml_GetAttrValue(node, "version"), "1") != 0 ) { + fprintf(stderr, "Unsupported XML-Python mapping file format\n"); + return NULL; + } + + // Find the <Mapping> section matching our request (mapname) + for( node = node->children->next; node != NULL; node = node->next ) { + if( xmlStrcmp(node->name, (xmlChar *) "Mapping") == 0) { + char *name = dmixml_GetAttrValue(node, "name"); + if( (name != NULL) && (strcmp(name, mapname) == 0) ) { + break; + } + } + } + + if( node == NULL ) { + fprintf(stderr, "No mapping for '%s' was found " + "in the XML-Python mapping file\n", mapname); + return NULL; + } + + // Start creating an internal map structure based on the mapping XML. + map = _do_dmimap_parsing(node); + + return map; +} + + +// +// Parser routines for converting XML data into Python structures +// + +inline PyObject *StringToPyObj(ptzTYPES type, const char *str) { + PyObject *value; + + switch( type ) { + case ptzINT: + case ptzLIST_INT: + value = PyInt_FromLong((str != NULL ? atoi(str) : 0)); + break; + + case ptzFLOAT: + case ptzLIST_FLOAT: + value = PyFloat_FromDouble((str != NULL ? atof(str) : 0)); + break; + + case ptzSTR: + case ptzLIST_STR: + value = PyString_FromString(str); + break; + + default: + fprintf(stderr, "Invalid type '%i' for value '%s'\n", type, str); + value = Py_None; + } + return value; +} + + +// Retrieve a value from the XML doc (XPath Context) based on a XPath query +xmlXPathObject *_get_xpath_values(xmlXPathContext *xpctx, const char *xpath) { + xmlChar *xp_xpr = NULL; + xmlXPathObject *xp_obj = NULL; + + if( xpath == NULL ) { + return NULL; + } + + xp_xpr = xmlCharStrdup(xpath); + xp_obj = xmlXPathEvalExpression(xp_xpr, xpctx); + assert( xp_obj != NULL ); + free(xp_xpr); + + if( (xp_obj->nodesetval == NULL) || (xp_obj->nodesetval->nodeNr == 0) ) { + xmlXPathFreeObject(xp_obj); + return NULL; + } + + return xp_obj; +} + +// Internal XML parser routine, which traverses the given mapping table, +// returning a Python structure accordingly to the map. +PyObject *_do_pythonizeXML(ptzMAP *in_map, xmlXPathContext *xpctx, int lvl) { + ptzMAP *map_p = NULL; + PyObject *retdata = NULL; + int i = 0; + + retdata = PyDict_New(); + for( map_p = in_map; map_p != NULL; map_p = map_p->next ) { + xmlXPathObject *xpobj = NULL; + char *key = NULL; + PyObject *value = NULL; + + // Get key value + switch( map_p->type_key ) { + case ptzCONST: + key = map_p->key; + break; + + case ptzSTR: + case ptzINT: + case ptzFLOAT: + xpobj = _get_xpath_values(xpctx, map_p->key); + if( xpobj != NULL ) { + key = dmixml_GetContent(xpobj->nodesetval->nodeTab[0]); + xmlXPathFreeObject(xpobj); + } + break; + default: + fprintf(stderr, "Unknown key type: %i\n", map_p->type_key); + return Py_None; + break; + } + + // Get 'value' value + switch( map_p->type_value ) { + case ptzCONST: + value = PyString_FromString(map_p->value); + break; + + case ptzSTR: + case ptzINT: + case ptzFLOAT: + xpobj = _get_xpath_values(xpctx, map_p->value); + if( xpobj != NULL ) { + value = StringToPyObj(map_p->type_value, + dmixml_GetContent(xpobj->nodesetval->nodeTab[0])); + xmlXPathFreeObject(xpobj); + } + break; + + case ptzLIST_STR: + case ptzLIST_INT: + case ptzLIST_FLOAT: + xpobj = _get_xpath_values(xpctx, map_p->value); + value = PyList_New(0); + if( xpobj != NULL ) { + for( i = 0; i < xpobj->nodesetval->nodeNr; i++ ) { + char *valstr = dmixml_GetContent(xpobj->nodesetval->nodeTab[i]); + PyList_Append(value, StringToPyObj(map_p->type_value, valstr)); + } + xmlXPathFreeObject(xpobj); + } + break; + + case ptzDICT: + // Traverse the children to get the value of this element + value = _do_pythonizeXML(map_p->child, xpctx, lvl+1); + Py_DECREF(value); + break; + + default: + fprintf(stderr, "Unknown value type: %i\n", map_p->type_value); + free(key); key = NULL; + return Py_None; + break; + } + + PyDict_SetItemString(retdata, key, value); + Py_DECREF(value); + } + Py_INCREF(retdata); + return retdata; +} + +// Convert a xmlDoc to a Python object, based on the given map +PyObject *pythonizeXMLdoc(ptzMAP *map, xmlDoc *xmldoc) +{ + xmlXPathContext *xp_ctx = NULL; + PyObject *retdata = NULL; + + // Prepare a XPath context for XPath queries + xp_ctx = xmlXPathNewContext(xmldoc); + assert( xp_ctx != NULL ); + + // Parse the XML and create Python data + retdata = _do_pythonizeXML(map, xp_ctx, 0); + + // Clean up and return data + xmlXPathFreeContext(xp_ctx); + + return retdata; +} + +// Convert a xmlNode to a Python object, based on the given map +PyObject *pythonizeXMLnode(ptzMAP *map, xmlNode *nodes) +{ + xmlDoc *xmldoc = NULL; + PyObject *retdata = NULL; + + // Create our own internal XML doc and + // copy over the input nodes to our internal doc. + // This is needed as the XPath parser in libxml2 + // only works with xmlDoc. + xmldoc = xmlNewDoc((xmlChar *) "1.0"); + assert( xmldoc != NULL ); + xmlDocSetRootElement(xmldoc, xmlCopyNode(nodes, 1)); + + // Parse the internal xmlDoc + retdata = pythonizeXMLdoc(map, xmldoc); + + // Clean up and return data + xmlFreeDoc(xmldoc); + return retdata; +} + + + +#if 0 +// Simple independent main function - only for debugging +int main() { + xmlDoc *doc = NULL; + ptzMAP *map = NULL; + + doc = xmlReadFile("pythonmap.xml", NULL, 0); + assert( doc != NULL ); + + map = dmiMAP_ParseMappingXML(doc, "BIOSLanguage"); + ptzmap_Dump(map); + + ptzmap_Free(map); + xmlFreeDoc(doc); + + return 0; +} +#endif + +#if 0 +// Simple test module for Python - only for debugging +PyObject* demo_xmlpy() +{ + xmlDoc *doc = NULL, *mapping_xml = NULL; + ptzMAP *mapping = NULL; + PyObject *ret = NULL; + + // Read the XML-Python mapping setup + mapping_xml = xmlReadFile("pythonmap.xml", NULL, 0); + assert( mapping_xml != NULL ); + + mapping = dmiMAP_ParseMappingXML(mapping_xml, "BIOSLanguage"); + assert( mapping != NULL ); + + // Read XML data from file + doc = xmlReadFile("test.xml", NULL, 0); + assert( doc != NULL ); + + // Create a PyObject out of the XML indata + ret = pythonizeXMLdoc(mapping, doc); + + // Clean up and return the data + ptzmap_Free(mapping); + xmlFreeDoc(doc); + xmlFreeDoc(mapping_xml); + + return ret; +} + + +static PyMethodDef DemoMethods[] = { + {"xmlpy", demo_xmlpy, METH_NOARGS, ""}, + {NULL, NULL, 0, NULL} +}; + +PyMODINIT_FUNC initxmlpythonizer(void) { + PyObject *module = + Py_InitModule3((char *)"xmlpythonizer", DemoMethods, + "XML to Python Proof-of-Concept Python Module"); + + PyObject *version = PyString_FromString("2.10"); + Py_INCREF(version); + PyModule_AddObject(module, "version", version); +} +#endif // Python test module + diff --git a/src/xmlpythonizer.h b/src/xmlpythonizer.h new file mode 100644 index 0000000..4882d81 --- /dev/null +++ b/src/xmlpythonizer.h @@ -0,0 +1,50 @@ +/* Converts XML docs and nodes to Python dicts and lists by + * using an XML file which describes the Python dict layout + * + * Copyright 2009 David Sommerseth <davids@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * For the avoidance of doubt the "preferred form" of this code is one which + * is in an open unpatent encumbered format. Where cryptographic key signing + * forms part of the process of creating an executable the information + * including keys needed to generate an equivalently functional executable + * are deemed to be part of the source code. + */ + +#ifndef _XMLPYTHONIZER_H +#define _XMLPYTHONIZER_H + +typedef enum ptzTYPES_e { ptzCONST, ptzSTR, ptzINT, ptzFLOAT, + ptzLIST_STR, ptzLIST_INT, ptzLIST_FLOAT, + ptzDICT } ptzTYPES; + +typedef struct ptzMAP_s { + ptzTYPES type_key; // Valid types: ptzCONST, ptzSTR, ptzINT, ptzFLOAT + char *key; // for ptzCONST key contains a static string, other types an XPath to XML data + ptzTYPES type_value; + char *value; // for ptzCONST key contains a static string, + // the rest of types, an XPath to XML data + struct ptzMAP_s *child; // Only used for type_value == pyDICT + struct ptzMAP_s *next; // Pointer chain + +} ptzMAP; + + +ptzMAP *dmiMAP_ParseMappingXML(xmlDoc *xmlmap, const char *mapname); +PyObject *pythonizeXMLdoc(ptzMAP *map, xmlDoc *xmldoc); +PyObject *pythonizeXMLnode(ptzMAP *map, xmlNode *nodes); + +#endif // _XMLPYTHONIZER_H |