diff options
author | David Sommerseth <davids@redhat.com> | 2009-08-11 11:34:21 +0200 |
---|---|---|
committer | David Sommerseth <davids@redhat.com> | 2009-08-11 11:34:21 +0200 |
commit | eb08fd406370a81172d7fdd0663233a5f140b784 (patch) | |
tree | c50e19f2b873441672b20a7e339c176d9a4a7cbf /src/xmlpythonizer.c | |
parent | 39cbdfb56e80cabbd67754d8d77f781e14eaa3da (diff) | |
parent | 3165a97a06f891622b913714bc4f8ca54565f9cc (diff) | |
download | python-dmidecode-eb08fd406370a81172d7fdd0663233a5f140b784.tar.gz python-dmidecode-eb08fd406370a81172d7fdd0663233a5f140b784.tar.xz python-dmidecode-eb08fd406370a81172d7fdd0663233a5f140b784.zip |
Merge commit 'nima/xml'
Conflicts:
debian/changelog
Had the same changelog entry in both xml and master
branch, with a minor wording difference. Removed the
duplicate and merged in the changelog entries from the
XML branch
src/dmidecode.c
Merge process got confused by some functions which was not
changed. Removed the code coming from the master branch and
let the XML be the base.
src/setup-dbg.py
src/setup.py
In the XML branch, the version of the python-dmidecode is
now a function which retrieves the version number from
src/version.h. Merged in this feature to master as well.
Diffstat (limited to 'src/xmlpythonizer.c')
-rw-r--r-- | src/xmlpythonizer.c | 1183 |
1 files changed, 1183 insertions, 0 deletions
diff --git a/src/xmlpythonizer.c b/src/xmlpythonizer.c new file mode 100644 index 0000000..ba018eb --- /dev/null +++ b/src/xmlpythonizer.c @@ -0,0 +1,1183 @@ +/*. ******* coding:utf-8 AUTOHEADER START v1.1 ******* + *. vim: fileencoding=utf-8 syntax=c sw=8 ts=8 et + *. + *. © 2009 David Sommerseth <davids@redhat.com> + *. © 2007-2009 Nima Talebi <nima@autonomy.net.au> + *. + *. This file is part of Python DMI-Decode. + *. + *. Python DMI-Decode 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. + *. + *. Python DMI-Decode 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 Python DMI-Decode. If not, see <http://www.gnu.org/licenses/>. + *. + *. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + *. WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + *. MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + *. EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + *. INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + *. LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + *. PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + *. LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + *. OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + *. ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *. + *. ADAPTED M. STONE & T. PARKER DISCLAIMER: THIS SOFTWARE COULD RESULT IN INJURY + *. AND/OR DEATH, AND AS SUCH, IT SHOULD NOT BE BUILT, INSTALLED OR USED BY ANYONE. + *. + *. $AutoHeaderSerial::20090522 $ + *. ******* AUTOHEADER END v1.1 ******* */ + +/* Converts XML docs and nodes to Python dicts and lists by + * using an XML file which describes the Python dict layout + * + * 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. + */ + +/** + * @file xmlpythonizer.c + * @brief Generic parser for converting XML documents or XML nodes + * into Python Dictionaries + * @author David Sommerseth <davids@redhat.com> + * @author Nima Talebi <nima@autonomy.net.au> + */ + + +#include <Python.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include <libxml/tree.h> +#include <libxml/xpath.h> + +#include "util.h" +#include "dmixml.h" +#include "dmierror.h" +#include "xmlpythonizer.h" +#include "version.h" + + +/** + * This functions appends a new ptzMAP structure to an already existing chain + * @author David Sommerseth <davids@redhat.com> + * @param ptzMAP* Pointer to the chain the new ptzMAP is to be appended + * @param ptzMAP* Pointer to the new ptzMAP to be appended to the already existing ptzMAP + * @return ptzMAP* Pointer to the ptzMAP which includes the newly added ptzMAP + */ +ptzMAP *ptzmap_AppendMap(const ptzMAP *chain, ptzMAP *newmap) +{ + if( chain != NULL ) { + newmap->next = (ptzMAP *) chain; + } + return newmap; +} + + +/** + * This function creates a new ptzMAP mapping record. This defines the key/value relationship in + * the resulting Python Dictionaries. + * @author David Sommerseth <davids@redhat.com> + * @param ptzMAP* Pointer to the chain the new mapping will be appended + * @param char* XPath root of the given key and value XPath expressions. + * If NULL, the key and value XPath expressions must be absolute. + * @param ptzTYPES Type of the 'key' value + * @param const char* XPath expression or constant string for the 'key' value + * @param ptzTYPES Type of the 'value' value + * @param const char* XPath expression or constant string for the 'value' value + * @param ptzMAP* Used if the value type is of one of the ptzDICT types, contains a new + * mapping level for the children + * @return ptzMAP* Pointer to the ptzMAP which includes the newly added ptzMAP + */ +ptzMAP *ptzmap_Add(const ptzMAP *chain, char *rootp, + 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 ); + + ret = (ptzMAP *) malloc(sizeof(ptzMAP)+2); + assert( ret != NULL ); + memset(ret, 0, sizeof(ptzMAP)+2); + + if( rootp != NULL ) { + ret->rootpath = strdup(rootp); + } + + ret->type_key = ktyp; + ret->key = strdup(key); + + ret->type_value = vtyp; + if( value != NULL ) { + ret->value = strdup(value); + } + + if( child != NULL ) { + ret->child = child; + } + + return ptzmap_AppendMap(chain, ret); +}; + + +/** + * This functions sets an ptzLIST typed map entry as a fixed list + * @author David Sommerseth <davids@redhat.com> + * @param ptzMAP* Pointer to the ptzMAP elemnt to be updated + * @param const char* Attribute name of the XML node of the 'key' to use as the list index + * @param int Defines the size of the list + */ +void ptzmap_SetFixedList(ptzMAP *map_p, const char *index, int size) +{ + assert( map_p != NULL ); + + switch( map_p->type_value ) { + case ptzLIST_STR: + case ptzLIST_INT: + case ptzLIST_FLOAT: + case ptzLIST_BOOL: + map_p->list_index = strdup(index); + map_p->fixed_list_size = size; + break; + + default: + break; + } +} + + +/** + * This functions frees up a complete pointer chain. This is normally called via #define ptzmap_Free() + * @author David Sommerseth <davids@redhat.com> + * @param ptzMAP* Pointer to the ptzMAP to free + */ +void ptzmap_Free_func(ptzMAP *ptr) +{ + if( ptr == NULL ) { + return; + } + + if( ptr->rootpath != NULL ) { + free(ptr->rootpath); + ptr->rootpath = NULL; + } + + if( ptr->list_index != NULL ) { + free(ptr->list_index); + ptr->list_index = NULL; + } + + if( ptr->emptyValue != NULL ) { + free(ptr->emptyValue); + ptr->emptyValue = NULL; + } + + 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 0 +// DEBUG FUNCTIONS +static const char *ptzTYPESstr[] = { "ptzCONST", "ptzSTR", "ptzINT", "ptzFLOAT", "ptzBOOL", + "ptzLIST_STR", "ptzLIST_INT", "ptzLIST_FLOAT", "ptzLIST_BOOL", + "ptzDICT", "ptzLIST_DICT", 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; + } + + if( ptr->rootpath != NULL ) { + indent(level); printf("root path: %s\n", ptr->rootpath); + } + 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 %s\n", + ptr->type_value, ptzTYPESstr[ptr->type_value], ptr->value, + (ptr->emptyIsNone ? "(EmptyIsNone)": "")); + if( ptr->list_index != NULL ) { + indent(level); + printf("List index: %s - Fixed size: %i\n", + ptr->list_index, ptr->fixed_list_size); + } + 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 + + +/** + * This functions converts a string to valid ptzTYPES values. This is used when parsing the XML mapping nodes + * @author David Sommerseth <davids@redhat.com> + * @param const char* String value containing the key/value type + * @return ptzTYPES The type value + */ +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, "boolean") == 0 ) { + return ptzBOOL; + } 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, "list:boolean") == 0 ) { + return ptzLIST_BOOL; + } else if( strcmp(str, "dict") == 0 ) { + return ptzDICT; + } else if( strcmp(str, "list:dict") == 0 ) { + return ptzLIST_DICT; + } else { + fprintf(stderr, "Unknown field type: %s - defaulting to 'constant'\n", str); + return ptzCONST; + } +} + + +/** + * This functions is the internal parser - SubMapper (Individual Types of a Group) + * @author David Sommerseth <davids@redhat.com> + * @param xmlNode* Node of the starting point for the parsing + * @return ptzMAP* The ptzMAP version of the XML definition + */ +ptzMAP *_do_dmimap_parsing_typeid(xmlNode *node) { + ptzMAP *retmap = NULL; + xmlNode *ptr_n = NULL, *map_n = NULL;; + + // Go to the next XML_ELEMENT_NODE + foreach_xmlnode(node, map_n) { + if( map_n->type == XML_ELEMENT_NODE ) { + break; + } + } + if( map_n == NULL ) { + PyReturnError(PyExc_NameError, "No mapping nodes were found"); + } + + // Go to the first <Map> node + if( xmlStrcmp(node->name, (xmlChar *) "Map") != 0 ) { + map_n = dmixml_FindNode(node, "Map"); + if( map_n == NULL ) { + // If we don't find a <Map> node, we just exit now. + // Other checks will raise an exception if needed. + return NULL; + } + } + + // Loop through it's children + foreach_xmlnode(map_n, ptr_n) { + ptzTYPES type_key, type_value; + char *key = NULL, *value = NULL; + char *rootpath = NULL; + char *listidx = NULL; + int fixedsize = 0; + 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")); + + rootpath = dmixml_GetAttrValue(ptr_n, "rootpath"); + + listidx = dmixml_GetAttrValue(ptr_n, "index_attr"); + if( listidx != NULL ) { + char *fsz = dmixml_GetAttrValue(ptr_n, "fixedsize"); + fixedsize = (fsz != NULL ? atoi(fsz) : 0); + } + + if( (type_value == ptzDICT) || (type_value == ptzLIST_DICT) ) { + // 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, rootpath, type_key, key, type_value, + (type_value == ptzLIST_DICT ? value : NULL), + _do_dmimap_parsing_typeid(ptr_n->children->next)); + } else { + char *tmpstr = NULL; + + // Append the value as a normal value when the + // value type is not a Python Dict + retmap = ptzmap_Add(retmap, rootpath, type_key, key, type_value, value, NULL); + + // Set emptyIsNone flag + if( (tmpstr = dmixml_GetAttrValue(ptr_n, "emptyIsNone")) != NULL ) { + switch( retmap->type_value ) { + case ptzSTR: + case ptzINT: + case ptzFLOAT: + case ptzBOOL: + case ptzLIST_STR: + case ptzLIST_INT: + case ptzLIST_FLOAT: + case ptzLIST_BOOL: + retmap->emptyIsNone = (tmpstr[0] == '1' ? 1 : 0); + break; + default: + break; + } + } + if( (tmpstr = dmixml_GetAttrValue(ptr_n, "emptyValue")) != NULL ) { + retmap->emptyValue = strdup(tmpstr); + } + } + + if( (retmap != NULL) && (listidx != NULL) && (fixedsize > 0) ) { + ptzmap_SetFixedList(retmap, listidx, fixedsize); + } + + value = NULL; + key = NULL; + } + + return retmap; +} + +/** + * This functions validates and retrieves the root node of the dmidecode_mapping XML node from an XML document + * @author David Sommerseth <davids@redhat.com> + * @param xmlDoc* XML mapping document pointer + * @return xmlNode* The root xmlNode of a valid XML mapping document. On invalid document NULL is returned. + */ +xmlNode *dmiMAP_GetRootElement(xmlDoc *mapdoc) { + xmlNode *rootnode = NULL; + + // Find the root tag and locate our mapping + rootnode = xmlDocGetRootElement(mapdoc); + assert( rootnode != NULL ); + + // Verify that the root node got the right name + if( (rootnode == NULL) + || (xmlStrcmp(rootnode->name, (xmlChar *) "dmidecode_mapping") != 0 )) { + PyReturnError(PyExc_IOError, "Invalid XML-Python mapping file. " + "Root node is not 'dmidecode_mapping'"); + } + + // Verify that it's of a version we support + if( strcmp(dmixml_GetAttrValue(rootnode, "version"), "1") != 0 ) { + PyReturnError(PyExc_RuntimeError, "Unsupported XML-Python mapping file format. " + "Only version 1 is supported"); + } + return rootnode; +} + + +/** + * Internal function which looks up the given Type ID among TypeMap nodes and and parses + * the found XML nodes into a ptzMAP + * @author David Sommerseth <davids@redhat.com> + * @param xmlNode* The node where the TypeMapping tags are found + * @param const char* The typeid to parse to a ptzMAP + * @return ptzMAP* The parsed result of the XML nodes + */ +ptzMAP *_dmimap_parse_mapping_node_typeid(xmlNode *mapnode, const char *typeid) { + xmlNode *node = NULL; + + assert( mapnode != NULL); + + // Find the <TypeMap> tag with our type ID + node = dmixml_FindNodeByAttr_NoCase(mapnode, "TypeMap", "id", typeid); + if( node == NULL ) { + // No exception handling possible here, as we don't return PyObject + fprintf(stderr,"** WARNING: Could not find any XML->Python " + "mapping for type ID '%s'", typeid); + return NULL; + } + // Create an internal map structure and return this structure + return _do_dmimap_parsing_typeid(node); +} + + +/** + * Exported function for parsing a XML mapping document for a given Type ID to a ptzMAP + * @author David Sommerseth <davids@redhat.com> + * @param xmlDoc* Pointer to the XML mapping document + * @param const char* The Type ID to create the map for + * @return ptzMAP* The parsed XML containing as a ptzMAP + */ +ptzMAP *dmiMAP_ParseMappingXML_TypeID(xmlDoc *xmlmap, int typeid) { + xmlNode *node = NULL; + char typeid_s[16]; + + node = dmiMAP_GetRootElement(xmlmap); + if( node == NULL ) { + PyReturnError(PyExc_RuntimeError, "Could not locate root XML node for mapping file"); + } + + memset(&typeid_s, 0, 16); + snprintf(typeid_s, 14, "0x%02X", typeid); + + // Find the <TypeMapping> section + node = dmixml_FindNode(node, "TypeMapping"); + assert( node != NULL ); + return _dmimap_parse_mapping_node_typeid(node, typeid_s); +} + + +/** + * Internal parser for GroupMapping (group of types). Converts a given GroupMapping to a ptzMAP + * from a XML node set + * @author Nima Talebi <nima@autonomy.net.au> + * @author David Sommerseth <davids@redhat.com> + * @param xmlNode* The source XML nodes of what to parse to a ptzMAP + * @param xmlDoc* A pointer to the source map, used for further parsing of each type defined in the GroupMapping + * @return ptzMAP* The resulting ptzMAP of the parsed xmlNode group mapping + */ +ptzMAP *_do_dmimap_parsing_group(xmlNode *node, xmlDoc *xmlmap) { + ptzMAP *retmap = NULL; + xmlNode *ptr_n = NULL, *map_n = NULL, *typemap = NULL; + char *type_id; + + // Go to the next XML_ELEMENT_NODE + foreach_xmlnode(node, map_n) { + if( map_n->type == XML_ELEMENT_NODE ) { + break; + } + } + if( map_n == NULL ) { + PyReturnError(PyExc_RuntimeError, "Could not find any valid XML nodes"); + } + + // Check that our "root" node is as expected + if( xmlStrcmp(node->name, (xmlChar *) "Mapping") != 0 ) { + PyReturnError(PyExc_NameError, "Expected to find <Mapping> node"); + } + + // Go to the first <TypeMap> node + map_n = dmixml_FindNode(node, "TypeMap"); + if( map_n == NULL ) { + PyReturnError(PyExc_NameError, "Could not locate any <TypeMap> nodes"); + } + + // Get the root element of the <TypeMapping> tag, needed for further parsing + typemap = dmixml_FindNode(xmlDocGetRootElement(xmlmap), "TypeMapping"); + if( typemap == NULL ) { + PyReturnError(PyExc_NameError, "Could not locate the <TypeMapping> node"); + } + + // Loop through it's children + foreach_xmlnode(map_n, ptr_n) { + // Validate if we have the right node name + if( xmlStrcmp(ptr_n->name, (xmlChar *) "TypeMap") != 0 ) { + continue; // Skip unexpected tag names + } + + // Make sure that we have an id attribute before trying to locate that in th + if( (type_id = dmixml_GetAttrValue(ptr_n, "id")) != NULL) { + ptzMAP *map = NULL; + + map = _dmimap_parse_mapping_node_typeid(typemap, type_id); + if( map ) { + retmap = ptzmap_AppendMap(retmap, map); + } + } + } + return retmap; +} + + +/** + * Exported function which parses a given GroupMapping (consisting of + * one or more TypeMaps) into a ptzMAP + * @author David Sommerseth <davids@redhat.com> + * @param xmlDoc* Pointer to the XML document holding the mapping + * @param const char* Defines which group mapping to parse to a ptzMAP + * @return ptzMAP* The parsed XML mapping in a ptzMAP + */ +ptzMAP *dmiMAP_ParseMappingXML_GroupName(xmlDoc *xmlmap, const char *mapname) { + xmlNode *node = NULL; + + // Validate the XML mapping document and get the root element + node = dmiMAP_GetRootElement(xmlmap); + if( node == NULL ) { + PyReturnError(PyExc_RuntimeError, "No valid mapping XML recieved"); + } + + // Find the <GroupMapping> section + node = dmixml_FindNode(node, "GroupMapping"); + if( node == NULL ) { + PyReturnError(PyExc_NameError, "Could not find the <GroupMapping> node"); + } + + // Find the <Mapping> section matching our request (mapname) + node = dmixml_FindNodeByAttr(node, "Mapping", "name", mapname); + if( node == NULL ) { + PyReturnError(PyExc_NameError, "No group mapping for '%s' was found " + "in the XML-Python mapping file", mapname); + } + + // Create an internal map structure and return this structure + return _do_dmimap_parsing_group(node, xmlmap); +} + + +/** + * Internal function for converting a given mapped value to the appropriate Python data type + * @author David Sommerseth <davids@redhat.com> + * @param ptzMAP* Pointer to the current mapping entry being parsed + * @param const char * String which contains the value to be converted to a Python value + * @return PyObject * The converted value as a Python object + */ +inline PyObject *StringToPyObj(ptzMAP *val_m, const char *instr) { + PyObject *value; + const char *workstr = NULL; + + if( instr == NULL ) { + return Py_None; + } + + if( (val_m->emptyIsNone == 1) || (val_m->emptyValue != NULL) ) { + char *cp = strdup(instr); + char *cp_p = NULL; + assert( cp != NULL ); + + // Trim the string for trailing spaces + cp_p = cp + strlen(cp) - 1; + while( (cp_p >= cp) && (*cp_p == ' ') ) { + *cp_p = 0; + cp_p--; + } + + // If our copy pointer is the same + // or less than the starting point, + // there is no data here + if( cp_p <= cp ) { + free(cp); + if( val_m->emptyIsNone == 1 ) { + return Py_None; + } + if( val_m->emptyValue != NULL ) { + workstr = (const char *)val_m->emptyValue; + } + } else { + free(cp); + } + } + + if( workstr == NULL ) { + workstr = instr; + } + + + switch( val_m->type_value ) { + case ptzINT: + case ptzLIST_INT: + value = PyInt_FromLong(atoi(workstr)); + break; + + case ptzFLOAT: + case ptzLIST_FLOAT: + value = PyFloat_FromDouble(atof(workstr)); + break; + + case ptzBOOL: + case ptzLIST_BOOL: + value = PyBool_FromLong((atoi(workstr) == 1 ? 1:0)); + break; + + case ptzSTR: + case ptzLIST_STR: + value = PyString_FromString(workstr); + break; + + default: + fprintf(stderr, "Invalid type '%i' for value '%s'\n", val_m->type_value, instr); + value = Py_None; + } + return value; +} + + +/** + * Retrieves a value from the data XML doc (via XPath Context) based on a XPath query + * @author David Sommerseth <davids@redhat.com> + * @param xmlXPathContext* Pointer to the XPath context holding the source data + * @param const char* The XPath expression where to find the data + * @return xmlXPathObject* If data is found, it is returned in an XPath object for further processing + */ + +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); + + return xp_obj; +} + + +/** + * Retrieves the value which is to be used as the key value in a Python dictionary. + * @author David Sommerseth <davids@redhat.com> + * @param char* Pointer to the return buffer for the value + * @param size_t Size of the return buffer + * @param ptzMAP* Pointer to the current mapping entry which is being parsed + * @param xmlXPathContext* Pointer to the XPath context containing the source data + * @param int Defines which of the XPath results to use, if more is found + * @returns char* Returns a pointer to the return buffer (parameter 1) if key value + * is found, or NULL if not found + */ +char *_get_key_value(char *key, size_t buflen, ptzMAP *map_p, xmlXPathContext *xpctx, int idx) { + xmlXPathObject *xpobj = NULL; + + memset(key, 0, buflen); + + switch( map_p->type_key ) { + case ptzCONST: + strncpy(key, map_p->key, buflen-1); + break; + + case ptzSTR: + case ptzINT: + case ptzFLOAT: + xpobj = _get_xpath_values(xpctx, map_p->key); + if( xpobj == NULL ) { + return NULL; + } + if( dmixml_GetXPathContent(key, buflen, xpobj, idx) == NULL ) { + xmlXPathFreeObject(xpobj); + return NULL; + } + xmlXPathFreeObject(xpobj); + break; + + default: + fprintf(stderr, "Unknown key type: %i\n", map_p->type_key); + return NULL; + } + // We consider to have a key, if the first byte is a readable + // character (usually starting at 0x20/32d) + return ((key != NULL) && (strlen(key) > 0) ? key : NULL) ; +} + + +/** + * Simple define to properly add a key/value pair to a Python dictionary + * @author David Sommerseth <davids@redhat.com> + * @param PyObject* Pointer to the Python dictionary to be updated + * @param const char* String containing the key value + * @param PyObject* Pointer to the Python value + */ + +#define PyADD_DICT_VALUE(p, k, v) { \ + PyDict_SetItemString(p, k, v); \ + if( v != Py_None ) { \ + Py_DECREF(v); \ + } \ + } + + +/** + * Internal function for adding a XPath result to the resulting Python dictionary + * @author David Sommerseth <davids@redhat.com> + * @param PyObject* Pointer to the resulting Python dictionary + * @param xmlXPathContext* Pointer to the XPath context containing the source data + * (used for retrieving the key value) + * @param ptzMAP* Pointer to the current mapping entry being parsed + * @param xmlXPathObject* Pointer to XPath object containing the data value(s) for the dictionary + */ +inline void _add_xpath_result(PyObject *pydat, xmlXPathContext *xpctx, ptzMAP *map_p, xmlXPathObject *value) { + int i = 0; + char *key = NULL; + char *val = NULL; + + assert( pydat != NULL && value != NULL ); + + key = (char *) malloc(258); + assert( key != NULL ); + + val = (char *) malloc(4098); + assert( val != NULL ); + + switch( value->type ) { + case XPATH_NODESET: + if( value->nodesetval == NULL ) { + break; + } + if( value->nodesetval->nodeNr == 0 ) { + if( _get_key_value(key, 256, map_p, xpctx, 0) != NULL ) { + PyADD_DICT_VALUE(pydat, key, Py_None); + } + } else { + for( i = 0; i < value->nodesetval->nodeNr; i++ ) { + if( _get_key_value(key, 256, map_p, xpctx, i) != NULL ) { + dmixml_GetXPathContent(val, 4097, value, i); + PyADD_DICT_VALUE(pydat, key, StringToPyObj(map_p, val)); + } + } + } + break; + default: + if( _get_key_value(key, 256, map_p, xpctx, 0) != NULL ) { + dmixml_GetXPathContent(val, 4097, value, 0); + PyADD_DICT_VALUE(pydat, key, StringToPyObj(map_p, val)); + } + break; + } + free(key); + free(val); +} + + +/** + * Internal XML parser routine, which traverses the given mapping table, + * returning a Python structure accordingly to the map. Data for the Python dictionary is + * take from the input XML node. + * @author David Sommerseth <davids@redhat.com> + * @param PyObject* Pointer to the Python dictionary of the result + * @param ptzMAP* Pointer to the starting point for the further parsing + * @param xmlNode* Pointer to the XML node containing the source data + * @param int For debug purpose only, to keep track of which element being parsed + * @return PyObject* Pointer to the input Python dictionary + */ +PyObject *_deep_pythonize(PyObject *retdata, ptzMAP *map_p, xmlNode *data_n, int elmtid) { + char *key = NULL; + xmlXPathContext *xpctx = NULL; + xmlDoc *xpdoc = NULL; + xmlXPathObject *xpo = NULL; + PyObject *value = NULL; + int i; + + xpdoc = xmlNewDoc((xmlChar *) "1.0"); + assert( xpdoc != NULL ); + xmlDocSetRootElement(xpdoc, xmlCopyNode(data_n, 1)); + + xpctx = xmlXPathNewContext(xpdoc); + assert( xpctx != NULL ); + xpctx->node = data_n; + + key = (char *) malloc(258); + assert( key != NULL ); + + // Extract value + switch( map_p->type_value ) { + case ptzCONST: + if( _get_key_value(key, 256, map_p, xpctx, 0) != NULL ) { + value = PyString_FromString(map_p->value); + PyADD_DICT_VALUE(retdata, key, value); + } else { + PyReturnError(PyExc_ValueError, "Could not get key value: %s [%i] (Defining key: %s)", + map_p->rootpath, elmtid, map_p->key); + } + break; + + case ptzSTR: + case ptzINT: + case ptzFLOAT: + case ptzBOOL: + xpo = _get_xpath_values(xpctx, map_p->value); + if( xpo != NULL ) { + _add_xpath_result(retdata, xpctx, map_p, xpo); + xmlXPathFreeObject(xpo); + } + break; + + case ptzLIST_STR: + case ptzLIST_INT: + case ptzLIST_FLOAT: + case ptzLIST_BOOL: + xpo = _get_xpath_values(xpctx, map_p->value); + if( xpo != NULL ) { + if( _get_key_value(key, 256, map_p, xpctx, 0) != NULL ) { + if( (xpo->nodesetval != NULL) && (xpo->nodesetval->nodeNr > 0) ) { + value = PyList_New(0); + + // If we're working on a fixed list, create one which contains + // only Py_None objects. Otherwise the list will be filled with + // <nil> elements. + if( map_p->fixed_list_size > 0 ) { + for( i = 0; i < map_p->fixed_list_size; i++ ) { + PyList_Append(value, Py_None); + } + } + + for( i = 0; i < xpo->nodesetval->nodeNr; i++ ) { + char *valstr = NULL; + valstr = (char *) malloc(4098); + dmixml_GetXPathContent(valstr, 4097, xpo, i); + + // If we have a fixed list and we have a index value for the list + if( (map_p->fixed_list_size > 0) && (map_p->list_index != NULL) ) { + char *idx = NULL; + + idx = dmixml_GetAttrValue(xpo->nodesetval->nodeTab[i], + map_p->list_index); + if( idx != NULL ) { + PyList_SetItem(value, atoi(idx)-1, + StringToPyObj(map_p, valstr) + ); + } + } else { + // No list index - append the value + PyList_Append(value, StringToPyObj(map_p, valstr)); + } + free(valstr); + } + } else { + value = Py_None; + } + PyADD_DICT_VALUE(retdata, key, value); + xmlXPathFreeObject(xpo); + } else { + PyReturnError(PyExc_ValueError, "Could not get key value: " + "%s [%i] (Defining key: %s)", + map_p->rootpath, elmtid, map_p->key); + } + } + break; + + case ptzDICT: + // Traverse children nodes + if( map_p->child == NULL ) { + break; + } + if( _get_key_value(key, 256, map_p, xpctx, 0) == NULL ) { + PyReturnError(PyExc_ValueError, + "Could not get key value: %s [%i] (Defining key: %s)", + map_p->rootpath, elmtid, map_p->key); + } + // Use recursion when procession child elements + value = pythonizeXMLnode(map_p->child, data_n); + PyADD_DICT_VALUE(retdata, key, (value != NULL ? value : Py_None)); + break; + + case ptzLIST_DICT: // List of dict arrays + if( map_p->child == NULL ) { + break; + } + if( _get_key_value(key, 256, map_p, xpctx, 0) == NULL ) { + PyReturnError(PyExc_ValueError, + "Could not get key value: %s [%i] (Defining key: %s)", + map_p->rootpath, elmtid, map_p->key); + } + + // Iterate all nodes which is found in the 'value' XPath + xpo = _get_xpath_values(xpctx, map_p->value); + if( (xpo == NULL) || (xpo->nodesetval == NULL) || (xpo->nodesetval->nodeNr == 0) ) { + if( xpo != NULL ) { + xmlXPathFreeObject(xpo); + } + PyReturnError(PyExc_ValueError, + "Could not get key value: %s [%i] (Defining key: %s)", + map_p->rootpath, elmtid, map_p->key); + } + + // Prepare a data list + value = PyList_New(0); + + // If we're working on a fixed list, create one which contains + // only Py_None objects. Otherwise the list will be filled with + // <nil> elements. + if( map_p->fixed_list_size > 0 ) { + for( i = 0; i < map_p->fixed_list_size; i++ ) { + PyList_Append(value, Py_None); + } + } + + for( i = 0; i < xpo->nodesetval->nodeNr; i++ ) { + PyObject *dataset = NULL; + + dataset = pythonizeXMLnode(map_p->child, xpo->nodesetval->nodeTab[i]); + if( dataset != NULL ) { + // If we have a fixed list and we have a index value for the list + if( (map_p->fixed_list_size > 0) && (map_p->list_index != NULL) ) { + char *idx = NULL; + idx = dmixml_GetAttrValue(xpo->nodesetval->nodeTab[i], + map_p->list_index); + if( idx != NULL ) { + PyList_SetItem(value, atoi(idx)-1, dataset); + } + } else { + // No list index - append the value + PyList_Append(value, dataset); + } + } else { + // If NULL, something is wrong - exception is already set. + return NULL; + } + } + PyADD_DICT_VALUE(retdata, key, value); + xmlXPathFreeObject(xpo); + break; + + default: + fprintf(stderr, "Unknown value type: %i\n", map_p->type_value); + break; + } + + free(key); + xmlXPathFreeContext(xpctx); + xmlFreeDoc(xpdoc); + return retdata; +} + + +/** + * Exported function, for parsing a XML node to a Python dictionary based on the given ptzMAP + * @author David Sommerseth <davids@redhat.com> + * @param ptzMAP* The map descriping the resulting Python dictionary + * @param xmlNode* XML node pointer to the source data to be used for populating the Python dictionary + */ +PyObject *pythonizeXMLnode(ptzMAP *in_map, xmlNode *data_n) { + xmlXPathContext *xpctx = NULL; + xmlDoc *xpdoc = NULL; + PyObject *retdata = NULL; + ptzMAP *map_p = NULL; + char *key = NULL; + + if( (in_map == NULL) || (data_n == NULL) ) { + PyReturnError(PyExc_RuntimeError, "pythonXMLnode() - xmlNode or ptzMAP is NULL"); + } + + key = (char *) malloc(258); + if( key == NULL ) { + PyReturnError(PyExc_MemoryError, "Could not allocate temporary buffer"); + } + + // Loop through all configured elements + retdata = PyDict_New(); + foreach_xmlnode(in_map, map_p) { + if( (map_p->type_value == ptzDICT) && (map_p->rootpath != NULL) ) { + xmlXPathObject *xpo = NULL; + int i; + + // Set the root node in the XPath context + xpdoc = xmlNewDoc((xmlChar *) "1.0"); + assert( xpdoc != NULL ); + xmlDocSetRootElement(xpdoc, xmlCopyNode(data_n, 1)); + + xpctx = xmlXPathNewContext(xpdoc); + if( xpctx == NULL ) { + PyReturnError(PyExc_MemoryError, "Could not setup new XPath context"); + } + xpctx->node = data_n; + + xpo = _get_xpath_values(xpctx, map_p->rootpath); + if( (xpo != NULL) && (xpo->nodesetval != NULL) && (xpo->nodesetval->nodeNr > 0) ) { + for( i = 0; i < xpo->nodesetval->nodeNr; i++ ) { + xpctx->node = xpo->nodesetval->nodeTab[i]; + + if( _get_key_value(key, 256, map_p, xpctx, 0) != NULL ) { + PyObject *res = _deep_pythonize(retdata, map_p, + xpo->nodesetval->nodeTab[i], i); + if( res == NULL ) { + // Exit if we get NULL - something is wrong + //and exception is set + return NULL; + } + } + } + xmlXPathFreeContext(xpctx); + xmlFreeDoc(xpdoc); + } +#ifdef DEBUG + else { + fprintf(stderr, "** pythonizeXMLnode :: Could not locate node for key value: " + "root path '%s', key '%s'\n", map_p->rootpath, map_p->key); + } +#endif + if( xpo != NULL ) { + xmlXPathFreeObject(xpo); xpo = NULL; + } + } else { + PyObject *res = _deep_pythonize(retdata, map_p, data_n, 0); + if( res == NULL ) { + // Exit if we get NULL - something is wrong + //and exception is set + return NULL; + } + } + } + free(key); + return retdata; +} + + +/** + * Exported function, for parsing a XML document to a Python dictionary based on the given ptzMAP + * @author David Sommerseth <davids@redhat.com> + * @param ptzMAP* The map descriping the resulting Python dictionary + * @param xmlDoc* XML document pointer to the source data to be used for populating the Python dictionary + */ +PyObject *pythonizeXMLdoc(ptzMAP *map, xmlDoc *doc) +{ + xmlNode *node = NULL; + + node = xmlDocGetRootElement(doc); + return pythonizeXMLnode(map, node); +} + + +#if 0 +// Simple independent main function - only for debugging +int main(int argc, char **argv) { + xmlDoc *doc = NULL, *data = NULL; + ptzMAP *map = NULL; + PyObject *pydat = NULL; + + Py_Initialize(); + + doc = xmlReadFile("pymap.xml", NULL, 0); + assert( doc != NULL ); + + map = dmiMAP_ParseMappingXML_GroupName(doc, argv[1]); + // map = dmiMAP_ParseMappingXML_TypeID(doc, atoi(rgv[1])); + ptzmap_Dump(map); + printf("----------------------\n"); + assert(map != NULL); + + data = xmlReadFile(argv[2], NULL, 0); + assert( data != NULL ); + + pydat = pythonizeXMLdoc(map, data); + assert( pydat != NULL ); + + Py_INCREF(pydat); + printf("\n\n"); + PyObject_Print(pydat, stdout, 0); + Py_DECREF(pydat); + printf("\n\n"); + ptzmap_Free(map); + xmlFreeDoc(data); + 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, "bios"); + assert( mapping != NULL ); + + // Read XML data from file + doc = xmlReadFile("cpu.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(VERSION); + Py_INCREF(version); + PyModule_AddObject(module, "version", version); +} +#endif // Python test module + + |