/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * 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 * * mod_xml.c: The SPL XML modules */ /** * A module for accessing XML data * * This module currently implements XML, XPath, XSLT (including some EXSLT * extensions), XML Namespaces and a simple interface for modifying XML data. */ #ifdef _WIN32 #define LIBXML_DLL_IMPORT __declspec(dllimport) #else extern int xmlLoadExtDtdDefaultValue; #endif #include #include #include #include #ifndef MAKEDEPS # include # include # include # ifdef ENABLE_LIBXSLT_SUPPORT # include # include # include # include # include # endif #endif #include "spl.h" #include "compat.h" extern void SPL_ABI(spl_mod_xml_init)(struct spl_vm *vm, struct spl_module *mod, int restore); extern void SPL_ABI(spl_mod_xml_done)(struct spl_vm *vm, struct spl_module *mod); struct xml_hnode_data { xmlDocPtr doc; #ifdef ENABLE_LIBXSLT_SUPPORT xsltStylesheetPtr xsl; #endif xmlNodePtr *node_id_list; int node_id_list_size; int node_id_counter; }; struct xml_node_private { struct xml_hnode_data *hnd; char seq_text[32]; int node_id; }; /** * This function parses the XML text passed as parameter and returns an XML * document handler. This document handler can then be used to access the * data in the XML file using XPATH queries. The XPATH queries are used like * array indexes: * * var xmldoc = xml_parse(file_read("demo.xml")); * * foreach[] i (xmldoc["//@*"].nodes) * debug "Attribute '$i' has the value '${xmldoc[i].data}'."; * * Virtual member variables (.nodes and .data in the example above) are used * to specify what kind of data should be accessed. The following virtual * member variables are defined for read accesses: * * .nodes * Returns an array with references to all nodes in the XML * tree which do match the XPATH query. This references are * auto-generated unique identifiers for the nodes which are * accepted as array indexes just like xpath queries. * (The 'i' variable in the example above.) * * .node * Only the first matching node. * * .type * The type of the XML node (e.g. "element", "attribute", * "text", "cdata", etc). * * .name * The name of the XML node (for element and attribute nodes). * * .path * An auto-generated xpath query to this XML node. * * .data * The data associated with the first matching XML node. This * is the content if the match is an element node or text node * and the value if the match is an attribute node. * * .xml * The XML text of the first matching XML node and its child * nodes. * * .innerxml * The XML text of the child nodes of the first matching XML * node. * * This member variables (except the .nodes variable) do only use the first * match. It is possible to specify a seperator using an additional virtual * index. Then all matches are included in the result set, seperated by the * specified seperator: * * debug "All attributes: ${xmldoc["//@*"].data.[", "]}"; * * The following virtual memeber variables are supported for writing: * * .data * .xml * .innerxml * This variables are also available for writing and have * the same meaning as for read accesses. * * .add_xml_before * .add_xml_after * This adds new XML text before or after the found matches. * * .add_xml_top * .add_xml_bottom * This adds new XML text as new child nodes before of after * the child nodes of the found matches. * * All write queries modify each found match. In cases such as the following * example you need to use an iterator because it contains a combined * read/write access. Without the iterator, the regex would read the first * match, do the substitution once and write the result to all matches: * * foreach[] i (xmldoc["//@year"].nodes) * xmldoc[i] =~ s/2004/2005/; * * The default behavior - if no virtual member varable is specified - is the * behavior of the .data variable. * * It is also possible to remove XML nodes using the 'delete' SPL keyword: * * delete xmldoc["//record[@year < 2005]"]; * * The 'node handlers' returned by the .nodes and .node variables can also * be used directly to make relative xpath queries. So the following two * code snippets are identical: * * foreach[] n (xmldoc["//data"].nodes) * debug xmldoc["$n/@value"]; * * foreach[] n (xmldoc["//data"].nodes) * debug n["@value"]; * * Check out the documents on www.w3.org for a detailed description of XML * and the XPath query language. * * The optional parameters load_dtd and substitute_entities can be set * to 1 if entity substitution should be performed. This is usually required * when transforming a complete document with xslt. * * If you parse a document that uses entities from the DTD but substitution * of the entities is not desired (i.e. the original document should be kept * as is), then you should set load_dtd to 1 and substitute_entities to 0. * This way the parser knows about the entities, but does not substitute * them. This also avoids parsing errors due to unknown entities. */ // builtin xml_parse(xml_text,load_dtd,substitute_entities); static struct spl_node *handler_xml_parse(struct spl_task *task, void UNUSED(*data)) { char *text = spl_clib_get_string(task); int load_external_dtd = spl_clib_get_int(task); int substitute_entities = spl_clib_get_int(task); xmlSubstituteEntitiesDefault(substitute_entities); xmlLoadExtDtdDefaultValue = load_external_dtd; struct spl_node *n = SPL_NEW_STRING_DUP("XML DATA"); struct xml_hnode_data *hnd = calloc(1, sizeof(struct xml_hnode_data)); n->hnode_name = strdup("xml_doc"); n->hnode_data = hnd; hnd->doc = xmlParseMemory(text, strlen(text)); if (!hnd->doc) { spl_clib_exception(task, "XmlEx", "description", SPL_NEW_PRINTF("XML Parser Error"), NULL); spl_put(task->vm, n); return 0; } else hnd->doc->_private = hnd; return n; } static void xml_tree_genseqtab(xmlNodePtr cur, xmlNodePtr *tab, int *seq) { while (cur) { tab[(*seq)++] = cur; if (cur->type == XML_ELEMENT_NODE) xml_tree_genseqtab((xmlNodePtr)cur->properties, tab, seq); if (cur->children) xml_tree_genseqtab(cur->children, tab, seq); cur = cur->next; } } static struct xml_hnode_data *get_xml_hnd(struct spl_node *node) { if (!node->hnode_data) node->hnode_data = calloc(1, sizeof(struct xml_hnode_data)); struct xml_hnode_data *hnd = node->hnode_data; if (!hnd->doc) { char *seq_list = strstr(node->hnode_dump, "<>"); if (seq_list) { *seq_list = 0; seq_list += 2; } hnd->doc = xmlParseMemory(node->hnode_dump, strlen(node->hnode_dump)); hnd->doc->_private = hnd; if (seq_list) { int seq_max = atoi(seq_list), seq = 0; seq_list += strspn(seq_list, "0123456789"); xmlNodePtr *seq_table = calloc(seq_max, sizeof(xmlNodePtr)); xml_tree_genseqtab(hnd->doc->children, seq_table, &seq); while (*seq_list == ',') { int inner_seq = atoi(++seq_list); seq_list += strspn(seq_list, "0123456789"); if (*seq_list != '=') break; int id = atoi(++seq_list); seq_list += strspn(seq_list, "0123456789"); if (*seq_list != ',' && *seq_list != '<') break; xmlNodePtr cur = seq_table[inner_seq-1]; cur->_private = calloc(1, sizeof(struct xml_node_private)); struct xml_node_private *p = cur->_private; p->hnd = hnd; p->node_id = id; if (id > hnd->node_id_counter) hnd->node_id_counter = id; if (hnd->node_id_counter > hnd->node_id_list_size) { hnd->node_id_list_size = hnd->node_id_counter * 2; hnd->node_id_list = realloc(hnd->node_id_list, hnd->node_id_list_size * sizeof(xmlNodePtr)); } hnd->node_id_list[p->node_id-1] = cur; } free(seq_table); } free(node->hnode_dump); node->hnode_dump = 0; } return hnd; } /** * This function expects an XML document handler (as returned by [[xml_parse]]) * as parameter and creates XML text from it. * * The xml is dumped with indenting spaces if the named parameter 'format' * is passed and has a 'true' (non-zero) value. * */ // builtin xml_dump(xmldoc); static struct spl_node *handler_xml_dump(struct spl_task *task, void UNUSED(*data)) { struct spl_node *hargs = spl_cleanup(task, spl_clib_get_hargs(task)); struct spl_node *hnode; struct spl_node *n = spl_cleanup(task, spl_clib_get_node(task)); int format = 0; hnode = spl_lookup(task, hargs, "format", SPL_LOOKUP_TEST); if (hnode && spl_get_int(hnode)) format = 1; if (!n->hnode_name || strcmp(n->hnode_name, "xml_doc")) { spl_clib_exception(task, "XmlEx", "description", SPL_NEW_PRINTF("Called xml_dump() with something not an XML document handler"), NULL); spl_put(task->vm, n); return 0; } struct xml_hnode_data *hnd = get_xml_hnd(n); int xmltext_size; xmlChar *xmltext; xmlDocDumpFormatMemory(hnd->doc, &xmltext, &xmltext_size, format); return SPL_NEW_STRING((char*)xmltext); } static void xml_tree_genseq(xmlNodePtr cur, int *seq, int *seq_len) { while (cur) { (*seq)++; if (cur->_private) { struct xml_node_private *p = cur->_private; snprintf(p->seq_text, 32, "%d=%d", *seq, p->node_id); *seq_len += strlen(p->seq_text) + 1; } if (cur->type == XML_ELEMENT_NODE) xml_tree_genseq((xmlNodePtr)cur->properties, seq, seq_len); if (cur->children) xml_tree_genseq(cur->children, seq, seq_len); cur = cur->next; } } static int xml_node_process_lookup(const char *xtype, const char *xarg1, xmlNodePtr cur, struct spl_string **n_string_p, struct spl_node *n, struct spl_task *task, struct xml_hnode_data *hnd, struct spl_node *doc_node) { if (!xtype || !strcmp(xtype, "data")) { char *raw_data = (char*)xmlNodeGetContent(cur); struct spl_string *data = spl_string_new(0, 0, 0, raw_data, 0); struct spl_string *old_str = *n_string_p; *n_string_p = spl_string_new(0, *n_string_p, data, *n_string_p && xarg1 ? strdup(xarg1) : 0, 0); spl_string_put(old_str); spl_string_put(data); if (!xarg1) return 0; } else if (!strcmp(xtype, "type")) { char *raw_data = "unknown"; #define XET(e, t) case e: raw_data = t; break; switch (cur->type) { XET( XML_ELEMENT_NODE, "element" ) XET( XML_ATTRIBUTE_NODE, "attribute" ) XET( XML_TEXT_NODE, "text" ) XET( XML_CDATA_SECTION_NODE, "cdata" ) XET( XML_ENTITY_REF_NODE, "entity_ref" ) XET( XML_ENTITY_NODE, "entity" ) XET( XML_PI_NODE, "pi" ) XET( XML_COMMENT_NODE, "comment" ) XET( XML_DOCUMENT_NODE, "document" ) XET( XML_DOCUMENT_TYPE_NODE, "document_type" ) XET( XML_DOCUMENT_FRAG_NODE, "document_frag" ) XET( XML_NOTATION_NODE, "notation" ) XET( XML_HTML_DOCUMENT_NODE, "html_document" ) XET( XML_DTD_NODE, "dtd" ) XET( XML_ELEMENT_DECL, "element_decl" ) XET( XML_ATTRIBUTE_DECL, "attribute_decl") XET( XML_ENTITY_DECL, "entity_decl" ) XET( XML_NAMESPACE_DECL, "namespace_decl") XET( XML_XINCLUDE_START, "xinclude_start") XET( XML_XINCLUDE_END, "xinclude_end" ) XET( XML_DOCB_DOCUMENT_NODE, "docb_document" ) } #undef XET struct spl_string *data = spl_string_new(SPL_STRING_STATIC, 0, 0, raw_data, 0); struct spl_string *old_str = *n_string_p; *n_string_p = spl_string_new(0, *n_string_p, data, *n_string_p && xarg1 ? strdup(xarg1) : 0, 0); spl_string_put(old_str); spl_string_put(data); if (!xarg1) return 0; } else if (!strcmp(xtype, "name")) { char *raw_data = cur->name ? strdup((char*)cur->name) : ""; struct spl_string *data = spl_string_new(0, 0, 0, raw_data, 0); struct spl_string *old_str = *n_string_p; *n_string_p = spl_string_new(0, *n_string_p, data, *n_string_p && xarg1 ? strdup(xarg1) : 0, 0); spl_string_put(old_str); spl_string_put(data); if (!xarg1) return 0; } else if (!strcmp(xtype, "path")) { char *raw_data = (char*)xmlGetNodePath(cur); struct spl_string *data = spl_string_new(0, 0, 0, raw_data, 0); struct spl_string *old_str = *n_string_p; *n_string_p = spl_string_new(0, *n_string_p, data, *n_string_p && xarg1 ? strdup(xarg1) : 0, 0); spl_string_put(old_str); spl_string_put(data); if (!xarg1) return 0; } else if (!strcmp(xtype, "node") || !strcmp(xtype, "nodes")) { if (!cur->_private) { cur->_private = calloc(1, sizeof(struct xml_node_private)); struct xml_node_private *p = cur->_private; p->hnd = hnd; p->node_id = ++hnd->node_id_counter; if (hnd->node_id_counter > hnd->node_id_list_size) { hnd->node_id_list_size = hnd->node_id_counter * 2; hnd->node_id_list = realloc(hnd->node_id_list, hnd->node_id_list_size * sizeof(xmlNodePtr)); } hnd->node_id_list[p->node_id-1] = cur; } struct xml_node_private *p = cur->_private; if (!strcmp(xtype, "node")) { struct spl_string *data = spl_string_printf(0, 0, 0, "_NodeBySplId_(%d)", p->node_id); struct spl_string *old_str = *n_string_p; *n_string_p = spl_string_new(0, *n_string_p, data, *n_string_p && xarg1 ? strdup(xarg1) : 0, 0); spl_string_put(old_str); spl_string_put(data); if (!xarg1) { if (!n->hnode_name && !n->cls) { n->hnode_name = strdup("xml_node"); n->cls = spl_get(doc_node); } return 0; } } else { struct spl_node *n1 = SPL_NEW_PRINTF("_NodeBySplId_(%d)", p->node_id); if (!n1->hnode_name && !n1->cls) { n1->hnode_name = strdup("xml_node"); n1->cls = spl_get(doc_node); } spl_create(task, n, NULL, n1, SPL_CREATE_LOCAL); } } else if (!strcmp(xtype, "xml") || !strcmp(xtype, "innerxml")) { xmlBufferPtr buf = xmlBufferCreate(); xmlNodeDump(buf, hnd->doc, cur, 0, 0); if (xarg1 && *n_string_p) { struct spl_string *old_str = *n_string_p; *n_string_p = spl_string_new(0, *n_string_p, 0, strdup(xarg1), 0); spl_string_put(old_str); } const char *xmltext_buffer = (const char*)xmlBufferContent(buf); int innerxml = !strcmp(xtype, "innerxml") && *xmltext_buffer == '<'; if (innerxml) { xmltext_buffer += strcspn(xmltext_buffer, ">"); if (*xmltext_buffer == '>') xmltext_buffer++; } char *xmltext = strdup(xmltext_buffer); if (innerxml) { char *p = strrchr(xmltext, '<'); if (p) *p = 0; } struct spl_string *old_str = *n_string_p; *n_string_p = spl_string_new(0, *n_string_p, 0, xmltext, 0); spl_string_put(old_str); xmlBufferFree(buf); if (!xarg1) return 0; } else { spl_clib_exception(task, "XmlEx", "description", SPL_NEW_PRINTF("Unknown XML query type (read): %s", xtype), NULL); return 0; } return 1; } static int xml_node_process_create(const char *xtype, xmlNodePtr cur, struct spl_node *value, struct spl_task *task) { if (!xtype || !strcmp(xtype, "data")) { xmlNodeSetContent(cur, (xmlChar*)spl_get_string(value)); } else if (!strcmp(xtype, "xml") || !strcmp(xtype, "innerxml") || !strcmp(xtype, "add_xml_before") || !strcmp(xtype, "add_xml_after") || !strcmp(xtype, "add_xml_top") || !strcmp(xtype, "add_xml_bottom")) { char *xmltext = spl_get_string(value); xmlNodePtr elem, last=cur, list=0; if (xmlParseInNodeContext(cur, xmltext, strlen(xmltext), 0, &list) != XML_ERR_OK) { spl_clib_exception(task, "XmlEx", "description", SPL_NEW_PRINTF("XML Parser Error"), NULL); return 0; } if (!strcmp(xtype, "xml")) { while (list) { list = (elem=list)->next; xmlAddNextSibling(last, elem); last = elem; } xmlUnlinkNode(cur); xmlFreeNode(cur); } if (!strcmp(xtype, "innerxml")) { while ((elem=xmlGetLastChild(cur)) != NULL) { xmlUnlinkNode(elem); xmlFreeNode(elem); } xmlAddChildList(cur, list); } if (!strcmp(xtype, "add_xml_before")) { while (list) { list = (elem=list)->next; xmlAddPrevSibling(cur, elem); } } if (!strcmp(xtype, "add_xml_after")) { while (list) { list = (elem=list)->next; xmlAddNextSibling(last, elem); last = elem; } } if (!strcmp(xtype, "add_xml_top")) { if ((last=last->children) != NULL) while (list) { list = (elem=list)->next; xmlAddPrevSibling(last, elem); } else xmlAddChildList(cur, list); } if (!strcmp(xtype, "add_xml_bottom")) { xmlAddChildList(cur, list); } } else { spl_clib_exception(task, "XmlEx", "description", SPL_NEW_PRINTF("Unknown XML query type (write): %s", xtype), NULL); return 0; } return 1; } static xmlNodePtr nodebysplid(struct xml_hnode_data *hnd, int id) { if (id >= 1 && id <= hnd->node_id_counter && hnd->node_id_list[id-1]) return hnd->node_id_list[id-1]; return 0; } static void xmlxpfunc_nodebysplid(xmlXPathParserContextPtr ctxt, int nargs) { if (ctxt && nargs == 1) { int id = xmlXPathPopNumber(ctxt); struct xml_hnode_data *hnd = ctxt->context->doc->_private; xmlNodePtr cur = nodebysplid(hnd, id); if (cur) valuePush(ctxt, xmlXPathNewNodeSet(cur)); } } static void handler_xml_node(struct spl_task *task, struct spl_vm UNUSED(*vm), struct spl_node *node, struct spl_hnode_args *args, void UNUSED(*data)) { int node_id = 0; if (!strcmp(node->hnode_name, "xml_node")) { sscanf(spl_get_string(node), "_NodeBySplId_(%d)", &node_id); if ((node = node->cls) == 0) return; goto redir_from_xml_node_hnode; } if (args->action == SPL_HNODE_ACTION_PUT) { struct xml_hnode_data *hnd = node->hnode_data; if (hnd) { #ifdef ENABLE_LIBXSLT_SUPPORT if (hnd->xsl) { xsltFreeStylesheet(hnd->xsl); hnd->doc = 0; } #endif if (hnd->doc) xmlFreeDoc(hnd->doc); if (hnd->node_id_list) free(hnd->node_id_list); free(hnd); } return; } if (args->action == SPL_HNODE_ACTION_DUMP) { struct xml_hnode_data *hnd = node->hnode_data; if (hnd && hnd->doc) { int xmltext_size, seq = 0, seq_len = 0; char max_seq_text[32]; xmlChar *xmltext; if (node->hnode_dump) { free(node->hnode_dump); node->hnode_dump = 0; } xmlDocDumpMemory(hnd->doc, &xmltext, &xmltext_size); xml_tree_genseq(hnd->doc->children, &seq, &seq_len); snprintf(max_seq_text, 32, "%d", seq); int new_xmltext_size = xmltext_size + seq_len + strlen(max_seq_text) + 5; xmltext = realloc(xmltext, new_xmltext_size); xmltext[xmltext_size++] = '<'; xmltext[xmltext_size++] = '>'; strcpy((char*)xmltext+xmltext_size, max_seq_text); xmltext_size += strlen(max_seq_text); xmltext[xmltext_size++] = ','; for (int i=0; inode_id_counter; i++) if (hnd->node_id_list[i]) { struct xml_node_private *p = hnd->node_id_list[i]->_private; strcpy((char*)xmltext+xmltext_size, p->seq_text); xmltext_size += strlen(p->seq_text); xmltext[xmltext_size++] = ','; } xmltext[xmltext_size-1] = '<'; xmltext[xmltext_size++] = '>'; xmltext[xmltext_size++] = 0; assert(new_xmltext_size == xmltext_size); node->hnode_dump = (char*)xmltext; } return; } redir_from_xml_node_hnode:; struct xml_hnode_data *hnd = get_xml_hnd(node); if (!hnd->doc) return; if (args->action == SPL_HNODE_ACTION_LOOKUP || args->action == SPL_HNODE_ACTION_CREATE || args->action == SPL_HNODE_ACTION_DELETE) { char *key_dup = strdup(args->key); #ifndef USEWIN32API char *strtokptr = 0; char *xpath = strtok_r(key_dup, ".", &strtokptr); char *xtype = strtok_r(0, ".", &strtokptr); char *xarg1 = strtok_r(0, ".", &strtokptr); #else // No strtok_r on windows char *xpath = strtok(key_dup, "."); char *xtype = strtok(0, "."); char *xarg1 = strtok(0, "."); #endif if (xpath) xpath = spl_hash_decode(xpath); else xpath = "NULL"; if (xtype) xtype = spl_hash_decode(xtype); if (xarg1) xarg1 = spl_hash_decode(xarg1); xmlXPathContextPtr xpathCtx = xmlXPathNewContext(hnd->doc); /* copy namespace declarations from root element */ xmlNsPtr *nsList = xmlGetNsList(hnd->doc, xmlDocGetRootElement(hnd->doc)); if (nsList) { for (xmlNsPtr *i = nsList; *i; i++) xmlXPathRegisterNs(xpathCtx, (*i)->prefix, (*i)->href); free(nsList); } xmlXPathRegisterFunc(xpathCtx, (xmlChar*)"_NodeBySplId_", xmlxpfunc_nodebysplid); if (node_id) xpathCtx->node = nodebysplid(hnd, node_id); xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression((xmlChar*)xpath, xpathCtx); if (!xpathObj) { spl_clib_exception(task, "XmlEx", "description", SPL_NEW_PRINTF("XPath Error"), NULL); xmlXPathFreeContext(xpathCtx); } else if (xpathObj->nodesetval) { int list_size = xpathObj->nodesetval->nodeNr; xmlNodePtr list[list_size]; for(int i = 0; i < list_size; i++) list[i] = xpathObj->nodesetval->nodeTab[i]; xmlXPathFreeObject(xpathObj); xmlXPathFreeContext(xpathCtx); if (args->action == SPL_HNODE_ACTION_LOOKUP) { struct spl_node *n = spl_get(0); struct spl_string *n_string = 0; for(int i = 0; i < list_size; i++) { xmlNodePtr cur = list[i]; if (!xml_node_process_lookup(xtype, xarg1, cur, &n_string, n, task, hnd, node)) break; } if (n_string) spl_set_spl_string(n, n_string); args->value = n; } if (args->action == SPL_HNODE_ACTION_CREATE) { // process in inverse order, so we can't try modifying a child node // after removing it by replacing the parrent... for(int i = list_size-1; i >= 0; i--) { xmlNodePtr cur = list[i]; if (!xml_node_process_create(xtype, cur, args->value, task)) break; } } if (args->action == SPL_HNODE_ACTION_DELETE) { // process in inverse order, so we can't try modifying a child node // after removing it by removing the parrent... for(int i = list_size-1; i >= 0; i--) { xmlNodePtr cur = list[i]; xmlUnlinkNode(cur); xmlFreeNode(cur); } } } else { xmlXPathFreeObject(xpathObj); xmlXPathFreeContext(xpathCtx); } free(key_dup); free(xpath); free(xtype); free(xarg1); return; } } /** * This function performs an XSLT transformation: * * var xmldoc = xml_parse(file_read("demo.xml")); * var stylesheet = xml_parse(file_read("demo.xsl")); * * debug xml_xslt_text(xmldoc, stylesheet, foo: "'bar'"); * * The first parameter is the handler of XML document to be transformed, as * returned by [[xml_parse()]]. The 2nd parameter is the XML document * handler for the XSLT stylesheet. * * Parameters for the stylesheet are passed as named function parameters. Note * that the parameters are XPath expressions. I.e. a string constant needs to * be passed with quotes. * * The return value is the XML text after the transformation. */ // builtin xml_xslt_text(xmldoc, stylesheet, %params); /** * This function works exactly like [[.xml_xslt_text]], but it returns a new * XML document handler. */ // builtin xml_xslt_xml(xmldoc, stylesheet, %params); #ifdef ENABLE_LIBXSLT_SUPPORT static struct spl_node *handler_xml_xslt(struct spl_task *task, void *data) { struct spl_node *xml_node = spl_cleanup(task, spl_clib_get_node(task)); struct spl_node *xsl_node = spl_cleanup(task, spl_clib_get_node(task)); if (strcmp(xml_node->hnode_name, "xml_doc")) { spl_clib_exception(task, "XmlEx", "description", SPL_NEW_PRINTF("First parameter to xml_xslt() is not an XML document handler!"), NULL); return 0; } if (strcmp(xsl_node->hnode_name, "xml_doc")) { spl_clib_exception(task, "XmlEx", "description", SPL_NEW_PRINTF("Second parameter to xml_xslt() is not an XML document handler!"), NULL); return 0; } struct xml_hnode_data *xml_hnd = get_xml_hnd(xml_node); if (!xml_hnd->doc) return 0; struct xml_hnode_data *xsl_hnd = get_xml_hnd(xsl_node); if (!xsl_hnd->doc) return 0; if (!xsl_hnd->xsl) xsl_hnd->xsl = xsltParseStylesheetDoc(xsl_hnd->doc); if (!xsl_hnd->xsl) { spl_clib_exception(task, "XmlEx", "description", SPL_NEW_PRINTF("Got an XSLT parser error!"), NULL); return 0; } char **params = 0; { struct spl_node *params_node = spl_clib_get_hargs(task); int params_len = 1; struct spl_node_sub *s = params_node->subs_begin; while (s) { params_len += 2; s = s->next; } params = calloc(params_len, sizeof(char *)); s = params_node->subs_begin; for (int i=0; s; i+=2, s=s->next) { params[i+0] = spl_hash_decode(s->key); params[i+1] = strdup(spl_get_string(s->node)); } spl_put(task->vm, params_node); } xmlDocPtr res = xsltApplyStylesheet(xsl_hnd->xsl, xml_hnd->doc, (const char **)params); for (int i=0; params[i]; i++) free(params[i]); free(params); params = 0; if (!res) { spl_clib_exception(task, "XmlEx", "description", SPL_NEW_PRINTF("Got an XSLT processor error!"), NULL); goto got_error; } if (!strcmp((char*)data, "text")) { xmlChar *res_text; int res_len; xsltSaveResultToString(&res_text, &res_len, res, xsl_hnd->xsl); xmlFreeDoc(res); return SPL_NEW_STRING((char*)res_text); } if (!strcmp((char*)data, "xml")) { struct spl_node *n = SPL_NEW_STRING_DUP("XML DATA"); struct xml_hnode_data *hnd = calloc(1, sizeof(struct xml_hnode_data)); n->hnode_name = strdup("xml"); n->hnode_data = hnd; hnd->doc = res; return n; } got_error: if (res) xmlFreeDoc(res); return 0; } #else static struct spl_node *handler_xml_xslt(struct spl_task *task, void *data) { spl_clib_exception(task, "XmlEx", "description", SPL_NEW_PRINTF("The XML module has been built without XSLT support!"), NULL); return 0; } #endif static void deregister_node(xmlNodePtr node) { if (node->type == XML_DOCUMENT_NODE) return; if (node->_private) { struct xml_node_private *p = node->_private; p->hnd->node_id_list[p->node_id-1] = 0; free(node->_private); node->_private = 0; } } /** * An instance of this object is thrown on XML parser errors. */ //object XmlEx /** * A description text describing the error. */ // var description; static int xml_module_loader_count = 0; void SPL_ABI(spl_mod_xml_init)(struct spl_vm *vm, struct spl_module *mod, int restore) { if (!restore) { spl_module_load(vm, "encode_xml", 0); spl_eval(vm, 0, strdup(mod->name), "object XmlEx { }"); } spl_clib_reg(vm, "xml_parse", handler_xml_parse, 0); spl_clib_reg(vm, "xml_dump", handler_xml_dump, 0); spl_hnode_reg(vm, "xml_doc", handler_xml_node, 0); spl_hnode_reg(vm, "xml_node", handler_xml_node, 0); spl_clib_reg(vm, "xml_xslt_text", handler_xml_xslt, "text"); spl_clib_reg(vm, "xml_xslt_xml", handler_xml_xslt, "xml"); if (!xml_module_loader_count) { xmlInitParser(); xmlDeregisterNodeDefault(deregister_node); //xmlSetExternalEntityLoader(xmlNoNetExternalEntityLoader); #ifdef ENABLE_LIBXSLT_SUPPORT exsltRegisterAll(); #endif } xml_module_loader_count++; } void SPL_ABI(spl_mod_xml_done)(struct spl_vm UNUSED(*vm), struct spl_module UNUSED(*mod)) { xml_module_loader_count--; if (!xml_module_loader_count) { #ifdef ENABLE_LIBXSLT_SUPPORT xsltCleanupGlobals(); #endif xmlCleanupParser(); } return; }