/*. ******* coding:utf-8 AUTOHEADER START v1.1 ******* *. vim: fileencoding=utf-8 syntax=c sw=8 ts=8 et *. *. © 2009 David Sommerseth *. © 2007-2009 Nima Talebi *. *. 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 . *. *. 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 * @author Nima Talebi */ #include #include #include #include #include #include #include #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 * @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 * @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 * @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 * @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 * @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 * @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 node if( xmlStrcmp(node->name, (xmlChar *) "Map") != 0 ) { map_n = dmixml_FindNode(node, "Map"); if( map_n == NULL ) { // If we don't find a 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 * @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 * @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 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 * @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 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 * @author David Sommerseth * @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 node"); } // Go to the first node map_n = dmixml_FindNode(node, "TypeMap"); if( map_n == NULL ) { PyReturnError(PyExc_NameError, "Could not locate any nodes"); } // Get the root element of the tag, needed for further parsing typemap = dmixml_FindNode(xmlDocGetRootElement(xmlmap), "TypeMapping"); if( typemap == NULL ) { PyReturnError(PyExc_NameError, "Could not locate the 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 * @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 received"); } // Find the section node = dmixml_FindNode(node, "GroupMapping"); if( node == NULL ) { PyReturnError(PyExc_NameError, "Could not find the node"); } // Find the 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 * @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 * @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 * @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 * @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 * @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 * @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 // 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 // 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 * @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 * @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