fix #68882, cleanup the XInclude copying of node, merge back IDs in the
* tree.c valid.c xinclude.c: fix #68882, cleanup the XInclude
copying of node, merge back IDs in the target document.
* result/XInclude/docids.xml test/XInclude/docs/docids.xml
test/XInclude/ents/ids.xml: test case
* result/VC/ElementValid4: output changed due to a typo fix
Daniel
diff --git a/xinclude.c b/xinclude.c
index 3f38b62..dfd90ab 100644
--- a/xinclude.c
+++ b/xinclude.c
@@ -332,6 +332,371 @@
/************************************************************************
* *
+ * 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 *
* *
************************************************************************/
@@ -417,18 +782,8 @@
* the same document */
ctxt->repTab[nr] = xmlCopyNodeList(ctxt->doc->children);
} else {
- /* DTD declarations can't be copied from included files */
- xmlNodePtr node = doc->children;
- while (node != NULL)
- {
- if (node->type == XML_DTD_NODE)
- {
- xmlUnlinkNode(node);
- xmlFreeNode(node);
- }
- node = node->next;
- }
- ctxt->repTab[nr] = xmlCopyNodeList(doc->children);
+ ctxt->repTab[nr] = xmlXIncludeCopyNodeList(ctxt, ctxt->doc,
+ doc, doc->children);
}
} else {
/*
@@ -530,7 +885,7 @@
}
}
}
- ctxt->repTab[nr] = xmlXPtrBuildNodeList(xptr);
+ ctxt->repTab[nr] = xmlXIncludeCopyXPointer(ctxt, ctxt->doc, doc, xptr);
xmlXPathFreeObject(xptr);
xmlXPathFreeContext(xptrctxt);
xmlFree(fragment);
@@ -835,6 +1190,8 @@
xmlAddPrevSibling(end, cur);
}
+
+
return(0);
}