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
This commit is contained in:
Daniel Veillard 2002-02-10 11:57:22 +00:00
parent 1c18e30b46
commit c5f05ada40
8 changed files with 440 additions and 15 deletions

View File

@ -1,3 +1,11 @@
Sun Feb 10 12:02:59 CET 2002 Daniel Veillard <daniel@veillard.com>
* 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
Sat Feb 9 23:15:04 CET 2002 Daniel Veillard <daniel@veillard.com>
* python/Makefile.am: seems some version of automake didn't

View File

@ -1,3 +1,3 @@
./test/VC/ElementValid4:7: validity error: Element c is not declared in doc list of possible childs
./test/VC/ElementValid4:7: validity error: Element c is not declared in doc list of possible children
<doc> This <b>seems</b> Ok <a/> but this <c>was not declared</c></doc>
^

View File

@ -0,0 +1,19 @@
<?xml version="1.0"?>
<!DOCTYPE book [
<!ELEMENT book (doc)*>
<!ELEMENT doc (isid)*>
<!ELEMENT isid EMPTY>
<!ATTLIST isid myid ID #IMPLIED>
]>
<book>
<doc>
<isid myid="mine"/>
<isid myid="dup"/>
</doc>
<!-- including another XML document with IDs -->
<doc>
<isid myid="dup"/>
<isid myid="foo"/>
<isid myid="bar"/>
</doc>
</book>

View File

@ -0,0 +1,15 @@
<!DOCTYPE book [
<!ELEMENT book (doc*)>
<!ELEMENT doc (isid*)>
<!ELEMENT isid EMPTY>
<!ATTLIST isid myid ID #IMPLIED>
]>
<book>
<doc>
<isid myid="mine"/>
<isid myid="dup"/>
</doc>
<!-- including another XML document with IDs -->
<xinclude:include xmlns:xinclude="http://www.w3.org/2001/XInclude"
href="../ents/ids.xml"/>
</book>

View File

@ -0,0 +1,10 @@
<!DOCTYPE doc [
<!ELEMENT doc (isid*)>
<!ELEMENT isid EMPTY>
<!ATTLIST isid myid ID #IMPLIED>
]>
<doc>
<isid myid="dup"/>
<isid myid="foo"/>
<isid myid="bar"/>
</doc>

15
tree.c
View File

@ -2845,6 +2845,21 @@ xmlCopyProp(xmlNodePtr target, xmlAttrPtr cur) {
tmp = tmp->next;
}
}
/*
* Try to handle IDs
*/
if ((target->doc != NULL) && (cur->doc != NULL) &&
(cur->doc->ids != NULL) && (cur->parent != NULL)) {
if (xmlIsID(cur->doc, cur->parent, cur)) {
xmlChar *id;
id = xmlNodeListGetString(cur->doc, cur->children, 1);
if (id != NULL) {
xmlAddID(NULL, target->doc, id, ret);
xmlFree(id);
}
}
}
return(ret);
}

View File

@ -1859,7 +1859,8 @@ xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
/*
* The id is already defined in this DTD.
*/
VERROR(ctxt->userData, "ID %s already defined\n", value);
if (ctxt != NULL)
VERROR(ctxt->userData, "ID %s already defined\n", value);
xmlFreeID(ret);
return(NULL);
}

View File

@ -330,6 +330,371 @@ xmlXIncludeAddTxt(xmlXIncludeCtxtPtr ctxt, xmlNodePtr txt, const xmlURL 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 *
@ -417,18 +782,8 @@ loaded:
* 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 @@ loaded:
}
}
}
ctxt->repTab[nr] = xmlXPtrBuildNodeList(xptr);
ctxt->repTab[nr] = xmlXIncludeCopyXPointer(ctxt, ctxt->doc, doc, xptr);
xmlXPathFreeObject(xptr);
xmlXPathFreeContext(xptrctxt);
xmlFree(fragment);
@ -835,6 +1190,8 @@ xmlXIncludeIncludeNode(xmlXIncludeCtxtPtr ctxt, int nr) {
xmlAddPrevSibling(end, cur);
}
return(0);
}