added TODO for the DTD compatibility spec more bug fixes driven by the
* relaxng.c: added TODO for the DTD compatibility spec
* xinclude.c: more bug fixes driven by the testsuite
Daniel
diff --git a/ChangeLog b/ChangeLog
index f49b925..81960fc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+Thu Feb 13 12:00:30 CET 2003 Daniel Veillard <daniel@veillard.com>
+
+ * relaxng.c: added TODO for the DTD compatibility spec
+ * xinclude.c: more bug fixes driven by the testsuite
+
Tue Feb 11 19:01:02 CET 2003 Daniel Veillard <daniel@veillard.com>
* check-xinclude-test-suite.py xinclude.c: Work on the W3C/NIST
diff --git a/relaxng.c b/relaxng.c
index 3676ec6..08b82d0 100644
--- a/relaxng.c
+++ b/relaxng.c
@@ -13,6 +13,8 @@
* - NOT_ALLOWED
* - EMPTY
* - handle namespace declarations as attributes.
+ * - add support for DTD compatibility spec
+ * http://www.oasis-open.org/committees/relax-ng/compatibility-20011203.html
*/
#define IN_LIBXML
diff --git a/xinclude.c b/xinclude.c
index d0d125f..d91dce0 100644
--- a/xinclude.c
+++ b/xinclude.c
@@ -40,6 +40,8 @@
#define XINCLUDE_PARSE_TEXT (const xmlChar *) "text"
#define XINCLUDE_PARSE_ENCODING (const xmlChar *) "encoding"
+#define XINCLUDE_MAX_DEPTH 40
+
/* #define DEBUG_XINCLUDE */
#ifdef DEBUG_XINCLUDE
#ifdef LIBXML_DEBUG_ENABLED
@@ -68,6 +70,7 @@
xmlNodePtr inc; /* the included copy */
int xml; /* xml or txt */
int count; /* how many refs use that specific doc */
+ xmlXPathObjectPtr xptr; /* the xpointer if needed */
};
typedef struct _xmlXIncludeCtxt xmlXIncludeCtxt;
@@ -84,6 +87,11 @@
xmlNodePtr *txtTab; /* array of unparsed text nodes */
xmlURL *txturlTab; /* array of unparsed txtuments URLs */
+ xmlChar * url; /* the current URL processed */
+ int urlNr; /* number of url stacked */
+ int urlMax; /* size of url stack */
+ xmlChar * *urlTab; /* url stack */
+
int nbErrors; /* the number of errors detected */
};
@@ -113,6 +121,8 @@
xmlFree(ref->URI);
if (ref->fragment != NULL)
xmlFree(ref->fragment);
+ if (ref->xptr != NULL)
+ xmlXPathFreeObject(ref->xptr);
xmlFree(ref);
}
@@ -205,6 +215,75 @@
}
/**
+ * xmlXIncludeURLPush:
+ * @ctxt: the parser context
+ * @value: the url
+ *
+ * Pushes a new url on top of the url stack
+ *
+ * Returns -1 in case of error, the index in the stack otherwise
+ */
+static int
+xmlXIncludeURLPush(xmlXIncludeCtxtPtr ctxt,
+ const xmlChar *value)
+{
+ if (ctxt->urlNr > XINCLUDE_MAX_DEPTH) {
+ xmlGenericError(xmlGenericErrorContext,
+ "XInclude: detected a recursion in %s\n",
+ value);
+ ctxt->nbErrors++;
+ return(-1);
+ }
+ if (ctxt->urlTab == NULL) {
+ ctxt->urlMax = 4;
+ ctxt->urlNr = 0;
+ ctxt->urlTab = (xmlChar * *) xmlMalloc(
+ ctxt->urlMax * sizeof(ctxt->urlTab[0]));
+ if (ctxt->urlTab == NULL) {
+ xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
+ return (-1);
+ }
+ }
+ if (ctxt->urlNr >= ctxt->urlMax) {
+ ctxt->urlMax *= 2;
+ ctxt->urlTab =
+ (xmlChar * *) xmlRealloc(ctxt->urlTab,
+ ctxt->urlMax *
+ sizeof(ctxt->urlTab[0]));
+ if (ctxt->urlTab == NULL) {
+ xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
+ return (-1);
+ }
+ }
+ ctxt->url = ctxt->urlTab[ctxt->urlNr] = xmlStrdup(value);
+ return (ctxt->urlNr++);
+}
+
+/**
+ * xmlXIncludeURLPop:
+ * @ctxt: the parser context
+ *
+ * Pops the top url from the url stack
+ */
+static void
+xmlXIncludeURLPop(xmlXIncludeCtxtPtr ctxt)
+{
+ xmlChar * ret;
+
+ if (ctxt->urlNr <= 0)
+ return;
+ ctxt->urlNr--;
+ if (ctxt->urlNr > 0)
+ ctxt->url = ctxt->urlTab[ctxt->urlNr - 1];
+ else
+ ctxt->url = NULL;
+ ret = ctxt->urlTab[ctxt->urlNr];
+ ctxt->urlTab[ctxt->urlNr] = 0;
+ if (ret != NULL)
+ xmlFree(ret);
+}
+
+/**
* xmlXIncludeFreeContext:
* @ctxt: the XInclude context
*
@@ -219,6 +298,10 @@
#endif
if (ctxt == NULL)
return;
+ while (ctxt->urlNr > 0)
+ xmlXIncludeURLPop(ctxt);
+ if (ctxt->urlTab != NULL)
+ xmlFree(ctxt->urlTab);
for (i = 0;i < ctxt->incNr;i++) {
if (ctxt->incTab[i] != NULL)
xmlXIncludeFreeRef(ctxt->incTab[i]);
@@ -253,7 +336,8 @@
xmlChar *parse;
xmlChar *base;
xmlChar *URI;
- int xml = 1; /* default Issue 64 */
+ int xml = 1, i; /* default Issue 64 */
+ int local = 0;
if (ctxt == NULL)
@@ -276,6 +360,8 @@
return(-1);
}
}
+ if (href[0] == '#')
+ local = 1;
parse = xmlGetNsProp(cur, XINCLUDE_NS, XINCLUDE_PARSE);
if (parse == NULL) {
parse = xmlGetProp(cur, XINCLUDE_PARSE);
@@ -359,6 +445,21 @@
return(-1);
}
+ /*
+ * Check the URL against the stack for recursions
+ */
+ if (!local) {
+ for (i = 0;i < ctxt->urlNr;i++) {
+ if (xmlStrEqual(URL, ctxt->urlTab[i])) {
+ xmlGenericError(xmlGenericErrorContext,
+ "XInclude: detected a recursion in %s\n",
+ URL);
+ ctxt->nbErrors++;
+ return(-1);
+ }
+ }
+ }
+
ref = xmlXIncludeNewRef(ctxt, URL, cur);
if (ref == NULL) {
return(-1);
@@ -385,6 +486,14 @@
xmlXIncludeCtxtPtr newctxt;
int i;
+ /*
+ * Avoid recursion in already substitued resources
+ for (i = 0;i < ctxt->urlNr;i++) {
+ if (xmlStrEqual(doc->URL, ctxt->urlTab[i]))
+ return;
+ }
+ */
+
#ifdef DEBUG_XINCLUDE
xmlGenericError(xmlGenericErrorContext, "Recursing in doc %s\n", doc->URL);
#endif
@@ -408,6 +517,12 @@
xmlFree(newctxt);
return;
}
+ /*
+ * copy the urlTab
+ */
+ newctxt->urlMax = ctxt->urlMax;
+ newctxt->urlNr = ctxt->urlNr;
+ newctxt->urlTab = ctxt->urlTab;
/*
* Inherit the documents already in use by others includes
@@ -423,6 +538,10 @@
newctxt->incTab[i]->count--;
newctxt->incTab[i] = NULL;
}
+ newctxt->urlMax = 0;
+ newctxt->urlNr = 0;
+ newctxt->urlTab = NULL;
+
xmlXIncludeFreeContext(newctxt);
}
#ifdef DEBUG_XINCLUDE
@@ -771,6 +890,8 @@
xmlNodePtr list = NULL, last = NULL;
int i;
+ if (source == NULL)
+ source = ctxt->doc;
if ((ctxt == NULL) || (target == NULL) || (source == NULL) ||
(obj == NULL))
return(NULL);
@@ -795,9 +916,38 @@
#ifdef LIBXML_DOCB_ENABLED
case XML_DOCB_DOCUMENT_NODE:
#endif
- case XML_XINCLUDE_START:
case XML_XINCLUDE_END:
break;
+ case XML_XINCLUDE_START: {
+ xmlNodePtr tmp, cur = set->nodeTab[i];
+
+ cur = cur->next;
+ while (cur != NULL) {
+ switch(cur->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:
+ tmp = xmlXIncludeCopyNode(ctxt, target,
+ source, cur);
+ if (last == NULL) {
+ list = last = tmp;
+ } else {
+ xmlAddNextSibling(last, tmp);
+ last = tmp;
+ }
+ cur = cur->next;
+ continue;
+ default:
+ break;
+ }
+ break;
+ }
+ continue;
+ }
case XML_ATTRIBUTE_NODE:
case XML_NAMESPACE_DECL:
case XML_DOCUMENT_TYPE_NODE:
@@ -857,6 +1007,13 @@
* *
************************************************************************/
+typedef struct _xmlXIncludeMergeData xmlXIncludeMergeData;
+typedef xmlXIncludeMergeData *xmlXIncludeMergeDataPtr;
+struct _xmlXIncludeMergeData {
+ xmlDocPtr doc;
+ xmlXIncludeCtxtPtr ctxt;
+};
+
/**
* xmlXIncludeMergeOneEntity:
* @ent: the entity
@@ -866,18 +1023,56 @@
* Inplements the merge of one entity
*/
static void
-xmlXIncludeMergeEntity(xmlEntityPtr ent, xmlDocPtr doc,
+xmlXIncludeMergeEntity(xmlEntityPtr ent, xmlXIncludeMergeDataPtr data,
xmlChar *name ATTRIBUTE_UNUSED) {
- xmlEntityPtr ret;
+ xmlEntityPtr ret, prev;
+ xmlDocPtr doc;
+ xmlXIncludeCtxtPtr ctxt;
- if ((ent == NULL) || (doc == NULL))
+ if ((ent == NULL) || (data == NULL))
return;
+ ctxt = data->ctxt;
+ doc = data->doc;
+ if ((ctxt == NULL) || (doc == NULL))
+ return;
+ switch (ent->etype) {
+ case XML_INTERNAL_PARAMETER_ENTITY:
+ case XML_EXTERNAL_PARAMETER_ENTITY:
+ case XML_INTERNAL_PREDEFINED_ENTITY:
+ return;
+ case XML_INTERNAL_GENERAL_ENTITY:
+ case XML_EXTERNAL_GENERAL_PARSED_ENTITY:
+ case XML_EXTERNAL_GENERAL_UNPARSED_ENTITY:
+ break;
+ }
ret = xmlAddDocEntity(doc, ent->name, ent->etype, ent->ExternalID,
ent->SystemID, ent->content);
if (ret != NULL) {
if (ent->URI != NULL)
ret->URI = xmlStrdup(ent->URI);
+ } else {
+ prev = xmlGetDocEntity(doc, ent->name);
+ if (prev != NULL) {
+ if (ent->etype != prev->etype)
+ goto error;
+
+ if ((ent->SystemID != NULL) && (prev->SystemID != NULL)) {
+ if (!xmlStrEqual(ent->SystemID, prev->SystemID))
+ goto error;
+ } else if ((ent->SystemID != NULL) && (prev->SystemID != NULL)) {
+ if (!xmlStrEqual(ent->ExternalID, prev->ExternalID))
+ goto error;
+ } else {
+ goto error;
+ }
+
+ }
}
+ return;
+error:
+ xmlGenericError(xmlGenericErrorContext,
+ "XInclude: mismatch in redefinition of entity %s\n", ent->name);
+ ctxt->nbErrors++;
}
/**
@@ -914,18 +1109,28 @@
source = from->intSubset;
if ((source != NULL) && (source->entities != NULL)) {
+ xmlXIncludeMergeData data;
+
+ data.ctxt = ctxt;
+ data.doc = doc;
+
xmlHashScan((xmlHashTablePtr) source->entities,
- (xmlHashScanner) xmlXIncludeMergeEntity, doc);
+ (xmlHashScanner) xmlXIncludeMergeEntity, &data);
}
source = from->extSubset;
if ((source != NULL) && (source->entities != NULL)) {
+ xmlXIncludeMergeData data;
+
+ data.ctxt = ctxt;
+ data.doc = doc;
+
/*
* don't duplicate existing stuff when external subsets are the same
*/
if ((!xmlStrEqual(target->ExternalID, source->ExternalID)) &&
(!xmlStrEqual(target->SystemID, source->SystemID))) {
xmlHashScan((xmlHashTablePtr) source->entities,
- (xmlHashScanner) xmlXIncludeMergeEntity, doc);
+ (xmlHashScanner) xmlXIncludeMergeEntity, &data);
}
}
return(0);
@@ -981,7 +1186,8 @@
* Handling of references to the local document are done
* directly through ctxt->doc.
*/
- if ((URL[0] == 0) || (URL[0] == '#')) {
+ if ((URL[0] == 0) || (URL[0] == '#') ||
+ ((ctxt->doc != NULL) && (xmlStrEqual(URL, ctxt->doc->URL)))) {
doc = NULL;
goto loaded;
}
@@ -1156,9 +1362,14 @@
}
}
}
- ctxt->incTab[nr]->inc =
- xmlXIncludeCopyXPointer(ctxt, ctxt->doc, doc, xptr);
- xmlXPathFreeObject(xptr);
+ if (doc == NULL) {
+ ctxt->incTab[nr]->xptr = xptr;
+ ctxt->incTab[nr]->inc = NULL;
+ } else {
+ ctxt->incTab[nr]->inc =
+ xmlXIncludeCopyXPointer(ctxt, ctxt->doc, doc, xptr);
+ xmlXPathFreeObject(xptr);
+ }
xmlXPathFreeContext(xptrctxt);
xmlFree(fragment);
}
@@ -1677,7 +1888,7 @@
*/
static int
xmlXIncludeIncludeNode(xmlXIncludeCtxtPtr ctxt, int nr) {
- xmlNodePtr cur, end, list;
+ xmlNodePtr cur, end, list, tmp;
if (ctxt == NULL)
return(-1);
@@ -1688,6 +1899,41 @@
return(-1);
/*
+ * If we stored an XPointer a late computation may be needed
+ */
+ if ((ctxt->incTab[nr]->inc == NULL) &&
+ (ctxt->incTab[nr]->xptr != NULL)) {
+ ctxt->incTab[nr]->inc =
+ xmlXIncludeCopyXPointer(ctxt, ctxt->doc, ctxt->doc,
+ ctxt->incTab[nr]->xptr);
+ xmlXPathFreeObject(ctxt->incTab[nr]->xptr);
+ ctxt->incTab[nr]->xptr = NULL;
+ }
+ list = ctxt->incTab[nr]->inc;
+ ctxt->incTab[nr]->inc = NULL;
+
+ /*
+ * Check against the risk of generating a multi-rooted document
+ */
+ if ((cur->parent != NULL) &&
+ (cur->parent->type != XML_ELEMENT_NODE)) {
+ int nb_elem = 0;
+
+ tmp = list;
+ while (tmp != NULL) {
+ if (tmp->type == XML_ELEMENT_NODE)
+ nb_elem++;
+ tmp = tmp->next;
+ }
+ if (nb_elem > 1) {
+ xmlGenericError(xmlGenericErrorContext,
+ "XInclude error: would result in multiple root nodes\n");
+ ctxt->nbErrors++;
+ return(-1);
+ }
+ }
+
+ /*
* Change the current node as an XInclude start one, and add an
* entity end one
*/
@@ -1705,8 +1951,6 @@
/*
* Add the list of nodes
*/
- list = ctxt->incTab[nr]->inc;
- ctxt->incTab[nr]->inc = NULL;
while (list != NULL) {
cur = list;
list = list->next;
@@ -1720,6 +1964,7 @@
/**
* xmlXIncludeTestNode:
+ * @ctxt: the XInclude processing context
* @node: an XInclude node
*
* test if the node is an XInclude node
@@ -1727,19 +1972,61 @@
* Returns 1 true, 0 otherwise
*/
static int
-xmlXIncludeTestNode(xmlNodePtr node) {
+xmlXIncludeTestNode(xmlXIncludeCtxtPtr ctxt, 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);
+ if (xmlStrEqual(node->ns->href, XINCLUDE_NS)) {
+ if (xmlStrEqual(node->name, XINCLUDE_NODE)) {
+ xmlNodePtr child = node->children;
+ int nb_fallback = 0;
+
+ while (child != NULL) {
+ if ((child->type == XML_ELEMENT_NODE) &&
+ (child->ns != NULL) &&
+ (xmlStrEqual(child->ns->href, XINCLUDE_NS))) {
+ if (xmlStrEqual(child->name, XINCLUDE_NODE)) {
+ xmlGenericError(xmlGenericErrorContext,
+ "XInclude: %s has an %s child\n",
+ XINCLUDE_NODE, XINCLUDE_NODE);
+ ctxt->nbErrors++;
+ return(0);
+ }
+ if (xmlStrEqual(child->name, XINCLUDE_FALLBACK)) {
+ nb_fallback++;
+ }
+ }
+ child = child->next;
+ }
+ if (nb_fallback > 1) {
+ xmlGenericError(xmlGenericErrorContext,
+ "XInclude: %s has %d %s children\n",
+ XINCLUDE_NODE, nb_fallback, XINCLUDE_FALLBACK);
+ ctxt->nbErrors++;
+ return(0);
+ }
+ return(1);
+ }
+ if (xmlStrEqual(node->name, XINCLUDE_FALLBACK)) {
+ if ((node->parent == NULL) ||
+ (node->parent->type != XML_ELEMENT_NODE) ||
+ (node->parent->ns == NULL) ||
+ (!xmlStrEqual(node->parent->ns->href, XINCLUDE_NS)) ||
+ (!xmlStrEqual(node->parent->name, XINCLUDE_NODE))) {
+ xmlGenericError(xmlGenericErrorContext,
+ "XInclude: %s is not the child of an %s\n",
+ XINCLUDE_FALLBACK, XINCLUDE_NODE);
+ ctxt->nbErrors++;
+ }
+ }
+ }
return(0);
}
/**
* xmlXIncludeDoProcess:
- * @ctxt:
+ * @ctxt: the XInclude processing context
* @doc: an XML document
*
* Implement the XInclude substitution on the XML document @doc
@@ -1758,22 +2045,28 @@
if (ctxt == NULL)
return(-1);
+ if (doc->URL != NULL) {
+ ret = xmlXIncludeURLPush(ctxt, doc->URL);
+ if (ret < 0)
+ return(-1);
+ }
+
/*
* First phase: lookup the elements in the document
*/
cur = xmlDocGetRootElement(doc);
- if (xmlXIncludeTestNode(cur))
+ if (xmlXIncludeTestNode(ctxt, cur) == 1)
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))
+ if (xmlXIncludeTestNode(ctxt, cur))
xmlXIncludePreProcessNode(ctxt, cur);
} else if (cur->next != NULL) {
cur = cur->next;
- if (xmlXIncludeTestNode(cur))
+ if (xmlXIncludeTestNode(ctxt, cur))
xmlXIncludePreProcessNode(ctxt, cur);
} else {
do {
@@ -1781,7 +2074,7 @@
if (cur == NULL) break; /* do */
if (cur->next != NULL) {
cur = cur->next;
- if (xmlXIncludeTestNode(cur))
+ if (xmlXIncludeTestNode(ctxt, cur))
xmlXIncludePreProcessNode(ctxt, cur);
break; /* do */
}
@@ -1800,10 +2093,14 @@
/*
* Third phase: extend the original document infoset.
*/
- for (i = ctxt->incBase;i < ctxt->incNr; i++) {
- xmlXIncludeIncludeNode(ctxt, i);
+ if (ctxt->nbErrors == 0) {
+ for (i = ctxt->incBase;i < ctxt->incNr; i++) {
+ xmlXIncludeIncludeNode(ctxt, i);
+ }
}
+ if (doc->URL != NULL)
+ xmlXIncludeURLPop(ctxt);
return(ret);
}