First commit of the new Relax-NG validation code, not generally useful

* Makefile.am configure.in relaxng.c include/libxml/relaxng.h:
  First commit of the new Relax-NG validation code, not generally
  useful yet.
* test/relaxng/* result/relaxng/*: current state of the regression
  tests
Daniel
diff --git a/relaxng.c b/relaxng.c
new file mode 100644
index 0000000..8777037
--- /dev/null
+++ b/relaxng.c
@@ -0,0 +1,2662 @@
+/*
+ * relaxng.c : implementation of the Relax-NG handling and validity checking
+ *
+ * See Copyright for the status of this software.
+ *
+ * Daniel Veillard <veillard@redhat.com>
+ */
+
+#define IN_LIBXML
+#include "libxml.h"
+
+#ifdef LIBXML_SCHEMAS_ENABLED
+
+#include <string.h>
+#include <stdio.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/parserInternals.h>
+#include <libxml/hash.h>
+#include <libxml/uri.h>
+
+#include <libxml/relaxng.h>
+
+#include <libxml/xmlschemastypes.h>
+#include <libxml/xmlautomata.h>
+#include <libxml/xmlregexp.h>
+
+/*
+ * The Relax-NG namespace
+ */
+static const xmlChar *xmlRelaxNGNs = (const xmlChar *)
+    "http://relaxng.org/ns/structure/1.0";
+
+#define IS_RELAXNG(node, type)						\
+   ((node != NULL) && (node->ns != NULL) &&				\
+    (xmlStrEqual(node->name, (const xmlChar *) type)) &&		\
+    (xmlStrEqual(node->ns->href, xmlRelaxNGNs)))
+
+
+#define DEBUG 1                 /* very verbose output */
+#define DEBUG_CONTENT 1
+#define DEBUG_TYPE 1
+/* #define DEBUG_CONTENT_REGEXP 1 */
+/* #define DEBUG_AUTOMATA 1 */
+
+#define UNBOUNDED (1 << 30)
+#define TODO 								\
+    xmlGenericError(xmlGenericErrorContext,				\
+	    "Unimplemented block at %s:%d\n",				\
+            __FILE__, __LINE__);
+
+typedef struct _xmlRelaxNGSchema xmlRelaxNGSchema;
+typedef xmlRelaxNGSchema *xmlRelaxNGSchemaPtr;
+
+typedef struct _xmlRelaxNGDefine xmlRelaxNGDefine;
+typedef xmlRelaxNGDefine *xmlRelaxNGDefinePtr;
+
+typedef enum {
+    XML_RELAXNG_COMBINE_UNDEFINED = 0,	/* undefined */
+    XML_RELAXNG_COMBINE_CHOICE,		/* choice */
+    XML_RELAXNG_COMBINE_INTERLEAVE	/* interleave */
+} xmlRelaxNGCombine;
+
+typedef struct _xmlRelaxNGGrammar xmlRelaxNGGrammar;
+typedef xmlRelaxNGGrammar *xmlRelaxNGGrammarPtr;
+
+struct _xmlRelaxNGGrammar {
+    xmlRelaxNGGrammarPtr parent;/* the parent grammar if any */
+    xmlRelaxNGGrammarPtr children;/* the children grammar if any */
+    xmlRelaxNGGrammarPtr next;	/* the next grammar if any */
+    xmlRelaxNGDefinePtr start;	/* <start> content */
+    xmlRelaxNGCombine combine;	/* the default combine value */
+    xmlHashTablePtr defs;	/* define* */
+    xmlHashTablePtr refs;	/* references */
+};
+
+
+#if 0
+struct _xmlRelaxNGSchema {
+};
+#endif
+
+typedef enum {
+    XML_RELAXNG_EMPTY = 0,	/* an empty pattern */
+    XML_RELAXNG_NOT_ALLOWED,    /* not allowed top */
+    XML_RELAXNG_TEXT,		/* textual content */
+    XML_RELAXNG_ELEMENT,	/* an element */
+    XML_RELAXNG_DATATYPE,	/* extenal data type definition */
+    XML_RELAXNG_VALUE,		/* value from an extenal data type definition */
+    XML_RELAXNG_LIST,		/* a list of patterns */
+    XML_RELAXNG_ATTRIBUTE,	/* an attrbute following a pattern */
+    XML_RELAXNG_DEF,		/* a definition */
+    XML_RELAXNG_REF,		/* reference to a definition */
+    XML_RELAXNG_OPTIONAL,	/* optional patterns */
+    XML_RELAXNG_ZEROORMORE,	/* zero or more non empty patterns */
+    XML_RELAXNG_ONEORMORE,	/* one or more non empty patterns */
+    XML_RELAXNG_CHOICE,		/* a choice between non empty patterns */
+    XML_RELAXNG_GROUP,		/* a pair/group of non empty patterns */
+    XML_RELAXNG_INTERLEAVE	/* interleaving choice of non-empty patterns */
+} xmlRelaxNGType;
+
+struct _xmlRelaxNGDefine {
+    xmlRelaxNGType type;	/* the type of definition */
+    xmlNodePtr	   node;	/* the node in the source */
+    xmlChar       *name;	/* the element local name if present */
+    xmlChar       *ns;		/* the namespace local name if present */
+    xmlRelaxNGDefinePtr content;/* the expected content */
+    xmlRelaxNGDefinePtr next;	/* list within grouping sequences */
+    xmlRelaxNGDefinePtr attrs;	/* list of attributes for elements */
+    xmlRelaxNGDefinePtr nextHash;/* next define in defs/refs hash tables */
+};
+
+/**
+ * _xmlRelaxNG:
+ *
+ * A RelaxNGs definition
+ */
+struct _xmlRelaxNG {
+    xmlRelaxNGGrammarPtr topgrammar;
+    xmlDocPtr doc;
+
+    xmlHashTablePtr defs;	/* define */
+    xmlHashTablePtr refs;	/* references */
+    void *_private;	/* unused by the library for users or bindings */
+};
+
+typedef enum {
+    XML_RELAXNG_ERR_OK		= 0,
+    XML_RELAXNG_ERR_NOROOT	= 1,
+    XML_RELAXNG_ERR_
+} xmlRelaxNGValidError;
+
+#define XML_RELAXNG_IN_ATTRIBUTE	1
+
+struct _xmlRelaxNGParserCtxt {
+    void *userData;			/* user specific data block */
+    xmlRelaxNGValidityErrorFunc error;	/* the callback in case of errors */
+    xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
+    xmlRelaxNGValidError err;
+
+    xmlRelaxNGPtr      schema;        /* The schema in use */
+    xmlRelaxNGGrammarPtr grammar;     /* the current grammar */
+    int                flags;         /* parser flags */
+    int                nbErrors;      /* number of errors at parse time */
+    int                nbWarnings;    /* number of warnings at parse time */
+
+    xmlChar	      *URL;
+    xmlDocPtr          doc;
+
+    const char     *buffer;
+    int               size;
+
+    /*
+     * Used to build complex element content models
+     */
+    xmlAutomataPtr     am;
+    xmlAutomataStatePtr start;
+    xmlAutomataStatePtr end;
+    xmlAutomataStatePtr state;
+};
+
+#define FLAGS_IGNORABLE		1
+#define FLAGS_NEGATIVE		2
+
+/**
+ * xmlRelaxNGValidState:
+ *
+ * A RelaxNGs validation state
+ */
+#define MAX_ATTR 20
+typedef struct _xmlRelaxNGValidState xmlRelaxNGValidState;
+typedef xmlRelaxNGValidState *xmlRelaxNGValidStatePtr;
+struct _xmlRelaxNGValidState {
+    xmlNodePtr node;		/* the current node */
+    xmlNodePtr  seq;		/* the sequence of children left to validate */
+    int     nbAttrs;		/* the number of attributes */
+    xmlChar  *value;		/* the value when operating on string */
+    xmlAttrPtr attrs[1];	/* the array of attributes */
+};
+
+/**
+ * xmlRelaxNGValidCtxt:
+ *
+ * A RelaxNGs validation context
+ */
+
+struct _xmlRelaxNGValidCtxt {
+    void *userData;			/* user specific data block */
+    xmlRelaxNGValidityErrorFunc error;	/* the callback in case of errors */
+    xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
+
+    xmlRelaxNGPtr           schema;	/* The schema in use */
+    xmlDocPtr               doc;	/* the document being validated */
+    xmlRelaxNGValidStatePtr state;	/* the current validation state */
+    int                     flags;	/* validation flags */
+};
+
+/************************************************************************
+ * 									*
+ * 			Allocation functions				*
+ * 									*
+ ************************************************************************/
+static void xmlRelaxNGFreeDefineList(xmlRelaxNGDefinePtr defines);
+static void xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar);
+static void xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define);
+
+/**
+ * xmlRelaxNGNewRelaxNG:
+ * @ctxt:  a Relax-NG validation context (optional)
+ *
+ * Allocate a new RelaxNG structure.
+ *
+ * Returns the newly allocated structure or NULL in case or error
+ */
+static xmlRelaxNGPtr
+xmlRelaxNGNewRelaxNG(xmlRelaxNGParserCtxtPtr ctxt)
+{
+    xmlRelaxNGPtr ret;
+
+    ret = (xmlRelaxNGPtr) xmlMalloc(sizeof(xmlRelaxNG));
+    if (ret == NULL) {
+        if ((ctxt != NULL) && (ctxt->error != NULL))
+            ctxt->error(ctxt->userData, "Out of memory\n");
+	ctxt->nbErrors++;
+        return (NULL);
+    }
+    memset(ret, 0, sizeof(xmlRelaxNG));
+
+    return (ret);
+}
+
+/**
+ * xmlRelaxNGFree:
+ * @schema:  a schema structure
+ *
+ * Deallocate a RelaxNG structure.
+ */
+void
+xmlRelaxNGFree(xmlRelaxNGPtr schema)
+{
+    if (schema == NULL)
+        return;
+
+#if 0
+    if (schema->elemDecl != NULL)
+        xmlHashFree(schema->elemDecl,
+                    (xmlHashDeallocator) xmlRelaxNGFreeElement);
+    if (schema->typeDecl != NULL)
+        xmlHashFree(schema->typeDecl,
+                    (xmlHashDeallocator) xmlRelaxNGFreeType);
+#endif
+
+    if (schema->topgrammar != NULL)
+	xmlRelaxNGFreeGrammar(schema->topgrammar);
+    if (schema->doc != NULL)
+	xmlFreeDoc(schema->doc);
+
+    xmlFree(schema);
+}
+
+/**
+ * xmlRelaxNGNewGrammar:
+ * @ctxt:  a Relax-NG validation context (optional)
+ *
+ * Allocate a new RelaxNG grammar.
+ *
+ * Returns the newly allocated structure or NULL in case or error
+ */
+static xmlRelaxNGGrammarPtr
+xmlRelaxNGNewGrammar(xmlRelaxNGParserCtxtPtr ctxt)
+{
+    xmlRelaxNGGrammarPtr ret;
+
+    ret = (xmlRelaxNGGrammarPtr) xmlMalloc(sizeof(xmlRelaxNGGrammar));
+    if (ret == NULL) {
+        if ((ctxt != NULL) && (ctxt->error != NULL))
+            ctxt->error(ctxt->userData, "Out of memory\n");
+	ctxt->nbErrors++;
+        return (NULL);
+    }
+    memset(ret, 0, sizeof(xmlRelaxNGGrammar));
+
+    return (ret);
+}
+
+/**
+ * xmlRelaxNGFreeGrammar:
+ * @grammar:  a grammar structure
+ *
+ * Deallocate a RelaxNG grammar structure.
+ */
+static void
+xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar)
+{
+    if (grammar == NULL)
+        return;
+
+    if (grammar->start != NULL)
+	xmlRelaxNGFreeDefine(grammar->start);
+    if (grammar->refs != NULL) {
+	xmlHashFree(grammar->refs, NULL);
+    }
+    if (grammar->defs != NULL) {
+	xmlHashFree(grammar->defs, NULL);
+    }
+
+    xmlFree(grammar);
+}
+
+/**
+ * xmlRelaxNGNewDefine:
+ * @ctxt:  a Relax-NG validation context
+ * @node:  the node in the input document.
+ *
+ * Allocate a new RelaxNG define.
+ *
+ * Returns the newly allocated structure or NULL in case or error
+ */
+static xmlRelaxNGDefinePtr
+xmlRelaxNGNewDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
+{
+    xmlRelaxNGDefinePtr ret;
+
+    ret = (xmlRelaxNGDefinePtr) xmlMalloc(sizeof(xmlRelaxNGDefine));
+    if (ret == NULL) {
+        if ((ctxt != NULL) && (ctxt->error != NULL))
+            ctxt->error(ctxt->userData, "Out of memory\n");
+	ctxt->nbErrors++;
+        return (NULL);
+    }
+    memset(ret, 0, sizeof(xmlRelaxNGDefine));
+    ret->node = node;
+
+    return (ret);
+}
+
+/**
+ * xmlRelaxNGFreeDefineList:
+ * @defines:  a list of define structures
+ *
+ * Deallocate a RelaxNG define structures.
+ */
+static void
+xmlRelaxNGFreeDefineList(xmlRelaxNGDefinePtr defines)
+{
+    xmlRelaxNGDefinePtr next;
+
+    while (defines != NULL) {
+	next = defines->next;
+	xmlRelaxNGFreeDefine(defines);
+	defines = next;
+    }
+}
+
+/**
+ * xmlRelaxNGFreeDefine:
+ * @define:  a define structure
+ *
+ * Deallocate a RelaxNG define structure.
+ */
+static void
+xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define)
+{
+    if (define == NULL)
+        return;
+
+    if (define->name != NULL)
+	xmlFree(define->name);
+    if (define->ns != NULL)
+	xmlFree(define->ns);
+    if (define->attrs != NULL)
+	xmlRelaxNGFreeDefineList(define->attrs);
+    if (define->content != NULL)
+	xmlRelaxNGFreeDefineList(define->content);
+    xmlFree(define);
+}
+
+/**
+ * xmlRelaxNGNewValidState:
+ * @ctxt:  a Relax-NG validation context
+ * @node:  the current node or NULL for the document
+ *
+ * Allocate a new RelaxNG validation state
+ *
+ * Returns the newly allocated structure or NULL in case or error
+ */
+static xmlRelaxNGValidStatePtr
+xmlRelaxNGNewValidState(xmlRelaxNGValidCtxtPtr ctxt, xmlNodePtr node)
+{
+    xmlRelaxNGValidStatePtr ret;
+    xmlAttrPtr attr;
+    xmlAttrPtr attrs[MAX_ATTR];
+    int nbAttrs = 0;
+    xmlNodePtr root = NULL;
+
+    if (node == NULL) {
+	root = xmlDocGetRootElement(ctxt->doc);
+	if (root == NULL)
+	    return(NULL);
+    } else {
+	attr = node->properties;
+	while (attr != NULL) {
+	    if (nbAttrs < MAX_ATTR)
+		attrs[nbAttrs++] = attr;
+	    else
+		nbAttrs++;
+	    attr = attr->next;
+	}
+    }
+    
+    if (nbAttrs < MAX_ATTR)
+	attrs[nbAttrs] = NULL;
+    ret = (xmlRelaxNGValidStatePtr) xmlMalloc(sizeof(xmlRelaxNGValidState) +
+	                                      nbAttrs * sizeof(xmlAttrPtr));
+    if (ret == NULL) {
+        if ((ctxt != NULL) && (ctxt->error != NULL))
+            ctxt->error(ctxt->userData, "Out of memory\n");
+        return (NULL);
+    }
+    if (node == NULL) {
+	ret->node = (xmlNodePtr) ctxt->doc;
+	ret->seq = root;
+	ret->nbAttrs = 0;
+    } else {
+	ret->node = node;
+	ret->seq = node->children;
+	ret->nbAttrs = nbAttrs;
+	if (nbAttrs > 0) {
+	    if (nbAttrs < MAX_ATTR) {
+		memcpy(&(ret->attrs[0]), attrs,
+			sizeof(xmlAttrPtr) * (nbAttrs + 1));
+	    } else {
+		attr = node->properties;
+		nbAttrs = 0;
+		while (attr != NULL) {
+		    ret->attrs[nbAttrs++] = attr;
+		    attr = attr->next;
+		}
+		ret->attrs[nbAttrs] = NULL;
+	    }
+	}
+    }
+    return (ret);
+}
+
+/**
+ * xmlRelaxNGCopyValidState:
+ * @ctxt:  a Relax-NG validation context
+ * @state:  a validation state
+ *
+ * Copy the validation state
+ *
+ * Returns the newly allocated structure or NULL in case or error
+ */
+static xmlRelaxNGValidStatePtr
+xmlRelaxNGCopyValidState(xmlRelaxNGValidCtxtPtr ctxt,
+	                 xmlRelaxNGValidStatePtr state)
+{
+    xmlRelaxNGValidStatePtr ret;
+    unsigned int size;
+
+    if (state == NULL)
+	return(NULL);
+    
+    size = sizeof(xmlRelaxNGValidState) +
+	   state->nbAttrs * sizeof(xmlAttrPtr);
+    ret = (xmlRelaxNGValidStatePtr) xmlMalloc(size);
+    if (ret == NULL) {
+        if ((ctxt != NULL) && (ctxt->error != NULL))
+            ctxt->error(ctxt->userData, "Out of memory\n");
+        return (NULL);
+    }
+    memcpy(ret, state, size);
+    return(ret);
+}
+
+/**
+ * xmlRelaxNGFreeValidState:
+ * @state:  a validation state structure
+ *
+ * Deallocate a RelaxNG validation state structure.
+ */
+static void
+xmlRelaxNGFreeValidState(xmlRelaxNGValidStatePtr state)
+{
+    if (state == NULL)
+        return;
+
+    xmlFree(state);
+}
+
+/************************************************************************
+ * 									*
+ * 			Error functions					*
+ * 									*
+ ************************************************************************/
+
+#define VALID_CTXT() 							\
+    if (ctxt->flags == 0) xmlGenericError(xmlGenericErrorContext,	\
+	    "error detected at %s:%d\n",				\
+            __FILE__, __LINE__);
+#define VALID_ERROR if (ctxt->flags == 0) printf
+
+#if 0
+/**
+ * xmlRelaxNGErrorContext:
+ * @ctxt:  the parsing context
+ * @schema:  the schema being built
+ * @node:  the node being processed
+ * @child:  the child being processed
+ *
+ * Dump a RelaxNGType structure
+ */
+static void
+xmlRelaxNGErrorContext(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGPtr schema,
+                      xmlNodePtr node, xmlNodePtr child)
+{
+    int line = 0;
+    const xmlChar *file = NULL;
+    const xmlChar *name = NULL;
+    const char *type = "error";
+
+    if ((ctxt == NULL) || (ctxt->error == NULL))
+	return;
+
+    if (child != NULL)
+	node = child;
+
+    if (node != NULL)  {
+	if ((node->type == XML_DOCUMENT_NODE) ||
+	    (node->type == XML_HTML_DOCUMENT_NODE)) {
+	    xmlDocPtr doc = (xmlDocPtr) node;
+
+	    file = doc->URL;
+	} else {
+	    /*
+	     * Try to find contextual informations to report
+	     */
+	    if (node->type == XML_ELEMENT_NODE) {
+		line = (int) node->content;
+	    } else if ((node->prev != NULL) &&
+		       (node->prev->type == XML_ELEMENT_NODE)) {
+		line = (int) node->prev->content;
+	    } else if ((node->parent != NULL) &&
+		       (node->parent->type == XML_ELEMENT_NODE)) {
+		line = (int) node->parent->content;
+	    }
+	    if ((node->doc != NULL) && (node->doc->URL != NULL))
+		file = node->doc->URL;
+	    if (node->name != NULL)
+		name = node->name;
+	}
+    } 
+    
+    if (ctxt != NULL)
+	type = "compilation error";
+    else if (schema != NULL)
+	type = "runtime error";
+
+    if ((file != NULL) && (line != 0) && (name != NULL))
+	ctxt->error(ctxt->userData, "%s: file %s line %d element %s\n",
+		type, file, line, name);
+    else if ((file != NULL) && (name != NULL))
+	ctxt->error(ctxt->userData, "%s: file %s element %s\n",
+		type, file, name);
+    else if ((file != NULL) && (line != 0))
+	ctxt->error(ctxt->userData, "%s: file %s line %d\n", type, file, line);
+    else if (file != NULL)
+	ctxt->error(ctxt->userData, "%s: file %s\n", type, file);
+    else if (name != NULL)
+	ctxt->error(ctxt->userData, "%s: element %s\n", type, name);
+    else
+	ctxt->error(ctxt->userData, "%s\n", type);
+}
+#endif
+
+/************************************************************************
+ * 									*
+ * 			Type library hooks				*
+ * 									*
+ ************************************************************************/
+
+static void
+xmlRelaxNGInitTypes(void) {
+}
+
+/**
+ * xmlRelaxNGCleanupTypes:
+ *
+ * Cleanup the default Schemas type library associated to RelaxNG
+ */
+void	
+xmlRelaxNGCleanupTypes(void) {
+    xmlSchemaCleanupTypes();
+}
+
+/************************************************************************
+ * 									*
+ * 			Parsing functions				*
+ * 									*
+ ************************************************************************/
+
+static xmlRelaxNGDefinePtr xmlRelaxNGParseAttribute(
+	      xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
+static xmlRelaxNGDefinePtr xmlRelaxNGParseElement(
+	      xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
+static xmlRelaxNGDefinePtr xmlRelaxNGParsePatterns(
+	      xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
+
+
+#define IS_BLANK_NODE(n)						\
+    (((n)->type == XML_TEXT_NODE) && (xmlRelaxNGIsBlank((n)->content)))
+
+/**
+ * xmlRelaxNGIsBlank:
+ * @str:  a string
+ *
+ * Check if a string is ignorable c.f. 4.2. Whitespace
+ *
+ * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
+ */
+static int
+xmlRelaxNGIsBlank(xmlChar *str) {
+    if (str == NULL)
+	return(1);
+    while (*str != 0) {
+	if (!(IS_BLANK(*str))) return(0);
+	str++;
+    }
+    return(1);
+}
+
+#if 0
+/**
+ * xmlRelaxNGGetDataTypeLibrary:
+ * @ctxt:  a Relax-NG parser context
+ * @node:  the current data or value element
+ *
+ * Applies algorithm from 4.3. datatypeLibrary attribute
+ *
+ * Returns the datatypeLibary value or NULL if not found
+ */
+static xmlChar *
+xmlRelaxNGGetDataTypeLibrary(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
+	                     xmlNodePtr node) {
+    xmlChar *ret, *escape;
+
+#ifdef DEBUG
+    xmlGenericError(xmlGenericErrorContext,
+		    "xmlRelaxNGGetDataTypeLibrary()\n");
+#endif
+
+    if ((IS_RELAXNG(node, "data")) || (IS_RELAXNG(node, "value"))) {
+	ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
+	if (ret != NULL) {
+	    escape = xmlURIEscapeStr(ret, BAD_CAST "");
+	    if (escape == NULL) {
+		return(ret);
+	    }
+	    xmlFree(ret);
+	    return(escape);
+	}
+    }
+    node = node->parent;
+    while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) {
+	if (IS_RELAXNG(node, "element")) {
+	    ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
+	    if (ret != NULL) {
+		escape = xmlURIEscapeStr(ret, BAD_CAST "");
+		if (escape == NULL) {
+		    return(ret);
+		}
+		xmlFree(ret);
+		return(escape);
+	    }
+	}
+	node = node->parent;
+    }
+    return(NULL);
+}
+#endif
+
+/**
+ * xmlRelaxNGParsePattern:
+ * @ctxt:  a Relax-NG parser context
+ * @node:  the pattern node.
+ *
+ * parse the content of a RelaxNG pattern node.
+ *
+ * Returns the definition pointer or NULL in case of error.
+ */
+static xmlRelaxNGDefinePtr
+xmlRelaxNGParsePattern(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
+    xmlRelaxNGDefinePtr def = NULL;
+
+    if (IS_RELAXNG(node, "element")) {
+	def = xmlRelaxNGParseElement(ctxt, node);
+    } else if (IS_RELAXNG(node, "attribute")) {
+	def = xmlRelaxNGParseAttribute(ctxt, node);
+    } else if (IS_RELAXNG(node, "empty")) {
+	def = xmlRelaxNGNewDefine(ctxt, node);
+	if (def == NULL)
+	    return(NULL);
+	def->type = XML_RELAXNG_EMPTY;
+    } else if (IS_RELAXNG(node, "text")) {
+	def = xmlRelaxNGNewDefine(ctxt, node);
+	if (def == NULL)
+	    return(NULL);
+	def->type = XML_RELAXNG_TEXT;
+	if (node->children != NULL) {
+	    if (ctxt->error != NULL)
+		ctxt->error(ctxt->userData, "text: had a child node\n");
+	    ctxt->nbErrors++;
+	}
+    } else if (IS_RELAXNG(node, "zeroOrMore")) {
+	def = xmlRelaxNGNewDefine(ctxt, node);
+	if (def == NULL)
+	    return(NULL);
+	def->type = XML_RELAXNG_ZEROORMORE;
+	def->content = xmlRelaxNGParsePatterns(ctxt, node->children);
+    } else if (IS_RELAXNG(node, "oneOrMore")) {
+	def = xmlRelaxNGNewDefine(ctxt, node);
+	if (def == NULL)
+	    return(NULL);
+	def->type = XML_RELAXNG_ZEROORMORE;
+	def->content = xmlRelaxNGParsePatterns(ctxt, node->children);
+    } else if (IS_RELAXNG(node, "optional")) {
+	def = xmlRelaxNGNewDefine(ctxt, node);
+	if (def == NULL)
+	    return(NULL);
+	def->type = XML_RELAXNG_OPTIONAL;
+	def->content = xmlRelaxNGParsePatterns(ctxt, node->children);
+    } else if (IS_RELAXNG(node, "choice")) {
+	def = xmlRelaxNGNewDefine(ctxt, node);
+	if (def == NULL)
+	    return(NULL);
+	def->type = XML_RELAXNG_CHOICE;
+	def->content = xmlRelaxNGParsePatterns(ctxt, node->children);
+    } else if (IS_RELAXNG(node, "group")) {
+	def = xmlRelaxNGNewDefine(ctxt, node);
+	if (def == NULL)
+	    return(NULL);
+	def->type = XML_RELAXNG_GROUP;
+	def->content = xmlRelaxNGParsePatterns(ctxt, node->children);
+    } else if (IS_RELAXNG(node, "ref")) {
+	def = xmlRelaxNGNewDefine(ctxt, node);
+	if (def == NULL)
+	    return(NULL);
+	def->type = XML_RELAXNG_REF;
+	def->name = xmlGetProp(node, BAD_CAST "name");
+	if (def->name == NULL) {
+	    if (ctxt->error != NULL)
+		ctxt->error(ctxt->userData,
+			    "ref has no name\n");
+	    ctxt->nbErrors++;
+	}
+	if (node->children != NULL) {
+	    if (ctxt->error != NULL)
+		ctxt->error(ctxt->userData,
+			    "ref is not empty\n");
+	    ctxt->nbErrors++;
+	}
+	if (ctxt->grammar->refs == NULL)
+	    ctxt->grammar->refs = xmlHashCreate(10);
+	if (ctxt->grammar->refs == NULL) {
+	    if (ctxt->error != NULL)
+		ctxt->error(ctxt->userData,
+			    "Could not create references hash\n");
+	    ctxt->nbErrors++;
+	    xmlRelaxNGFreeDefine(def);
+	    def = NULL;
+	} else {
+	    int tmp;
+
+	    tmp = xmlHashAddEntry(ctxt->grammar->refs, def->name, def);
+	    if (tmp < 0) {
+		xmlRelaxNGDefinePtr prev;
+
+		prev = (xmlRelaxNGDefinePtr)
+		      xmlHashLookup(ctxt->grammar->refs, def->name);
+		if (prev == NULL) {
+		    if (ctxt->error != NULL)
+			ctxt->error(ctxt->userData,
+			    "Internal error refs definitions '%s'\n",
+				    def->name);
+		    ctxt->nbErrors++;
+		    xmlRelaxNGFreeDefine(def);
+		    def = NULL;
+		} else {
+		    def->nextHash = prev->nextHash;
+		    prev->nextHash = def;
+		}
+	    }
+	}
+    } else {
+	TODO
+    }
+    return(def);
+}
+
+/**
+ * xmlRelaxNGParseAttribute:
+ * @ctxt:  a Relax-NG parser context
+ * @node:  the element node
+ *
+ * parse the content of a RelaxNG attribute node.
+ *
+ * Returns the definition pointer or NULL in case of error.
+ */
+static xmlRelaxNGDefinePtr
+xmlRelaxNGParseAttribute(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
+    xmlRelaxNGDefinePtr ret, cur, last;
+    xmlNodePtr child;
+    xmlChar *val;
+    int old_flags;
+
+    ret = xmlRelaxNGNewDefine(ctxt, node);
+    if (ret == NULL)
+	return(NULL);
+    ret->type = XML_RELAXNG_ATTRIBUTE;
+    child = node->children;
+    if (child == NULL) {
+	if (ctxt->error != NULL)
+	    ctxt->error(ctxt->userData,
+		    "xmlRelaxNGParseattribute: attribute has no children\n");
+	ctxt->nbErrors++;
+	return(ret);
+    } 
+    old_flags = ctxt->flags;
+    ctxt->flags |= XML_RELAXNG_IN_ATTRIBUTE;
+    if (IS_RELAXNG(child, "name")) {
+	val = xmlNodeGetContent(child);
+	ret->name = val;
+	val = xmlGetProp(child, BAD_CAST "ns");
+	ret->ns = val;
+    } else if (IS_RELAXNG(child, "anyName")) {
+	TODO
+    } else if (IS_RELAXNG(child, "nsName")) {
+	TODO
+    } else if (IS_RELAXNG(child, "choice")) {
+	TODO
+    } else {
+	if (ctxt->error != NULL)
+	    ctxt->error(ctxt->userData,
+    "element: expecting name, anyName, nsName or choice : got %s\n",
+			child->name);
+	ctxt->nbErrors++;
+	ctxt->flags = old_flags;
+	return(ret);
+    }
+    child = child->next;
+    last = NULL;
+    while (child != NULL) {
+	cur = xmlRelaxNGParsePattern(ctxt, child);
+	if (cur != NULL) {
+	    switch (cur->type) {
+		case XML_RELAXNG_EMPTY:
+		case XML_RELAXNG_NOT_ALLOWED:
+		case XML_RELAXNG_TEXT:
+		case XML_RELAXNG_ELEMENT:
+		case XML_RELAXNG_DATATYPE:
+		case XML_RELAXNG_VALUE:
+		case XML_RELAXNG_LIST:
+		case XML_RELAXNG_REF:
+		case XML_RELAXNG_DEF:
+		case XML_RELAXNG_ONEORMORE:
+		case XML_RELAXNG_ZEROORMORE:
+		case XML_RELAXNG_OPTIONAL:
+		case XML_RELAXNG_CHOICE:
+		case XML_RELAXNG_GROUP:
+		case XML_RELAXNG_INTERLEAVE:
+		    if (last == NULL) {
+			ret->content = last = cur;
+		    } else {
+			if ((last->type == XML_RELAXNG_ELEMENT) &&
+			    (ret->content == last)) {
+			    ret->content = xmlRelaxNGNewDefine(ctxt, node);
+			    if (ret->content != NULL) {
+				ret->content->type = XML_RELAXNG_GROUP;
+				ret->content->content = last;
+			    } else {
+				ret->content = last;
+			    }
+			}
+			last->next = cur;
+			last = cur;
+		    }
+		    break;
+		case XML_RELAXNG_ATTRIBUTE:
+		    cur->next = ret->attrs;
+		    ret->attrs = cur;
+		    break;
+	    }
+	}
+	child = child->next;
+    }
+    ctxt->flags = old_flags;
+    return(ret);
+}
+
+/**
+ * xmlRelaxNGParseElement:
+ * @ctxt:  a Relax-NG parser context
+ * @node:  the element node
+ *
+ * parse the content of a RelaxNG element node.
+ *
+ * Returns the definition pointer or NULL in case of error.
+ */
+static xmlRelaxNGDefinePtr
+xmlRelaxNGParseElement(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
+    xmlRelaxNGDefinePtr ret, cur, last;
+    xmlNodePtr child;
+    xmlChar *val;
+
+    ret = xmlRelaxNGNewDefine(ctxt, node);
+    if (ret == NULL)
+	return(NULL);
+    ret->type = XML_RELAXNG_ELEMENT;
+    child = node->children;
+    if (child == NULL) {
+	if (ctxt->error != NULL)
+	    ctxt->error(ctxt->userData,
+			"xmlRelaxNGParseElement: element has no children\n");
+	ctxt->nbErrors++;
+	return(ret);
+    } 
+    if (IS_RELAXNG(child, "name")) {
+	val = xmlNodeGetContent(child);
+	ret->name = val;
+	val = xmlGetProp(child, BAD_CAST "ns");
+	ret->ns = val;
+    } else if (IS_RELAXNG(child, "anyName")) {
+	TODO
+    } else if (IS_RELAXNG(child, "nsName")) {
+	TODO
+    } else if (IS_RELAXNG(child, "choice")) {
+	TODO
+    } else {
+	if (ctxt->error != NULL)
+	    ctxt->error(ctxt->userData,
+    "element: expecting name, anyName, nsName or choice : got %s\n",
+			child->name);
+	ctxt->nbErrors++;
+	return(ret);
+    }
+    child = child->next;
+    if (child == NULL) {
+	if (ctxt->error != NULL)
+	    ctxt->error(ctxt->userData,
+			"xmlRelaxNGParseElement: element has no content\n");
+	ctxt->nbErrors++;
+	return(ret);
+    } 
+    last = NULL;
+    while (child != NULL) {
+	cur = xmlRelaxNGParsePattern(ctxt, child);
+	if (cur != NULL) {
+	    switch (cur->type) {
+		case XML_RELAXNG_EMPTY:
+		case XML_RELAXNG_NOT_ALLOWED:
+		case XML_RELAXNG_TEXT:
+		case XML_RELAXNG_ELEMENT:
+		case XML_RELAXNG_DATATYPE:
+		case XML_RELAXNG_VALUE:
+		case XML_RELAXNG_LIST:
+		case XML_RELAXNG_REF:
+		case XML_RELAXNG_DEF:
+		case XML_RELAXNG_ZEROORMORE:
+		case XML_RELAXNG_ONEORMORE:
+		case XML_RELAXNG_OPTIONAL:
+		case XML_RELAXNG_CHOICE:
+		case XML_RELAXNG_GROUP:
+		case XML_RELAXNG_INTERLEAVE:
+		    if (last == NULL) {
+			ret->content = last = cur;
+		    } else {
+			if ((last->type == XML_RELAXNG_ELEMENT) &&
+			    (ret->content == last)) {
+			    ret->content = xmlRelaxNGNewDefine(ctxt, node);
+			    if (ret->content != NULL) {
+				ret->content->type = XML_RELAXNG_GROUP;
+				ret->content->content = last;
+			    } else {
+				ret->content = last;
+			    }
+			}
+			last->next = cur;
+			last = cur;
+		    }
+		    break;
+		case XML_RELAXNG_ATTRIBUTE:
+		    cur->next = ret->attrs;
+		    ret->attrs = cur;
+		    break;
+	    }
+	}
+	child = child->next;
+    }
+    return(ret);
+}
+
+/**
+ * xmlRelaxNGParsePatterns:
+ * @ctxt:  a Relax-NG parser context
+ * @nodes:  list of nodes
+ *
+ * parse the content of a RelaxNG start node.
+ *
+ * Returns the definition pointer or NULL in case of error.
+ */
+static xmlRelaxNGDefinePtr
+xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
+    xmlRelaxNGDefinePtr def = NULL, last = NULL, cur;
+
+    while (nodes != NULL) {
+	if (IS_RELAXNG(nodes, "element")) {
+	    cur = xmlRelaxNGParseElement(ctxt, nodes);
+	    if (def == NULL) {
+		def = last = cur;
+	    } else {
+		if ((def->type == XML_RELAXNG_ELEMENT) && (def == last)) {
+		    def = xmlRelaxNGNewDefine(ctxt, nodes);
+		    def->type = XML_RELAXNG_GROUP;
+		    def->content = last;
+		}
+		last->next = cur;
+		last = cur;
+	    }
+	} else {
+	    cur = xmlRelaxNGParsePattern(ctxt, nodes);
+	    if (def == NULL) {
+		def = last = cur;
+	    } else {
+		last->next = cur;
+		last = cur;
+	    }
+	}
+	nodes = nodes->next;
+    }
+    return(def);
+}
+
+/**
+ * xmlRelaxNGParseStart:
+ * @ctxt:  a Relax-NG parser context
+ * @nodes:  start children nodes
+ *
+ * parse the content of a RelaxNG start node.
+ *
+ * Returns 0 in case of success, -1 in case of error
+ */
+static int
+xmlRelaxNGParseStart(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
+    int ret = 0;
+    xmlRelaxNGDefinePtr def = NULL;
+
+    while (nodes != NULL) {
+	if (IS_RELAXNG(nodes, "empty")) {
+	    TODO
+	    xmlElemDump(stdout, nodes->doc, nodes);
+	} else if (IS_RELAXNG(nodes, "notAllowed")) {
+	    TODO
+	    xmlElemDump(stdout, nodes->doc, nodes);
+	} else {
+	    def = xmlRelaxNGParsePatterns(ctxt, nodes);
+	    ctxt->grammar->start = def;
+	}
+	nodes = nodes->next;
+    }
+    return(ret);
+}
+
+/**
+ * xmlRelaxNGParseGrammarContent:
+ * @ctxt:  a Relax-NG parser context
+ * @nodes:  grammar children nodes
+ *
+ * parse the content of a RelaxNG grammar node.
+ *
+ * Returns 0 in case of success, -1 in case of error
+ */
+static int
+xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt
+                              ATTRIBUTE_UNUSED, xmlNodePtr nodes)
+{
+    int ret = 0, tmp;
+    xmlRelaxNGDefinePtr def;
+    xmlChar *name;
+
+    if (nodes == NULL) {
+	if (ctxt->error != NULL)
+	    ctxt->error(ctxt->userData,
+			"grammar has no children\n");
+	ctxt->nbErrors++;
+	return(-1);
+    }
+    if (IS_RELAXNG(nodes, "start")) {
+	if (nodes->children == NULL) {
+	    if (ctxt->error != NULL)
+		ctxt->error(ctxt->userData,
+			    "grammar has no children\n");
+	    ctxt->nbErrors++;
+	} else {
+	    xmlRelaxNGParseStart(ctxt, nodes->children);
+	}
+	nodes = nodes->next;
+    } else {
+	if (ctxt->error != NULL)
+	    ctxt->error(ctxt->userData,
+			"grammar first child must be a <start>\n");
+	ctxt->nbErrors++;
+	return(-1);
+    }
+    while (nodes != NULL) {
+        if (IS_RELAXNG(nodes, "define")) {
+	    name = xmlGetProp(nodes, BAD_CAST "name");
+	    if (name == NULL) {
+		if (ctxt->error != NULL)
+		    ctxt->error(ctxt->userData,
+				"define has no name\n");
+		ctxt->nbErrors++;
+	    } else {
+		def = xmlRelaxNGNewDefine(ctxt, nodes);
+		if (def == NULL)
+		    break;
+		def->type = XML_RELAXNG_DEF;
+		def->name = name;
+		if (nodes->children == NULL) {
+		    if (ctxt->error != NULL)
+			ctxt->error(ctxt->userData,
+				    "define has no children\n");
+		    ctxt->nbErrors++;
+		} else {
+		    def->content = xmlRelaxNGParsePatterns(ctxt,
+						       nodes->children);
+		}
+		if (ctxt->grammar->defs == NULL)
+		    ctxt->grammar->defs = xmlHashCreate(10);
+		if (ctxt->grammar->defs == NULL) {
+		    if (ctxt->error != NULL)
+			ctxt->error(ctxt->userData,
+				    "Could not create definition hash\n");
+		    ctxt->nbErrors++;
+		    ret = -1;
+		    xmlRelaxNGFreeDefine(def);
+		} else {
+		    tmp = xmlHashAddEntry(ctxt->grammar->defs, name, def);
+		    if (tmp < 0) {
+			TODO
+			/* store and implement 4.17 on combining */
+			ctxt->nbErrors++;
+			ret = -1;
+			xmlRelaxNGFreeDefine(def);
+		    }
+		}
+	    }
+        } else {
+	    if (ctxt->error != NULL)
+		ctxt->error(ctxt->userData,
+			"grammar allows onlys <define> child after <start>\n");
+	    ctxt->nbErrors++;
+	    ret = -1;
+	}
+        nodes = nodes->next;
+    }
+    return (ret);
+}
+
+/**
+ * xmlRelaxNGCheckReference:
+ * @ref:  the ref
+ * @ctxt:  a Relax-NG parser context
+ * @name:  the name associated to the defines
+ *
+ * Applies the 4.17. combine attribute rule for all the define
+ * element of a given grammar using the same name.
+ */
+static void
+xmlRelaxNGCheckReference(xmlRelaxNGDefinePtr ref,
+		xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
+    xmlRelaxNGGrammarPtr grammar;
+    xmlRelaxNGDefinePtr def;
+
+    grammar = ctxt->grammar;
+    if (grammar == NULL) {
+	if (ctxt->error != NULL)
+	    ctxt->error(ctxt->userData,
+		    "Internal error: no grammar in CheckReference %s\n",
+			name);
+	ctxt->nbErrors++;
+	return;
+    }
+    if (ref->content != NULL) {
+	if (ctxt->error != NULL)
+	    ctxt->error(ctxt->userData,
+	    "Internal error: reference has content in CheckReference %s\n",
+			name);
+	ctxt->nbErrors++;
+	return;
+    }
+    if (grammar->defs != NULL) {
+	def = xmlHashLookup(grammar->defs, name);
+	if (def != NULL) {
+	    ref->content = def;
+	} else {
+	    TODO
+	}
+    }
+    /*
+     * TODO: make a closure and verify there is no loop !
+     */
+}
+
+/**
+ * xmlRelaxNGCheckCombine:
+ * @define:  the define(s) list
+ * @ctxt:  a Relax-NG parser context
+ * @name:  the name associated to the defines
+ *
+ * Applies the 4.17. combine attribute rule for all the define
+ * element of a given grammar using the same name.
+ */
+static void
+xmlRelaxNGCheckCombine(xmlRelaxNGDefinePtr define,
+	xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
+    xmlChar *combine;
+    int choiceOrInterleave = -1;
+    int missing = 0;
+    xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
+
+    if (define->nextHash == NULL)
+	return;
+    cur = define;
+    while (cur != NULL) {
+	combine = xmlGetProp(cur->node, BAD_CAST "combine");
+	if (combine != NULL) {
+	    if (xmlStrEqual(combine, BAD_CAST "choice")) {
+		if (choiceOrInterleave == -1)
+		    choiceOrInterleave = 1;
+		else if (choiceOrInterleave == 0) {
+		    if (ctxt->error != NULL)
+			ctxt->error(ctxt->userData,
+		    "Defines for %s use both 'choice' and 'interleave'\n",
+		                    name);
+		    ctxt->nbErrors++;
+		}
+	    } else if (xmlStrEqual(combine, BAD_CAST "choice")) {
+		if (choiceOrInterleave == -1)
+		    choiceOrInterleave = 0;
+		else if (choiceOrInterleave == 1) {
+		    if (ctxt->error != NULL)
+			ctxt->error(ctxt->userData,
+		    "Defines for %s use both 'choice' and 'interleave'\n",
+		                    name);
+		    ctxt->nbErrors++;
+		}
+	    } else {
+		if (ctxt->error != NULL)
+		    ctxt->error(ctxt->userData,
+		    "Defines for %s use unknown combine value '%s''\n",
+				name, combine);
+		ctxt->nbErrors++;
+	    }
+	    xmlFree(combine);
+	} else {
+	    if (missing == 0)
+		missing = 1;
+	    else {
+		if (ctxt->error != NULL)
+		    ctxt->error(ctxt->userData,
+		    "Some defines for %s lacks the combine attribute\n",
+				name);
+		ctxt->nbErrors++;
+	    }
+	}
+
+	cur = cur->nextHash;
+    }
+#ifdef DEBUG
+    xmlGenericError(xmlGenericErrorContext,
+		    "xmlRelaxNGCheckCombine(): merging %s defines: %d\n",
+		    name, choiceOrInterleave);
+#endif
+    if (choiceOrInterleave == -1)
+	choiceOrInterleave = 0;
+    cur = xmlRelaxNGNewDefine(ctxt, define->node);
+    if (cur == NULL)
+	return;
+    if (choiceOrInterleave == 0)
+	cur->type = XML_RELAXNG_CHOICE;
+    else
+	cur->type = XML_RELAXNG_INTERLEAVE;
+    tmp = define;
+    last = NULL;
+    while (tmp != NULL) {
+	if (tmp->content != NULL) {
+	    if (tmp->content->next != NULL) {
+		/*
+		 * we need first to create a wrapper.
+		 */
+		tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
+		if (tmp2 == NULL)
+		    break;
+		tmp2->type = XML_RELAXNG_GROUP;
+		tmp2->content = tmp->content;
+	    } else {
+		tmp2 = tmp->content;
+	    }
+	    if (last == NULL) {
+		cur->content = tmp2;
+	    } else {
+		last->next = tmp2;
+	    }
+	    last = tmp2;
+	    tmp->content = NULL;
+	}
+	tmp = tmp->nextHash;
+    }
+    define->content = cur;
+}
+
+/**
+ * xmlRelaxNGCombineStart:
+ * @ctxt:  a Relax-NG parser context
+ * @grammar:  the grammar
+ *
+ * Applies the 4.17. combine rule for all the start
+ * element of a given grammar.
+ */
+static void
+xmlRelaxNGCombineStart(xmlRelaxNGParserCtxtPtr ctxt,
+	               xmlRelaxNGGrammarPtr grammar) {
+    xmlRelaxNGDefinePtr starts;
+    xmlChar *combine;
+    int choiceOrInterleave = -1;
+    int missing = 0;
+    xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
+
+    starts = grammar->start;
+    if (starts->nextHash == NULL)
+	return;
+    cur = starts;
+    while (cur != NULL) {
+	combine = xmlGetProp(cur->node, BAD_CAST "combine");
+	if (combine != NULL) {
+	    if (xmlStrEqual(combine, BAD_CAST "choice")) {
+		if (choiceOrInterleave == -1)
+		    choiceOrInterleave = 1;
+		else if (choiceOrInterleave == 0) {
+		    if (ctxt->error != NULL)
+			ctxt->error(ctxt->userData,
+		    "<start> use both 'choice' and 'interleave'\n");
+		    ctxt->nbErrors++;
+		}
+	    } else if (xmlStrEqual(combine, BAD_CAST "choice")) {
+		if (choiceOrInterleave == -1)
+		    choiceOrInterleave = 0;
+		else if (choiceOrInterleave == 1) {
+		    if (ctxt->error != NULL)
+			ctxt->error(ctxt->userData,
+		    "<start> use both 'choice' and 'interleave'\n");
+		    ctxt->nbErrors++;
+		}
+	    } else {
+		if (ctxt->error != NULL)
+		    ctxt->error(ctxt->userData,
+		    "<start> uses unknown combine value '%s''\n", combine);
+		ctxt->nbErrors++;
+	    }
+	    xmlFree(combine);
+	} else {
+	    if (missing == 0)
+		missing = 1;
+	    else {
+		if (ctxt->error != NULL)
+		    ctxt->error(ctxt->userData,
+		    "Some <start> elements lacks the combine attribute\n");
+		ctxt->nbErrors++;
+	    }
+	}
+
+	cur = cur->nextHash;
+    }
+#ifdef DEBUG
+    xmlGenericError(xmlGenericErrorContext,
+		    "xmlRelaxNGCombineStart(): merging <start>: %d\n",
+		    choiceOrInterleave);
+#endif
+    if (choiceOrInterleave == -1)
+	choiceOrInterleave = 0;
+    cur = xmlRelaxNGNewDefine(ctxt, starts->node);
+    if (cur == NULL)
+	return;
+    if (choiceOrInterleave == 0)
+	cur->type = XML_RELAXNG_CHOICE;
+    else
+	cur->type = XML_RELAXNG_INTERLEAVE;
+    tmp = starts;
+    last = NULL;
+    while (tmp != NULL) {
+	if (tmp->content != NULL) {
+	    if (tmp->content->next != NULL) {
+		/*
+		 * we need first to create a wrapper.
+		 */
+		tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
+		if (tmp2 == NULL)
+		    break;
+		tmp2->type = XML_RELAXNG_GROUP;
+		tmp2->content = tmp->content;
+	    } else {
+		tmp2 = tmp->content;
+	    }
+	    if (last == NULL) {
+		cur->content = tmp2;
+	    } else {
+		last->next = tmp2;
+	    }
+	    last = tmp2;
+	    tmp->content = NULL;
+	}
+	tmp = tmp->nextHash;
+    }
+    starts->content = cur;
+}
+
+/**
+ * xmlRelaxNGParseGrammar:
+ * @ctxt:  a Relax-NG parser context
+ * @nodes:  grammar children nodes
+ *
+ * parse a Relax-NG <grammar> node
+ *
+ * Returns the internal xmlRelaxNGGrammarPtr built or
+ *         NULL in case of error
+ */
+static xmlRelaxNGGrammarPtr
+xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
+    xmlRelaxNGGrammarPtr ret, tmp, old;
+
+#ifdef DEBUG
+    xmlGenericError(xmlGenericErrorContext,
+		    "xmlRelaxNGParseGrammar()\n");
+#endif
+    ret = xmlRelaxNGNewGrammar(ctxt);
+    if (ret == NULL)
+        return(NULL);
+
+    /*
+     * Link the new grammar in the tree
+     */
+    ret->parent = ctxt->grammar;
+    if (ctxt->grammar != NULL) {
+	tmp = ctxt->grammar->children;
+	if (tmp == NULL) {
+	    ctxt->grammar->children = ret;
+	} else {
+	    while (tmp->next != NULL)
+		tmp = tmp->next;
+	    tmp->next = ret;
+	}
+    }
+
+    old = ctxt->grammar;
+    ctxt->grammar = ret;
+    xmlRelaxNGParseGrammarContent(ctxt, nodes);
+    ctxt->grammar = ret;
+
+    /*
+     * Apply 4.17 mergingd rules to defines and starts
+     */
+    xmlRelaxNGCombineStart(ctxt, ret);
+    if (ret->defs != NULL) {
+	xmlHashScan(ret->defs, (xmlHashScanner) xmlRelaxNGCheckCombine,
+		    ctxt);
+    }
+
+    /*
+     * link together defines and refs in this grammar
+     */
+    if (ret->refs != NULL) {
+	xmlHashScan(ret->refs, (xmlHashScanner) xmlRelaxNGCheckReference,
+		    ctxt);
+    }
+    ctxt->grammar = old;
+    return(ret);
+}
+
+/**
+ * xmlRelaxNGParseDocument:
+ * @ctxt:  a Relax-NG parser context
+ * @node:  the root node of the RelaxNG schema
+ *
+ * parse a Relax-NG definition resource and build an internal
+ * xmlRelaxNG struture which can be used to validate instances.
+ *
+ * Returns the internal XML RelaxNG structure built or
+ *         NULL in case of error
+ */
+static xmlRelaxNGPtr
+xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
+    xmlRelaxNGPtr schema = NULL;
+
+    if ((ctxt == NULL) || (node == NULL))
+        return (NULL);
+
+    schema = xmlRelaxNGNewRelaxNG(ctxt);
+    if (schema == NULL)
+	return(NULL);
+
+    if (IS_RELAXNG(node, "grammar")) {
+	schema->topgrammar = xmlRelaxNGParseGrammar(ctxt, node->children);
+    } else {
+	schema->topgrammar = xmlRelaxNGNewGrammar(ctxt);
+	if (schema->topgrammar == NULL) {
+	    return(schema);
+	}
+	schema->topgrammar->parent = NULL;
+	ctxt->grammar = schema->topgrammar;
+	xmlRelaxNGParseStart(ctxt, node);
+    }
+
+#ifdef DEBUG
+    if (schema == NULL)
+        xmlGenericError(xmlGenericErrorContext,
+                        "xmlRelaxNGParseDocument() failed\n");
+#endif
+
+    return (schema);
+}
+
+/************************************************************************
+ * 									*
+ * 			Reading RelaxNGs				*
+ * 									*
+ ************************************************************************/
+
+/**
+ * xmlRelaxNGNewParserCtxt:
+ * @URL:  the location of the schema
+ *
+ * Create an XML RelaxNGs parse context for that file/resource expected
+ * to contain an XML RelaxNGs file.
+ *
+ * Returns the parser context or NULL in case of error
+ */
+xmlRelaxNGParserCtxtPtr
+xmlRelaxNGNewParserCtxt(const char *URL) {
+    xmlRelaxNGParserCtxtPtr ret;
+
+    if (URL == NULL)
+	return(NULL);
+
+    ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
+    if (ret == NULL) {
+	xmlGenericError(xmlGenericErrorContext,
+		"Failed to allocate new schama parser context for %s\n", URL);
+        return (NULL);
+    }
+    memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
+    ret->URL = xmlStrdup((const xmlChar *)URL);
+    return (ret);
+}
+
+/**
+ * xmlRelaxNGNewMemParserCtxt:
+ * @buffer:  a pointer to a char array containing the schemas
+ * @size:  the size of the array
+ *
+ * Create an XML RelaxNGs parse context for that memory buffer expected
+ * to contain an XML RelaxNGs file.
+ *
+ * Returns the parser context or NULL in case of error
+ */
+xmlRelaxNGParserCtxtPtr
+xmlRelaxNGNewMemParserCtxt(const char *buffer, int size) {
+    xmlRelaxNGParserCtxtPtr ret;
+
+    if ((buffer == NULL) || (size <= 0))
+	return(NULL);
+
+    ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
+    if (ret == NULL) {
+	xmlGenericError(xmlGenericErrorContext,
+		"Failed to allocate new schama parser context\n");
+        return (NULL);
+    }
+    memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
+    ret->buffer = buffer;
+    ret->size = size;
+    return (ret);
+}
+
+/**
+ * xmlRelaxNGFreeParserCtxt:
+ * @ctxt:  the schema parser context
+ *
+ * Free the resources associated to the schema parser context
+ */
+void
+xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxtPtr ctxt) {
+    if (ctxt == NULL)
+	return;
+    if (ctxt->URL != NULL)
+	xmlFree(ctxt->URL);
+    if (ctxt->doc != NULL)
+	xmlFreeDoc(ctxt->doc);
+    xmlFree(ctxt);
+}
+
+
+/**
+ * xmlRelaxNGParse:
+ * @ctxt:  a Relax-NG validation context
+ *
+ * parse a schema definition resource and build an internal
+ * XML Shema struture which can be used to validate instances.
+ * *WARNING* this interface is highly subject to change
+ *
+ * Returns the internal XML RelaxNG structure built from the resource or
+ *         NULL in case of error
+ */
+xmlRelaxNGPtr
+xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt)
+{
+    xmlRelaxNGPtr ret = NULL;
+    xmlDocPtr doc;
+    xmlNodePtr root, cur, delete;
+
+    xmlRelaxNGInitTypes();
+
+    if (ctxt == NULL)
+        return (NULL);
+
+    /*
+     * First step is to parse the input document into an DOM/Infoset
+     */
+    if (ctxt->URL != NULL) {
+	doc = xmlParseFile((const char *) ctxt->URL);
+	if (doc == NULL) {
+	    if (ctxt->error != NULL)
+		ctxt->error(ctxt->userData,
+			    "xmlRelaxNGParse: could not load %s\n", ctxt->URL);
+	    ctxt->nbErrors++;
+	    return (NULL);
+	}
+    } else if (ctxt->buffer != NULL) {
+	doc = xmlParseMemory(ctxt->buffer, ctxt->size);
+	if (doc == NULL) {
+	    if (ctxt->error != NULL)
+		ctxt->error(ctxt->userData,
+			    "xmlRelaxNGParse: could not parse schemas\n");
+	    ctxt->nbErrors++;
+	    return (NULL);
+	}
+	doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
+	ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
+    } else {
+	if (ctxt->error != NULL)
+	    ctxt->error(ctxt->userData,
+			"xmlRelaxNGParse: nothing to parse\n");
+	ctxt->nbErrors++;
+	return (NULL);
+    }
+    ctxt->doc = doc;
+
+    /*
+     * Then extract the root and RelaxNG parse it
+     */
+    root = xmlDocGetRootElement(doc);
+    if (root == NULL) {
+        if (ctxt->error != NULL)
+            ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
+                        ctxt->URL);
+	ctxt->nbErrors++;
+        return (NULL);
+    }
+
+    /*
+     * Remove all the blank text nodes
+     */
+    delete = NULL;
+    cur = root;
+    while (cur != NULL) {
+	if (delete != NULL) {
+	    xmlUnlinkNode(delete);
+	    xmlFreeNode(delete);
+	    delete = NULL;
+	}
+	if (cur->type == XML_ELEMENT_NODE) {
+	    /*
+	     * Simplification 4.1. Annotations
+	     */
+	    if ((cur->ns == NULL) ||
+		(!xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
+		delete = cur;
+		goto skip_children;
+	    } else {
+		if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
+		    TODO
+		} else if (xmlStrEqual(cur->name, BAD_CAST "include")) {
+		    TODO
+		} else if ((xmlStrEqual(cur->name, BAD_CAST "element")) ||
+	            (xmlStrEqual(cur->name, BAD_CAST "attribute"))) {
+		    xmlChar *name;
+		    xmlNodePtr text = NULL;
+		    
+		    /*
+		     * Simplification 4.8. name attribute of element
+		     * and attribute elements
+		     */
+		    name = xmlGetProp(cur, BAD_CAST "name");
+		    if (name != NULL) {
+			if (cur->children == NULL) {
+			    text = xmlNewChild(cur, cur->ns, BAD_CAST "name",
+				               name);
+			} else {
+			    xmlNodePtr node;
+			    node = xmlNewNode(cur->ns, BAD_CAST "name");
+			    if (node != NULL) {
+				xmlAddPrevSibling(cur->children, node);
+				text = xmlNewText(name);
+				xmlAddChild(node, text);
+				text = node;
+			    }
+			}
+			xmlUnsetProp(cur, BAD_CAST "name");
+			xmlFree(name);
+		    }
+		    if (xmlStrEqual(cur->name, BAD_CAST "attribute")) {
+			if (text == NULL) {
+			    text = cur->children;
+			    while (text != NULL) {
+				if ((text->type == XML_ELEMENT_NODE) &&
+			            (xmlStrEqual(text->name, BAD_CAST "name")))
+				    break;
+				text = text->next;
+			    }
+			}
+			if (text == NULL) {
+			    if (ctxt->error != NULL)
+				ctxt->error(ctxt->userData,
+			    "xmlRelaxNGParse: attribute without name\n");
+			    ctxt->nbErrors++;
+			} else {
+			    xmlSetProp(text, BAD_CAST "ns", BAD_CAST "");
+			}
+		    }
+		} else if ((xmlStrEqual(cur->name, BAD_CAST "name")) ||
+			   (xmlStrEqual(cur->name, BAD_CAST "nsName")) ||
+			   (xmlStrEqual(cur->name, BAD_CAST "value"))) {
+		    /*
+		     * Simplification 4.8. name attribute of element
+		     * and attribute elements
+		     */
+		    if (xmlHasProp(cur, BAD_CAST "ns") == NULL) {
+			xmlNodePtr node;
+			xmlChar *ns = NULL;
+
+			node = cur->parent;
+			while ((node != NULL) &&
+			       (node->type == XML_ELEMENT_NODE)) {
+			    ns = xmlGetProp(node, BAD_CAST "ns");
+			    if (ns != NULL) {
+				break;
+			    }
+			    node = node->parent;
+			}
+			if (ns == NULL) {
+			    xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
+			} else {
+			    xmlSetProp(cur, BAD_CAST "ns", ns);
+			    xmlFree(ns);
+			}
+		    }
+		    if (xmlStrEqual(cur->name, BAD_CAST "name")) {
+			xmlChar *name, *local, *prefix;
+
+			/*
+			 * Simplification: 4.10. QNames
+			 */
+			name = xmlNodeGetContent(cur);
+			if (name != NULL) {
+			    local = xmlSplitQName2(name, &prefix);
+			    if (local != NULL) {
+				xmlNsPtr ns;
+
+				ns = xmlSearchNs(cur->doc, cur, prefix);
+				if (ns == NULL) {
+				    if (ctxt->error != NULL)
+					ctxt->error(ctxt->userData,
+		    "xmlRelaxNGParse: no namespace for prefix %s\n", prefix);
+				    ctxt->nbErrors++;
+				} else {
+				    xmlSetProp(cur, BAD_CAST "ns", ns->href);
+				    xmlNodeSetContent(cur, local);
+				}
+				xmlFree(local);
+				xmlFree(prefix);
+			    }
+			    xmlFree(name);
+			} 
+		    }
+		}
+	    }
+	}
+	/*
+	 * Simplification 4.2 whitespaces
+	 */
+	else if (cur->type == XML_TEXT_NODE) {
+	    if (IS_BLANK_NODE(cur)) {
+	        if (cur->parent->type == XML_ELEMENT_NODE) {
+		    if ((!xmlStrEqual(cur->parent->name, BAD_CAST "value")) &&
+			(!xmlStrEqual(cur->parent->name, BAD_CAST "param")))
+			delete = cur;
+		} else {
+		    delete = cur;
+		    goto skip_children;
+		}
+	    }
+	} else if (cur->type != XML_CDATA_SECTION_NODE) {
+	    delete = cur;
+	    goto skip_children;
+	}
+
+	/*
+	 * Skip to next node
+	 */
+	if (cur->children != NULL) {
+	    if ((cur->children->type != XML_ENTITY_DECL) &&
+		(cur->children->type != XML_ENTITY_REF_NODE) &&
+		(cur->children->type != XML_ENTITY_NODE)) {
+		cur = cur->children;
+		continue;
+	    }
+	}
+skip_children:
+	if (cur->next != NULL) {
+	    cur = cur->next;
+	    continue;
+	}
+	
+	do {
+	    cur = cur->parent;
+	    if (cur == NULL)
+		break;
+	    if (cur == root) {
+		cur = NULL;
+		break;
+	    }
+	    if (cur->next != NULL) {
+		cur = cur->next;
+		break;
+	    }
+	} while (cur != NULL);
+    }
+    if (delete != NULL) {
+	xmlUnlinkNode(delete);
+	xmlFreeNode(delete);
+	delete = NULL;
+    }
+
+    /*
+     * Then do the parsing for good
+     */
+    root = xmlDocGetRootElement(doc);
+    if (root == NULL) {
+        if (ctxt->error != NULL)
+            ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
+                        ctxt->URL);
+	ctxt->nbErrors++;
+        return (NULL);
+    }
+    ret = xmlRelaxNGParseDocument(ctxt, root);
+    if (ret == NULL)
+	return(NULL);
+
+    /*
+     * Check the ref/defines links
+     */
+
+    /*
+     * if there was a parsing error return NULL
+     */
+    if (ctxt->nbErrors > 0) {
+	xmlRelaxNGFree(ret);
+	return(NULL);
+    }
+
+    /*
+     * Transfer the pointer for cleanup at the schema level.
+     */
+    ret->doc = doc;
+    ctxt->doc = NULL;
+
+    return (ret);
+}
+ 
+/**
+ * xmlRelaxNGSetParserErrors:
+ * @ctxt:  a Relax-NG validation context
+ * @err:  the error callback
+ * @warn:  the warning callback
+ * @ctx:  contextual data for the callbacks
+ *
+ * Set the callback functions used to handle errors for a validation context
+ */
+void
+xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
+	xmlRelaxNGValidityErrorFunc err,
+	xmlRelaxNGValidityWarningFunc warn, void *ctx) {
+    if (ctxt == NULL)
+	return;
+    ctxt->error = err;
+    ctxt->warning = warn;
+    ctxt->userData = ctx;
+}
+/************************************************************************
+ * 									*
+ * 			Dump back a compiled form			*
+ * 									*
+ ************************************************************************/
+static void xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define);
+
+/**
+ * xmlRelaxNGDumpDefines:
+ * @output:  the file output
+ * @defines:  a list of define structures
+ *
+ * Dump a RelaxNG structure back
+ */
+static void
+xmlRelaxNGDumpDefines(FILE * output, xmlRelaxNGDefinePtr defines) {
+    while (defines != NULL) {
+	xmlRelaxNGDumpDefine(output, defines);
+	defines = defines->next;
+    }
+}
+
+/**
+ * xmlRelaxNGDumpDefine:
+ * @output:  the file output
+ * @define:  a define structure
+ *
+ * Dump a RelaxNG structure back
+ */
+static void
+xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define) {
+    if (define == NULL)
+	return;
+    switch(define->type) {
+        case XML_RELAXNG_EMPTY:
+	    fprintf(output, "<empty/>\n");
+	    break;
+        case XML_RELAXNG_NOT_ALLOWED:
+	    fprintf(output, "<notAllowed/>\n");
+	    break;
+        case XML_RELAXNG_TEXT:
+	    fprintf(output, "<text/>\n");
+	    break;
+        case XML_RELAXNG_ELEMENT:
+	    fprintf(output, "<element>\n");
+	    if (define->name != NULL) {
+		fprintf(output, "<name");
+		if (define->ns != NULL)
+		    fprintf(output, " ns=\"%s\"", define->ns);
+		fprintf(output, ">%s</name>\n", define->name);
+	    }
+	    xmlRelaxNGDumpDefines(output, define->attrs);
+	    xmlRelaxNGDumpDefines(output, define->content);
+	    fprintf(output, "</element>\n");
+	    break;
+        case XML_RELAXNG_LIST:
+	    fprintf(output, "<list>\n");
+	    xmlRelaxNGDumpDefines(output, define->content);
+	    fprintf(output, "</list>\n");
+	    break;
+        case XML_RELAXNG_ONEORMORE:
+	    fprintf(output, "<oneOrMore>\n");
+	    xmlRelaxNGDumpDefines(output, define->content);
+	    fprintf(output, "</oneOrMore>\n");
+	    break;
+        case XML_RELAXNG_ZEROORMORE:
+	    fprintf(output, "<zeroOrMore>\n");
+	    xmlRelaxNGDumpDefines(output, define->content);
+	    fprintf(output, "</zeroOrMore>\n");
+	    break;
+        case XML_RELAXNG_CHOICE:
+	    fprintf(output, "<choice>\n");
+	    xmlRelaxNGDumpDefines(output, define->content);
+	    fprintf(output, "</choice>\n");
+	    break;
+        case XML_RELAXNG_GROUP:
+	    fprintf(output, "<group>\n");
+	    xmlRelaxNGDumpDefines(output, define->content);
+	    fprintf(output, "</group>\n");
+	    break;
+        case XML_RELAXNG_INTERLEAVE:
+	    fprintf(output, "<interleave>\n");
+	    xmlRelaxNGDumpDefines(output, define->content);
+	    fprintf(output, "</interleave>\n");
+	    break;
+	case XML_RELAXNG_OPTIONAL:
+	    fprintf(output, "<optional>\n");
+	    xmlRelaxNGDumpDefines(output, define->content);
+	    fprintf(output, "</optional>\n");
+	    break;
+        case XML_RELAXNG_ATTRIBUTE:
+	    fprintf(output, "<attribute>\n");
+	    xmlRelaxNGDumpDefines(output, define->content);
+	    fprintf(output, "</attribute>\n");
+	    break;
+        case XML_RELAXNG_DEF:
+	    fprintf(output, "<define");
+	    if (define->name != NULL)
+		fprintf(output, " name=\"%s\"", define->name);
+	    fprintf(output, ">\n");
+	    xmlRelaxNGDumpDefines(output, define->content);
+	    fprintf(output, "</define>\n");
+	    break;
+        case XML_RELAXNG_REF:
+	    fprintf(output, "<ref");
+	    if (define->name != NULL)
+		fprintf(output, " name=\"%s\"", define->name);
+	    fprintf(output, ">\n");
+	    xmlRelaxNGDumpDefines(output, define->content);
+	    fprintf(output, "</ref>\n");
+	    break;
+        case XML_RELAXNG_DATATYPE:
+        case XML_RELAXNG_VALUE:
+	    TODO
+	    break;
+    }
+}
+   
+/**
+ * xmlRelaxNGDumpGrammar:
+ * @output:  the file output
+ * @grammar:  a grammar structure
+ * @top:  is this a top grammar 
+ *
+ * Dump a RelaxNG structure back
+ */
+static void
+xmlRelaxNGDumpGrammar(FILE * output, xmlRelaxNGGrammarPtr grammar, int top)
+{
+    if (grammar == NULL)
+	return;
+   
+    fprintf(output, "<grammar");
+    if (top)
+	fprintf(output,
+		" xmlns=\"http://relaxng.org/ns/structure/1.0\"");
+    switch(grammar->combine) {
+	case XML_RELAXNG_COMBINE_UNDEFINED:
+	    break;
+	case XML_RELAXNG_COMBINE_CHOICE:
+	    fprintf(output, " combine=\"choice\"");
+	    break;
+	case XML_RELAXNG_COMBINE_INTERLEAVE:
+	    fprintf(output, " combine=\"interleave\"");
+	    break;
+	default:
+	    fprintf(output, " <!-- invalid combine value -->");
+    }
+    fprintf(output, ">\n");
+    if (grammar->start == NULL) {
+	fprintf(output, " <!-- grammar had no start -->");
+    } else {
+	fprintf(output, "<start>\n");
+	xmlRelaxNGDumpDefine(output, grammar->start);
+	fprintf(output, "</start>\n");
+    }
+    /* TODO ? Dump the defines ? */
+    fprintf(output, "</grammar>\n");
+}
+
+/**
+ * xmlRelaxNGDump:
+ * @output:  the file output
+ * @schema:  a schema structure
+ *
+ * Dump a RelaxNG structure back
+ */
+void
+xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema)
+{
+    if (schema == NULL) {
+	fprintf(output, "RelaxNG empty or failed to compile\n");
+	return;
+    }
+    fprintf(output, "RelaxNG: ");
+    if (schema->doc == NULL) {
+	fprintf(output, "no document\n");
+    } else if (schema->doc->URL != NULL) {
+	fprintf(output, "%s\n", schema->doc->URL);
+    } else {
+	fprintf(output, "\n");
+    }
+    if (schema->topgrammar == NULL) {
+	fprintf(output, "RelaxNG has no top grammar\n");
+	return;
+    }
+    xmlRelaxNGDumpGrammar(output, schema->topgrammar, 1);
+}
+
+/************************************************************************
+ * 									*
+ * 			Validation implementation			*
+ * 									*
+ ************************************************************************/
+static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt, 
+	                                xmlRelaxNGDefinePtr define);
+
+/**
+ * xmlRelaxNGSkipIgnored:
+ * @ctxt:  a schema validation context
+ * @node:  the top node.
+ *
+ * Skip ignorable nodes in that context
+ *
+ * Returns the new sibling or NULL in case of error.
+ */
+static xmlNodePtr
+xmlRelaxNGSkipIgnored(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
+	              xmlNodePtr node) {
+    /*
+     * TODO complete and handle entities
+     */
+    while ((node != NULL) &&
+	   ((node->type == XML_COMMENT_NODE) ||
+	    ((node->type == XML_TEXT_NODE) &&
+	     (IS_BLANK_NODE(node))))) {
+	node = node->next;
+    }
+    return(node);
+}
+
+/**
+ * xmlRelaxNGValidateValue:
+ * @ctxt:  a Relax-NG validation context
+ * @define:  the definition to verify
+ *
+ * Validate the given definition for the current value
+ *
+ * Returns 0 if the validation succeeded or an error code.
+ */
+static int
+xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt, 
+	                xmlRelaxNGDefinePtr define) {
+    int ret = 0;
+    xmlChar *value;
+
+    value = ctxt->state->value;
+    switch (define->type) {
+	case XML_RELAXNG_EMPTY:
+	    if ((value != NULL) && (value[0] != '0'))
+		ret = -1;
+	    break;
+	case XML_RELAXNG_TEXT:
+	    break;
+	default:
+	    TODO
+	    ret = -1;
+    }
+    return(ret);
+}
+
+/**
+ * xmlRelaxNGValidateValueContent:
+ * @ctxt:  a Relax-NG validation context
+ * @defines:  the list of definitions to verify
+ *
+ * Validate the given definitions for the current value
+ *
+ * Returns 0 if the validation succeeded or an error code.
+ */
+static int
+xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt, 
+	                       xmlRelaxNGDefinePtr defines) {
+    int ret = 0;
+
+    while (defines != NULL) {
+	ret = xmlRelaxNGValidateValue(ctxt, defines);
+	if (ret != 0)
+	    break;
+	defines = defines->next;
+    }
+    return(ret);
+}
+
+/**
+ * xmlRelaxNGValidateAttribute:
+ * @ctxt:  a Relax-NG validation context
+ * @define:  the definition to verify
+ *
+ * Validate the given attribute definition for that node
+ *
+ * Returns 0 if the validation succeeded or an error code.
+ */
+static int
+xmlRelaxNGValidateAttribute(xmlRelaxNGValidCtxtPtr ctxt, 
+	                    xmlRelaxNGDefinePtr define) {
+    int ret = 0, i;
+    xmlChar *value, *oldvalue;
+    xmlAttrPtr prop = NULL, tmp;
+
+    if (define->name != NULL) {
+        for (i = 0;i < ctxt->state->nbAttrs;i++) {
+	    tmp = ctxt->state->attrs[i];
+	    if ((tmp != NULL) && (xmlStrEqual(define->name, tmp->name))) {
+		if ((((define->ns == NULL) || (define->ns[0] == 0)) &&
+		     (tmp->ns == NULL)) ||
+		    ((tmp->ns != NULL) &&
+		     (xmlStrEqual(define->ns, tmp->ns->href)))) {
+		    prop = tmp;
+		    break;
+		}
+	    }
+	}
+	if (prop != NULL) {
+	    value = xmlNodeListGetString(prop->doc, prop->children, 1);
+	    oldvalue = ctxt->state->value;
+	    ctxt->state->value = value;
+	    ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
+	    value = ctxt->state->value;
+	    ctxt->state->value = oldvalue;
+	    if (value != NULL)
+		xmlFree(value);
+	    if (ret == 0) {
+		/*
+		 * flag the attribute as processed
+		 */
+		ctxt->state->attrs[i] = NULL;
+	    }
+	} else {
+	    ret = -1;
+	}
+#ifdef DEBUG
+	xmlGenericError(xmlGenericErrorContext,
+                    "xmlRelaxNGValidateAttribute(%s): %d\n", define->name, ret);
+#endif
+    } else {
+	TODO
+    }
+    
+    return(ret);
+}
+
+/**
+ * xmlRelaxNGValidateAttributeList:
+ * @ctxt:  a Relax-NG validation context
+ * @define:  the list of definition to verify
+ *
+ * Validate the given node against the list of attribute definitions
+ *
+ * Returns 0 if the validation succeeded or an error code.
+ */
+static int
+xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt, 
+	                        xmlRelaxNGDefinePtr defines) {
+    int ret = 0;
+    while (defines != NULL) {
+	if (xmlRelaxNGValidateAttribute(ctxt, defines) != 0)
+	    ret = -1;
+        defines = defines->next;
+    }
+    return(ret);
+}
+
+/**
+ * xmlRelaxNGValidateElementContent:
+ * @ctxt:  a Relax-NG validation context
+ * @define:  the list of definition to verify
+ *
+ * Validate the given node content against the (list) of definitions
+ *
+ * Returns 0 if the validation succeeded or an error code.
+ */
+static int
+xmlRelaxNGValidateElementContent(xmlRelaxNGValidCtxtPtr ctxt, 
+	                  xmlRelaxNGDefinePtr defines) {
+    int ret = 0, res;
+
+    if (ctxt->state == NULL) {
+	VALID_CTXT();
+	VALID_ERROR("Internal: no state\n");
+	return(-1);
+    }
+    while (defines != NULL) {
+	res = xmlRelaxNGValidateDefinition(ctxt, defines);
+	if (res < 0)
+	    ret = -1;
+	defines = defines->next;
+    }
+
+    return(ret);
+}
+
+/**
+ * xmlRelaxNGValidateDefinition:
+ * @ctxt:  a Relax-NG validation context
+ * @define:  the definition to verify
+ *
+ * Validate the current node against the definition
+ *
+ * Returns 0 if the validation succeeded or an error code.
+ */
+static int
+xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt, 
+	                     xmlRelaxNGDefinePtr define) {
+    xmlNodePtr node;
+    int ret = 0, i, tmp, oldflags;
+    xmlRelaxNGValidStatePtr oldstate, state;
+
+    if (define == NULL) {
+	VALID_CTXT();
+	VALID_ERROR("internal error: define == NULL\n");
+	return(-1);
+    }
+    if (ctxt->state != NULL) {
+	node = ctxt->state->seq;
+    } else {
+	node = NULL;
+    }
+    switch (define->type) {
+        case XML_RELAXNG_EMPTY:
+	    if (node != NULL) {
+		VALID_CTXT();
+		VALID_ERROR("Expecting an empty element\n");
+		return(-1);
+	    }
+#ifdef DEBUG
+	    xmlGenericError(xmlGenericErrorContext,
+                    "xmlRelaxNGValidateDefinition(): validated empty\n");
+#endif
+	    return(0);
+        case XML_RELAXNG_NOT_ALLOWED:
+	    TODO
+	    break;
+        case XML_RELAXNG_TEXT:
+	    if (node == NULL)
+		return(0);
+	    while ((node != NULL) &&
+		   ((node->type == XML_TEXT_NODE) ||
+		    (node->type == XML_CDATA_SECTION_NODE)))
+		node = node->next;
+	    ctxt->state->seq = node;
+	    if (node == NULL) {
+		return(0);
+	    }
+	    VALID_CTXT();
+	    VALID_ERROR("Expecting text content\n");
+	    return(-1);
+        case XML_RELAXNG_ELEMENT:
+	    node = xmlRelaxNGSkipIgnored(ctxt, node);
+	    if ((node == NULL) || (node->type != XML_ELEMENT_NODE)) {
+		VALID_CTXT();
+		VALID_ERROR("Expecting an element\n");
+		return(-1);
+	    }
+	    if (define->name != NULL) {
+		if (!xmlStrEqual(node->name, define->name)) {
+		    VALID_CTXT();
+		    VALID_ERROR("Expecting element %s, got %s\n",
+			        define->name, node->name);
+		    return(-1);
+		}
+	    }
+	    if ((define->ns != NULL) && (define->ns[0] != 0)) {
+		if (node->ns == NULL) {
+		    VALID_CTXT();
+		    VALID_ERROR("Expecting a namespace for element %s\n",
+			        node->name);
+		    return(-1);
+		} else if (!xmlStrEqual(node->ns->href, define->ns)) {
+		    VALID_CTXT();
+		    VALID_ERROR("Expecting element %s has wrong namespace: expecting %s\n",
+			        node->name, define->ns);
+		    return(-1);
+		}
+	    } else {
+		if (node->ns != NULL) {
+		    VALID_CTXT();
+		    VALID_ERROR("Expecting no namespace for element %s\n",
+			        node->name);
+		    return(-1);
+		}
+	    }
+	    
+	    state = xmlRelaxNGNewValidState(ctxt, node);
+	    if (state == NULL) {
+		return(-1);
+	    }
+
+	    oldstate = ctxt->state;
+	    ctxt->state = state;
+	    if (define->attrs != NULL) {
+		tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
+		if (tmp != 0)
+		    ret = -1;
+	    }
+	    if (define->content != NULL) {
+		tmp = xmlRelaxNGValidateElementContent(ctxt, define->content);
+		if (tmp != 0)
+		    ret = -1;
+	    }
+	    state = ctxt->state;
+	    if (state->seq != NULL) {
+		state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq);
+		if (state->seq != NULL) {
+		    VALID_CTXT();
+		    VALID_ERROR("Extra content for element %s\n",
+				node->name);
+		    ret = -1;
+		}
+	    }
+	    for (i = 0;i < state->nbAttrs;i++) {
+		if (state->attrs[i] != NULL) {
+		    VALID_CTXT();
+		    VALID_ERROR("Extra attribute %s for element %s\n",
+				state->attrs[i]->name, node->name);
+		    ret = -1;
+		}
+	    }
+	    ctxt->state = oldstate;
+	    xmlRelaxNGFreeValidState(state);
+	    if (oldstate != NULL)
+		oldstate->seq = node->next;
+
+
+#ifdef DEBUG
+	    xmlGenericError(xmlGenericErrorContext,
+                    "xmlRelaxNGValidateDefinition(): validated %s : %d\n",
+		            node->name, ret);
+#endif
+	    break;
+        case XML_RELAXNG_LIST:
+	    TODO
+	    break;
+        case XML_RELAXNG_OPTIONAL:
+	    oldflags = ctxt->flags;
+	    ctxt->flags |= FLAGS_IGNORABLE;
+	    oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
+	    ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
+	    if (ret != 0) {
+		xmlRelaxNGFreeValidState(ctxt->state);
+		ctxt->state = oldstate;
+		ret = 0;
+		break;
+	    }
+	    xmlRelaxNGFreeValidState(oldstate);
+	    ctxt->flags = oldflags;
+	    ret = 0;
+	    break;
+        case XML_RELAXNG_ONEORMORE:
+	    ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
+	    if (ret != 0) {
+		break;
+	    }
+	    /* no break on purpose */
+        case XML_RELAXNG_ZEROORMORE:
+	    oldflags = ctxt->flags;
+	    ctxt->flags |= FLAGS_IGNORABLE;
+	    while (node != NULL) {
+		oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
+		ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
+		if (ret != 0) {
+		    xmlRelaxNGFreeValidState(ctxt->state);
+		    ctxt->state = oldstate;
+		    ret = 0;
+		    break;
+		}
+		xmlRelaxNGFreeValidState(oldstate);
+		node = ctxt->state->node;
+	    }
+	    ctxt->flags = oldflags;
+	    break;
+        case XML_RELAXNG_CHOICE: {
+	    xmlRelaxNGDefinePtr list = define->content;
+
+	    oldflags = ctxt->flags;
+	    ctxt->flags |= FLAGS_IGNORABLE;
+
+	    while (list != NULL) {
+		oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
+		ret = xmlRelaxNGValidateDefinition(ctxt, list);
+		if (ret == 0) {
+		    xmlRelaxNGFreeValidState(oldstate);
+		    break;
+		}
+		xmlRelaxNGFreeValidState(ctxt->state);
+		ctxt->state = oldstate;
+		list = list->next;
+	    }
+	    ctxt->flags = oldflags;
+	    break;
+	}
+        case XML_RELAXNG_GROUP: {
+	    xmlRelaxNGDefinePtr list = define->content;
+
+	    while (list != NULL) {
+		ret = xmlRelaxNGValidateDefinition(ctxt, list);
+		if (ret != 0)
+		    break;
+		list = list->next;
+	    }
+	    break;
+	}
+        case XML_RELAXNG_INTERLEAVE:
+	    TODO
+	    break;
+        case XML_RELAXNG_ATTRIBUTE:
+	    ret = xmlRelaxNGValidateAttribute(ctxt, define);
+	    break;
+        case XML_RELAXNG_REF:
+	    ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
+	    break;
+        case XML_RELAXNG_DEF:
+	    ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
+	    break;
+        case XML_RELAXNG_DATATYPE:
+        case XML_RELAXNG_VALUE:
+	    TODO
+	    break;
+    }
+    return(ret);
+}
+
+/**
+ * xmlRelaxNGValidateDocument:
+ * @ctxt:  a Relax-NG validation context
+ * @doc:  the document
+ *
+ * Validate the given document
+ *
+ * Returns 0 if the validation succeeded or an error code.
+ */
+static int
+xmlRelaxNGValidateDocument(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
+    int ret;
+    xmlRelaxNGPtr schema;
+    xmlRelaxNGGrammarPtr grammar;
+    xmlRelaxNGValidStatePtr state;
+
+    if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL))
+	return(-1);
+
+    schema = ctxt->schema;
+    grammar = schema->topgrammar;
+    if (grammar == NULL) {
+	VALID_CTXT();
+	VALID_ERROR("No top grammar defined\n");
+	return(-1);
+    }
+    state = xmlRelaxNGNewValidState(ctxt, NULL);
+    ctxt->state = state;
+    ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start);
+    state = ctxt->state;
+    if ((state != NULL) && (state->seq != NULL)) {
+	xmlNodePtr node;
+
+	node = state->seq;
+	node = xmlRelaxNGSkipIgnored(ctxt, node);
+	if (node != NULL) {
+	    VALID_CTXT();
+	    VALID_ERROR("extra data on the document\n");
+	    ret = -1;
+	}
+    }
+    xmlRelaxNGFreeValidState(state);
+
+    return(ret);
+}
+
+/************************************************************************
+ * 									*
+ * 			Validation interfaces				*
+ * 									*
+ ************************************************************************/
+/**
+ * xmlRelaxNGNewValidCtxt:
+ * @schema:  a precompiled XML RelaxNGs
+ *
+ * Create an XML RelaxNGs validation context based on the given schema
+ *
+ * Returns the validation context or NULL in case of error
+ */
+xmlRelaxNGValidCtxtPtr
+xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema) {
+    xmlRelaxNGValidCtxtPtr ret;
+
+    ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt));
+    if (ret == NULL) {
+	xmlGenericError(xmlGenericErrorContext,
+		"Failed to allocate new schama validation context\n");
+        return (NULL);
+    }
+    memset(ret, 0, sizeof(xmlRelaxNGValidCtxt));
+    ret->schema = schema;
+    return (ret);
+}
+
+/**
+ * xmlRelaxNGFreeValidCtxt:
+ * @ctxt:  the schema validation context
+ *
+ * Free the resources associated to the schema validation context
+ */
+void
+xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt) {
+    if (ctxt == NULL)
+	return;
+    xmlFree(ctxt);
+}
+
+/**
+ * xmlRelaxNGSetValidErrors:
+ * @ctxt:  a Relax-NG validation context
+ * @err:  the error function
+ * @warn: the warning function
+ * @ctx: the functions context
+ *
+ * Set the error and warning callback informations
+ */
+void
+xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
+	xmlRelaxNGValidityErrorFunc err,
+	xmlRelaxNGValidityWarningFunc warn, void *ctx) {
+    if (ctxt == NULL)
+	return;
+    ctxt->error = err;
+    ctxt->warning = warn;
+    ctxt->userData = ctx;
+}
+
+/**
+ * xmlRelaxNGValidateDoc:
+ * @ctxt:  a Relax-NG validation context
+ * @doc:  a parsed document tree
+ *
+ * Validate a document tree in memory.
+ *
+ * Returns 0 if the document is valid, a positive error code
+ *     number otherwise and -1 in case of internal or API error.
+ */
+int
+xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
+    int ret;
+
+    if ((ctxt == NULL) || (doc == NULL))
+	return(-1);
+
+    ctxt->doc = doc;
+
+    ret = xmlRelaxNGValidateDocument(ctxt, doc);
+    return(ret);
+}
+
+#endif /* LIBXML_SCHEMAS_ENABLED */
+