a bit of work done in the train back. added one of the include tests
* relaxng.c: a bit of work done in the train back.
* test/relaxng/*: added one of the include tests
Daniel
diff --git a/relaxng.c b/relaxng.c
index 86ad215..5dcd046 100644
--- a/relaxng.c
+++ b/relaxng.c
@@ -67,6 +67,9 @@
typedef struct _xmlRelaxNGDocument xmlRelaxNGDocument;
typedef xmlRelaxNGDocument *xmlRelaxNGDocumentPtr;
+typedef struct _xmlRelaxNGInclude xmlRelaxNGInclude;
+typedef xmlRelaxNGInclude *xmlRelaxNGIncludePtr;
+
typedef enum {
XML_RELAXNG_COMBINE_UNDEFINED = 0, /* undefined */
XML_RELAXNG_COMBINE_CHOICE, /* choice */
@@ -135,6 +138,7 @@
xmlHashTablePtr defs; /* define */
xmlHashTablePtr refs; /* references */
xmlHashTablePtr documents; /* all the documents loaded */
+ xmlHashTablePtr includes; /* all the includes loaded */
void *_private; /* unused by the library for users or bindings */
};
@@ -164,6 +168,7 @@
xmlHashTablePtr interleaves; /* keep track of all the interleaves */
xmlHashTablePtr documents; /* all the documents loaded */
+ xmlHashTablePtr includes; /* all the includes loaded */
xmlChar *URL;
xmlDocPtr document;
@@ -171,10 +176,16 @@
int size;
/* the document stack */
- xmlRelaxNGDocumentPtr doc; /* Current parsed Node */
+ xmlRelaxNGDocumentPtr doc; /* Current parsed external ref */
int docNr; /* Depth of the parsing stack */
int docMax; /* Max depth of the parsing stack */
xmlRelaxNGDocumentPtr *docTab; /* array of docs */
+
+ /* the include stack */
+ xmlRelaxNGIncludePtr inc; /* Current parsed include */
+ int incNr; /* Depth of the include parsing stack */
+ int incMax; /* Max depth of the parsing stack */
+ xmlRelaxNGIncludePtr *incTab; /* array of incs */
};
#define FLAGS_IGNORABLE 1
@@ -239,6 +250,18 @@
};
/**
+ * xmlRelaxNGInclude:
+ *
+ * Structure associated to a RelaxNGs document element
+ */
+struct _xmlRelaxNGInclude {
+ xmlChar *href; /* the normalized href value */
+ xmlDocPtr doc; /* the associated XML document */
+ xmlRelaxNGDefinePtr content;/* the definitions */
+ xmlRelaxNGPtr schema; /* the schema */
+};
+
+/**
* xmlRelaxNGDocument:
*
* Structure associated to a RelaxNGs document element
@@ -336,6 +359,27 @@
}
/**
+ * xmlRelaxNGFreeInclude:
+ * @incl: a include structure
+ *
+ * Deallocate a RelaxNG include structure.
+ */
+static void
+xmlRelaxNGFreeInclude(xmlRelaxNGIncludePtr incl)
+{
+ if (incl == NULL)
+ return;
+
+ if (incl->href != NULL)
+ xmlFree(incl->href);
+ if (incl->doc != NULL)
+ xmlFreeDoc(incl->doc);
+ if (incl->schema != NULL)
+ xmlRelaxNGFree(incl->schema);
+ xmlFree(incl);
+}
+
+/**
* xmlRelaxNGNewRelaxNG:
* @ctxt: a Relax-NG validation context (optional)
*
@@ -379,6 +423,9 @@
if (schema->documents != NULL)
xmlHashFree(schema->documents, (xmlHashDeallocator)
xmlRelaxNGFreeDocument);
+ if (schema->includes != NULL)
+ xmlHashFree(schema->includes, (xmlHashDeallocator)
+ xmlRelaxNGFreeInclude);
xmlFree(schema);
}
@@ -675,6 +722,266 @@
xmlDocPtr doc);
/**
+ * xmlRelaxNGIncludePush:
+ * @ctxt: the parser context
+ * @value: the element doc
+ *
+ * Pushes a new include on top of the include stack
+ *
+ * Returns 0 in case of error, the index in the stack otherwise
+ */
+static int
+xmlRelaxNGIncludePush(xmlRelaxNGParserCtxtPtr ctxt,
+ xmlRelaxNGIncludePtr value)
+{
+ if (ctxt->incTab == NULL) {
+ ctxt->incMax = 4;
+ ctxt->incNr = 0;
+ ctxt->incTab = (xmlRelaxNGIncludePtr *) xmlMalloc(
+ ctxt->incMax * sizeof(ctxt->incTab[0]));
+ if (ctxt->incTab == NULL) {
+ xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
+ return (0);
+ }
+ }
+ if (ctxt->incNr >= ctxt->incMax) {
+ ctxt->incMax *= 2;
+ ctxt->incTab =
+ (xmlRelaxNGIncludePtr *) xmlRealloc(ctxt->incTab,
+ ctxt->incMax *
+ sizeof(ctxt->incTab[0]));
+ if (ctxt->incTab == NULL) {
+ xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
+ return (0);
+ }
+ }
+ ctxt->incTab[ctxt->incNr] = value;
+ ctxt->inc = value;
+ return (ctxt->incNr++);
+}
+
+/**
+ * xmlRelaxNGIncludePop:
+ * @ctxt: the parser context
+ *
+ * Pops the top include from the include stack
+ *
+ * Returns the include just removed
+ */
+static xmlRelaxNGIncludePtr
+xmlRelaxNGIncludePop(xmlRelaxNGParserCtxtPtr ctxt)
+{
+ xmlRelaxNGIncludePtr ret;
+
+ if (ctxt->incNr <= 0)
+ return (0);
+ ctxt->incNr--;
+ if (ctxt->incNr > 0)
+ ctxt->inc = ctxt->incTab[ctxt->incNr - 1];
+ else
+ ctxt->inc = NULL;
+ ret = ctxt->incTab[ctxt->incNr];
+ ctxt->incTab[ctxt->incNr] = 0;
+ return (ret);
+}
+
+/**
+ * xmlRelaxNGLoadInclude:
+ * @ctxt: the parser context
+ * @URL: the normalized URL
+ * @node: the include node.
+ *
+ * First lookup if the document is already loaded into the parser context,
+ * check against recursion. If not found the resource is loaded and
+ * the content is preprocessed before being returned back to the caller.
+ *
+ * Returns the xmlRelaxNGIncludePtr or NULL in case of error
+ */
+static xmlRelaxNGIncludePtr
+xmlRelaxNGLoadInclude(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
+ xmlNodePtr node) {
+ xmlRelaxNGIncludePtr ret = NULL;
+ xmlDocPtr doc;
+ int i;
+ xmlNodePtr root, tmp, tmp2, cur;
+
+ /*
+ * check against recursion in the stack
+ */
+ for (i = 0;i < ctxt->incNr;i++) {
+ if (xmlStrEqual(ctxt->incTab[i]->href, URL)) {
+ if (ctxt->error != NULL)
+ ctxt->error(ctxt->userData,
+ "Detected an externalRef recursion for %s\n",
+ URL);
+ ctxt->nbErrors++;
+ return(NULL);
+ }
+ }
+
+ /*
+ * Lookup in the hash table
+ */
+ if (ctxt->includes == NULL) {
+ ctxt->includes = xmlHashCreate(10);
+ if (ctxt->includes == NULL) {
+ if (ctxt->error != NULL)
+ ctxt->error(ctxt->userData,
+ "Failed to allocate hash table for document\n");
+ ctxt->nbErrors++;
+ return(NULL);
+ }
+ } else {
+ ret = xmlHashLookup(ctxt->includes, URL);
+ if (ret != NULL)
+ return(ret);
+ }
+
+
+ /*
+ * load the document
+ */
+ doc = xmlParseFile((const char *) URL);
+ if (doc == NULL) {
+ if (ctxt->error != NULL)
+ ctxt->error(ctxt->userData,
+ "xmlRelaxNG: could not load %s\n", URL);
+ ctxt->nbErrors++;
+ return (NULL);
+ }
+
+ /*
+ * Allocate the document structures and register it first.
+ */
+ ret = (xmlRelaxNGIncludePtr) xmlMalloc(sizeof(xmlRelaxNGInclude));
+ if (ret == NULL) {
+ if (ctxt->error != NULL)
+ ctxt->error(ctxt->userData,
+ "xmlRelaxNG: allocate memory for doc %s\n", URL);
+ ctxt->nbErrors++;
+ xmlFreeDoc(doc);
+ return (NULL);
+ }
+ memset(ret, 0, sizeof(xmlRelaxNGInclude));
+ ret->doc = doc;
+ ret->href = xmlStrdup(URL);
+
+ /*
+ * push it on the stack and register it in the hash table
+ */
+ xmlHashAddEntry(ctxt->includes, URL, ret);
+ xmlRelaxNGIncludePush(ctxt, ret);
+
+ /*
+ * Some preprocessing of the document content, this include recursing
+ * in the include stack.
+ */
+ doc = xmlRelaxNGCleanupDoc(ctxt, doc);
+ if (doc == NULL) {
+ /* xmlFreeDoc(ctxt->include); */
+ ctxt->inc = NULL;
+ return(NULL);
+ }
+
+ /*
+ * Pop up the include from the stack
+ */
+ xmlRelaxNGIncludePop(ctxt);
+
+ /*
+ * Check that the top element is a grammar
+ */
+ root = xmlDocGetRootElement(doc);
+ if (root == NULL) {
+ if (ctxt->error != NULL)
+ ctxt->error(ctxt->userData,
+ "xmlRelaxNG: included document is empty %s\n", URL);
+ ctxt->nbErrors++;
+ xmlFreeDoc(doc);
+ return (NULL);
+ }
+ if (!IS_RELAXNG(root, "grammar")) {
+ if (ctxt->error != NULL)
+ ctxt->error(ctxt->userData,
+ "xmlRelaxNG: included document %s root is not a grammar\n",
+ URL);
+ ctxt->nbErrors++;
+ xmlFreeDoc(doc);
+ return (NULL);
+ }
+
+ /*
+ * Elimination of redefined rules in the include.
+ */
+ cur = node->children;
+ while (cur != NULL) {
+ if (IS_RELAXNG(cur, "start")) {
+ int found = 0;
+
+ tmp = root->children;
+ while (tmp != NULL) {
+ tmp2 = tmp->next;
+ if (IS_RELAXNG(tmp, "start")) {
+ found = 1;
+ xmlUnlinkNode(tmp);
+ xmlFreeNode(tmp);
+ }
+ tmp = tmp2;
+ }
+ if (!found) {
+ if (ctxt->error != NULL)
+ ctxt->error(ctxt->userData,
+ "xmlRelaxNG: include %s has a start but not the included grammar\n",
+ URL);
+ ctxt->nbErrors++;
+ }
+ } else if (IS_RELAXNG(cur, "define")) {
+ xmlChar *name, *name2;
+
+ name = xmlGetProp(cur, BAD_CAST "name");
+ if (name == NULL) {
+ if (ctxt->error != NULL)
+ ctxt->error(ctxt->userData,
+ "xmlRelaxNG: include %s has define without name\n",
+ URL);
+ ctxt->nbErrors++;
+ } else {
+ int found = 0;
+
+ tmp = root->children;
+ while (tmp != NULL) {
+ tmp2 = tmp->next;
+ if (IS_RELAXNG(tmp, "define")) {
+ name2 = xmlGetProp(tmp, BAD_CAST "name");
+ if (name2 != NULL) {
+ if (xmlStrEqual(name, name2)) {
+ found = 1;
+ xmlUnlinkNode(tmp);
+ xmlFreeNode(tmp);
+ }
+ xmlFree(name2);
+ }
+ }
+ tmp = tmp2;
+ }
+ if (!found) {
+ if (ctxt->error != NULL)
+ ctxt->error(ctxt->userData,
+ "xmlRelaxNG: include %s has a define %s but not the included grammar\n",
+ URL, name);
+ ctxt->nbErrors++;
+ }
+ xmlFree(name);
+ }
+ }
+ cur = cur->next;
+ }
+
+
+ return(ret);
+}
+
+/**
* xmlRelaxNGDocumentPush:
* @ctxt: the parser context
* @value: the element doc
@@ -739,7 +1046,7 @@
}
/**
- * xmlRelaxNGLoadocument:
+ * xmlRelaxNGLoadExternalRef:
* @ctxt: the parser context
* @URL: the normalized URL
* @ns: the inherited ns if any
@@ -751,7 +1058,7 @@
* Returns the xmlRelaxNGDocumentPtr or NULL in case of error
*/
static xmlRelaxNGDocumentPtr
-xmlRelaxNGLoaddocument(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
+xmlRelaxNGLoadExternalRef(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
const xmlChar *ns) {
xmlRelaxNGDocumentPtr ret = NULL;
xmlDocPtr doc;
@@ -2855,6 +3162,8 @@
xmlRelaxNGFreeDocument);
if (ctxt->docTab != NULL)
xmlFree(ctxt->docTab);
+ if (ctxt->incTab != NULL)
+ xmlFree(ctxt->incTab);
xmlFree(ctxt);
}
@@ -2904,7 +3213,23 @@
delete = cur;
goto skip_children;
} else {
- if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
+ if (xmlStrEqual(cur->name, BAD_CAST "div")) {
+ /*
+ * implements rule 4.11
+ */
+ xmlNodePtr child, ins, tmp;
+
+ child = cur->children;
+ ins = child;
+ while (child != NULL) {
+ tmp = child->next;
+ xmlUnlinkNode(child);
+ ins = xmlAddNextSibling(ins, child);
+ child = tmp;
+ }
+ delete = cur;
+ goto skip_children;
+ } else if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
xmlChar *href, *ns, *base, *URL;
xmlRelaxNGDocumentPtr docu;
@@ -2936,7 +3261,7 @@
xmlFree(href);
if (base != NULL)
xmlFree(base);
- docu = xmlRelaxNGLoaddocument(ctxt, URL, ns);
+ docu = xmlRelaxNGLoadExternalRef(ctxt, URL, ns);
if (docu == NULL) {
if (ctxt->error != NULL)
ctxt->error(ctxt->userData,
@@ -2949,7 +3274,48 @@
xmlFree(URL);
cur->_private = docu;
} else if (xmlStrEqual(cur->name, BAD_CAST "include")) {
- TODO
+ xmlChar *href, *base, *URL;
+ xmlRelaxNGIncludePtr incl;
+
+ href = xmlGetProp(cur, BAD_CAST "href");
+ if (href == NULL) {
+ if (ctxt->error != NULL)
+ ctxt->error(ctxt->userData,
+ "xmlRelaxNGParse: externalRef has no href attribute\n");
+ ctxt->nbErrors++;
+ delete = cur;
+ goto skip_children;
+ }
+ base = xmlNodeGetBase(cur->doc, cur);
+ URL = xmlBuildURI(href, base);
+ if (URL == NULL) {
+ if (ctxt->error != NULL)
+ ctxt->error(ctxt->userData,
+ "Failed to compute URL for externalRef %s\n", href);
+ ctxt->nbErrors++;
+ if (href != NULL)
+ xmlFree(href);
+ if (base != NULL)
+ xmlFree(base);
+ delete = cur;
+ goto skip_children;
+ }
+ if (href != NULL)
+ xmlFree(href);
+ if (base != NULL)
+ xmlFree(base);
+ incl = xmlRelaxNGLoadInclude(ctxt, URL, cur);
+ if (incl == NULL) {
+ if (ctxt->error != NULL)
+ ctxt->error(ctxt->userData,
+ "Failed to load externalRef %s\n", URL);
+ ctxt->nbErrors++;
+ xmlFree(URL);
+ delete = cur;
+ goto skip_children;
+ }
+ xmlFree(URL);
+ cur->_private = incl;
} else if ((xmlStrEqual(cur->name, BAD_CAST "element")) ||
(xmlStrEqual(cur->name, BAD_CAST "attribute"))) {
xmlChar *name;