1
0
mirror of https://gitlab.gnome.org/GNOME/libxml2.git synced 2024-10-26 20:25:14 +03:00
libxml2/xinclude.c
Daniel Veillard 34ce8bece2 preparing 2.4.18 updated and rebuilt the web site implement the new
* configure.in: preparing 2.4.18
* doc/*: updated and rebuilt the web site
* *.c libxml.h: implement the new IN_LIBXML scheme discussed with
  the Windows and Cygwin maintainers.
* parser.c: humm, changed the way the SAX parser work when
  xmlSubstituteEntitiesDefault(1) is set, it will then
  do the entity registration and loading by itself in case the
  user provided SAX getEntity() returns NULL.
* testSAX.c: added --noent to test the behaviour.
Daniel
2002-03-18 19:37:11 +00:00

1314 lines
32 KiB
C

/*
* 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