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);
 }