| /* |
| * xinclude.c : Code to implement XInclude processing |
| * |
| * World Wide Web Consortium W3C Last Call Working Draft 16 May 2001 |
| * http://www.w3.org/TR/2001/WD-xinclude-20010516/ |
| * |
| * See Copyright for the status of this software. |
| * |
| * daniel@veillard.com |
| */ |
| |
| /* |
| * TODO: compute XPointers nodesets |
| * TODO: add an node intermediate API and handle recursion at this level |
| */ |
| |
| #define IN_LIBXML |
| #include "libxml.h" |
| |
| #include <string.h> |
| #include <libxml/xmlmemory.h> |
| #include <libxml/tree.h> |
| #include <libxml/parser.h> |
| #include <libxml/uri.h> |
| #include <libxml/xpointer.h> |
| #include <libxml/parserInternals.h> |
| #include <libxml/xmlerror.h> |
| #include <libxml/globals.h> |
| |
| #ifdef LIBXML_XINCLUDE_ENABLED |
| #include <libxml/xinclude.h> |
| |
| #define XINCLUDE_NS (const xmlChar *) "http://www.w3.org/2001/XInclude" |
| #define XINCLUDE_NODE (const xmlChar *) "include" |
| #define XINCLUDE_HREF (const xmlChar *) "href" |
| #define XINCLUDE_PARSE (const xmlChar *) "parse" |
| #define XINCLUDE_PARSE_XML (const xmlChar *) "xml" |
| #define XINCLUDE_PARSE_TEXT (const xmlChar *) "text" |
| |
| /* #define DEBUG_XINCLUDE */ |
| #ifdef DEBUG_XINCLUDE |
| #ifdef LIBXML_DEBUG_ENABLED |
| #include <libxml/debugXML.h> |
| #endif |
| #endif |
| |
| /************************************************************************ |
| * * |
| * XInclude contexts handling * |
| * * |
| ************************************************************************/ |
| |
| /* |
| * An XInclude context |
| */ |
| typedef xmlChar *xmlURL; |
| typedef struct _xmlXIncludeCtxt xmlXIncludeCtxt; |
| typedef xmlXIncludeCtxt *xmlXIncludeCtxtPtr; |
| struct _xmlXIncludeCtxt { |
| xmlDocPtr doc; /* the source document */ |
| int incNr; /* number of includes */ |
| int incMax; /* size of includes tab */ |
| xmlNodePtr *incTab; /* array of include nodes */ |
| xmlNodePtr *repTab; /* array of replacement node lists */ |
| int docNr; /* number of parsed documents */ |
| int docMax; /* size of parsed documents tab */ |
| xmlDocPtr *docTab; /* array of parsed documents */ |
| xmlURL *urlTab; /* array of parsed documents URLs */ |
| int txtNr; /* number of unparsed documents */ |
| int txtMax; /* size of unparsed documents tab */ |
| xmlNodePtr *txtTab; /* array of unparsed text nodes */ |
| xmlURL *txturlTab; /* array of unparsed txtuments URLs */ |
| }; |
| |
| static int |
| xmlXIncludeDoProcess(xmlXIncludeCtxtPtr ctxt, xmlDocPtr doc); |
| |
| /** |
| * xmlXIncludeNewContext: |
| * @doc: an XML Document |
| * |
| * Creates a new XInclude context |
| * |
| * Returns the new set |
| */ |
| static xmlXIncludeCtxtPtr |
| xmlXIncludeNewContext(xmlDocPtr doc) { |
| xmlXIncludeCtxtPtr ret; |
| |
| if (doc == NULL) |
| return(NULL); |
| ret = (xmlXIncludeCtxtPtr) xmlMalloc(sizeof(xmlXIncludeCtxt)); |
| if (ret == NULL) |
| return(NULL); |
| memset(ret, 0, sizeof(xmlXIncludeCtxt)); |
| ret->doc = doc; |
| ret->incNr = 0; |
| ret->incMax = 0; |
| ret->incTab = NULL; |
| ret->repTab = NULL; |
| ret->docNr = 0; |
| ret->docMax = 0; |
| ret->docTab = NULL; |
| ret->urlTab = NULL; |
| return(ret); |
| } |
| |
| /** |
| * xmlXIncludeFreeContext: |
| * @ctxt: the XInclude context |
| * |
| * Free an XInclude context |
| */ |
| static void |
| xmlXIncludeFreeContext(xmlXIncludeCtxtPtr ctxt) { |
| int i; |
| |
| if (ctxt == NULL) |
| return; |
| for (i = 0;i < ctxt->docNr;i++) { |
| xmlFreeDoc(ctxt->docTab[i]); |
| if (ctxt->urlTab[i] != NULL) |
| xmlFree(ctxt->urlTab[i]); |
| } |
| for (i = 0;i < ctxt->txtNr;i++) { |
| if (ctxt->txturlTab[i] != NULL) |
| xmlFree(ctxt->txturlTab[i]); |
| } |
| if (ctxt->incTab != NULL) |
| xmlFree(ctxt->incTab); |
| if (ctxt->repTab != NULL) |
| xmlFree(ctxt->repTab); |
| if (ctxt->urlTab != NULL) |
| xmlFree(ctxt->urlTab); |
| if (ctxt->docTab != NULL) |
| xmlFree(ctxt->docTab); |
| if (ctxt->txtTab != NULL) |
| xmlFree(ctxt->txtTab); |
| if (ctxt->txturlTab != NULL) |
| xmlFree(ctxt->txturlTab); |
| xmlFree(ctxt); |
| } |
| |
| /** |
| * xmlXIncludeAddNode: |
| * @ctxt: the XInclude context |
| * @node: the new node |
| * |
| * Add a new node to process to an XInclude context |
| */ |
| static void |
| xmlXIncludeAddNode(xmlXIncludeCtxtPtr ctxt, xmlNodePtr node) { |
| if (ctxt->incMax == 0) { |
| ctxt->incMax = 4; |
| ctxt->incTab = (xmlNodePtr *) xmlMalloc(ctxt->incMax * |
| sizeof(ctxt->incTab[0])); |
| if (ctxt->incTab == NULL) { |
| xmlGenericError(xmlGenericErrorContext, |
| "malloc failed !\n"); |
| return; |
| } |
| ctxt->repTab = (xmlNodePtr *) xmlMalloc(ctxt->incMax * |
| sizeof(ctxt->repTab[0])); |
| if (ctxt->repTab == NULL) { |
| xmlGenericError(xmlGenericErrorContext, |
| "malloc failed !\n"); |
| return; |
| } |
| } |
| if (ctxt->incNr >= ctxt->incMax) { |
| ctxt->incMax *= 2; |
| ctxt->incTab = (xmlNodePtr *) xmlRealloc(ctxt->incTab, |
| ctxt->incMax * sizeof(ctxt->incTab[0])); |
| if (ctxt->incTab == NULL) { |
| xmlGenericError(xmlGenericErrorContext, |
| "realloc failed !\n"); |
| return; |
| } |
| ctxt->repTab = (xmlNodePtr *) xmlRealloc(ctxt->repTab, |
| ctxt->incMax * sizeof(ctxt->repTab[0])); |
| if (ctxt->repTab == NULL) { |
| xmlGenericError(xmlGenericErrorContext, |
| "realloc failed !\n"); |
| return; |
| } |
| } |
| ctxt->incTab[ctxt->incNr] = node; |
| ctxt->repTab[ctxt->incNr] = NULL; |
| ctxt->incNr++; |
| } |
| |
| /** |
| * xmlXIncludeAddDoc: |
| * @ctxt: the XInclude context |
| * @doc: the new document |
| * @url: the associated URL |
| * |
| * Add a new document to the list. The XInclude recursive nature is handled |
| * at this point. |
| */ |
| static void |
| xmlXIncludeAddDoc(xmlXIncludeCtxtPtr ctxt, xmlDocPtr doc, const xmlURL url) { |
| xmlXIncludeCtxtPtr newctxt; |
| int i; |
| |
| if (ctxt->docMax == 0) { |
| ctxt->docMax = 4; |
| ctxt->docTab = (xmlDocPtr *) xmlMalloc(ctxt->docMax * |
| sizeof(ctxt->docTab[0])); |
| if (ctxt->docTab == NULL) { |
| xmlGenericError(xmlGenericErrorContext, |
| "malloc failed !\n"); |
| return; |
| } |
| ctxt->urlTab = (xmlURL *) xmlMalloc(ctxt->docMax * |
| sizeof(ctxt->urlTab[0])); |
| if (ctxt->urlTab == NULL) { |
| xmlGenericError(xmlGenericErrorContext, |
| "malloc failed !\n"); |
| return; |
| } |
| } |
| if (ctxt->docNr >= ctxt->docMax) { |
| ctxt->docMax *= 2; |
| ctxt->docTab = (xmlDocPtr *) xmlRealloc(ctxt->docTab, |
| ctxt->docMax * sizeof(ctxt->docTab[0])); |
| if (ctxt->docTab == NULL) { |
| xmlGenericError(xmlGenericErrorContext, |
| "realloc failed !\n"); |
| return; |
| } |
| ctxt->urlTab = (xmlURL *) xmlRealloc(ctxt->urlTab, |
| ctxt->docMax * sizeof(ctxt->urlTab[0])); |
| if (ctxt->urlTab == NULL) { |
| xmlGenericError(xmlGenericErrorContext, |
| "realloc failed !\n"); |
| return; |
| } |
| } |
| ctxt->docTab[ctxt->docNr] = doc; |
| ctxt->urlTab[ctxt->docNr] = xmlStrdup(url); |
| ctxt->docNr++; |
| |
| /* |
| * Handle recursion here. |
| */ |
| |
| newctxt = xmlXIncludeNewContext(doc); |
| if (newctxt != NULL) { |
| /* |
| * Copy the existing document set |
| */ |
| newctxt->docMax = ctxt->docMax; |
| newctxt->docNr = ctxt->docNr; |
| newctxt->docTab = (xmlDocPtr *) xmlMalloc(newctxt->docMax * |
| sizeof(newctxt->docTab[0])); |
| if (newctxt->docTab == NULL) { |
| xmlGenericError(xmlGenericErrorContext, |
| "malloc failed !\n"); |
| xmlFree(newctxt); |
| return; |
| } |
| newctxt->urlTab = (xmlURL *) xmlMalloc(newctxt->docMax * |
| sizeof(newctxt->urlTab[0])); |
| if (ctxt->urlTab == NULL) { |
| xmlGenericError(xmlGenericErrorContext, |
| "malloc failed !\n"); |
| xmlFree(newctxt); |
| return; |
| } |
| |
| for (i = 0;i < ctxt->docNr;i++) { |
| newctxt->docTab[i] = ctxt->docTab[i]; |
| newctxt->urlTab[i] = ctxt->urlTab[i]; |
| } |
| xmlXIncludeDoProcess(newctxt, doc); |
| for (i = 0;i < ctxt->docNr;i++) { |
| newctxt->docTab[i] = NULL; |
| newctxt->urlTab[i] = NULL; |
| } |
| xmlXIncludeFreeContext(newctxt); |
| } |
| } |
| |
| /** |
| * xmlXIncludeAddTxt: |
| * @ctxt: the XInclude context |
| * @txt: the new text node |
| * @url: the associated URL |
| * |
| * Add a new txtument to the list |
| */ |
| static void |
| xmlXIncludeAddTxt(xmlXIncludeCtxtPtr ctxt, xmlNodePtr txt, const xmlURL url) { |
| if (ctxt->txtMax == 0) { |
| ctxt->txtMax = 4; |
| ctxt->txtTab = (xmlNodePtr *) xmlMalloc(ctxt->txtMax * |
| sizeof(ctxt->txtTab[0])); |
| if (ctxt->txtTab == NULL) { |
| xmlGenericError(xmlGenericErrorContext, |
| "malloc failed !\n"); |
| return; |
| } |
| ctxt->txturlTab = (xmlURL *) xmlMalloc(ctxt->txtMax * |
| sizeof(ctxt->txturlTab[0])); |
| if (ctxt->txturlTab == NULL) { |
| xmlGenericError(xmlGenericErrorContext, |
| "malloc failed !\n"); |
| return; |
| } |
| } |
| if (ctxt->txtNr >= ctxt->txtMax) { |
| ctxt->txtMax *= 2; |
| ctxt->txtTab = (xmlNodePtr *) xmlRealloc(ctxt->txtTab, |
| ctxt->txtMax * sizeof(ctxt->txtTab[0])); |
| if (ctxt->txtTab == NULL) { |
| xmlGenericError(xmlGenericErrorContext, |
| "realloc failed !\n"); |
| return; |
| } |
| ctxt->txturlTab = (xmlURL *) xmlRealloc(ctxt->txturlTab, |
| ctxt->txtMax * sizeof(ctxt->urlTab[0])); |
| if (ctxt->txturlTab == NULL) { |
| xmlGenericError(xmlGenericErrorContext, |
| "realloc failed !\n"); |
| return; |
| } |
| } |
| ctxt->txtTab[ctxt->txtNr] = txt; |
| ctxt->txturlTab[ctxt->txtNr] = xmlStrdup(url); |
| ctxt->txtNr++; |
| } |
| |
| /************************************************************************ |
| * * |
| * Node copy with specific semantic * |
| * * |
| ************************************************************************/ |
| |
| /** |
| * xmlXIncludeCopyNode: |
| * @ctxt: the XInclude context |
| * @target: the document target |
| * @source: the document source |
| * @elem: the element |
| * |
| * Make a copy of the node while preserving the XInclude semantic |
| * of the Infoset copy |
| */ |
| static xmlNodePtr |
| xmlXIncludeCopyNode(xmlXIncludeCtxtPtr ctxt, xmlDocPtr target, |
| xmlDocPtr source, xmlNodePtr elem) { |
| xmlNodePtr result = NULL; |
| |
| if ((ctxt == NULL) || (target == NULL) || (source == NULL) || |
| (elem == NULL)) |
| return(NULL); |
| if (elem->type == XML_DTD_NODE) |
| return(NULL); |
| result = xmlDocCopyNode(elem, target, 1); |
| return(result); |
| } |
| |
| /** |
| * xmlXIncludeCopyNodeList: |
| * @ctxt: the XInclude context |
| * @target: the document target |
| * @source: the document source |
| * @elem: the element list |
| * |
| * Make a copy of the node list while preserving the XInclude semantic |
| * of the Infoset copy |
| */ |
| static xmlNodePtr |
| xmlXIncludeCopyNodeList(xmlXIncludeCtxtPtr ctxt, xmlDocPtr target, |
| xmlDocPtr source, xmlNodePtr elem) { |
| xmlNodePtr cur, res, result = NULL, last = NULL; |
| |
| if ((ctxt == NULL) || (target == NULL) || (source == NULL) || |
| (elem == NULL)) |
| return(NULL); |
| cur = elem; |
| while (cur != NULL) { |
| res = xmlXIncludeCopyNode(ctxt, target, source, cur); |
| if (res != NULL) { |
| if (result == NULL) { |
| result = last = res; |
| } else { |
| last->next = res; |
| res->prev = last; |
| last = res; |
| } |
| } |
| cur = cur->next; |
| } |
| return(result); |
| } |
| |
| /** |
| * xmlXInclueGetNthChild: |
| * @cur: the node |
| * @no: the child number |
| * |
| * Returns the @no'th element child of @cur or NULL |
| */ |
| static xmlNodePtr |
| xmlXIncludeGetNthChild(xmlNodePtr cur, int no) { |
| int i; |
| if (cur == NULL) |
| return(cur); |
| cur = cur->children; |
| for (i = 0;i <= no;cur = cur->next) { |
| if (cur == NULL) |
| return(cur); |
| if ((cur->type == XML_ELEMENT_NODE) || |
| (cur->type == XML_DOCUMENT_NODE) || |
| (cur->type == XML_HTML_DOCUMENT_NODE)) { |
| i++; |
| if (i == no) |
| break; |
| } |
| } |
| return(cur); |
| } |
| |
| xmlNodePtr xmlXPtrAdvanceNode(xmlNodePtr cur); |
| |
| /** |
| * xmlXIncludeCopyRange: |
| * @ctxt: the XInclude context |
| * @target: the document target |
| * @source: the document source |
| * @obj: the XPointer result from the evaluation. |
| * |
| * Build a node list tree copy of the XPointer result. |
| * |
| * Returns an xmlNodePtr list or NULL. |
| * the caller has to free the node tree. |
| */ |
| static xmlNodePtr |
| xmlXIncludeCopyRange(xmlXIncludeCtxtPtr ctxt, xmlDocPtr target, |
| xmlDocPtr source, xmlXPathObjectPtr range) { |
| /* pointers to generated nodes */ |
| xmlNodePtr list = NULL, last = NULL, parent = NULL, tmp; |
| /* pointers to traversal nodes */ |
| xmlNodePtr start, cur, end; |
| int index1, index2; |
| |
| if ((ctxt == NULL) || (target == NULL) || (source == NULL) || |
| (range == NULL)) |
| return(NULL); |
| if (range->type != XPATH_RANGE) |
| return(NULL); |
| start = (xmlNodePtr) range->user; |
| |
| if (start == NULL) |
| return(NULL); |
| end = range->user2; |
| if (end == NULL) |
| return(xmlDocCopyNode(start, target, 1)); |
| |
| cur = start; |
| index1 = range->index; |
| index2 = range->index2; |
| while (cur != NULL) { |
| if (cur == end) { |
| if (cur->type == XML_TEXT_NODE) { |
| const xmlChar *content = cur->content; |
| int len; |
| |
| if (content == NULL) { |
| tmp = xmlNewTextLen(NULL, 0); |
| } else { |
| len = index2; |
| if ((cur == start) && (index1 > 1)) { |
| content += (index1 - 1); |
| len -= (index1 - 1); |
| index1 = 0; |
| } else { |
| len = index2; |
| } |
| tmp = xmlNewTextLen(content, len); |
| } |
| /* single sub text node selection */ |
| if (list == NULL) |
| return(tmp); |
| /* prune and return full set */ |
| if (last != NULL) |
| xmlAddNextSibling(last, tmp); |
| else |
| xmlAddChild(parent, tmp); |
| return(list); |
| } else { |
| tmp = xmlDocCopyNode(cur, target, 0); |
| if (list == NULL) |
| list = tmp; |
| else { |
| if (last != NULL) |
| xmlAddNextSibling(last, tmp); |
| else |
| xmlAddChild(parent, tmp); |
| } |
| last = NULL; |
| parent = tmp; |
| |
| if (index2 > 1) { |
| end = xmlXIncludeGetNthChild(cur, index2 - 1); |
| index2 = 0; |
| } |
| if ((cur == start) && (index1 > 1)) { |
| cur = xmlXIncludeGetNthChild(cur, index1 - 1); |
| index1 = 0; |
| } else { |
| cur = cur->children; |
| } |
| /* |
| * Now gather the remaining nodes from cur to end |
| */ |
| continue; /* while */ |
| } |
| } else if ((cur == start) && |
| (list == NULL) /* looks superfluous but ... */ ) { |
| if ((cur->type == XML_TEXT_NODE) || |
| (cur->type == XML_CDATA_SECTION_NODE)) { |
| const xmlChar *content = cur->content; |
| |
| if (content == NULL) { |
| tmp = xmlNewTextLen(NULL, 0); |
| } else { |
| if (index1 > 1) { |
| content += (index1 - 1); |
| } |
| tmp = xmlNewText(content); |
| } |
| last = list = tmp; |
| } else { |
| if ((cur == start) && (index1 > 1)) { |
| tmp = xmlDocCopyNode(cur, target, 0); |
| list = tmp; |
| parent = tmp; |
| last = NULL; |
| cur = xmlXIncludeGetNthChild(cur, index1 - 1); |
| index1 = 0; |
| /* |
| * Now gather the remaining nodes from cur to end |
| */ |
| continue; /* while */ |
| } |
| tmp = xmlDocCopyNode(cur, target, 1); |
| list = tmp; |
| parent = NULL; |
| last = tmp; |
| } |
| } else { |
| tmp = NULL; |
| switch (cur->type) { |
| case XML_DTD_NODE: |
| case XML_ELEMENT_DECL: |
| case XML_ATTRIBUTE_DECL: |
| case XML_ENTITY_NODE: |
| /* Do not copy DTD informations */ |
| break; |
| case XML_ENTITY_DECL: |
| /* handle crossing entities -> stack needed */ |
| break; |
| case XML_XINCLUDE_START: |
| case XML_XINCLUDE_END: |
| /* don't consider it part of the tree content */ |
| break; |
| case XML_ATTRIBUTE_NODE: |
| /* Humm, should not happen ! */ |
| break; |
| default: |
| tmp = xmlDocCopyNode(cur, target, 1); |
| break; |
| } |
| if (tmp != NULL) { |
| if ((list == NULL) || ((last == NULL) && (parent == NULL))) { |
| return(NULL); |
| } |
| if (last != NULL) |
| xmlAddNextSibling(last, tmp); |
| else { |
| xmlAddChild(parent, tmp); |
| last = tmp; |
| } |
| } |
| } |
| /* |
| * Skip to next node in document order |
| */ |
| if ((list == NULL) || ((last == NULL) && (parent == NULL))) { |
| return(NULL); |
| } |
| cur = xmlXPtrAdvanceNode(cur); |
| } |
| return(list); |
| } |
| |
| /** |
| * xmlXIncludeBuildNodeList: |
| * @ctxt: the XInclude context |
| * @target: the document target |
| * @source: the document source |
| * @obj: the XPointer result from the evaluation. |
| * |
| * Build a node list tree copy of the XPointer result. |
| * This will drop Attributes and Namespace declarations. |
| * |
| * Returns an xmlNodePtr list or NULL. |
| * the caller has to free the node tree. |
| */ |
| static xmlNodePtr |
| xmlXIncludeCopyXPointer(xmlXIncludeCtxtPtr ctxt, xmlDocPtr target, |
| xmlDocPtr source, xmlXPathObjectPtr obj) { |
| xmlNodePtr list = NULL, last = NULL; |
| int i; |
| |
| if ((ctxt == NULL) || (target == NULL) || (source == NULL) || |
| (obj == NULL)) |
| return(NULL); |
| switch (obj->type) { |
| case XPATH_NODESET: { |
| xmlNodeSetPtr set = obj->nodesetval; |
| if (set == NULL) |
| return(NULL); |
| for (i = 0;i < set->nodeNr;i++) { |
| if (set->nodeTab[i] == NULL) |
| continue; |
| switch (set->nodeTab[i]->type) { |
| case XML_TEXT_NODE: |
| case XML_CDATA_SECTION_NODE: |
| case XML_ELEMENT_NODE: |
| case XML_ENTITY_REF_NODE: |
| case XML_ENTITY_NODE: |
| case XML_PI_NODE: |
| case XML_COMMENT_NODE: |
| case XML_DOCUMENT_NODE: |
| case XML_HTML_DOCUMENT_NODE: |
| #ifdef LIBXML_DOCB_ENABLED |
| case XML_DOCB_DOCUMENT_NODE: |
| #endif |
| case XML_XINCLUDE_START: |
| case XML_XINCLUDE_END: |
| break; |
| case XML_ATTRIBUTE_NODE: |
| case XML_NAMESPACE_DECL: |
| case XML_DOCUMENT_TYPE_NODE: |
| case XML_DOCUMENT_FRAG_NODE: |
| case XML_NOTATION_NODE: |
| case XML_DTD_NODE: |
| case XML_ELEMENT_DECL: |
| case XML_ATTRIBUTE_DECL: |
| case XML_ENTITY_DECL: |
| continue; /* for */ |
| } |
| if (last == NULL) |
| list = last = xmlXIncludeCopyNode(ctxt, target, source, |
| set->nodeTab[i]); |
| else { |
| xmlAddNextSibling(last, |
| xmlXIncludeCopyNode(ctxt, target, source, |
| set->nodeTab[i])); |
| if (last->next != NULL) |
| last = last->next; |
| } |
| } |
| break; |
| } |
| case XPATH_LOCATIONSET: { |
| xmlLocationSetPtr set = (xmlLocationSetPtr) obj->user; |
| if (set == NULL) |
| return(NULL); |
| for (i = 0;i < set->locNr;i++) { |
| if (last == NULL) |
| list = last = xmlXIncludeCopyXPointer(ctxt, target, source, |
| set->locTab[i]); |
| else |
| xmlAddNextSibling(last, |
| xmlXIncludeCopyXPointer(ctxt, target, source, |
| set->locTab[i])); |
| if (last != NULL) { |
| while (last->next != NULL) |
| last = last->next; |
| } |
| } |
| break; |
| } |
| case XPATH_RANGE: |
| return(xmlXIncludeCopyRange(ctxt, target, source, obj)); |
| case XPATH_POINT: |
| /* points are ignored in XInclude */ |
| break; |
| default: |
| break; |
| } |
| return(list); |
| } |
| /************************************************************************ |
| * * |
| * XInclude I/O handling * |
| * * |
| ************************************************************************/ |
| |
| /** |
| * xmlXIncludeLoadDoc: |
| * @ctxt: the XInclude context |
| * @url: the associated URL |
| * @nr: the xinclude node number |
| * |
| * Load the document, and store the result in the XInclude context |
| */ |
| static void |
| xmlXIncludeLoadDoc(xmlXIncludeCtxtPtr ctxt, const xmlChar *url, int nr) { |
| xmlDocPtr doc; |
| xmlURIPtr uri; |
| xmlChar *URL; |
| xmlChar *fragment = NULL; |
| int i; |
| /* |
| * Check the URL and remove any fragment identifier |
| */ |
| uri = xmlParseURI((const char *)url); |
| if (uri == NULL) { |
| xmlGenericError(xmlGenericErrorContext, |
| "XInclude: invalid value URI %s\n", url); |
| return; |
| } |
| if (uri->fragment != NULL) { |
| fragment = (xmlChar *) uri->fragment; |
| uri->fragment = NULL; |
| } |
| URL = xmlSaveUri(uri); |
| xmlFreeURI(uri); |
| if (URL == NULL) { |
| xmlGenericError(xmlGenericErrorContext, |
| "XInclude: invalid value URI %s\n", url); |
| if (fragment != NULL) |
| xmlFree(fragment); |
| return; |
| } |
| |
| /* |
| * Handling of references to the local document are done |
| * directly through ctxt->doc. |
| */ |
| if ((URL[0] == 0) || (URL[0] == '#')) { |
| doc = NULL; |
| goto loaded; |
| } |
| |
| /* |
| * Prevent reloading twice the document. |
| */ |
| for (i = 0; i < ctxt->docNr; i++) { |
| if (xmlStrEqual(URL, ctxt->urlTab[i])) { |
| doc = ctxt->docTab[i]; |
| goto loaded; |
| } |
| } |
| /* |
| * Load it. |
| */ |
| doc = xmlParseFile((const char *)URL); |
| if (doc == NULL) { |
| xmlGenericError(xmlGenericErrorContext, |
| "XInclude: could not load %s\n", URL); |
| xmlFree(URL); |
| if (fragment != NULL) |
| xmlFree(fragment); |
| return; |
| } |
| xmlXIncludeAddDoc(ctxt, doc, URL); |
| |
| loaded: |
| if (fragment == NULL) { |
| /* |
| * Add the top children list as the replacement copy. |
| */ |
| if (doc == NULL) |
| { |
| /* Hopefully a DTD declaration won't be copied from |
| * the same document */ |
| ctxt->repTab[nr] = xmlCopyNodeList(ctxt->doc->children); |
| } else { |
| ctxt->repTab[nr] = xmlXIncludeCopyNodeList(ctxt, ctxt->doc, |
| doc, doc->children); |
| } |
| } else { |
| /* |
| * Computes the XPointer expression and make a copy used |
| * as the replacement copy. |
| */ |
| xmlXPathObjectPtr xptr; |
| xmlXPathContextPtr xptrctxt; |
| xmlNodeSetPtr set; |
| |
| if (doc == NULL) { |
| xptrctxt = xmlXPtrNewContext(ctxt->doc, ctxt->incTab[nr], NULL); |
| } else { |
| xptrctxt = xmlXPtrNewContext(doc, NULL, NULL); |
| } |
| if (xptrctxt == NULL) { |
| xmlGenericError(xmlGenericErrorContext, |
| "XInclude: could create XPointer context\n"); |
| xmlFree(URL); |
| xmlFree(fragment); |
| return; |
| } |
| xptr = xmlXPtrEval(fragment, xptrctxt); |
| if (xptr == NULL) { |
| xmlGenericError(xmlGenericErrorContext, |
| "XInclude: XPointer evaluation failed: #%s\n", |
| fragment); |
| xmlXPathFreeContext(xptrctxt); |
| xmlFree(URL); |
| xmlFree(fragment); |
| return; |
| } |
| switch (xptr->type) { |
| case XPATH_UNDEFINED: |
| case XPATH_BOOLEAN: |
| case XPATH_NUMBER: |
| case XPATH_STRING: |
| case XPATH_POINT: |
| case XPATH_USERS: |
| case XPATH_XSLT_TREE: |
| xmlGenericError(xmlGenericErrorContext, |
| "XInclude: XPointer is not a range: #%s\n", |
| fragment); |
| xmlXPathFreeContext(xptrctxt); |
| xmlFree(URL); |
| xmlFree(fragment); |
| return; |
| case XPATH_NODESET: |
| case XPATH_RANGE: |
| case XPATH_LOCATIONSET: |
| break; |
| } |
| set = xptr->nodesetval; |
| if (set != NULL) { |
| for (i = 0;i < set->nodeNr;i++) { |
| if (set->nodeTab[i] == NULL) |
| continue; |
| switch (set->nodeTab[i]->type) { |
| case XML_TEXT_NODE: |
| case XML_CDATA_SECTION_NODE: |
| case XML_ELEMENT_NODE: |
| case XML_ENTITY_REF_NODE: |
| case XML_ENTITY_NODE: |
| case XML_PI_NODE: |
| case XML_COMMENT_NODE: |
| case XML_DOCUMENT_NODE: |
| case XML_HTML_DOCUMENT_NODE: |
| #ifdef LIBXML_DOCB_ENABLED |
| case XML_DOCB_DOCUMENT_NODE: |
| #endif |
| continue; |
| case XML_ATTRIBUTE_NODE: |
| xmlGenericError(xmlGenericErrorContext, |
| "XInclude: XPointer selects an attribute: #%s\n", |
| fragment); |
| set->nodeTab[i] = NULL; |
| continue; |
| case XML_NAMESPACE_DECL: |
| xmlGenericError(xmlGenericErrorContext, |
| "XInclude: XPointer selects a namespace: #%s\n", |
| fragment); |
| set->nodeTab[i] = NULL; |
| continue; |
| case XML_DOCUMENT_TYPE_NODE: |
| case XML_DOCUMENT_FRAG_NODE: |
| case XML_NOTATION_NODE: |
| case XML_DTD_NODE: |
| case XML_ELEMENT_DECL: |
| case XML_ATTRIBUTE_DECL: |
| case XML_ENTITY_DECL: |
| case XML_XINCLUDE_START: |
| case XML_XINCLUDE_END: |
| xmlGenericError(xmlGenericErrorContext, |
| "XInclude: XPointer selects unexpected nodes: #%s\n", |
| fragment); |
| set->nodeTab[i] = NULL; |
| set->nodeTab[i] = NULL; |
| continue; /* for */ |
| } |
| } |
| } |
| ctxt->repTab[nr] = xmlXIncludeCopyXPointer(ctxt, ctxt->doc, doc, xptr); |
| xmlXPathFreeObject(xptr); |
| xmlXPathFreeContext(xptrctxt); |
| xmlFree(fragment); |
| } |
| xmlFree(URL); |
| } |
| |
| /** |
| * xmlXIncludeLoadTxt: |
| * @ctxt: the XInclude context |
| * @url: the associated URL |
| * @nr: the xinclude node number |
| * |
| * Load the content, and store the result in the XInclude context |
| */ |
| static void |
| xmlXIncludeLoadTxt(xmlXIncludeCtxtPtr ctxt, const xmlChar *url, int nr) { |
| xmlParserInputBufferPtr buf; |
| xmlNodePtr node; |
| xmlURIPtr uri; |
| xmlChar *URL; |
| int i; |
| /* |
| * Check the URL and remove any fragment identifier |
| */ |
| uri = xmlParseURI((const char *)url); |
| if (uri == NULL) { |
| xmlGenericError(xmlGenericErrorContext, |
| "XInclude: invalid value URI %s\n", url); |
| return; |
| } |
| if (uri->fragment != NULL) { |
| xmlGenericError(xmlGenericErrorContext, |
| "XInclude: fragment identifier forbidden for text: %s\n", |
| uri->fragment); |
| xmlFreeURI(uri); |
| return; |
| } |
| URL = xmlSaveUri(uri); |
| xmlFreeURI(uri); |
| if (URL == NULL) { |
| xmlGenericError(xmlGenericErrorContext, |
| "XInclude: invalid value URI %s\n", url); |
| return; |
| } |
| |
| /* |
| * Handling of references to the local document are done |
| * directly through ctxt->doc. |
| */ |
| if (URL[0] == 0) { |
| xmlGenericError(xmlGenericErrorContext, |
| "XInclude: text serialization of document not available\n"); |
| xmlFree(URL); |
| return; |
| } |
| |
| /* |
| * Prevent reloading twice the document. |
| */ |
| for (i = 0; i < ctxt->txtNr; i++) { |
| if (xmlStrEqual(URL, ctxt->txturlTab[i])) { |
| node = xmlCopyNode(ctxt->txtTab[i], 1); |
| goto loaded; |
| } |
| } |
| /* |
| * Load it. |
| * Issue 62: how to detect the encoding |
| */ |
| buf = xmlParserInputBufferCreateFilename((const char *)URL, 0); |
| if (buf == NULL) { |
| xmlGenericError(xmlGenericErrorContext, |
| "XInclude: could not load %s\n", URL); |
| xmlFree(URL); |
| return; |
| } |
| node = xmlNewText(NULL); |
| |
| /* |
| * Scan all chars from the resource and add the to the node |
| */ |
| while (xmlParserInputBufferRead(buf, 128) > 0) { |
| int len; |
| const xmlChar *content; |
| |
| content = xmlBufferContent(buf->buffer); |
| len = xmlBufferLength(buf->buffer); |
| for (i = 0;i < len; i++) { |
| /* |
| * TODO: if the encoding issue is solved, scan UTF8 chars instead |
| */ |
| if (!IS_CHAR(content[i])) { |
| xmlGenericError(xmlGenericErrorContext, |
| "XInclude: %s contains invalid char %d\n", URL, content[i]); |
| } else { |
| xmlNodeAddContentLen(node, &content[i], 1); |
| } |
| } |
| xmlBufferShrink(buf->buffer, len); |
| } |
| xmlFreeParserInputBuffer(buf); |
| xmlXIncludeAddTxt(ctxt, node, URL); |
| |
| loaded: |
| /* |
| * Add the element as the replacement copy. |
| */ |
| ctxt->repTab[nr] = node; |
| xmlFree(URL); |
| } |
| |
| /************************************************************************ |
| * * |
| * XInclude Processing * |
| * * |
| ************************************************************************/ |
| |
| /** |
| * xmlXIncludePreProcessNode: |
| * @ctxt: an XInclude context |
| * @node: an XInclude node |
| * |
| * Implement the XInclude preprocessing, currently just adding the element |
| * for further processing. |
| * |
| * Returns the result list or NULL in case of error |
| */ |
| static xmlNodePtr |
| xmlXIncludePreProcessNode(xmlXIncludeCtxtPtr ctxt, xmlNodePtr node) { |
| xmlXIncludeAddNode(ctxt, node); |
| return(0); |
| } |
| |
| /** |
| * xmlXIncludeLoadNode: |
| * @ctxt: an XInclude context |
| * @nr: the node number |
| * |
| * Find and load the infoset replacement for the given node. |
| * |
| * Returns 0 if substitution succeeded, -1 if some processing failed |
| */ |
| static int |
| xmlXIncludeLoadNode(xmlXIncludeCtxtPtr ctxt, int nr) { |
| xmlNodePtr cur; |
| xmlChar *href; |
| xmlChar *parse; |
| xmlChar *base; |
| xmlChar *URI; |
| int xml = 1; /* default Issue 64 */ |
| |
| if (ctxt == NULL) |
| return(-1); |
| if ((nr < 0) || (nr >= ctxt->incNr)) |
| return(-1); |
| cur = ctxt->incTab[nr]; |
| if (cur == NULL) |
| return(-1); |
| |
| #ifdef DEBUG_XINCLUDE |
| xmlDebugDumpNode(stdout, cur, 0); |
| #endif |
| /* |
| * read the attributes |
| */ |
| href = xmlGetNsProp(cur, XINCLUDE_NS, XINCLUDE_HREF); |
| if (href == NULL) { |
| href = xmlGetProp(cur, XINCLUDE_HREF); |
| if (href == NULL) { |
| xmlGenericError(xmlGenericErrorContext, "XInclude: no href\n"); |
| return(-1); |
| } |
| } |
| parse = xmlGetNsProp(cur, XINCLUDE_NS, XINCLUDE_PARSE); |
| if (parse == NULL) { |
| parse = xmlGetProp(cur, XINCLUDE_PARSE); |
| } |
| if (parse != NULL) { |
| if (xmlStrEqual(parse, XINCLUDE_PARSE_XML)) |
| xml = 1; |
| else if (xmlStrEqual(parse, XINCLUDE_PARSE_TEXT)) |
| xml = 0; |
| else { |
| xmlGenericError(xmlGenericErrorContext, |
| "XInclude: invalid value %s for %s\n", |
| parse, XINCLUDE_PARSE); |
| if (href != NULL) |
| xmlFree(href); |
| if (parse != NULL) |
| xmlFree(parse); |
| return(-1); |
| } |
| } |
| |
| /* |
| * compute the URI |
| */ |
| base = xmlNodeGetBase(ctxt->doc, cur); |
| if (base == NULL) { |
| URI = xmlBuildURI(href, ctxt->doc->URL); |
| } else { |
| URI = xmlBuildURI(href, base); |
| } |
| if (URI == NULL) { |
| xmlChar *escbase; |
| xmlChar *eschref; |
| /* |
| * Some escaping may be needed |
| */ |
| escbase = xmlURIEscape(base); |
| eschref = xmlURIEscape(href); |
| URI = xmlBuildURI(eschref, escbase); |
| if (escbase != NULL) |
| xmlFree(escbase); |
| if (eschref != NULL) |
| xmlFree(eschref); |
| } |
| if (URI == NULL) { |
| xmlGenericError(xmlGenericErrorContext, "XInclude: failed build URL\n"); |
| if (parse != NULL) |
| xmlFree(parse); |
| if (href != NULL) |
| xmlFree(href); |
| if (base != NULL) |
| xmlFree(base); |
| return(-1); |
| } |
| #ifdef DEBUG_XINCLUDE |
| xmlGenericError(xmlGenericErrorContext, "parse: %s\n", |
| xml ? "xml": "text"); |
| xmlGenericError(xmlGenericErrorContext, "URI: %s\n", URI); |
| #endif |
| |
| /* |
| * Cleanup |
| */ |
| if (xml) { |
| xmlXIncludeLoadDoc(ctxt, URI, nr); |
| /* xmlXIncludeGetFragment(ctxt, cur, URI); */ |
| } else { |
| xmlXIncludeLoadTxt(ctxt, URI, nr); |
| } |
| |
| /* |
| * Cleanup |
| */ |
| if (URI != NULL) |
| xmlFree(URI); |
| if (parse != NULL) |
| xmlFree(parse); |
| if (href != NULL) |
| xmlFree(href); |
| if (base != NULL) |
| xmlFree(base); |
| return(0); |
| } |
| |
| /** |
| * xmlXIncludeIncludeNode: |
| * @ctxt: an XInclude context |
| * @nr: the node number |
| * |
| * Inplement the infoset replacement for the given node |
| * |
| * Returns 0 if substitution succeeded, -1 if some processing failed |
| */ |
| static int |
| xmlXIncludeIncludeNode(xmlXIncludeCtxtPtr ctxt, int nr) { |
| xmlNodePtr cur, end, list; |
| |
| if (ctxt == NULL) |
| return(-1); |
| if ((nr < 0) || (nr >= ctxt->incNr)) |
| return(-1); |
| cur = ctxt->incTab[nr]; |
| if (cur == NULL) |
| return(-1); |
| |
| /* |
| * Change the current node as an XInclude start one, and add an |
| * entity end one |
| */ |
| cur->type = XML_XINCLUDE_START; |
| end = xmlNewNode(cur->ns, cur->name); |
| if (end == NULL) { |
| xmlGenericError(xmlGenericErrorContext, |
| "XInclude: failed to build node\n"); |
| return(-1); |
| } |
| end->type = XML_XINCLUDE_END; |
| xmlAddNextSibling(cur, end); |
| |
| /* |
| * Add the list of nodes |
| */ |
| list = ctxt->repTab[nr]; |
| ctxt->repTab[nr] = NULL; |
| while (list != NULL) { |
| cur = list; |
| list = list->next; |
| |
| xmlAddPrevSibling(end, cur); |
| } |
| |
| |
| return(0); |
| } |
| |
| /** |
| * xmlXIncludeTestNode: |
| * @node: an XInclude node |
| * |
| * test if the node is an XInclude node |
| * |
| * Returns 1 true, 0 otherwise |
| */ |
| static int |
| xmlXIncludeTestNode(xmlNodePtr node) { |
| if (node == NULL) |
| return(0); |
| if (node->ns == NULL) |
| return(0); |
| if ((xmlStrEqual(node->name, XINCLUDE_NODE)) && |
| (xmlStrEqual(node->ns->href, XINCLUDE_NS))) return(1); |
| return(0); |
| } |
| |
| /** |
| * xmlXIncludeDoProcess: |
| * @ctxt: |
| * @doc: an XML document |
| * |
| * Implement the XInclude substitution on the XML document @doc |
| * |
| * Returns 0 if no substitution were done, -1 if some processing failed |
| * or the number of substitutions done. |
| */ |
| static int |
| xmlXIncludeDoProcess(xmlXIncludeCtxtPtr ctxt, xmlDocPtr doc) { |
| xmlNodePtr cur; |
| int ret = 0; |
| int i; |
| |
| if (doc == NULL) |
| return(-1); |
| if (ctxt == NULL) |
| return(-1); |
| |
| /* |
| * First phase: lookup the elements in the document |
| */ |
| cur = xmlDocGetRootElement(doc); |
| if (xmlXIncludeTestNode(cur)) |
| xmlXIncludePreProcessNode(ctxt, cur); |
| while (cur != NULL) { |
| /* TODO: need to work on entities -> stack */ |
| if ((cur->children != NULL) && |
| (cur->children->type != XML_ENTITY_DECL)) { |
| cur = cur->children; |
| if (xmlXIncludeTestNode(cur)) |
| xmlXIncludePreProcessNode(ctxt, cur); |
| } else if (cur->next != NULL) { |
| cur = cur->next; |
| if (xmlXIncludeTestNode(cur)) |
| xmlXIncludePreProcessNode(ctxt, cur); |
| } else { |
| do { |
| cur = cur->parent; |
| if (cur == NULL) break; /* do */ |
| if (cur->next != NULL) { |
| cur = cur->next; |
| if (xmlXIncludeTestNode(cur)) |
| xmlXIncludePreProcessNode(ctxt, cur); |
| break; /* do */ |
| } |
| } while (cur != NULL); |
| } |
| } |
| |
| /* |
| * Second Phase : collect the infosets fragments |
| */ |
| for (i = 0;i < ctxt->incNr; i++) { |
| xmlXIncludeLoadNode(ctxt, i); |
| } |
| |
| /* |
| * Third phase: extend the original document infoset. |
| */ |
| for (i = 0;i < ctxt->incNr; i++) { |
| xmlXIncludeIncludeNode(ctxt, i); |
| } |
| |
| return(ret); |
| } |
| |
| /** |
| * xmlXIncludeProcess: |
| * @doc: an XML document |
| * |
| * Implement the XInclude substitution on the XML document @doc |
| * |
| * Returns 0 if no substitution were done, -1 if some processing failed |
| * or the number of substitutions done. |
| */ |
| int |
| xmlXIncludeProcess(xmlDocPtr doc) { |
| xmlXIncludeCtxtPtr ctxt; |
| int ret = 0; |
| |
| if (doc == NULL) |
| return(-1); |
| ctxt = xmlXIncludeNewContext(doc); |
| if (ctxt == NULL) |
| return(-1); |
| ret = xmlXIncludeDoProcess(ctxt, doc); |
| |
| xmlXIncludeFreeContext(ctxt); |
| return(ret); |
| } |
| |
| #else /* !LIBXML_XINCLUDE_ENABLED */ |
| #endif |