Edit

kc3-lang/libxml2/relaxng.c

Branch :

  • Show log

    Commit

  • Author : Nick Wellnhofer
    Date : 2025-05-06 17:42:46
    Hash : 9bbffec5
    Message : doc: Move brief to top, params to bottom of doc comments

  • relaxng.c
  • /*
     * relaxng.c : implementation of the Relax-NG handling and validity checking
     *
     * See Copyright for the status of this software.
     *
     * Author: Daniel Veillard
     */
    
    /**
     * TODO:
     * - add support for DTD compatibility spec
     *   http://www.oasis-open.org/committees/relax-ng/compatibility-20011203.html
     * - report better mem allocations pbms at runtime and abort immediately.
     */
    
    #define IN_LIBXML
    #include "libxml.h"
    
    #ifdef LIBXML_RELAXNG_ENABLED
    
    #include <string.h>
    #include <stdio.h>
    #include <stddef.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/xmlautomata.h>
    #include <libxml/xmlregexp.h>
    #include <libxml/xmlschemastypes.h>
    
    #include "private/error.h"
    #include "private/regexp.h"
    #include "private/string.h"
    
    /*
     * The Relax-NG namespace
     */
    static const xmlChar *xmlRelaxNGNs = (const xmlChar *)
        "http://relaxng.org/ns/structure/1.0";
    
    #define IS_RELAXNG(node, typ)						\
       ((node != NULL) && (node->ns != NULL) &&				\
        (node->type == XML_ELEMENT_NODE) &&					\
        (xmlStrEqual(node->name, (const xmlChar *) typ)) &&		\
        (xmlStrEqual(node->ns->href, xmlRelaxNGNs)))
    
    
    #define MAX_ERROR 5
    
    typedef struct _xmlRelaxNGSchema xmlRelaxNGSchema;
    typedef xmlRelaxNGSchema *xmlRelaxNGSchemaPtr;
    
    typedef struct _xmlRelaxNGDefine xmlRelaxNGDefine;
    typedef xmlRelaxNGDefine *xmlRelaxNGDefinePtr;
    
    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 */
        XML_RELAXNG_COMBINE_INTERLEAVE      /* interleave */
    } xmlRelaxNGCombine;
    
    typedef enum {
        XML_RELAXNG_CONTENT_ERROR = -1,
        XML_RELAXNG_CONTENT_EMPTY = 0,
        XML_RELAXNG_CONTENT_SIMPLE,
        XML_RELAXNG_CONTENT_COMPLEX
    } xmlRelaxNGContentType;
    
    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 */
        xmlRelaxNGDefinePtr startList;      /* list of <start> definitions */
        xmlHashTablePtr defs;       /* define* */
        xmlHashTablePtr refs;       /* references */
    };
    
    
    typedef enum {
        XML_RELAXNG_NOOP = -1,      /* a no operation from simplification  */
        XML_RELAXNG_EMPTY = 0,      /* an empty pattern */
        XML_RELAXNG_NOT_ALLOWED,    /* not allowed top */
        XML_RELAXNG_EXCEPT,         /* except present in nameclass defs */
        XML_RELAXNG_TEXT,           /* textual content */
        XML_RELAXNG_ELEMENT,        /* an element */
        XML_RELAXNG_DATATYPE,       /* external data type definition */
        XML_RELAXNG_PARAM,          /* external data type parameter */
        XML_RELAXNG_VALUE,          /* value from an external data type definition */
        XML_RELAXNG_LIST,           /* a list of patterns */
        XML_RELAXNG_ATTRIBUTE,      /* an attribute following a pattern */
        XML_RELAXNG_DEF,            /* a definition */
        XML_RELAXNG_REF,            /* reference to a definition */
        XML_RELAXNG_EXTERNALREF,    /* reference to an external def */
        XML_RELAXNG_PARENTREF,      /* reference to a def in the parent grammar */
        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 */
        XML_RELAXNG_START           /* Used to keep track of starts on grammars */
    } xmlRelaxNGType;
    
    #define IS_NULLABLE		(1 << 0)
    #define IS_NOT_NULLABLE		(1 << 1)
    #define IS_INDETERMINIST	(1 << 2)
    #define IS_MIXED		(1 << 3)
    #define IS_TRIABLE		(1 << 4)
    #define IS_PROCESSED		(1 << 5)
    #define IS_COMPILABLE		(1 << 6)
    #define IS_NOT_COMPILABLE	(1 << 7)
    #define IS_EXTERNAL_REF	        (1 << 8)
    
    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 */
        xmlChar *value;             /* value when available */
        void *data;                 /* data lib or specific pointer */
        xmlRelaxNGDefinePtr content;        /* the expected content */
        xmlRelaxNGDefinePtr parent; /* the parent definition, if any */
        xmlRelaxNGDefinePtr next;   /* list within grouping sequences */
        xmlRelaxNGDefinePtr attrs;  /* list of attributes for elements */
        xmlRelaxNGDefinePtr nameClass;      /* the nameClass definition if any */
        xmlRelaxNGDefinePtr nextHash;       /* next define in defs/refs hash tables */
        short depth;                /* used for the cycle detection */
        short dflags;               /* define related flags */
        xmlRegexpPtr contModel;     /* a compiled content model if available */
    };
    
    /**
     * A RelaxNGs definition
     */
    struct _xmlRelaxNG {
        void *_private;             /* unused by the library for users or bindings */
        xmlRelaxNGGrammarPtr topgrammar;
        xmlDocPtr doc;
    
        int idref;                  /* requires idref checking */
    
        xmlHashTablePtr defs;       /* define */
        xmlHashTablePtr refs;       /* references */
        xmlRelaxNGDocumentPtr documents;    /* all the documents loaded */
        xmlRelaxNGIncludePtr includes;      /* all the includes loaded */
        int defNr;                  /* number of defines used */
        xmlRelaxNGDefinePtr *defTab;        /* pointer to the allocated definitions */
    
    };
    
    #define XML_RELAXNG_IN_ATTRIBUTE	(1 << 0)
    #define XML_RELAXNG_IN_ONEORMORE	(1 << 1)
    #define XML_RELAXNG_IN_LIST		(1 << 2)
    #define XML_RELAXNG_IN_DATAEXCEPT	(1 << 3)
    #define XML_RELAXNG_IN_START		(1 << 4)
    #define XML_RELAXNG_IN_OOMGROUP		(1 << 5)
    #define XML_RELAXNG_IN_OOMINTERLEAVE	(1 << 6)
    #define XML_RELAXNG_IN_EXTERNALREF	(1 << 7)
    #define XML_RELAXNG_IN_ANYEXCEPT	(1 << 8)
    #define XML_RELAXNG_IN_NSEXCEPT		(1 << 9)
    
    struct _xmlRelaxNGParserCtxt {
        void *userData;             /* user specific data block */
        xmlRelaxNGValidityErrorFunc error;  /* the callback in case of errors */
        xmlRelaxNGValidityWarningFunc warning;      /* the callback in case of warning */
        xmlStructuredErrorFunc serror;
        xmlRelaxNGValidErr err;
    
        xmlRelaxNGPtr schema;       /* The schema in use */
        xmlRelaxNGGrammarPtr grammar;       /* the current grammar */
        xmlRelaxNGGrammarPtr parentgrammar; /* the parent grammar */
        int flags;                  /* parser flags */
        int nbErrors;               /* number of errors at parse time */
        int nbWarnings;             /* number of warnings at parse time */
        const xmlChar *define;      /* the current define scope */
        xmlRelaxNGDefinePtr def;    /* the current define */
    
        int nbInterleaves;
        xmlHashTablePtr interleaves;        /* keep track of all the interleaves */
    
        xmlRelaxNGDocumentPtr documents;    /* all the documents loaded */
        xmlRelaxNGIncludePtr includes;      /* all the includes loaded */
        xmlChar *URL;
        xmlDocPtr document;
    
        int defNr;                  /* number of defines used */
        int defMax;                 /* number of defines allocated */
        xmlRelaxNGDefinePtr *defTab;        /* pointer to the allocated definitions */
    
        const char *buffer;
        int size;
    
        /* the document stack */
        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 */
    
        int idref;                  /* requires idref checking */
    
        /* used to compile content models */
        xmlAutomataPtr am;          /* the automata */
        xmlAutomataStatePtr state;  /* used to build the automata */
    
        int crng;			/* compact syntax and other flags */
        int freedoc;		/* need to free the document */
    
        xmlResourceLoader resourceLoader;
        void *resourceCtxt;
    };
    
    #define FLAGS_IGNORABLE		1
    #define FLAGS_NEGATIVE		2
    #define FLAGS_MIXED_CONTENT	4
    #define FLAGS_NOERROR		8
    
    /**
     * A RelaxNGs partition set associated to lists of definitions
     */
    typedef struct _xmlRelaxNGInterleaveGroup xmlRelaxNGInterleaveGroup;
    typedef xmlRelaxNGInterleaveGroup *xmlRelaxNGInterleaveGroupPtr;
    struct _xmlRelaxNGInterleaveGroup {
        xmlRelaxNGDefinePtr rule;   /* the rule to satisfy */
        xmlRelaxNGDefinePtr *defs;  /* the array of element definitions */
        xmlRelaxNGDefinePtr *attrs; /* the array of attributes definitions */
    };
    
    #define IS_DETERMINIST		1
    #define IS_NEEDCHECK		2
    
    /**
     * A RelaxNGs partition associated to an interleave group
     */
    typedef struct _xmlRelaxNGPartition xmlRelaxNGPartition;
    typedef xmlRelaxNGPartition *xmlRelaxNGPartitionPtr;
    struct _xmlRelaxNGPartition {
        int nbgroups;               /* number of groups in the partitions */
        xmlHashTablePtr triage;     /* hash table used to direct nodes to the
                                     * right group when possible */
        int flags;                  /* determinist ? */
        xmlRelaxNGInterleaveGroupPtr *groups;
    };
    
    /**
     * 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 */
        int maxAttrs;               /* the size of attrs */
        int nbAttrLeft;             /* the number of attributes left to validate */
        xmlChar *value;             /* the value when operating on string */
        xmlChar *endvalue;          /* the end value when operating on string */
        xmlAttrPtr *attrs;          /* the array of attributes */
    };
    
    /**
     * A RelaxNGs container for validation state
     */
    typedef struct _xmlRelaxNGStates xmlRelaxNGStates;
    typedef xmlRelaxNGStates *xmlRelaxNGStatesPtr;
    struct _xmlRelaxNGStates {
        int nbState;                /* the number of states */
        int maxState;               /* the size of the array */
        xmlRelaxNGValidStatePtr *tabState;
    };
    
    #define ERROR_IS_DUP	1
    
    /**
     * A RelaxNGs validation error
     */
    typedef struct _xmlRelaxNGValidError xmlRelaxNGValidError;
    typedef xmlRelaxNGValidError *xmlRelaxNGValidErrorPtr;
    struct _xmlRelaxNGValidError {
        xmlRelaxNGValidErr err;     /* the error number */
        int flags;                  /* flags */
        xmlNodePtr node;            /* the current node */
        xmlNodePtr seq;             /* the current child */
        const xmlChar *arg1;        /* first arg */
        const xmlChar *arg2;        /* second arg */
    };
    
    /**
     * 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 */
        xmlStructuredErrorFunc serror;
        int nbErrors;               /* number of errors in validation */
    
        xmlRelaxNGPtr schema;       /* The schema in use */
        xmlDocPtr doc;              /* the document being validated */
        int flags;                  /* validation flags */
        int depth;                  /* validation depth */
        int idref;                  /* requires idref checking */
        int errNo;                  /* the first error found */
    
        /*
         * Errors accumulated in branches may have to be stacked to be
         * provided back when it's sure they affect validation.
         */
        xmlRelaxNGValidErrorPtr err;        /* Last error */
        int errNr;                  /* Depth of the error stack */
        int errMax;                 /* Max depth of the error stack */
        xmlRelaxNGValidErrorPtr errTab;     /* stack of errors */
    
        xmlRelaxNGValidStatePtr state;      /* the current validation state */
        xmlRelaxNGStatesPtr states; /* the accumulated state list */
    
        xmlRelaxNGStatesPtr freeState;      /* the pool of free valid states */
        int freeStatesNr;
        int freeStatesMax;
        xmlRelaxNGStatesPtr *freeStates;    /* the pool of free state groups */
    
        /*
         * This is used for "progressive" validation
         */
        xmlRegExecCtxtPtr elem;     /* the current element regexp */
        int elemNr;                 /* the number of element validated */
        int elemMax;                /* the max depth of elements */
        xmlRegExecCtxtPtr *elemTab; /* the stack of regexp runtime */
        int pstate;                 /* progressive state */
        xmlNodePtr pnode;           /* the current node */
        xmlRelaxNGDefinePtr pdef;   /* the non-streamable definition */
        int perr;                   /* signal error in content model
                                     * outside the regexp */
    };
    
    /**
     * Structure associated to a RelaxNGs document element
     */
    struct _xmlRelaxNGInclude {
        xmlRelaxNGIncludePtr next;  /* keep a chain of includes */
        xmlChar *href;              /* the normalized href value */
        xmlDocPtr doc;              /* the associated XML document */
        xmlRelaxNGDefinePtr content;        /* the definitions */
        xmlRelaxNGPtr schema;       /* the schema */
    };
    
    /**
     * Structure associated to a RelaxNGs document element
     */
    struct _xmlRelaxNGDocument {
        xmlRelaxNGDocumentPtr next; /* keep a chain of documents */
        xmlChar *href;              /* the normalized href value */
        xmlDocPtr doc;              /* the associated XML document */
        xmlRelaxNGDefinePtr content;        /* the definitions */
        xmlRelaxNGPtr schema;       /* the schema */
        int externalRef;            /* 1 if an external ref */
    };
    
    
    /************************************************************************
     *									*
     *		Some factorized error routines				*
     *									*
     ************************************************************************/
    
    /**
     * Handle a redefinition of attribute error
     *
     * @param ctxt  an Relax-NG parser context
     */
    static void
    xmlRngPErrMemory(xmlRelaxNGParserCtxtPtr ctxt)
    {
        xmlStructuredErrorFunc schannel = NULL;
        xmlGenericErrorFunc channel = NULL;
        void *data = NULL;
    
        if (ctxt != NULL) {
            if (ctxt->serror != NULL)
    	    schannel = ctxt->serror;
    	else
    	    channel = ctxt->error;
            data = ctxt->userData;
            ctxt->nbErrors++;
        }
    
        xmlRaiseMemoryError(schannel, channel, data, XML_FROM_RELAXNGP, NULL);
    }
    
    /**
     * Handle a redefinition of attribute error
     *
     * @param ctxt  a Relax-NG validation context
     */
    static void
    xmlRngVErrMemory(xmlRelaxNGValidCtxtPtr ctxt)
    {
        xmlStructuredErrorFunc schannel = NULL;
        xmlGenericErrorFunc channel = NULL;
        void *data = NULL;
    
        if (ctxt != NULL) {
            if (ctxt->serror != NULL)
    	    schannel = ctxt->serror;
    	else
    	    channel = ctxt->error;
            data = ctxt->userData;
            ctxt->nbErrors++;
        }
    
        xmlRaiseMemoryError(schannel, channel, data, XML_FROM_RELAXNGV, NULL);
    }
    
    /**
     * Handle a Relax NG Parsing error
     *
     * @param ctxt  a Relax-NG parser context
     * @param node  the node raising the error
     * @param error  the error code
     * @param msg  message
     * @param str1  extra info
     * @param str2  extra info
     */
    static void LIBXML_ATTR_FORMAT(4,0)
    xmlRngPErr(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node, int error,
               const char *msg, const xmlChar * str1, const xmlChar * str2)
    {
        xmlStructuredErrorFunc schannel = NULL;
        xmlGenericErrorFunc channel = NULL;
        void *data = NULL;
        int res;
    
        if (ctxt != NULL) {
            if (ctxt->serror != NULL)
    	    schannel = ctxt->serror;
    	else
    	    channel = ctxt->error;
            data = ctxt->userData;
            ctxt->nbErrors++;
        }
    
        if ((channel == NULL) && (schannel == NULL)) {
            channel = xmlGenericError;
            data = xmlGenericErrorContext;
        }
    
        res = xmlRaiseError(schannel, channel, data, NULL, node,
                            XML_FROM_RELAXNGP, error, XML_ERR_ERROR, NULL, 0,
                            (const char *) str1, (const char *) str2, NULL, 0, 0,
                            msg, str1, str2);
        if (res < 0)
            xmlRngPErrMemory(ctxt);
    }
    
    /**
     * Handle a Relax NG Validation error
     *
     * @param ctxt  a Relax-NG validation context
     * @param node  the node raising the error
     * @param error  the error code
     * @param msg  message
     * @param str1  extra info
     * @param str2  extra info
     */
    static void LIBXML_ATTR_FORMAT(4,0)
    xmlRngVErr(xmlRelaxNGValidCtxtPtr ctxt, xmlNodePtr node, int error,
               const char *msg, const xmlChar * str1, const xmlChar * str2)
    {
        xmlStructuredErrorFunc schannel = NULL;
        xmlGenericErrorFunc channel = NULL;
        void *data = NULL;
        int res;
    
        if (ctxt != NULL) {
            if (ctxt->serror != NULL)
    	    schannel = ctxt->serror;
    	else
    	    channel = ctxt->error;
            data = ctxt->userData;
            ctxt->nbErrors++;
        }
    
        if ((channel == NULL) && (schannel == NULL)) {
            channel = xmlGenericError;
            data = xmlGenericErrorContext;
        }
    
        res = xmlRaiseError(schannel, channel, data, NULL, node,
                            XML_FROM_RELAXNGV, error, XML_ERR_ERROR, NULL, 0,
                            (const char *) str1, (const char *) str2, NULL, 0, 0,
                            msg, str1, str2);
        if (res < 0)
            xmlRngVErrMemory(ctxt);
    }
    
    /************************************************************************
     *									*
     *		Preliminary type checking interfaces			*
     *									*
     ************************************************************************/
    
    /**
     * Function provided by a type library to check if a type is exported
     *
     * @param data  data needed for the library
     * @param type  the type name
     * @returns 1 if yes, 0 if no and -1 in case of error.
     */
    typedef int (*xmlRelaxNGTypeHave) (void *data, const xmlChar * type);
    
    /**
     * Function provided by a type library to check if a value match a type
     *
     * @param data  data needed for the library
     * @param type  the type name
     * @param value  the value to check
     * @param result  place to store the result if needed
     * @returns 1 if yes, 0 if no and -1 in case of error.
     */
    typedef int (*xmlRelaxNGTypeCheck) (void *data, const xmlChar * type,
                                        const xmlChar * value, void **result,
                                        xmlNodePtr node);
    
    /**
     * Function provided by a type library to check a value facet
     *
     * @param data  data needed for the library
     * @param type  the type name
     * @param facet  the facet name
     * @param val  the facet value
     * @param strval  the string value
     * @param value  the value to check
     * @returns 1 if yes, 0 if no and -1 in case of error.
     */
    typedef int (*xmlRelaxNGFacetCheck) (void *data, const xmlChar * type,
                                         const xmlChar * facet,
                                         const xmlChar * val,
                                         const xmlChar * strval, void *value);
    
    /**
     * Function provided by a type library to free a returned result
     *
     * @param data  data needed for the library
     * @param result  the value to free
     */
    typedef void (*xmlRelaxNGTypeFree) (void *data, void *result);
    
    /**
     * Function provided by a type library to compare two values accordingly
     * to a type.
     *
     * @param data  data needed for the library
     * @param type  the type name
     * @param value1  the first value
     * @param value2  the second value
     * @returns 1 if yes, 0 if no and -1 in case of error.
     */
    typedef int (*xmlRelaxNGTypeCompare) (void *data, const xmlChar * type,
                                          const xmlChar * value1,
                                          xmlNodePtr ctxt1,
                                          void *comp1,
                                          const xmlChar * value2,
                                          xmlNodePtr ctxt2);
    typedef struct _xmlRelaxNGTypeLibrary xmlRelaxNGTypeLibrary;
    typedef xmlRelaxNGTypeLibrary *xmlRelaxNGTypeLibraryPtr;
    struct _xmlRelaxNGTypeLibrary {
        const xmlChar *namespace;   /* the datatypeLibrary value */
        void *data;                 /* data needed for the library */
        xmlRelaxNGTypeHave have;    /* the export function */
        xmlRelaxNGTypeCheck check;  /* the checking function */
        xmlRelaxNGTypeCompare comp; /* the compare function */
        xmlRelaxNGFacetCheck facet; /* the facet check function */
        xmlRelaxNGTypeFree freef;   /* the freeing function */
    };
    
    /************************************************************************
     *									*
     *			Allocation functions				*
     *									*
     ************************************************************************/
    static void xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar);
    static void xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define);
    static void xmlRelaxNGNormExtSpace(xmlChar * value);
    static void xmlRelaxNGFreeInnerSchema(xmlRelaxNGPtr schema);
    static int xmlRelaxNGEqualValidState(xmlRelaxNGValidCtxtPtr ctxt
                                         ATTRIBUTE_UNUSED,
                                         xmlRelaxNGValidStatePtr state1,
                                         xmlRelaxNGValidStatePtr state2);
    static void xmlRelaxNGFreeValidState(xmlRelaxNGValidCtxtPtr ctxt,
                                         xmlRelaxNGValidStatePtr state);
    
    /**
     * Deallocate a RelaxNG document structure.
     *
     * @param docu  a document structure
     */
    static void
    xmlRelaxNGFreeDocument(xmlRelaxNGDocumentPtr docu)
    {
        if (docu == NULL)
            return;
    
        if (docu->href != NULL)
            xmlFree(docu->href);
        if (docu->doc != NULL)
            xmlFreeDoc(docu->doc);
        if (docu->schema != NULL)
            xmlRelaxNGFreeInnerSchema(docu->schema);
        xmlFree(docu);
    }
    
    /**
     * Deallocate a RelaxNG document structures.
     *
     * @param docu  a list of  document structure
     */
    static void
    xmlRelaxNGFreeDocumentList(xmlRelaxNGDocumentPtr docu)
    {
        xmlRelaxNGDocumentPtr next;
    
        while (docu != NULL) {
            next = docu->next;
            xmlRelaxNGFreeDocument(docu);
            docu = next;
        }
    }
    
    /**
     * Deallocate a RelaxNG include structure.
     *
     * @param incl  a 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);
    }
    
    /**
     * Deallocate a RelaxNG include structure.
     *
     * @param incl  a include structure list
     */
    static void
    xmlRelaxNGFreeIncludeList(xmlRelaxNGIncludePtr incl)
    {
        xmlRelaxNGIncludePtr next;
    
        while (incl != NULL) {
            next = incl->next;
            xmlRelaxNGFreeInclude(incl);
            incl = next;
        }
    }
    
    /**
     * Allocate a new RelaxNG structure.
     *
     * @param ctxt  a Relax-NG validation context (optional)
     * @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) {
            xmlRngPErrMemory(ctxt);
            return (NULL);
        }
        memset(ret, 0, sizeof(xmlRelaxNG));
    
        return (ret);
    }
    
    /**
     * Deallocate a RelaxNG schema structure.
     *
     * @param schema  a schema structure
     */
    static void
    xmlRelaxNGFreeInnerSchema(xmlRelaxNGPtr schema)
    {
        if (schema == NULL)
            return;
    
        if (schema->doc != NULL)
            xmlFreeDoc(schema->doc);
        if (schema->defTab != NULL) {
            int i;
    
            for (i = 0; i < schema->defNr; i++)
                xmlRelaxNGFreeDefine(schema->defTab[i]);
            xmlFree(schema->defTab);
        }
    
        xmlFree(schema);
    }
    
    /**
     * Deallocate a RelaxNG structure.
     *
     * @param schema  a schema structure
     */
    void
    xmlRelaxNGFree(xmlRelaxNGPtr schema)
    {
        if (schema == NULL)
            return;
    
        if (schema->topgrammar != NULL)
            xmlRelaxNGFreeGrammar(schema->topgrammar);
        if (schema->doc != NULL)
            xmlFreeDoc(schema->doc);
        if (schema->documents != NULL)
            xmlRelaxNGFreeDocumentList(schema->documents);
        if (schema->includes != NULL)
            xmlRelaxNGFreeIncludeList(schema->includes);
        if (schema->defTab != NULL) {
            int i;
    
            for (i = 0; i < schema->defNr; i++)
                xmlRelaxNGFreeDefine(schema->defTab[i]);
            xmlFree(schema->defTab);
        }
    
        xmlFree(schema);
    }
    
    /**
     * Allocate a new RelaxNG grammar.
     *
     * @param ctxt  a Relax-NG validation context (optional)
     * @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) {
            xmlRngPErrMemory(ctxt);
            return (NULL);
        }
        memset(ret, 0, sizeof(xmlRelaxNGGrammar));
    
        return (ret);
    }
    
    /**
     * Deallocate a RelaxNG grammar structure.
     *
     * @param grammar  a grammar structure
     */
    static void
    xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar)
    {
        if (grammar == NULL)
            return;
    
        if (grammar->children != NULL) {
            xmlRelaxNGFreeGrammar(grammar->children);
        }
        if (grammar->next != NULL) {
            xmlRelaxNGFreeGrammar(grammar->next);
        }
        if (grammar->refs != NULL) {
            xmlHashFree(grammar->refs, NULL);
        }
        if (grammar->defs != NULL) {
            xmlHashFree(grammar->defs, NULL);
        }
    
        xmlFree(grammar);
    }
    
    /**
     * Allocate a new RelaxNG define.
     *
     * @param ctxt  a Relax-NG validation context
     * @param node  the node in the input document.
     * @returns the newly allocated structure or NULL in case or error
     */
    static xmlRelaxNGDefinePtr
    xmlRelaxNGNewDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
    {
        xmlRelaxNGDefinePtr ret;
    
        if (ctxt->defMax == 0) {
            ctxt->defMax = 16;
            ctxt->defNr = 0;
            ctxt->defTab = (xmlRelaxNGDefinePtr *)
                xmlMalloc(ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
            if (ctxt->defTab == NULL) {
                xmlRngPErrMemory(ctxt);
                return (NULL);
            }
        } else if (ctxt->defMax <= ctxt->defNr) {
            xmlRelaxNGDefinePtr *tmp;
    
            ctxt->defMax *= 2;
            tmp = (xmlRelaxNGDefinePtr *) xmlRealloc(ctxt->defTab,
                                                     ctxt->defMax *
                                                     sizeof
                                                     (xmlRelaxNGDefinePtr));
            if (tmp == NULL) {
                xmlRngPErrMemory(ctxt);
                return (NULL);
            }
            ctxt->defTab = tmp;
        }
        ret = (xmlRelaxNGDefinePtr) xmlMalloc(sizeof(xmlRelaxNGDefine));
        if (ret == NULL) {
            xmlRngPErrMemory(ctxt);
            return (NULL);
        }
        memset(ret, 0, sizeof(xmlRelaxNGDefine));
        ctxt->defTab[ctxt->defNr++] = ret;
        ret->node = node;
        ret->depth = -1;
        return (ret);
    }
    
    /**
     * Deallocate RelaxNG partition set structures.
     *
     * @param partitions  a partition set structure
     */
    static void
    xmlRelaxNGFreePartition(xmlRelaxNGPartitionPtr partitions)
    {
        xmlRelaxNGInterleaveGroupPtr group;
        int j;
    
        if (partitions != NULL) {
            if (partitions->groups != NULL) {
                for (j = 0; j < partitions->nbgroups; j++) {
                    group = partitions->groups[j];
                    if (group != NULL) {
                        if (group->defs != NULL)
                            xmlFree(group->defs);
                        if (group->attrs != NULL)
                            xmlFree(group->attrs);
                        xmlFree(group);
                    }
                }
                xmlFree(partitions->groups);
            }
            if (partitions->triage != NULL) {
                xmlHashFree(partitions->triage, NULL);
            }
            xmlFree(partitions);
        }
    }
    
    /**
     * Deallocate a RelaxNG define structure.
     *
     * @param define  a define structure
     */
    static void
    xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define)
    {
        if (define == NULL)
            return;
    
        if ((define->type == XML_RELAXNG_VALUE) && (define->attrs != NULL)) {
            xmlRelaxNGTypeLibraryPtr lib;
    
            lib = (xmlRelaxNGTypeLibraryPtr) define->data;
            if ((lib != NULL) && (lib->freef != NULL))
                lib->freef(lib->data, (void *) define->attrs);
        }
        if ((define->data != NULL) && (define->type == XML_RELAXNG_INTERLEAVE))
            xmlRelaxNGFreePartition((xmlRelaxNGPartitionPtr) define->data);
        if ((define->data != NULL) && (define->type == XML_RELAXNG_CHOICE))
            xmlHashFree((xmlHashTablePtr) define->data, NULL);
        if (define->name != NULL)
            xmlFree(define->name);
        if (define->ns != NULL)
            xmlFree(define->ns);
        if (define->value != NULL)
            xmlFree(define->value);
        if (define->contModel != NULL)
            xmlRegFreeRegexp(define->contModel);
        xmlFree(define);
    }
    
    /**
     * Allocate a new RelaxNG validation state container
     *
     * @param ctxt  a Relax-NG validation context
     * @param size  the default size for the container
     * @returns the newly allocated structure or NULL in case or error
     */
    static xmlRelaxNGStatesPtr
    xmlRelaxNGNewStates(xmlRelaxNGValidCtxtPtr ctxt, int size)
    {
        xmlRelaxNGStatesPtr ret;
    
        if ((ctxt != NULL) &&
            (ctxt->freeStates != NULL) && (ctxt->freeStatesNr > 0)) {
            ctxt->freeStatesNr--;
            ret = ctxt->freeStates[ctxt->freeStatesNr];
            ret->nbState = 0;
            return (ret);
        }
        if (size < 16)
            size = 16;
    
        ret = (xmlRelaxNGStatesPtr) xmlMalloc(sizeof(xmlRelaxNGStates) +
                                              (size -
                                               1) *
                                              sizeof(xmlRelaxNGValidStatePtr));
        if (ret == NULL) {
            xmlRngVErrMemory(ctxt);
            return (NULL);
        }
        ret->nbState = 0;
        ret->maxState = size;
        ret->tabState = (xmlRelaxNGValidStatePtr *) xmlMalloc((size) *
                                                              sizeof
                                                              (xmlRelaxNGValidStatePtr));
        if (ret->tabState == NULL) {
            xmlRngVErrMemory(ctxt);
            xmlFree(ret);
            return (NULL);
        }
        return (ret);
    }
    
    /**
     * Add a RelaxNG validation state to the container without checking
     * for unicity.
     *
     * @param ctxt  a Relax-NG validation context
     * @param states  the states container
     * @param state  the validation state
     * @returns 1 in case of success and 0 if this is a duplicate and -1 on error
     */
    static int
    xmlRelaxNGAddStatesUniq(xmlRelaxNGValidCtxtPtr ctxt,
                            xmlRelaxNGStatesPtr states,
                            xmlRelaxNGValidStatePtr state)
    {
        if (state == NULL) {
            return (-1);
        }
        if (states->nbState >= states->maxState) {
            xmlRelaxNGValidStatePtr *tmp;
            int size;
    
            size = states->maxState * 2;
            tmp = (xmlRelaxNGValidStatePtr *) xmlRealloc(states->tabState,
                                                         (size) *
                                                         sizeof
                                                         (xmlRelaxNGValidStatePtr));
            if (tmp == NULL) {
                xmlRngVErrMemory(ctxt);
                return (-1);
            }
            states->tabState = tmp;
            states->maxState = size;
        }
        states->tabState[states->nbState++] = state;
        return (1);
    }
    
    /**
     * Add a RelaxNG validation state to the container
     *
     * @param ctxt  a Relax-NG validation context
     * @param states  the states container
     * @param state  the validation state
     * @returns 1 in case of success and 0 if this is a duplicate and -1 on error
     */
    static int
    xmlRelaxNGAddStates(xmlRelaxNGValidCtxtPtr ctxt,
                        xmlRelaxNGStatesPtr states,
                        xmlRelaxNGValidStatePtr state)
    {
        int i;
    
        if (state == NULL || states == NULL) {
            return (-1);
        }
        if (states->nbState >= states->maxState) {
            xmlRelaxNGValidStatePtr *tmp;
            int size;
    
            size = states->maxState * 2;
            tmp = (xmlRelaxNGValidStatePtr *) xmlRealloc(states->tabState,
                                                         (size) *
                                                         sizeof
                                                         (xmlRelaxNGValidStatePtr));
            if (tmp == NULL) {
                xmlRngVErrMemory(ctxt);
                return (-1);
            }
            states->tabState = tmp;
            states->maxState = size;
        }
        for (i = 0; i < states->nbState; i++) {
            if (xmlRelaxNGEqualValidState(ctxt, state, states->tabState[i])) {
                xmlRelaxNGFreeValidState(ctxt, state);
                return (0);
            }
        }
        states->tabState[states->nbState++] = state;
        return (1);
    }
    
    /**
     * Free a RelaxNG validation state container
     *
     * @param ctxt  a Relax-NG validation context
     * @param states  the container
     */
    static void
    xmlRelaxNGFreeStates(xmlRelaxNGValidCtxtPtr ctxt,
                         xmlRelaxNGStatesPtr states)
    {
        if (states == NULL)
            return;
        if ((ctxt != NULL) && (ctxt->freeStates == NULL)) {
            ctxt->freeStatesMax = 40;
            ctxt->freeStatesNr = 0;
            ctxt->freeStates = (xmlRelaxNGStatesPtr *)
                xmlMalloc(ctxt->freeStatesMax * sizeof(xmlRelaxNGStatesPtr));
            if (ctxt->freeStates == NULL) {
                xmlRngVErrMemory(ctxt);
            }
        } else if ((ctxt != NULL)
                   && (ctxt->freeStatesNr >= ctxt->freeStatesMax)) {
            xmlRelaxNGStatesPtr *tmp;
    
            tmp = (xmlRelaxNGStatesPtr *) xmlRealloc(ctxt->freeStates,
                                                     2 * ctxt->freeStatesMax *
                                                     sizeof
                                                     (xmlRelaxNGStatesPtr));
            if (tmp == NULL) {
                xmlRngVErrMemory(ctxt);
                xmlFree(states->tabState);
                xmlFree(states);
                return;
            }
            ctxt->freeStates = tmp;
            ctxt->freeStatesMax *= 2;
        }
        if ((ctxt == NULL) || (ctxt->freeStates == NULL)) {
            xmlFree(states->tabState);
            xmlFree(states);
        } else {
            ctxt->freeStates[ctxt->freeStatesNr++] = states;
        }
    }
    
    /**
     * Allocate a new RelaxNG validation state
     *
     * @param ctxt  a Relax-NG validation context
     * @param node  the current node or NULL for the document
     * @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 ((ctxt->freeState != NULL) && (ctxt->freeState->nbState > 0)) {
            ctxt->freeState->nbState--;
            ret = ctxt->freeState->tabState[ctxt->freeState->nbState];
        } else {
            ret =
                (xmlRelaxNGValidStatePtr)
                xmlMalloc(sizeof(xmlRelaxNGValidState));
            if (ret == NULL) {
                xmlRngVErrMemory(ctxt);
                return (NULL);
            }
            memset(ret, 0, sizeof(xmlRelaxNGValidState));
        }
        ret->value = NULL;
        ret->endvalue = NULL;
        if (node == NULL) {
            ret->node = (xmlNodePtr) ctxt->doc;
            ret->seq = root;
        } else {
            ret->node = node;
            ret->seq = node->children;
        }
        ret->nbAttrs = 0;
        if (nbAttrs > 0) {
            if (ret->attrs == NULL) {
                if (nbAttrs < 4)
                    ret->maxAttrs = 4;
                else
                    ret->maxAttrs = nbAttrs;
                ret->attrs = (xmlAttrPtr *) xmlMalloc(ret->maxAttrs *
                                                      sizeof(xmlAttrPtr));
                if (ret->attrs == NULL) {
                    xmlRngVErrMemory(ctxt);
                    return (ret);
                }
            } else if (ret->maxAttrs < nbAttrs) {
                xmlAttrPtr *tmp;
    
                tmp = (xmlAttrPtr *) xmlRealloc(ret->attrs, nbAttrs *
                                                sizeof(xmlAttrPtr));
                if (tmp == NULL) {
                    xmlRngVErrMemory(ctxt);
                    return (ret);
                }
                ret->attrs = tmp;
                ret->maxAttrs = nbAttrs;
            }
            ret->nbAttrs = nbAttrs;
            if (nbAttrs < MAX_ATTR) {
                memcpy(ret->attrs, attrs, sizeof(xmlAttrPtr) * nbAttrs);
            } else {
                attr = node->properties;
                nbAttrs = 0;
                while (attr != NULL) {
                    ret->attrs[nbAttrs++] = attr;
                    attr = attr->next;
                }
            }
        }
        ret->nbAttrLeft = ret->nbAttrs;
        return (ret);
    }
    
    /**
     * Copy the validation state
     *
     * @param ctxt  a Relax-NG validation context
     * @param state  a validation state
     * @returns the newly allocated structure or NULL in case or error
     */
    static xmlRelaxNGValidStatePtr
    xmlRelaxNGCopyValidState(xmlRelaxNGValidCtxtPtr ctxt,
                             xmlRelaxNGValidStatePtr state)
    {
        xmlRelaxNGValidStatePtr ret;
        unsigned int maxAttrs;
        xmlAttrPtr *attrs;
    
        if (state == NULL)
            return (NULL);
        if ((ctxt->freeState != NULL) && (ctxt->freeState->nbState > 0)) {
            ctxt->freeState->nbState--;
            ret = ctxt->freeState->tabState[ctxt->freeState->nbState];
        } else {
            ret =
                (xmlRelaxNGValidStatePtr)
                xmlMalloc(sizeof(xmlRelaxNGValidState));
            if (ret == NULL) {
                xmlRngVErrMemory(ctxt);
                return (NULL);
            }
            memset(ret, 0, sizeof(xmlRelaxNGValidState));
        }
        attrs = ret->attrs;
        maxAttrs = ret->maxAttrs;
        memcpy(ret, state, sizeof(xmlRelaxNGValidState));
        ret->attrs = attrs;
        ret->maxAttrs = maxAttrs;
        if (state->nbAttrs > 0) {
            if (ret->attrs == NULL) {
                ret->maxAttrs = state->maxAttrs;
                ret->attrs = (xmlAttrPtr *) xmlMalloc(ret->maxAttrs *
                                                      sizeof(xmlAttrPtr));
                if (ret->attrs == NULL) {
                    xmlRngVErrMemory(ctxt);
                    ret->nbAttrs = 0;
                    return (ret);
                }
            } else if (ret->maxAttrs < state->nbAttrs) {
                xmlAttrPtr *tmp;
    
                tmp = (xmlAttrPtr *) xmlRealloc(ret->attrs, state->maxAttrs *
                                                sizeof(xmlAttrPtr));
                if (tmp == NULL) {
                    xmlRngVErrMemory(ctxt);
                    ret->nbAttrs = 0;
                    return (ret);
                }
                ret->maxAttrs = state->maxAttrs;
                ret->attrs = tmp;
            }
            memcpy(ret->attrs, state->attrs,
                   state->nbAttrs * sizeof(xmlAttrPtr));
        }
        return (ret);
    }
    
    /**
     * Compare the validation states for equality
     *
     * @param ctxt  a Relax-NG validation context
     * @param state1  a validation state
     * @param state2  a validation state
     * @returns 1 if equal, 0 otherwise
     */
    static int
    xmlRelaxNGEqualValidState(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
                              xmlRelaxNGValidStatePtr state1,
                              xmlRelaxNGValidStatePtr state2)
    {
        int i;
    
        if ((state1 == NULL) || (state2 == NULL))
            return (0);
        if (state1 == state2)
            return (1);
        if (state1->node != state2->node)
            return (0);
        if (state1->seq != state2->seq)
            return (0);
        if (state1->nbAttrLeft != state2->nbAttrLeft)
            return (0);
        if (state1->nbAttrs != state2->nbAttrs)
            return (0);
        if (state1->endvalue != state2->endvalue)
            return (0);
        if ((state1->value != state2->value) &&
            (!xmlStrEqual(state1->value, state2->value)))
            return (0);
        for (i = 0; i < state1->nbAttrs; i++) {
            if (state1->attrs[i] != state2->attrs[i])
                return (0);
        }
        return (1);
    }
    
    /**
     * Deallocate a RelaxNG validation state structure.
     *
     * @param ctxt  validation context
     * @param state  a validation state structure
     */
    static void
    xmlRelaxNGFreeValidState(xmlRelaxNGValidCtxtPtr ctxt,
                             xmlRelaxNGValidStatePtr state)
    {
        if (state == NULL)
            return;
    
        if ((ctxt != NULL) && (ctxt->freeState == NULL)) {
            ctxt->freeState = xmlRelaxNGNewStates(ctxt, 40);
        }
        if ((ctxt == NULL) || (ctxt->freeState == NULL)) {
            if (state->attrs != NULL)
                xmlFree(state->attrs);
            xmlFree(state);
        } else {
            xmlRelaxNGAddStatesUniq(ctxt, ctxt->freeState, state);
        }
    }
    
    /************************************************************************
     *									*
     *			Semi internal functions				*
     *									*
     ************************************************************************/
    
    /**
     * Semi private function used to pass information to a parser context
     * which are a combination of xmlRelaxNGParserFlag .
     *
     * @param ctxt  a RelaxNG parser context
     * @param flags  a set of flags values
     * @returns 0 if success and -1 in case of error
     */
    int
    xmlRelaxParserSetFlag(xmlRelaxNGParserCtxtPtr ctxt, int flags)
    {
        if (ctxt == NULL) return(-1);
        if (flags & XML_RELAXNGP_FREE_DOC) {
            ctxt->crng |= XML_RELAXNGP_FREE_DOC;
    	flags -= XML_RELAXNGP_FREE_DOC;
        }
        if (flags & XML_RELAXNGP_CRNG) {
            ctxt->crng |= XML_RELAXNGP_CRNG;
    	flags -= XML_RELAXNGP_CRNG;
        }
        if (flags != 0) return(-1);
        return(0);
    }
    
    /************************************************************************
     *									*
     *			Document functions				*
     *									*
     ************************************************************************/
    static xmlDocPtr xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt,
                                          xmlDocPtr doc);
    
    static xmlDoc *
    xmlRelaxReadFile(xmlRelaxNGParserCtxtPtr ctxt, const char *filename) {
        xmlParserCtxtPtr pctxt;
        xmlDocPtr doc;
    
        pctxt = xmlNewParserCtxt();
        if (pctxt == NULL) {
            xmlRngPErrMemory(ctxt);
            return(NULL);
        }
        if (ctxt->serror != NULL)
            xmlCtxtSetErrorHandler(pctxt, ctxt->serror, ctxt->userData);
        if (ctxt->resourceLoader != NULL)
            xmlCtxtSetResourceLoader(pctxt, ctxt->resourceLoader,
                                     ctxt->resourceCtxt);
        doc = xmlCtxtReadFile(pctxt, filename, NULL, 0);
        xmlFreeParserCtxt(pctxt);
    
        return(doc);
    }
    
    static xmlDoc *
    xmlRelaxReadMemory(xmlRelaxNGParserCtxtPtr ctxt, const char *buf, int size) {
        xmlParserCtxtPtr pctxt;
        xmlDocPtr doc;
    
        pctxt = xmlNewParserCtxt();
        if (pctxt == NULL) {
            xmlRngPErrMemory(ctxt);
            return(NULL);
        }
        if (ctxt->serror != NULL)
            xmlCtxtSetErrorHandler(pctxt, ctxt->serror, ctxt->userData);
        if (ctxt->resourceLoader != NULL)
            xmlCtxtSetResourceLoader(pctxt, ctxt->resourceLoader,
                                     ctxt->resourceCtxt);
        doc = xmlCtxtReadMemory(pctxt, buf, size, NULL, NULL, 0);
        xmlFreeParserCtxt(pctxt);
    
        return(doc);
    }
    
    /**
     * Pushes a new include on top of the include stack
     *
     * @param ctxt  the parser context
     * @param value  the element doc
     * @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) {
                xmlRngPErrMemory(ctxt);
                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) {
                xmlRngPErrMemory(ctxt);
                return (0);
            }
        }
        ctxt->incTab[ctxt->incNr] = value;
        ctxt->inc = value;
        return (ctxt->incNr++);
    }
    
    /**
     * Pops the top include from the include stack
     *
     * @param ctxt  the parser context
     * @returns the include just removed
     */
    static xmlRelaxNGIncludePtr
    xmlRelaxNGIncludePop(xmlRelaxNGParserCtxtPtr ctxt)
    {
        xmlRelaxNGIncludePtr ret;
    
        if (ctxt->incNr <= 0)
            return (NULL);
        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] = NULL;
        return (ret);
    }
    
    /**
     * Applies the elimination algorithm of 4.7
     *
     * @param ctxt  the parser context
     * @param URL  the normalized URL
     * @param target  the included target
     * @param name  the define name to eliminate
     * @returns 0 in case of error, 1 in case of success.
     */
    static int
    xmlRelaxNGRemoveRedefine(xmlRelaxNGParserCtxtPtr ctxt,
                             const xmlChar * URL ATTRIBUTE_UNUSED,
                             xmlNodePtr target, const xmlChar * name)
    {
        int found = 0;
        xmlNodePtr tmp, tmp2;
        xmlChar *name2;
    
        tmp = target;
        while (tmp != NULL) {
            tmp2 = tmp->next;
            if ((name == NULL) && (IS_RELAXNG(tmp, "start"))) {
                found = 1;
                xmlUnlinkNode(tmp);
                xmlFreeNode(tmp);
            } else if ((name != NULL) && (IS_RELAXNG(tmp, "define"))) {
                name2 = xmlGetProp(tmp, BAD_CAST "name");
                xmlRelaxNGNormExtSpace(name2);
                if (name2 != NULL) {
                    if (xmlStrEqual(name, name2)) {
                        found = 1;
                        xmlUnlinkNode(tmp);
                        xmlFreeNode(tmp);
                    }
                    xmlFree(name2);
                }
            } else if (IS_RELAXNG(tmp, "include")) {
                xmlChar *href = NULL;
                xmlRelaxNGDocumentPtr inc = tmp->psvi;
    
                if ((inc != NULL) && (inc->doc != NULL) &&
                    (inc->doc->children != NULL)) {
    
                    if (xmlStrEqual
                        (inc->doc->children->name, BAD_CAST "grammar")) {
                        if (xmlRelaxNGRemoveRedefine(ctxt, href,
                                                     xmlDocGetRootElement(inc->doc)->children,
                                                     name) == 1) {
                            found = 1;
                        }
                    }
                }
                if (xmlRelaxNGRemoveRedefine(ctxt, URL, tmp->children, name) == 1) {
                    found = 1;
                }
            }
            tmp = tmp2;
        }
        return (found);
    }
    
    /**
     * 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.
     *
     * @param ctxt  the parser context
     * @param URL  the normalized URL
     * @param node  the include node.
     * @param ns  the namespace passed from the context.
     * @returns the xmlRelaxNGIncludePtr or NULL in case of error
     */
    static xmlRelaxNGIncludePtr
    xmlRelaxNGLoadInclude(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar * URL,
                          xmlNodePtr node, const xmlChar * ns)
    {
        xmlRelaxNGIncludePtr ret = NULL;
        xmlDocPtr doc;
        int i;
        xmlNodePtr root, cur;
    
        /*
         * check against recursion in the stack
         */
        for (i = 0; i < ctxt->incNr; i++) {
            if (xmlStrEqual(ctxt->incTab[i]->href, URL)) {
                xmlRngPErr(ctxt, NULL, XML_RNGP_INCLUDE_RECURSE,
                           "Detected an Include recursion for %s\n", URL,
                           NULL);
                return (NULL);
            }
        }
    
        /*
         * load the document
         */
        doc = xmlRelaxReadFile(ctxt, (const char *) URL);
        if (doc == NULL) {
            xmlRngPErr(ctxt, node, XML_RNGP_PARSE_ERROR,
                       "xmlRelaxNG: could not load %s\n", URL, NULL);
            return (NULL);
        }
    
        /*
         * Allocate the document structures and register it first.
         */
        ret = (xmlRelaxNGIncludePtr) xmlMalloc(sizeof(xmlRelaxNGInclude));
        if (ret == NULL) {
            xmlRngPErrMemory(ctxt);
            xmlFreeDoc(doc);
            return (NULL);
        }
        memset(ret, 0, sizeof(xmlRelaxNGInclude));
        ret->doc = doc;
        ret->href = xmlStrdup(URL);
        ret->next = ctxt->includes;
        ctxt->includes = ret;
    
        /*
         * transmit the ns if needed
         */
        if (ns != NULL) {
            root = xmlDocGetRootElement(doc);
            if (root != NULL) {
                if (xmlHasProp(root, BAD_CAST "ns") == NULL) {
                    xmlSetProp(root, BAD_CAST "ns", ns);
                }
            }
        }
    
        /*
         * push it on the stack
         */
        xmlRelaxNGIncludePush(ctxt, ret);
    
        /*
         * Some preprocessing of the document content, this include recursing
         * in the include stack.
         */
    
        doc = xmlRelaxNGCleanupDoc(ctxt, doc);
        if (doc == NULL) {
            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) {
            xmlRngPErr(ctxt, node, XML_RNGP_EMPTY,
                       "xmlRelaxNG: included document is empty %s\n", URL,
                       NULL);
            return (NULL);
        }
        if (!IS_RELAXNG(root, "grammar")) {
            xmlRngPErr(ctxt, node, XML_RNGP_GRAMMAR_MISSING,
                       "xmlRelaxNG: included document %s root is not a grammar\n",
                       URL, NULL);
            return (NULL);
        }
    
        /*
         * Elimination of redefined rules in the include.
         */
        cur = node->children;
        while (cur != NULL) {
            if (IS_RELAXNG(cur, "start")) {
                int found = 0;
    
                found =
                    xmlRelaxNGRemoveRedefine(ctxt, URL, root->children, NULL);
                if (!found) {
                    xmlRngPErr(ctxt, node, XML_RNGP_START_MISSING,
                               "xmlRelaxNG: include %s has a start but not the included grammar\n",
                               URL, NULL);
                }
            } else if (IS_RELAXNG(cur, "define")) {
                xmlChar *name;
    
                name = xmlGetProp(cur, BAD_CAST "name");
                if (name == NULL) {
                    xmlRngPErr(ctxt, node, XML_RNGP_NAME_MISSING,
                               "xmlRelaxNG: include %s has define without name\n",
                               URL, NULL);
                } else {
                    int found;
    
                    xmlRelaxNGNormExtSpace(name);
                    found = xmlRelaxNGRemoveRedefine(ctxt, URL,
                                                     root->children, name);
                    if (!found) {
                        xmlRngPErr(ctxt, node, XML_RNGP_DEFINE_MISSING,
                                   "xmlRelaxNG: include %s has a define %s but not the included grammar\n",
                                   URL, name);
                    }
                    xmlFree(name);
                }
            }
            if (IS_RELAXNG(cur, "div") && cur->children != NULL) {
                cur = cur->children;
            } else {
                if (cur->next != NULL) {
                    cur = cur->next;
                } else {
                    while (cur->parent != node && cur->parent->next == NULL) {
                        cur = cur->parent;
                    }
                    cur = cur->parent != node ? cur->parent->next : NULL;
                }
            }
        }
    
    
        return (ret);
    }
    
    /**
     * Pushes a new error on top of the error stack
     *
     * @param ctxt  the validation context
     * @param err  the error code
     * @param arg1  the first string argument
     * @param arg2  the second string argument
     * @param dup  arg need to be duplicated
     * @returns 0 in case of error, the index in the stack otherwise
     */
    static int
    xmlRelaxNGValidErrorPush(xmlRelaxNGValidCtxtPtr ctxt,
                             xmlRelaxNGValidErr err, const xmlChar * arg1,
                             const xmlChar * arg2, int dup)
    {
        xmlRelaxNGValidErrorPtr cur;
    
        if (ctxt->errTab == NULL) {
            ctxt->errMax = 8;
            ctxt->errNr = 0;
            ctxt->errTab =
                (xmlRelaxNGValidErrorPtr) xmlMalloc(ctxt->errMax *
                                                    sizeof
                                                    (xmlRelaxNGValidError));
            if (ctxt->errTab == NULL) {
                xmlRngVErrMemory(ctxt);
                return (0);
            }
            ctxt->err = NULL;
        }
        if (ctxt->errNr >= ctxt->errMax) {
            ctxt->errMax *= 2;
            ctxt->errTab =
                (xmlRelaxNGValidErrorPtr) xmlRealloc(ctxt->errTab,
                                                     ctxt->errMax *
                                                     sizeof
                                                     (xmlRelaxNGValidError));
            if (ctxt->errTab == NULL) {
                xmlRngVErrMemory(ctxt);
                return (0);
            }
            ctxt->err = &ctxt->errTab[ctxt->errNr - 1];
        }
        if ((ctxt->err != NULL) && (ctxt->state != NULL) &&
            (ctxt->err->node == ctxt->state->node) && (ctxt->err->err == err))
            return (ctxt->errNr);
        cur = &ctxt->errTab[ctxt->errNr];
        cur->err = err;
        if (dup) {
            cur->arg1 = xmlStrdup(arg1);
            cur->arg2 = xmlStrdup(arg2);
            cur->flags = ERROR_IS_DUP;
        } else {
            cur->arg1 = arg1;
            cur->arg2 = arg2;
            cur->flags = 0;
        }
        if (ctxt->state != NULL) {
            cur->node = ctxt->state->node;
            cur->seq = ctxt->state->seq;
        } else {
            cur->node = NULL;
            cur->seq = NULL;
        }
        ctxt->err = cur;
        return (ctxt->errNr++);
    }
    
    /**
     * Pops the top error from the error stack
     *
     * @param ctxt  the validation context
     */
    static void
    xmlRelaxNGValidErrorPop(xmlRelaxNGValidCtxtPtr ctxt)
    {
        xmlRelaxNGValidErrorPtr cur;
    
        if (ctxt->errNr <= 0) {
            ctxt->err = NULL;
            return;
        }
        ctxt->errNr--;
        if (ctxt->errNr > 0)
            ctxt->err = &ctxt->errTab[ctxt->errNr - 1];
        else
            ctxt->err = NULL;
        cur = &ctxt->errTab[ctxt->errNr];
        if (cur->flags & ERROR_IS_DUP) {
            if (cur->arg1 != NULL)
                xmlFree((xmlChar *) cur->arg1);
            cur->arg1 = NULL;
            if (cur->arg2 != NULL)
                xmlFree((xmlChar *) cur->arg2);
            cur->arg2 = NULL;
            cur->flags = 0;
        }
    }
    
    /**
     * Pushes a new doc on top of the doc stack
     *
     * @param ctxt  the parser context
     * @param value  the element doc
     * @returns 0 in case of error, the index in the stack otherwise
     */
    static int
    xmlRelaxNGDocumentPush(xmlRelaxNGParserCtxtPtr ctxt,
                           xmlRelaxNGDocumentPtr value)
    {
        if (ctxt->docTab == NULL) {
            ctxt->docMax = 4;
            ctxt->docNr = 0;
            ctxt->docTab =
                (xmlRelaxNGDocumentPtr *) xmlMalloc(ctxt->docMax *
                                                    sizeof(ctxt->docTab[0]));
            if (ctxt->docTab == NULL) {
                xmlRngPErrMemory(ctxt);
                return (0);
            }
        }
        if (ctxt->docNr >= ctxt->docMax) {
            ctxt->docMax *= 2;
            ctxt->docTab =
                (xmlRelaxNGDocumentPtr *) xmlRealloc(ctxt->docTab,
                                                     ctxt->docMax *
                                                     sizeof(ctxt->docTab[0]));
            if (ctxt->docTab == NULL) {
                xmlRngPErrMemory(ctxt);
                return (0);
            }
        }
        ctxt->docTab[ctxt->docNr] = value;
        ctxt->doc = value;
        return (ctxt->docNr++);
    }
    
    /**
     * Pops the top doc from the doc stack
     *
     * @param ctxt  the parser context
     * @returns the doc just removed
     */
    static xmlRelaxNGDocumentPtr
    xmlRelaxNGDocumentPop(xmlRelaxNGParserCtxtPtr ctxt)
    {
        xmlRelaxNGDocumentPtr ret;
    
        if (ctxt->docNr <= 0)
            return (NULL);
        ctxt->docNr--;
        if (ctxt->docNr > 0)
            ctxt->doc = ctxt->docTab[ctxt->docNr - 1];
        else
            ctxt->doc = NULL;
        ret = ctxt->docTab[ctxt->docNr];
        ctxt->docTab[ctxt->docNr] = NULL;
        return (ret);
    }
    
    /**
     * 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.
     *
     * @param ctxt  the parser context
     * @param URL  the normalized URL
     * @param ns  the inherited ns if any
     * @returns the xmlRelaxNGDocumentPtr or NULL in case of error
     */
    static xmlRelaxNGDocumentPtr
    xmlRelaxNGLoadExternalRef(xmlRelaxNGParserCtxtPtr ctxt,
                              const xmlChar * URL, const xmlChar * ns)
    {
        xmlRelaxNGDocumentPtr ret = NULL;
        xmlDocPtr doc;
        xmlNodePtr root;
        int i;
    
        /*
         * check against recursion in the stack
         */
        for (i = 0; i < ctxt->docNr; i++) {
            if (xmlStrEqual(ctxt->docTab[i]->href, URL)) {
                xmlRngPErr(ctxt, NULL, XML_RNGP_EXTERNALREF_RECURSE,
                           "Detected an externalRef recursion for %s\n", URL,
                           NULL);
                return (NULL);
            }
        }
    
        /*
         * load the document
         */
        doc = xmlRelaxReadFile(ctxt, (const char *) URL);
        if (doc == NULL) {
            xmlRngPErr(ctxt, NULL, XML_RNGP_PARSE_ERROR,
                       "xmlRelaxNG: could not load %s\n", URL, NULL);
            return (NULL);
        }
    
        /*
         * Allocate the document structures and register it first.
         */
        ret = (xmlRelaxNGDocumentPtr) xmlMalloc(sizeof(xmlRelaxNGDocument));
        if (ret == NULL) {
            xmlRngPErrMemory(ctxt);
            xmlFreeDoc(doc);
            return (NULL);
        }
        memset(ret, 0, sizeof(xmlRelaxNGDocument));
        ret->doc = doc;
        ret->href = xmlStrdup(URL);
        ret->next = ctxt->documents;
        ret->externalRef = 1;
        ctxt->documents = ret;
    
        /*
         * transmit the ns if needed
         */
        if (ns != NULL) {
            root = xmlDocGetRootElement(doc);
            if (root != NULL) {
                if (xmlHasProp(root, BAD_CAST "ns") == NULL) {
                    xmlSetProp(root, BAD_CAST "ns", ns);
                }
            }
        }
    
        /*
         * push it on the stack and register it in the hash table
         */
        xmlRelaxNGDocumentPush(ctxt, ret);
    
        /*
         * Some preprocessing of the document content
         */
        doc = xmlRelaxNGCleanupDoc(ctxt, doc);
        if (doc == NULL) {
            ctxt->doc = NULL;
            return (NULL);
        }
    
        xmlRelaxNGDocumentPop(ctxt);
    
        return (ret);
    }
    
    /************************************************************************
     *									*
     *			Error functions					*
     *									*
     ************************************************************************/
    
    #define VALID_ERR(a) xmlRelaxNGAddValidError(ctxt, a, NULL, NULL, 0);
    #define VALID_ERR2(a, b) xmlRelaxNGAddValidError(ctxt, a, b, NULL, 0);
    #define VALID_ERR3(a, b, c) xmlRelaxNGAddValidError(ctxt, a, b, c, 0);
    #define VALID_ERR2P(a, b) xmlRelaxNGAddValidError(ctxt, a, b, NULL, 1);
    #define VALID_ERR3P(a, b, c) xmlRelaxNGAddValidError(ctxt, a, b, c, 1);
    
    static const char *
    xmlRelaxNGDefName(xmlRelaxNGDefinePtr def)
    {
        if (def == NULL)
            return ("none");
        switch (def->type) {
            case XML_RELAXNG_EMPTY:
                return ("empty");
            case XML_RELAXNG_NOT_ALLOWED:
                return ("notAllowed");
            case XML_RELAXNG_EXCEPT:
                return ("except");
            case XML_RELAXNG_TEXT:
                return ("text");
            case XML_RELAXNG_ELEMENT:
                return ("element");
            case XML_RELAXNG_DATATYPE:
                return ("datatype");
            case XML_RELAXNG_VALUE:
                return ("value");
            case XML_RELAXNG_LIST:
                return ("list");
            case XML_RELAXNG_ATTRIBUTE:
                return ("attribute");
            case XML_RELAXNG_DEF:
                return ("def");
            case XML_RELAXNG_REF:
                return ("ref");
            case XML_RELAXNG_EXTERNALREF:
                return ("externalRef");
            case XML_RELAXNG_PARENTREF:
                return ("parentRef");
            case XML_RELAXNG_OPTIONAL:
                return ("optional");
            case XML_RELAXNG_ZEROORMORE:
                return ("zeroOrMore");
            case XML_RELAXNG_ONEORMORE:
                return ("oneOrMore");
            case XML_RELAXNG_CHOICE:
                return ("choice");
            case XML_RELAXNG_GROUP:
                return ("group");
            case XML_RELAXNG_INTERLEAVE:
                return ("interleave");
            case XML_RELAXNG_START:
                return ("start");
            case XML_RELAXNG_NOOP:
                return ("noop");
            case XML_RELAXNG_PARAM:
                return ("param");
        }
        return ("unknown");
    }
    
    /**
     * computes a formatted error string for the given error code and args
     *
     * @param err  the error code
     * @param arg1  the first string argument
     * @param arg2  the second string argument
     * @returns the error string, it must be deallocated by the caller
     */
    static xmlChar *
    xmlRelaxNGGetErrorString(xmlRelaxNGValidErr err, const xmlChar * arg1,
                             const xmlChar * arg2)
    {
        char msg[1000];
        xmlChar *result;
    
        if (arg1 == NULL)
            arg1 = BAD_CAST "";
        if (arg2 == NULL)
            arg2 = BAD_CAST "";
    
        msg[0] = 0;
        switch (err) {
            case XML_RELAXNG_OK:
                return (NULL);
            case XML_RELAXNG_ERR_MEMORY:
                return (xmlCharStrdup("out of memory\n"));
            case XML_RELAXNG_ERR_TYPE:
                snprintf(msg, 1000, "failed to validate type %s\n", arg1);
                break;
            case XML_RELAXNG_ERR_TYPEVAL:
                snprintf(msg, 1000, "Type %s doesn't allow value '%s'\n", arg1,
                         arg2);
                break;
            case XML_RELAXNG_ERR_DUPID:
                snprintf(msg, 1000, "ID %s redefined\n", arg1);
                break;
            case XML_RELAXNG_ERR_TYPECMP:
                snprintf(msg, 1000, "failed to compare type %s\n", arg1);
                break;
            case XML_RELAXNG_ERR_NOSTATE:
                return (xmlCharStrdup("Internal error: no state\n"));
            case XML_RELAXNG_ERR_NODEFINE:
                return (xmlCharStrdup("Internal error: no define\n"));
            case XML_RELAXNG_ERR_INTERNAL:
                snprintf(msg, 1000, "Internal error: %s\n", arg1);
                break;
            case XML_RELAXNG_ERR_LISTEXTRA:
                snprintf(msg, 1000, "Extra data in list: %s\n", arg1);
                break;
            case XML_RELAXNG_ERR_INTERNODATA:
                return (xmlCharStrdup
                        ("Internal: interleave block has no data\n"));
            case XML_RELAXNG_ERR_INTERSEQ:
                return (xmlCharStrdup("Invalid sequence in interleave\n"));
            case XML_RELAXNG_ERR_INTEREXTRA:
                snprintf(msg, 1000, "Extra element %s in interleave\n", arg1);
                break;
            case XML_RELAXNG_ERR_ELEMNAME:
                snprintf(msg, 1000, "Expecting element %s, got %s\n", arg1,
                         arg2);
                break;
            case XML_RELAXNG_ERR_ELEMNONS:
                snprintf(msg, 1000, "Expecting a namespace for element %s\n",
                         arg1);
                break;
            case XML_RELAXNG_ERR_ELEMWRONGNS:
                snprintf(msg, 1000,
                         "Element %s has wrong namespace: expecting %s\n", arg1,
                         arg2);
                break;
            case XML_RELAXNG_ERR_ELEMWRONG:
                snprintf(msg, 1000, "Did not expect element %s there\n", arg1);
                break;
            case XML_RELAXNG_ERR_TEXTWRONG:
                snprintf(msg, 1000,
                         "Did not expect text in element %s content\n", arg1);
                break;
            case XML_RELAXNG_ERR_ELEMEXTRANS:
                snprintf(msg, 1000, "Expecting no namespace for element %s\n",
                         arg1);
                break;
            case XML_RELAXNG_ERR_ELEMNOTEMPTY:
                snprintf(msg, 1000, "Expecting element %s to be empty\n", arg1);
                break;
            case XML_RELAXNG_ERR_NOELEM:
                snprintf(msg, 1000, "Expecting an element %s, got nothing\n",
                         arg1);
                break;
            case XML_RELAXNG_ERR_NOTELEM:
                return (xmlCharStrdup("Expecting an element got text\n"));
            case XML_RELAXNG_ERR_ATTRVALID:
                snprintf(msg, 1000, "Element %s failed to validate attributes\n",
                         arg1);
                break;
            case XML_RELAXNG_ERR_CONTENTVALID:
                snprintf(msg, 1000, "Element %s failed to validate content\n",
                         arg1);
                break;
            case XML_RELAXNG_ERR_EXTRACONTENT:
                snprintf(msg, 1000, "Element %s has extra content: %s\n",
                         arg1, arg2);
                break;
            case XML_RELAXNG_ERR_INVALIDATTR:
                snprintf(msg, 1000, "Invalid attribute %s for element %s\n",
                         arg1, arg2);
                break;
            case XML_RELAXNG_ERR_LACKDATA:
                snprintf(msg, 1000, "Datatype element %s contains no data\n",
                         arg1);
                break;
            case XML_RELAXNG_ERR_DATAELEM:
                snprintf(msg, 1000, "Datatype element %s has child elements\n",
                         arg1);
                break;
            case XML_RELAXNG_ERR_VALELEM:
                snprintf(msg, 1000, "Value element %s has child elements\n",
                         arg1);
                break;
            case XML_RELAXNG_ERR_LISTELEM:
                snprintf(msg, 1000, "List element %s has child elements\n",
                         arg1);
                break;
            case XML_RELAXNG_ERR_DATATYPE:
                snprintf(msg, 1000, "Error validating datatype %s\n", arg1);
                break;
            case XML_RELAXNG_ERR_VALUE:
                snprintf(msg, 1000, "Error validating value %s\n", arg1);
                break;
            case XML_RELAXNG_ERR_LIST:
                return (xmlCharStrdup("Error validating list\n"));
            case XML_RELAXNG_ERR_NOGRAMMAR:
                return (xmlCharStrdup("No top grammar defined\n"));
            case XML_RELAXNG_ERR_EXTRADATA:
                return (xmlCharStrdup("Extra data in the document\n"));
            default:
                return (xmlCharStrdup("Unknown error !\n"));
        }
        if (msg[0] == 0) {
            snprintf(msg, 1000, "Unknown error code %d\n", err);
        }
        msg[1000 - 1] = 0;
        result = xmlCharStrdup(msg);
        return (xmlEscapeFormatString(&result));
    }
    
    /**
     * Show a validation error.
     *
     * @param ctxt  the validation context
     * @param err  the error number
     * @param node  the node
     * @param child  the node child generating the problem.
     * @param arg1  the first argument
     * @param arg2  the second argument
     */
    static void
    xmlRelaxNGShowValidError(xmlRelaxNGValidCtxtPtr ctxt,
                             xmlRelaxNGValidErr err, xmlNodePtr node,
                             xmlNodePtr child, const xmlChar * arg1,
                             const xmlChar * arg2)
    {
        xmlChar *msg;
    
        if (ctxt->flags & FLAGS_NOERROR)
            return;
    
        msg = xmlRelaxNGGetErrorString(err, arg1, arg2);
        if (msg == NULL)
            return;
    
        if (ctxt->errNo == XML_RELAXNG_OK)
            ctxt->errNo = err;
        xmlRngVErr(ctxt, (child == NULL ? node : child), err,
                   (const char *) msg, arg1, arg2);
        xmlFree(msg);
    }
    
    /**
     * pop and discard all errors until the given level is reached
     *
     * @param ctxt  the validation context
     * @param level  the error level in the stack
     */
    static void
    xmlRelaxNGPopErrors(xmlRelaxNGValidCtxtPtr ctxt, int level)
    {
        int i;
        xmlRelaxNGValidErrorPtr err;
    
        for (i = level; i < ctxt->errNr; i++) {
            err = &ctxt->errTab[i];
            if (err->flags & ERROR_IS_DUP) {
                if (err->arg1 != NULL)
                    xmlFree((xmlChar *) err->arg1);
                err->arg1 = NULL;
                if (err->arg2 != NULL)
                    xmlFree((xmlChar *) err->arg2);
                err->arg2 = NULL;
                err->flags = 0;
            }
        }
        ctxt->errNr = level;
        if (ctxt->errNr <= 0)
            ctxt->err = NULL;
    }
    
    /**
     * Show all validation error over a given index.
     *
     * @param ctxt  the validation context
     */
    static void
    xmlRelaxNGDumpValidError(xmlRelaxNGValidCtxtPtr ctxt)
    {
        int i, j, k;
        xmlRelaxNGValidErrorPtr err, dup;
    
        for (i = 0, k = 0; i < ctxt->errNr; i++) {
            err = &ctxt->errTab[i];
            if (k < MAX_ERROR) {
                for (j = 0; j < i; j++) {
                    dup = &ctxt->errTab[j];
                    if ((err->err == dup->err) && (err->node == dup->node) &&
                        (xmlStrEqual(err->arg1, dup->arg1)) &&
                        (xmlStrEqual(err->arg2, dup->arg2))) {
                        goto skip;
                    }
                }
                xmlRelaxNGShowValidError(ctxt, err->err, err->node, err->seq,
                                         err->arg1, err->arg2);
                k++;
            }
          skip:
            if (err->flags & ERROR_IS_DUP) {
                if (err->arg1 != NULL)
                    xmlFree((xmlChar *) err->arg1);
                err->arg1 = NULL;
                if (err->arg2 != NULL)
                    xmlFree((xmlChar *) err->arg2);
                err->arg2 = NULL;
                err->flags = 0;
            }
        }
        ctxt->errNr = 0;
    }
    
    /**
     * Register a validation error, either generating it if it's sure
     * or stacking it for later handling if unsure.
     *
     * @param ctxt  the validation context
     * @param err  the error number
     * @param arg1  the first argument
     * @param arg2  the second argument
     * @param dup  need to dup the args
     */
    static void
    xmlRelaxNGAddValidError(xmlRelaxNGValidCtxtPtr ctxt,
                            xmlRelaxNGValidErr err, const xmlChar * arg1,
                            const xmlChar * arg2, int dup)
    {
        if (ctxt == NULL)
            return;
        if (ctxt->flags & FLAGS_NOERROR)
            return;
    
        /*
         * generate the error directly
         */
        if (((ctxt->flags & FLAGS_IGNORABLE) == 0) ||
    	 (ctxt->flags & FLAGS_NEGATIVE)) {
            xmlNodePtr node, seq;
    
            /*
             * Flush first any stacked error which might be the
             * real cause of the problem.
             */
            if (ctxt->errNr != 0)
                xmlRelaxNGDumpValidError(ctxt);
            if (ctxt->state != NULL) {
                node = ctxt->state->node;
                seq = ctxt->state->seq;
            } else {
                node = seq = NULL;
            }
            if ((node == NULL) && (seq == NULL)) {
                node = ctxt->pnode;
            }
            xmlRelaxNGShowValidError(ctxt, err, node, seq, arg1, arg2);
        }
        /*
         * Stack the error for later processing if needed
         */
        else {
            xmlRelaxNGValidErrorPush(ctxt, err, arg1, arg2, dup);
        }
    }
    
    
    /************************************************************************
     *									*
     *			Type library hooks				*
     *									*
     ************************************************************************/
    static xmlChar *xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt,
                                        const xmlChar * str);
    
    /**
     * Check if the given type is provided by
     * the W3C XMLSchema Datatype library.
     *
     * @param data  data needed for the library
     * @param type  the type name
     * @returns 1 if yes, 0 if no and -1 in case of error.
     */
    static int
    xmlRelaxNGSchemaTypeHave(void *data ATTRIBUTE_UNUSED, const xmlChar * type)
    {
        xmlSchemaTypePtr typ;
    
        if (type == NULL)
            return (-1);
        typ = xmlSchemaGetPredefinedType(type,
                                         BAD_CAST
                                         "http://www.w3.org/2001/XMLSchema");
        if (typ == NULL)
            return (0);
        return (1);
    }
    
    /**
     * Check if the given type and value are validated by
     * the W3C XMLSchema Datatype library.
     *
     * @param data  data needed for the library
     * @param type  the type name
     * @param value  the value to check
     * @param result  pointer to result
     * @param node  the node
     * @returns 1 if yes, 0 if no and -1 in case of error.
     */
    static int
    xmlRelaxNGSchemaTypeCheck(void *data ATTRIBUTE_UNUSED,
                              const xmlChar * type,
                              const xmlChar * value,
                              void **result, xmlNodePtr node)
    {
        xmlSchemaTypePtr typ;
        int ret;
    
        if ((type == NULL) || (value == NULL))
            return (-1);
        typ = xmlSchemaGetPredefinedType(type,
                                         BAD_CAST
                                         "http://www.w3.org/2001/XMLSchema");
        if (typ == NULL)
            return (-1);
        ret = xmlSchemaValPredefTypeNode(typ, value,
                                         (xmlSchemaValPtr *) result, node);
        if (ret == 2)               /* special ID error code */
            return (2);
        if (ret == 0)
            return (1);
        if (ret > 0)
            return (0);
        return (-1);
    }
    
    /**
     * Function provided by a type library to check a value facet
     *
     * @param data  data needed for the library
     * @param type  the type name
     * @param facetname  the facet name
     * @param val  the facet value
     * @param strval  the string value
     * @param value  the value to check
     * @returns 1 if yes, 0 if no and -1 in case of error.
     */
    static int
    xmlRelaxNGSchemaFacetCheck(void *data ATTRIBUTE_UNUSED,
                               const xmlChar * type, const xmlChar * facetname,
                               const xmlChar * val, const xmlChar * strval,
                               void *value)
    {
        xmlSchemaFacetPtr facet;
        xmlSchemaTypePtr typ;
        int ret;
    
        if ((type == NULL) || (strval == NULL))
            return (-1);
        typ = xmlSchemaGetPredefinedType(type,
                                         BAD_CAST
                                         "http://www.w3.org/2001/XMLSchema");
        if (typ == NULL)
            return (-1);
    
        facet = xmlSchemaNewFacet();
        if (facet == NULL)
            return (-1);
    
        if (xmlStrEqual(facetname, BAD_CAST "minInclusive")) {
            facet->type = XML_SCHEMA_FACET_MININCLUSIVE;
        } else if (xmlStrEqual(facetname, BAD_CAST "minExclusive")) {
            facet->type = XML_SCHEMA_FACET_MINEXCLUSIVE;
        } else if (xmlStrEqual(facetname, BAD_CAST "maxInclusive")) {
            facet->type = XML_SCHEMA_FACET_MAXINCLUSIVE;
        } else if (xmlStrEqual(facetname, BAD_CAST "maxExclusive")) {
            facet->type = XML_SCHEMA_FACET_MAXEXCLUSIVE;
        } else if (xmlStrEqual(facetname, BAD_CAST "totalDigits")) {
            facet->type = XML_SCHEMA_FACET_TOTALDIGITS;
        } else if (xmlStrEqual(facetname, BAD_CAST "fractionDigits")) {
            facet->type = XML_SCHEMA_FACET_FRACTIONDIGITS;
        } else if (xmlStrEqual(facetname, BAD_CAST "pattern")) {
            facet->type = XML_SCHEMA_FACET_PATTERN;
        } else if (xmlStrEqual(facetname, BAD_CAST "enumeration")) {
            facet->type = XML_SCHEMA_FACET_ENUMERATION;
        } else if (xmlStrEqual(facetname, BAD_CAST "whiteSpace")) {
            facet->type = XML_SCHEMA_FACET_WHITESPACE;
        } else if (xmlStrEqual(facetname, BAD_CAST "length")) {
            facet->type = XML_SCHEMA_FACET_LENGTH;
        } else if (xmlStrEqual(facetname, BAD_CAST "maxLength")) {
            facet->type = XML_SCHEMA_FACET_MAXLENGTH;
        } else if (xmlStrEqual(facetname, BAD_CAST "minLength")) {
            facet->type = XML_SCHEMA_FACET_MINLENGTH;
        } else {
            xmlSchemaFreeFacet(facet);
            return (-1);
        }
        facet->value = val;
        ret = xmlSchemaCheckFacet(facet, typ, NULL, type);
        if (ret != 0) {
            xmlSchemaFreeFacet(facet);
            return (-1);
        }
        ret = xmlSchemaValidateFacet(typ, facet, strval, value);
        xmlSchemaFreeFacet(facet);
        if (ret != 0)
            return (-1);
        return (0);
    }
    
    /**
     * Function provided by a type library to free a Schemas value
     *
     * @param data  data needed for the library
     * @param value  the value to free
     * @returns 1 if yes, 0 if no and -1 in case of error.
     */
    static void
    xmlRelaxNGSchemaFreeValue(void *data ATTRIBUTE_UNUSED, void *value)
    {
        xmlSchemaFreeValue(value);
    }
    
    /**
     * Compare two values for equality accordingly a type from the W3C XMLSchema
     * Datatype library.
     *
     * @param data  data needed for the library
     * @param type  the type name
     * @param value1  the first value
     * @param ctxt1  the first context node
     * @param comp1  value to compare with
     * @param value2  the second value
     * @param ctxt2  the second context node
     * @returns 1 if equal, 0 if no and -1 in case of error.
     */
    static int
    xmlRelaxNGSchemaTypeCompare(void *data ATTRIBUTE_UNUSED,
                                const xmlChar * type,
                                const xmlChar * value1,
                                xmlNodePtr ctxt1,
                                void *comp1,
                                const xmlChar * value2, xmlNodePtr ctxt2)
    {
        int ret;
        xmlSchemaTypePtr typ;
        xmlSchemaValPtr res1 = NULL, res2 = NULL;
    
        if ((type == NULL) || (value1 == NULL) || (value2 == NULL))
            return (-1);
        typ = xmlSchemaGetPredefinedType(type,
                                         BAD_CAST
                                         "http://www.w3.org/2001/XMLSchema");
        if (typ == NULL)
            return (-1);
        if (comp1 == NULL) {
            ret = xmlSchemaValPredefTypeNode(typ, value1, &res1, ctxt1);
            if (ret != 0)
                return (-1);
            if (res1 == NULL)
                return (-1);
        } else {
            res1 = (xmlSchemaValPtr) comp1;
        }
        ret = xmlSchemaValPredefTypeNode(typ, value2, &res2, ctxt2);
        if (ret != 0) {
    	if (res1 != (xmlSchemaValPtr) comp1)
    	    xmlSchemaFreeValue(res1);
            return (-1);
        }
        ret = xmlSchemaCompareValues(res1, res2);
        if (res1 != (xmlSchemaValPtr) comp1)
            xmlSchemaFreeValue(res1);
        xmlSchemaFreeValue(res2);
        if (ret == -2)
            return (-1);
        if (ret == 0)
            return (1);
        return (0);
    }
    
    /**
     * Check if the given type is provided by
     * the default datatype library.
     *
     * @param data  data needed for the library
     * @param type  the type name
     * @returns 1 if yes, 0 if no and -1 in case of error.
     */
    static int
    xmlRelaxNGDefaultTypeHave(void *data ATTRIBUTE_UNUSED,
                              const xmlChar * type)
    {
        if (type == NULL)
            return (-1);
        if (xmlStrEqual(type, BAD_CAST "string"))
            return (1);
        if (xmlStrEqual(type, BAD_CAST "token"))
            return (1);
        return (0);
    }
    
    /**
     * Check if the given type and value are validated by
     * the default datatype library.
     *
     * @param data  data needed for the library
     * @param type  the type name
     * @param value  the value to check
     * @param result  pointer to result
     * @param node  the node
     * @returns 1 if yes, 0 if no and -1 in case of error.
     */
    static int
    xmlRelaxNGDefaultTypeCheck(void *data ATTRIBUTE_UNUSED,
                               const xmlChar * type ATTRIBUTE_UNUSED,
                               const xmlChar * value ATTRIBUTE_UNUSED,
                               void **result ATTRIBUTE_UNUSED,
                               xmlNodePtr node ATTRIBUTE_UNUSED)
    {
        if (value == NULL)
            return (-1);
        if (xmlStrEqual(type, BAD_CAST "string"))
            return (1);
        if (xmlStrEqual(type, BAD_CAST "token")) {
            return (1);
        }
    
        return (0);
    }
    
    /**
     * Compare two values accordingly a type from the default
     * datatype library.
     *
     * @param data  data needed for the library
     * @param type  the type name
     * @param value1  the first value
     * @param ctxt1  the first context node
     * @param comp1  value to compare with
     * @param value2  the second value
     * @param ctxt2  the second context node
     * @returns 1 if yes, 0 if no and -1 in case of error.
     */
    static int
    xmlRelaxNGDefaultTypeCompare(void *data ATTRIBUTE_UNUSED,
                                 const xmlChar * type,
                                 const xmlChar * value1,
                                 xmlNodePtr ctxt1 ATTRIBUTE_UNUSED,
                                 void *comp1 ATTRIBUTE_UNUSED,
                                 const xmlChar * value2,
                                 xmlNodePtr ctxt2 ATTRIBUTE_UNUSED)
    {
        int ret = -1;
    
        if (xmlStrEqual(type, BAD_CAST "string")) {
            ret = xmlStrEqual(value1, value2);
        } else if (xmlStrEqual(type, BAD_CAST "token")) {
            if (!xmlStrEqual(value1, value2)) {
                xmlChar *nval, *nvalue;
    
                /*
                 * TODO: trivial optimizations are possible by
                 * computing at compile-time
                 */
                nval = xmlRelaxNGNormalize(NULL, value1);
                nvalue = xmlRelaxNGNormalize(NULL, value2);
    
                if ((nval == NULL) || (nvalue == NULL))
                    ret = -1;
                else if (xmlStrEqual(nval, nvalue))
                    ret = 1;
                else
                    ret = 0;
                if (nval != NULL)
                    xmlFree(nval);
                if (nvalue != NULL)
                    xmlFree(nvalue);
            } else
                ret = 1;
        }
        return (ret);
    }
    
    static int xmlRelaxNGTypeInitialized = 0;
    static xmlHashTablePtr xmlRelaxNGRegisteredTypes = NULL;
    
    /**
     * Free the structure associated to the type library
     *
     * @param payload  the type library structure
     * @param namespace  the URI bound to the library
     */
    static void
    xmlRelaxNGFreeTypeLibrary(void *payload,
                              const xmlChar * namespace ATTRIBUTE_UNUSED)
    {
        xmlRelaxNGTypeLibraryPtr lib = (xmlRelaxNGTypeLibraryPtr) payload;
        if (lib == NULL)
            return;
        if (lib->namespace != NULL)
            xmlFree((xmlChar *) lib->namespace);
        xmlFree(lib);
    }
    
    /**
     * Register a new type library
     *
     * @param namespace  the URI bound to the library
     * @param data  data associated to the library
     * @param have  the provide function
     * @param check  the checking function
     * @param comp  the comparison function
     * @param facet  facet check function
     * @param freef  free function
     * @returns 0 in case of success and -1 in case of error.
     */
    static int
    xmlRelaxNGRegisterTypeLibrary(const xmlChar * namespace, void *data,
                                  xmlRelaxNGTypeHave have,
                                  xmlRelaxNGTypeCheck check,
                                  xmlRelaxNGTypeCompare comp,
                                  xmlRelaxNGFacetCheck facet,
                                  xmlRelaxNGTypeFree freef)
    {
        xmlRelaxNGTypeLibraryPtr lib;
        int ret;
    
        if ((xmlRelaxNGRegisteredTypes == NULL) || (namespace == NULL) ||
            (check == NULL) || (comp == NULL))
            return (-1);
        if (xmlHashLookup(xmlRelaxNGRegisteredTypes, namespace) != NULL)
            return (-1);
        lib =
            (xmlRelaxNGTypeLibraryPtr)
            xmlMalloc(sizeof(xmlRelaxNGTypeLibrary));
        if (lib == NULL) {
            xmlRngVErrMemory(NULL);
            return (-1);
        }
        memset(lib, 0, sizeof(xmlRelaxNGTypeLibrary));
        lib->namespace = xmlStrdup(namespace);
        lib->data = data;
        lib->have = have;
        lib->comp = comp;
        lib->check = check;
        lib->facet = facet;
        lib->freef = freef;
        ret = xmlHashAddEntry(xmlRelaxNGRegisteredTypes, namespace, lib);
        if (ret < 0) {
            xmlRelaxNGFreeTypeLibrary(lib, namespace);
            return (-1);
        }
        return (0);
    }
    
    /**
     * Initialize the default type libraries.
     *
     * @returns 0 in case of success and -1 in case of error.
     */
    int
    xmlRelaxNGInitTypes(void)
    {
        if (xmlRelaxNGTypeInitialized != 0)
            return (0);
        xmlRelaxNGRegisteredTypes = xmlHashCreate(10);
        if (xmlRelaxNGRegisteredTypes == NULL)
            return (-1);
        xmlRelaxNGRegisterTypeLibrary(BAD_CAST
                                      "http://www.w3.org/2001/XMLSchema-datatypes",
                                      NULL, xmlRelaxNGSchemaTypeHave,
                                      xmlRelaxNGSchemaTypeCheck,
                                      xmlRelaxNGSchemaTypeCompare,
                                      xmlRelaxNGSchemaFacetCheck,
                                      xmlRelaxNGSchemaFreeValue);
        xmlRelaxNGRegisterTypeLibrary(xmlRelaxNGNs, NULL,
                                      xmlRelaxNGDefaultTypeHave,
                                      xmlRelaxNGDefaultTypeCheck,
                                      xmlRelaxNGDefaultTypeCompare, NULL,
                                      NULL);
        xmlRelaxNGTypeInitialized = 1;
        return (0);
    }
    
    /**
     * Cleanup the default Schemas type library associated to RelaxNG
     *
     * @deprecated This function will be made private. Call xmlCleanupParser()
     * to free global state but see the warnings there. xmlCleanupParser()
     * should be only called once at program exit. In most cases, you don't
     * have call cleanup functions at all.
     *
     */
    void
    xmlRelaxNGCleanupTypes(void)
    {
        xmlSchemaCleanupTypes();
        if (xmlRelaxNGTypeInitialized == 0)
            return;
        xmlHashFree(xmlRelaxNGRegisteredTypes, xmlRelaxNGFreeTypeLibrary);
        xmlRelaxNGTypeInitialized = 0;
    }
    
    /************************************************************************
     *									*
     *		Compiling element content into regexp			*
     *									*
     * Sometime the element content can be compiled into a pure regexp,	*
     * This allows a faster execution and streamability at that level	*
     *									*
     ************************************************************************/
    
    static int xmlRelaxNGTryCompile(xmlRelaxNGParserCtxtPtr ctxt,
                                    xmlRelaxNGDefinePtr def);
    
    /**
     * Check if a definition is nullable.
     *
     * @param def  the definition to check
     * @returns 1 if yes, 0 if no and -1 in case of error
     */
    static int
    xmlRelaxNGIsCompilable(xmlRelaxNGDefinePtr def)
    {
        int ret = -1;
    
        if (def == NULL) {
            return (-1);
        }
        if ((def->type != XML_RELAXNG_ELEMENT) &&
            (def->dflags & IS_COMPILABLE))
            return (1);
        if ((def->type != XML_RELAXNG_ELEMENT) &&
            (def->dflags & IS_NOT_COMPILABLE))
            return (0);
        switch (def->type) {
            case XML_RELAXNG_NOOP:
                ret = xmlRelaxNGIsCompilable(def->content);
                break;
            case XML_RELAXNG_TEXT:
            case XML_RELAXNG_EMPTY:
                ret = 1;
                break;
            case XML_RELAXNG_ELEMENT:
                /*
                 * Check if the element content is compilable
                 */
                if (((def->dflags & IS_NOT_COMPILABLE) == 0) &&
                    ((def->dflags & IS_COMPILABLE) == 0)) {
                    xmlRelaxNGDefinePtr list;
    
                    list = def->content;
                    while (list != NULL) {
                        ret = xmlRelaxNGIsCompilable(list);
                        if (ret != 1)
                            break;
                        list = list->next;
                    }
    		/*
    		 * Because the routine is recursive, we must guard against
    		 * discovering both COMPILABLE and NOT_COMPILABLE
    		 */
                    if (ret == 0) {
    		    def->dflags &= ~IS_COMPILABLE;
                        def->dflags |= IS_NOT_COMPILABLE;
    		}
                    if ((ret == 1) && !(def->dflags &= IS_NOT_COMPILABLE))
                        def->dflags |= IS_COMPILABLE;
                }
                /*
                 * All elements return a compilable status unless they
                 * are generic like anyName
                 */
                if ((def->nameClass != NULL) || (def->name == NULL))
                    ret = 0;
                else
                    ret = 1;
                return (ret);
            case XML_RELAXNG_REF:
            case XML_RELAXNG_EXTERNALREF:
            case XML_RELAXNG_PARENTREF:
                if (def->depth == -20) {
                    return (1);
                } else {
                    xmlRelaxNGDefinePtr list;
    
                    def->depth = -20;
                    list = def->content;
                    while (list != NULL) {
                        ret = xmlRelaxNGIsCompilable(list);
                        if (ret != 1)
                            break;
                        list = list->next;
                    }
                }
                break;
            case XML_RELAXNG_START:
            case XML_RELAXNG_OPTIONAL:
            case XML_RELAXNG_ZEROORMORE:
            case XML_RELAXNG_ONEORMORE:
            case XML_RELAXNG_CHOICE:
            case XML_RELAXNG_GROUP:
            case XML_RELAXNG_DEF:{
                    xmlRelaxNGDefinePtr list;
    
                    list = def->content;
                    while (list != NULL) {
                        ret = xmlRelaxNGIsCompilable(list);
                        if (ret != 1)
                            break;
                        list = list->next;
                    }
                    break;
                }
            case XML_RELAXNG_EXCEPT:
            case XML_RELAXNG_ATTRIBUTE:
            case XML_RELAXNG_INTERLEAVE:
            case XML_RELAXNG_DATATYPE:
            case XML_RELAXNG_LIST:
            case XML_RELAXNG_PARAM:
            case XML_RELAXNG_VALUE:
            case XML_RELAXNG_NOT_ALLOWED:
                ret = 0;
                break;
        }
        if (ret == 0)
            def->dflags |= IS_NOT_COMPILABLE;
        if (ret == 1)
            def->dflags |= IS_COMPILABLE;
        return (ret);
    }
    
    /**
     * Compile the set of definitions, it works recursively, till the
     * element boundaries, where it tries to compile the content if possible
     *
     * @param ctxt  the RelaxNG parser context
     * @param def  the definition tree to compile
     * @returns 0 if success and -1 in case of error
     */
    static int
    xmlRelaxNGCompile(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGDefinePtr def)
    {
        int ret = 0;
        xmlRelaxNGDefinePtr list;
    
        if ((ctxt == NULL) || (def == NULL))
            return (-1);
    
        switch (def->type) {
            case XML_RELAXNG_START:
                if ((xmlRelaxNGIsCompilable(def) == 1) && (def->depth != -25)) {
                    xmlAutomataPtr oldam = ctxt->am;
                    xmlAutomataStatePtr oldstate = ctxt->state;
    
                    def->depth = -25;
    
                    list = def->content;
                    ctxt->am = xmlNewAutomata();
                    if (ctxt->am == NULL)
                        return (-1);
    
                    /*
                     * assume identical strings but not same pointer are different
                     * atoms, needed for non-determinism detection
                     * That way if 2 elements with the same name are in a choice
                     * branch the automata is found non-deterministic and
                     * we fallback to the normal validation which does the right
                     * thing of exploring both choices.
                     */
                    xmlAutomataSetFlags(ctxt->am, 1);
    
                    ctxt->state = xmlAutomataGetInitState(ctxt->am);
                    while (list != NULL) {
                        xmlRelaxNGCompile(ctxt, list);
                        list = list->next;
                    }
                    xmlAutomataSetFinalState(ctxt->am, ctxt->state);
                    if (xmlAutomataIsDeterminist(ctxt->am))
                        def->contModel = xmlAutomataCompile(ctxt->am);
    
                    xmlFreeAutomata(ctxt->am);
                    ctxt->state = oldstate;
                    ctxt->am = oldam;
                }
                break;
            case XML_RELAXNG_ELEMENT:
                if ((ctxt->am != NULL) && (def->name != NULL)) {
                    ctxt->state = xmlAutomataNewTransition2(ctxt->am,
                                                            ctxt->state, NULL,
                                                            def->name, def->ns,
                                                            def);
                }
                if ((def->dflags & IS_COMPILABLE) && (def->depth != -25)) {
                    xmlAutomataPtr oldam = ctxt->am;
                    xmlAutomataStatePtr oldstate = ctxt->state;
    
                    def->depth = -25;
    
                    list = def->content;
                    ctxt->am = xmlNewAutomata();
                    if (ctxt->am == NULL)
                        return (-1);
                    xmlAutomataSetFlags(ctxt->am, 1);
                    ctxt->state = xmlAutomataGetInitState(ctxt->am);
                    while (list != NULL) {
                        xmlRelaxNGCompile(ctxt, list);
                        list = list->next;
                    }
                    xmlAutomataSetFinalState(ctxt->am, ctxt->state);
                    def->contModel = xmlAutomataCompile(ctxt->am);
                    if (!xmlRegexpIsDeterminist(def->contModel)) {
                        /*
                         * we can only use the automata if it is determinist
                         */
                        xmlRegFreeRegexp(def->contModel);
                        def->contModel = NULL;
                    }
                    xmlFreeAutomata(ctxt->am);
                    ctxt->state = oldstate;
                    ctxt->am = oldam;
                } else {
                    xmlAutomataPtr oldam = ctxt->am;
    
                    /*
                     * we can't build the content model for this element content
                     * but it still might be possible to build it for some of its
                     * children, recurse.
                     */
                    ret = xmlRelaxNGTryCompile(ctxt, def);
                    ctxt->am = oldam;
                }
                break;
            case XML_RELAXNG_NOOP:
                ret = xmlRelaxNGCompile(ctxt, def->content);
                break;
            case XML_RELAXNG_OPTIONAL:{
                    xmlAutomataStatePtr oldstate = ctxt->state;
    
                    list = def->content;
                    while (list != NULL) {
                        xmlRelaxNGCompile(ctxt, list);
                        list = list->next;
                    }
                    xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
                    break;
                }
            case XML_RELAXNG_ZEROORMORE:{
                    xmlAutomataStatePtr oldstate;
    
                    ctxt->state =
                        xmlAutomataNewEpsilon(ctxt->am, ctxt->state, NULL);
                    oldstate = ctxt->state;
                    list = def->content;
                    while (list != NULL) {
                        xmlRelaxNGCompile(ctxt, list);
                        list = list->next;
                    }
                    xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldstate);
                    ctxt->state =
                        xmlAutomataNewEpsilon(ctxt->am, oldstate, NULL);
                    break;
                }
            case XML_RELAXNG_ONEORMORE:{
                    xmlAutomataStatePtr oldstate;
    
                    list = def->content;
                    while (list != NULL) {
                        xmlRelaxNGCompile(ctxt, list);
                        list = list->next;
                    }
                    oldstate = ctxt->state;
                    list = def->content;
                    while (list != NULL) {
                        xmlRelaxNGCompile(ctxt, list);
                        list = list->next;
                    }
                    xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldstate);
                    ctxt->state =
                        xmlAutomataNewEpsilon(ctxt->am, oldstate, NULL);
                    break;
                }
            case XML_RELAXNG_CHOICE:{
                    xmlAutomataStatePtr target = NULL;
                    xmlAutomataStatePtr oldstate = ctxt->state;
    
                    list = def->content;
                    while (list != NULL) {
                        ctxt->state = oldstate;
                        ret = xmlRelaxNGCompile(ctxt, list);
                        if (ret != 0)
                            break;
                        if (target == NULL)
                            target = ctxt->state;
                        else {
                            xmlAutomataNewEpsilon(ctxt->am, ctxt->state,
                                                  target);
                        }
                        list = list->next;
                    }
                    ctxt->state = target;
    
                    break;
                }
            case XML_RELAXNG_REF:
            case XML_RELAXNG_EXTERNALREF:
            case XML_RELAXNG_PARENTREF:
            case XML_RELAXNG_GROUP:
            case XML_RELAXNG_DEF:
                list = def->content;
                while (list != NULL) {
                    ret = xmlRelaxNGCompile(ctxt, list);
                    if (ret != 0)
                        break;
                    list = list->next;
                }
                break;
            case XML_RELAXNG_TEXT:{
                    xmlAutomataStatePtr oldstate;
    
                    ctxt->state =
                        xmlAutomataNewEpsilon(ctxt->am, ctxt->state, NULL);
                    oldstate = ctxt->state;
                    xmlRelaxNGCompile(ctxt, def->content);
                    xmlAutomataNewTransition(ctxt->am, ctxt->state,
                                             ctxt->state, BAD_CAST "#text",
                                             NULL);
                    ctxt->state =
                        xmlAutomataNewEpsilon(ctxt->am, oldstate, NULL);
                    break;
                }
            case XML_RELAXNG_EMPTY:
                ctxt->state =
                    xmlAutomataNewEpsilon(ctxt->am, ctxt->state, NULL);
                break;
            case XML_RELAXNG_EXCEPT:
            case XML_RELAXNG_ATTRIBUTE:
            case XML_RELAXNG_INTERLEAVE:
            case XML_RELAXNG_NOT_ALLOWED:
            case XML_RELAXNG_DATATYPE:
            case XML_RELAXNG_LIST:
            case XML_RELAXNG_PARAM:
            case XML_RELAXNG_VALUE:
                xmlRngPErr(ctxt, NULL, XML_ERR_INTERNAL_ERROR,
                           "RNG internal error trying to compile %s\n",
                           BAD_CAST xmlRelaxNGDefName(def), NULL);
                break;
        }
        return (ret);
    }
    
    /**
     * Try to compile the set of definitions, it works recursively,
     * possibly ignoring parts which cannot be compiled.
     *
     * @param ctxt  the RelaxNG parser context
     * @param def  the definition tree to compile
     * @returns 0 if success and -1 in case of error
     */
    static int
    xmlRelaxNGTryCompile(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGDefinePtr def)
    {
        int ret = 0;
        xmlRelaxNGDefinePtr list;
    
        if ((ctxt == NULL) || (def == NULL))
            return (-1);
    
        if ((def->type == XML_RELAXNG_START) ||
            (def->type == XML_RELAXNG_ELEMENT)) {
            ret = xmlRelaxNGIsCompilable(def);
            if ((def->dflags & IS_COMPILABLE) && (def->depth != -25)) {
                ctxt->am = NULL;
                ret = xmlRelaxNGCompile(ctxt, def);
                return (ret);
            }
        }
        switch (def->type) {
            case XML_RELAXNG_NOOP:
                ret = xmlRelaxNGTryCompile(ctxt, def->content);
                break;
            case XML_RELAXNG_TEXT:
            case XML_RELAXNG_DATATYPE:
            case XML_RELAXNG_LIST:
            case XML_RELAXNG_PARAM:
            case XML_RELAXNG_VALUE:
            case XML_RELAXNG_EMPTY:
            case XML_RELAXNG_ELEMENT:
                ret = 0;
                break;
            case XML_RELAXNG_OPTIONAL:
            case XML_RELAXNG_ZEROORMORE:
            case XML_RELAXNG_ONEORMORE:
            case XML_RELAXNG_CHOICE:
            case XML_RELAXNG_GROUP:
            case XML_RELAXNG_DEF:
            case XML_RELAXNG_START:
            case XML_RELAXNG_REF:
            case XML_RELAXNG_EXTERNALREF:
            case XML_RELAXNG_PARENTREF:
                list = def->content;
                while (list != NULL) {
                    ret = xmlRelaxNGTryCompile(ctxt, list);
                    if (ret != 0)
                        break;
                    list = list->next;
                }
                break;
            case XML_RELAXNG_EXCEPT:
            case XML_RELAXNG_ATTRIBUTE:
            case XML_RELAXNG_INTERLEAVE:
            case XML_RELAXNG_NOT_ALLOWED:
                ret = 0;
                break;
        }
        return (ret);
    }
    
    /************************************************************************
     *									*
     *			Parsing functions				*
     *									*
     ************************************************************************/
    
    static xmlRelaxNGDefinePtr xmlRelaxNGParseAttribute(xmlRelaxNGParserCtxtPtr
                                                        ctxt, xmlNodePtr node);
    static xmlRelaxNGDefinePtr xmlRelaxNGParseElement(xmlRelaxNGParserCtxtPtr
                                                      ctxt, xmlNodePtr node);
    static xmlRelaxNGDefinePtr xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr
                                                       ctxt, xmlNodePtr nodes,
                                                       int group);
    static xmlRelaxNGDefinePtr xmlRelaxNGParsePattern(xmlRelaxNGParserCtxtPtr
                                                      ctxt, xmlNodePtr node);
    static xmlRelaxNGPtr xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt,
                                                 xmlNodePtr node);
    static int xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt,
                                             xmlNodePtr nodes);
    static xmlRelaxNGDefinePtr xmlRelaxNGParseNameClass(xmlRelaxNGParserCtxtPtr
                                                        ctxt, xmlNodePtr node,
                                                        xmlRelaxNGDefinePtr
                                                        def);
    static xmlRelaxNGGrammarPtr xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr
                                                       ctxt, xmlNodePtr nodes);
    static int xmlRelaxNGElementMatch(xmlRelaxNGValidCtxtPtr ctxt,
                                      xmlRelaxNGDefinePtr define,
                                      xmlNodePtr elem);
    
    
    #define IS_BLANK_NODE(n) (xmlRelaxNGIsBlank((n)->content))
    
    /**
     * Check if a definition is nullable.
     *
     * @param define  the definition to verify
     * @returns 1 if yes, 0 if no and -1 in case of error
     */
    static int
    xmlRelaxNGIsNullable(xmlRelaxNGDefinePtr define)
    {
        int ret;
    
        if (define == NULL)
            return (-1);
    
        if (define->dflags & IS_NULLABLE)
            return (1);
        if (define->dflags & IS_NOT_NULLABLE)
            return (0);
        switch (define->type) {
            case XML_RELAXNG_EMPTY:
            case XML_RELAXNG_TEXT:
                ret = 1;
                break;
            case XML_RELAXNG_NOOP:
            case XML_RELAXNG_DEF:
            case XML_RELAXNG_REF:
            case XML_RELAXNG_EXTERNALREF:
            case XML_RELAXNG_PARENTREF:
            case XML_RELAXNG_ONEORMORE:
                ret = xmlRelaxNGIsNullable(define->content);
                break;
            case XML_RELAXNG_EXCEPT:
            case XML_RELAXNG_NOT_ALLOWED:
            case XML_RELAXNG_ELEMENT:
            case XML_RELAXNG_DATATYPE:
            case XML_RELAXNG_PARAM:
            case XML_RELAXNG_VALUE:
            case XML_RELAXNG_LIST:
            case XML_RELAXNG_ATTRIBUTE:
                ret = 0;
                break;
            case XML_RELAXNG_CHOICE:{
                    xmlRelaxNGDefinePtr list = define->content;
    
                    while (list != NULL) {
                        ret = xmlRelaxNGIsNullable(list);
                        if (ret != 0)
                            goto done;
                        list = list->next;
                    }
                    ret = 0;
                    break;
                }
            case XML_RELAXNG_START:
            case XML_RELAXNG_INTERLEAVE:
            case XML_RELAXNG_GROUP:{
                    xmlRelaxNGDefinePtr list = define->content;
    
                    while (list != NULL) {
                        ret = xmlRelaxNGIsNullable(list);
                        if (ret != 1)
                            goto done;
                        list = list->next;
                    }
                    return (1);
                }
            default:
                return (-1);
        }
      done:
        if (ret == 0)
            define->dflags |= IS_NOT_NULLABLE;
        if (ret == 1)
            define->dflags |= IS_NULLABLE;
        return (ret);
    }
    
    /**
     * Check if a string is ignorable c.f. 4.2. Whitespace
     *
     * @param str  a string
     * @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_CH(*str)))
                return (0);
            str++;
        }
        return (1);
    }
    
    /**
     * Applies algorithm from 4.3. datatypeLibrary attribute
     *
     * @param ctxt  a Relax-NG parser context
     * @param node  the current data or value element
     * @returns the datatypeLibrary value or NULL if not found
     */
    static xmlChar *
    xmlRelaxNGGetDataTypeLibrary(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
                                 xmlNodePtr node)
    {
        xmlChar *ret, *escape;
    
        if (node == NULL)
            return(NULL);
    
        if ((IS_RELAXNG(node, "data")) || (IS_RELAXNG(node, "value"))) {
            ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
            if (ret != NULL) {
                if (ret[0] == 0) {
                    xmlFree(ret);
                    return (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)) {
            ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
            if (ret != NULL) {
                if (ret[0] == 0) {
                    xmlFree(ret);
                    return (NULL);
                }
                escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
                if (escape == NULL) {
                    return (ret);
                }
                xmlFree(ret);
                return (escape);
            }
            node = node->parent;
        }
        return (NULL);
    }
    
    /**
     * parse the content of a RelaxNG value node.
     *
     * @param ctxt  a Relax-NG parser context
     * @param node  the data node.
     * @returns the definition pointer or NULL in case of error
     */
    static xmlRelaxNGDefinePtr
    xmlRelaxNGParseValue(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
    {
        xmlRelaxNGDefinePtr def = NULL;
        xmlRelaxNGTypeLibraryPtr lib = NULL;
        xmlChar *type;
        xmlChar *library;
        int success = 0;
    
        def = xmlRelaxNGNewDefine(ctxt, node);
        if (def == NULL)
            return (NULL);
        def->type = XML_RELAXNG_VALUE;
    
        type = xmlGetProp(node, BAD_CAST "type");
        if (type != NULL) {
            xmlRelaxNGNormExtSpace(type);
            if (xmlValidateNCName(type, 0)) {
                xmlRngPErr(ctxt, node, XML_RNGP_TYPE_VALUE,
                           "value type '%s' is not an NCName\n", type, NULL);
            }
            library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
            if (library == NULL)
                library =
                    xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
    
            def->name = type;
            def->ns = library;
    
            lib = (xmlRelaxNGTypeLibraryPtr)
                xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
            if (lib == NULL) {
                xmlRngPErr(ctxt, node, XML_RNGP_UNKNOWN_TYPE_LIB,
                           "Use of unregistered type library '%s'\n", library,
                           NULL);
                def->data = NULL;
            } else {
                def->data = lib;
                if (lib->have == NULL) {
                    xmlRngPErr(ctxt, node, XML_RNGP_ERROR_TYPE_LIB,
                               "Internal error with type library '%s': no 'have'\n",
                               library, NULL);
                } else {
                    success = lib->have(lib->data, def->name);
                    if (success != 1) {
                        xmlRngPErr(ctxt, node, XML_RNGP_TYPE_NOT_FOUND,
                                   "Error type '%s' is not exported by type library '%s'\n",
                                   def->name, library);
                    }
                }
            }
        }
        if (node->children == NULL) {
            def->value = xmlStrdup(BAD_CAST "");
        } else if (((node->children->type != XML_TEXT_NODE) &&
                    (node->children->type != XML_CDATA_SECTION_NODE)) ||
                   (node->children->next != NULL)) {
            xmlRngPErr(ctxt, node, XML_RNGP_TEXT_EXPECTED,
                       "Expecting a single text value for <value>content\n",
                       NULL, NULL);
        } else if (def != NULL) {
            def->value = xmlNodeGetContent(node);
            if (def->value == NULL) {
                xmlRngPErr(ctxt, node, XML_RNGP_VALUE_NO_CONTENT,
                           "Element <value> has no content\n", NULL, NULL);
            } else if ((lib != NULL) && (lib->check != NULL) && (success == 1)) {
                void *val = NULL;
    
                success =
                    lib->check(lib->data, def->name, def->value, &val, node);
                if (success != 1) {
                    xmlRngPErr(ctxt, node, XML_RNGP_INVALID_VALUE,
                               "Value '%s' is not acceptable for type '%s'\n",
                               def->value, def->name);
                } else {
                    if (val != NULL)
                        def->attrs = val;
                }
            }
        }
        return (def);
    }
    
    /**
     * parse the content of a RelaxNG data node.
     *
     * @param ctxt  a Relax-NG parser context
     * @param node  the data node.
     * @returns the definition pointer or NULL in case of error
     */
    static xmlRelaxNGDefinePtr
    xmlRelaxNGParseData(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
    {
        xmlRelaxNGDefinePtr def = NULL, except;
        xmlRelaxNGDefinePtr param, lastparam = NULL;
        xmlRelaxNGTypeLibraryPtr lib;
        xmlChar *type;
        xmlChar *library;
        xmlNodePtr content;
        int tmp;
    
        type = xmlGetProp(node, BAD_CAST "type");
        if (type == NULL) {
            xmlRngPErr(ctxt, node, XML_RNGP_TYPE_MISSING, "data has no type\n", NULL,
                       NULL);
            return (NULL);
        }
        xmlRelaxNGNormExtSpace(type);
        if (xmlValidateNCName(type, 0)) {
            xmlRngPErr(ctxt, node, XML_RNGP_TYPE_VALUE,
                       "data type '%s' is not an NCName\n", type, NULL);
        }
        library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
        if (library == NULL)
            library =
                xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
    
        def = xmlRelaxNGNewDefine(ctxt, node);
        if (def == NULL) {
            xmlFree(library);
            xmlFree(type);
            return (NULL);
        }
        def->type = XML_RELAXNG_DATATYPE;
        def->name = type;
        def->ns = library;
    
        lib = (xmlRelaxNGTypeLibraryPtr)
            xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
        if (lib == NULL) {
            xmlRngPErr(ctxt, node, XML_RNGP_UNKNOWN_TYPE_LIB,
                       "Use of unregistered type library '%s'\n", library,
                       NULL);
            def->data = NULL;
        } else {
            def->data = lib;
            if (lib->have == NULL) {
                xmlRngPErr(ctxt, node, XML_RNGP_ERROR_TYPE_LIB,
                           "Internal error with type library '%s': no 'have'\n",
                           library, NULL);
            } else {
                tmp = lib->have(lib->data, def->name);
                if (tmp != 1) {
                    xmlRngPErr(ctxt, node, XML_RNGP_TYPE_NOT_FOUND,
                               "Error type '%s' is not exported by type library '%s'\n",
                               def->name, library);
                } else
                    if ((xmlStrEqual
                         (library,
                          BAD_CAST
                          "http://www.w3.org/2001/XMLSchema-datatypes"))
                        && ((xmlStrEqual(def->name, BAD_CAST "IDREF"))
                            || (xmlStrEqual(def->name, BAD_CAST "IDREFS")))) {
                    ctxt->idref = 1;
                }
            }
        }
        content = node->children;
    
        /*
         * Handle optional params
         */
        while (content != NULL) {
            if (!xmlStrEqual(content->name, BAD_CAST "param"))
                break;
            if (xmlStrEqual(library,
                            BAD_CAST "http://relaxng.org/ns/structure/1.0")) {
                xmlRngPErr(ctxt, node, XML_RNGP_PARAM_FORBIDDEN,
                           "Type library '%s' does not allow type parameters\n",
                           library, NULL);
                content = content->next;
                while ((content != NULL) &&
                       (xmlStrEqual(content->name, BAD_CAST "param")))
                    content = content->next;
            } else {
                param = xmlRelaxNGNewDefine(ctxt, node);
                if (param != NULL) {
                    param->type = XML_RELAXNG_PARAM;
                    param->name = xmlGetProp(content, BAD_CAST "name");
                    if (param->name == NULL) {
                        xmlRngPErr(ctxt, node, XML_RNGP_PARAM_NAME_MISSING,
                                   "param has no name\n", NULL, NULL);
                    }
                    param->value = xmlNodeGetContent(content);
                    if (lastparam == NULL) {
                        def->attrs = lastparam = param;
                    } else {
                        lastparam->next = param;
                        lastparam = param;
                    }
                    if (lib != NULL) {
                    }
                }
                content = content->next;
            }
        }
        /*
         * Handle optional except
         */
        if ((content != NULL)
            && (xmlStrEqual(content->name, BAD_CAST "except"))) {
            xmlNodePtr child;
            xmlRelaxNGDefinePtr tmp2, last = NULL;
    
            except = xmlRelaxNGNewDefine(ctxt, node);
            if (except == NULL) {
                return (def);
            }
            except->type = XML_RELAXNG_EXCEPT;
            child = content->children;
    	def->content = except;
            if (child == NULL) {
                xmlRngPErr(ctxt, content, XML_RNGP_EXCEPT_NO_CONTENT,
                           "except has no content\n", NULL, NULL);
            }
            while (child != NULL) {
                tmp2 = xmlRelaxNGParsePattern(ctxt, child);
                if (tmp2 != NULL) {
                    if (last == NULL) {
                        except->content = last = tmp2;
                    } else {
                        last->next = tmp2;
                        last = tmp2;
                    }
                }
                child = child->next;
            }
            content = content->next;
        }
        /*
         * Check there is no unhandled data
         */
        if (content != NULL) {
            xmlRngPErr(ctxt, content, XML_RNGP_DATA_CONTENT,
                       "Element data has unexpected content %s\n",
                       content->name, NULL);
        }
    
        return (def);
    }
    
    static const xmlChar *invalidName = BAD_CAST "\1";
    
    /**
     * Compare the 2 lists of element definitions. The comparison is
     * that if both lists do not accept the same QNames, it returns 1
     * If the 2 lists can accept the same QName the comparison returns 0
     *
     * @param def1  the first element/attribute defs
     * @param def2  the second element/attribute defs
     * @returns 1 distinct, 0 if equal
     */
    static int
    xmlRelaxNGCompareNameClasses(xmlRelaxNGDefinePtr def1,
                                 xmlRelaxNGDefinePtr def2)
    {
        int ret = 1;
        xmlNode node;
        xmlNs ns;
        xmlRelaxNGValidCtxt ctxt;
    
        memset(&ctxt, 0, sizeof(xmlRelaxNGValidCtxt));
    
        ctxt.flags = FLAGS_IGNORABLE | FLAGS_NOERROR;
    
        if ((def1->type == XML_RELAXNG_ELEMENT) ||
            (def1->type == XML_RELAXNG_ATTRIBUTE)) {
            if (def2->type == XML_RELAXNG_TEXT)
                return (1);
            if (def1->name != NULL) {
                node.name = def1->name;
            } else {
                node.name = invalidName;
            }
            if (def1->ns != NULL) {
                if (def1->ns[0] == 0) {
                    node.ns = NULL;
                } else {
    	        node.ns = &ns;
                    ns.href = def1->ns;
                }
            } else {
                node.ns = NULL;
            }
            if (xmlRelaxNGElementMatch(&ctxt, def2, &node)) {
                if (def1->nameClass != NULL) {
                    ret = xmlRelaxNGCompareNameClasses(def1->nameClass, def2);
                } else {
                    ret = 0;
                }
            } else {
                ret = 1;
            }
        } else if (def1->type == XML_RELAXNG_TEXT) {
            if (def2->type == XML_RELAXNG_TEXT)
                return (0);
            return (1);
        } else if (def1->type == XML_RELAXNG_EXCEPT) {
            ret = xmlRelaxNGCompareNameClasses(def1->content, def2);
    	if (ret == 0)
    	    ret = 1;
    	else if (ret == 1)
    	    ret = 0;
        } else {
            /* TODO */
            ret = 0;
        }
        if (ret == 0)
            return (ret);
        if ((def2->type == XML_RELAXNG_ELEMENT) ||
            (def2->type == XML_RELAXNG_ATTRIBUTE)) {
            if (def2->name != NULL) {
                node.name = def2->name;
            } else {
                node.name = invalidName;
            }
            node.ns = &ns;
            if (def2->ns != NULL) {
                if (def2->ns[0] == 0) {
                    node.ns = NULL;
                } else {
                    ns.href = def2->ns;
                }
            } else {
                ns.href = invalidName;
            }
            if (xmlRelaxNGElementMatch(&ctxt, def1, &node)) {
                if (def2->nameClass != NULL) {
                    ret = xmlRelaxNGCompareNameClasses(def2->nameClass, def1);
                } else {
                    ret = 0;
                }
            } else {
                ret = 1;
            }
        } else {
            /* TODO */
            ret = 0;
        }
    
        return (ret);
    }
    
    /**
     * Compare the 2 lists of element or attribute definitions. The comparison
     * is that if both lists do not accept the same QNames, it returns 1
     * If the 2 lists can accept the same QName the comparison returns 0
     *
     * @param ctxt  a Relax-NG parser context
     * @param def1  the first list of element/attribute defs
     * @param def2  the second list of element/attribute defs
     * @returns 1 distinct, 0 if equal
     */
    static int
    xmlRelaxNGCompareElemDefLists(xmlRelaxNGParserCtxtPtr ctxt
                                  ATTRIBUTE_UNUSED, xmlRelaxNGDefinePtr * def1,
                                  xmlRelaxNGDefinePtr * def2)
    {
        xmlRelaxNGDefinePtr *basedef2 = def2;
    
        if ((def1 == NULL) || (def2 == NULL))
            return (1);
        if ((*def1 == NULL) || (*def2 == NULL))
            return (1);
        while (*def1 != NULL) {
            while ((*def2) != NULL) {
                if (xmlRelaxNGCompareNameClasses(*def1, *def2) == 0)
                    return (0);
                def2++;
            }
            def2 = basedef2;
            def1++;
        }
        return (1);
    }
    
    /**
     * Check if the definition can only generate attributes
     *
     * @param ctxt  a Relax-NG parser context
     * @param def  the definition definition
     * @returns 1 if yes, 0 if no and -1 in case of error.
     */
    static int
    xmlRelaxNGGenerateAttributes(xmlRelaxNGParserCtxtPtr ctxt,
                                 xmlRelaxNGDefinePtr def)
    {
        xmlRelaxNGDefinePtr parent, cur, tmp;
    
        /*
         * Don't run that check in case of error. Infinite recursion
         * becomes possible.
         */
        if (ctxt->nbErrors != 0)
            return (-1);
    
        parent = NULL;
        cur = def;
        while (cur != NULL) {
            if ((cur->type == XML_RELAXNG_ELEMENT) ||
                (cur->type == XML_RELAXNG_TEXT) ||
                (cur->type == XML_RELAXNG_DATATYPE) ||
                (cur->type == XML_RELAXNG_PARAM) ||
                (cur->type == XML_RELAXNG_LIST) ||
                (cur->type == XML_RELAXNG_VALUE) ||
                (cur->type == XML_RELAXNG_EMPTY))
                return (0);
            if ((cur->type == XML_RELAXNG_CHOICE) ||
                (cur->type == XML_RELAXNG_INTERLEAVE) ||
                (cur->type == XML_RELAXNG_GROUP) ||
                (cur->type == XML_RELAXNG_ONEORMORE) ||
                (cur->type == XML_RELAXNG_ZEROORMORE) ||
                (cur->type == XML_RELAXNG_OPTIONAL) ||
                (cur->type == XML_RELAXNG_PARENTREF) ||
                (cur->type == XML_RELAXNG_EXTERNALREF) ||
                (cur->type == XML_RELAXNG_REF) ||
                (cur->type == XML_RELAXNG_DEF)) {
                if (cur->content != NULL) {
                    parent = cur;
                    cur = cur->content;
                    tmp = cur;
                    while (tmp != NULL) {
                        tmp->parent = parent;
                        tmp = tmp->next;
                    }
                    continue;
                }
            }
            if (cur == def)
                break;
            if (cur->next != NULL) {
                cur = cur->next;
                continue;
            }
            do {
                cur = cur->parent;
                if (cur == NULL)
                    break;
                if (cur == def)
                    return (1);
                if (cur->next != NULL) {
                    cur = cur->next;
                    break;
                }
            } while (cur != NULL);
        }
        return (1);
    }
    
    /**
     * Compute the list of top elements a definition can generate
     *
     * @param ctxt  a Relax-NG parser context
     * @param def  the definition definition
     * @param eora  gather elements (0), attributes (1) or elements and text (2)
     * @returns a list of elements or NULL if none was found.
     */
    static xmlRelaxNGDefinePtr *
    xmlRelaxNGGetElements(xmlRelaxNGParserCtxtPtr ctxt,
                          xmlRelaxNGDefinePtr def, int eora)
    {
        xmlRelaxNGDefinePtr *ret = NULL, parent, cur, tmp;
        int len = 0;
        int max = 0;
    
        /*
         * Don't run that check in case of error. Infinite recursion
         * becomes possible.
         */
        if (ctxt->nbErrors != 0)
            return (NULL);
    
        parent = NULL;
        cur = def;
        while (cur != NULL) {
            if (((eora == 0) && ((cur->type == XML_RELAXNG_ELEMENT) ||
                                 (cur->type == XML_RELAXNG_TEXT))) ||
                ((eora == 1) && (cur->type == XML_RELAXNG_ATTRIBUTE)) ||
                ((eora == 2) && ((cur->type == XML_RELAXNG_DATATYPE) ||
    	                     (cur->type == XML_RELAXNG_ELEMENT) ||
    			     (cur->type == XML_RELAXNG_LIST) ||
                                 (cur->type == XML_RELAXNG_TEXT) ||
    			     (cur->type == XML_RELAXNG_VALUE)))) {
                if (ret == NULL) {
                    max = 10;
                    ret = (xmlRelaxNGDefinePtr *)
                        xmlMalloc((max + 1) * sizeof(xmlRelaxNGDefinePtr));
                    if (ret == NULL) {
                        xmlRngPErrMemory(ctxt);
                        return (NULL);
                    }
                } else if (max <= len) {
    	        xmlRelaxNGDefinePtr *temp;
    
                    max *= 2;
                    temp = xmlRealloc(ret,
                                   (max + 1) * sizeof(xmlRelaxNGDefinePtr));
                    if (temp == NULL) {
                        xmlRngPErrMemory(ctxt);
    		    xmlFree(ret);
                        return (NULL);
                    }
    		ret = temp;
                }
                ret[len++] = cur;
                ret[len] = NULL;
            } else if ((cur->type == XML_RELAXNG_CHOICE) ||
                       (cur->type == XML_RELAXNG_INTERLEAVE) ||
                       (cur->type == XML_RELAXNG_GROUP) ||
                       (cur->type == XML_RELAXNG_ONEORMORE) ||
                       (cur->type == XML_RELAXNG_ZEROORMORE) ||
                       (cur->type == XML_RELAXNG_OPTIONAL) ||
                       (cur->type == XML_RELAXNG_PARENTREF) ||
                       (cur->type == XML_RELAXNG_REF) ||
                       (cur->type == XML_RELAXNG_DEF) ||
    		   (cur->type == XML_RELAXNG_EXTERNALREF)) {
                /*
                 * Don't go within elements or attributes or string values.
                 * Just gather the element top list
                 */
                if (cur->content != NULL) {
                    parent = cur;
                    cur = cur->content;
                    tmp = cur;
                    while (tmp != NULL) {
                        tmp->parent = parent;
                        tmp = tmp->next;
                    }
                    continue;
                }
            }
            if (cur == def)
                break;
            if (cur->next != NULL) {
                cur = cur->next;
                continue;
            }
            do {
                cur = cur->parent;
                if (cur == NULL)
                    break;
                if (cur == def)
                    return (ret);
                if (cur->next != NULL) {
                    cur = cur->next;
                    break;
                }
            } while (cur != NULL);
        }
        return (ret);
    }
    
    /**
     * Also used to find indeterministic pattern in choice
     *
     * @param ctxt  a Relax-NG parser context
     * @param def  the choice definition
     */
    static void
    xmlRelaxNGCheckChoiceDeterminism(xmlRelaxNGParserCtxtPtr ctxt,
                                     xmlRelaxNGDefinePtr def)
    {
        xmlRelaxNGDefinePtr **list;
        xmlRelaxNGDefinePtr cur;
        int nbchild = 0, i, j, ret;
        int is_nullable = 0;
        int is_indeterminist = 0;
        xmlHashTablePtr triage = NULL;
        int is_triable = 1;
    
        if ((def == NULL) || (def->type != XML_RELAXNG_CHOICE))
            return;
    
        if (def->dflags & IS_PROCESSED)
            return;
    
        /*
         * Don't run that check in case of error. Infinite recursion
         * becomes possible.
         */
        if (ctxt->nbErrors != 0)
            return;
    
        is_nullable = xmlRelaxNGIsNullable(def);
    
        cur = def->content;
        while (cur != NULL) {
            nbchild++;
            cur = cur->next;
        }
    
        list = (xmlRelaxNGDefinePtr **) xmlMalloc(nbchild *
                                                  sizeof(xmlRelaxNGDefinePtr
                                                         *));
        if (list == NULL) {
            xmlRngPErrMemory(ctxt);
            return;
        }
        i = 0;
        /*
         * a bit strong but safe
         */
        if (is_nullable == 0) {
            triage = xmlHashCreate(10);
        } else {
            is_triable = 0;
        }
        cur = def->content;
        while (cur != NULL) {
            list[i] = xmlRelaxNGGetElements(ctxt, cur, 0);
            if ((list[i] == NULL) || (list[i][0] == NULL)) {
                is_triable = 0;
            } else if (is_triable == 1) {
                xmlRelaxNGDefinePtr *tmp;
                int res;
    
                tmp = list[i];
                while ((*tmp != NULL) && (is_triable == 1)) {
                    if ((*tmp)->type == XML_RELAXNG_TEXT) {
                        res = xmlHashAddEntry2(triage,
                                               BAD_CAST "#text", NULL,
                                               (void *) cur);
                        if (res != 0)
                            is_triable = -1;
                    } else if (((*tmp)->type == XML_RELAXNG_ELEMENT) &&
                               ((*tmp)->name != NULL)) {
                        if (((*tmp)->ns == NULL) || ((*tmp)->ns[0] == 0))
                            res = xmlHashAddEntry2(triage,
                                                   (*tmp)->name, NULL,
                                                   (void *) cur);
                        else
                            res = xmlHashAddEntry2(triage,
                                                   (*tmp)->name, (*tmp)->ns,
                                                   (void *) cur);
                        if (res != 0)
                            is_triable = -1;
                    } else if ((*tmp)->type == XML_RELAXNG_ELEMENT) {
                        if (((*tmp)->ns == NULL) || ((*tmp)->ns[0] == 0))
                            res = xmlHashAddEntry2(triage,
                                                   BAD_CAST "#any", NULL,
                                                   (void *) cur);
                        else
                            res = xmlHashAddEntry2(triage,
                                                   BAD_CAST "#any", (*tmp)->ns,
                                                   (void *) cur);
                        if (res != 0)
                            is_triable = -1;
                    } else {
                        is_triable = -1;
                    }
                    tmp++;
                }
            }
            i++;
            cur = cur->next;
        }
    
        for (i = 0; i < nbchild; i++) {
            if (list[i] == NULL)
                continue;
            for (j = 0; j < i; j++) {
                if (list[j] == NULL)
                    continue;
                ret = xmlRelaxNGCompareElemDefLists(ctxt, list[i], list[j]);
                if (ret == 0) {
                    is_indeterminist = 1;
                }
            }
        }
        for (i = 0; i < nbchild; i++) {
            if (list[i] != NULL)
                xmlFree(list[i]);
        }
    
        xmlFree(list);
        if (is_indeterminist) {
            def->dflags |= IS_INDETERMINIST;
        }
        if (is_triable == 1) {
            def->dflags |= IS_TRIABLE;
            def->data = triage;
        } else if (triage != NULL) {
            xmlHashFree(triage, NULL);
        }
        def->dflags |= IS_PROCESSED;
    }
    
    /**
     * Detects violations of rule 7.3
     *
     * @param ctxt  a Relax-NG parser context
     * @param def  the group definition
     */
    static void
    xmlRelaxNGCheckGroupAttrs(xmlRelaxNGParserCtxtPtr ctxt,
                              xmlRelaxNGDefinePtr def)
    {
        xmlRelaxNGDefinePtr **list;
        xmlRelaxNGDefinePtr cur;
        int nbchild = 0, i, j, ret;
    
        if ((def == NULL) ||
            ((def->type != XML_RELAXNG_GROUP) &&
             (def->type != XML_RELAXNG_ELEMENT)))
            return;
    
        if (def->dflags & IS_PROCESSED)
            return;
    
        /*
         * Don't run that check in case of error. Infinite recursion
         * becomes possible.
         */
        if (ctxt->nbErrors != 0)
            return;
    
        cur = def->attrs;
        while (cur != NULL) {
            nbchild++;
            cur = cur->next;
        }
        cur = def->content;
        while (cur != NULL) {
            nbchild++;
            cur = cur->next;
        }
    
        list = (xmlRelaxNGDefinePtr **) xmlMalloc(nbchild *
                                                  sizeof(xmlRelaxNGDefinePtr
                                                         *));
        if (list == NULL) {
            xmlRngPErrMemory(ctxt);
            return;
        }
        i = 0;
        cur = def->attrs;
        while (cur != NULL) {
            list[i] = xmlRelaxNGGetElements(ctxt, cur, 1);
            i++;
            cur = cur->next;
        }
        cur = def->content;
        while (cur != NULL) {
            list[i] = xmlRelaxNGGetElements(ctxt, cur, 1);
            i++;
            cur = cur->next;
        }
    
        for (i = 0; i < nbchild; i++) {
            if (list[i] == NULL)
                continue;
            for (j = 0; j < i; j++) {
                if (list[j] == NULL)
                    continue;
                ret = xmlRelaxNGCompareElemDefLists(ctxt, list[i], list[j]);
                if (ret == 0) {
                    xmlRngPErr(ctxt, def->node, XML_RNGP_GROUP_ATTR_CONFLICT,
                               "Attributes conflicts in group\n", NULL, NULL);
                }
            }
        }
        for (i = 0; i < nbchild; i++) {
            if (list[i] != NULL)
                xmlFree(list[i]);
        }
    
        xmlFree(list);
        def->dflags |= IS_PROCESSED;
    }
    
    /**
     * A lot of work for preprocessing interleave definitions
     * is potentially needed to get a decent execution speed at runtime
     *   - trying to get a total order on the element nodes generated
     *     by the interleaves, order the list of interleave definitions
     *     following that order.
     *   - if `<text/>` is used to handle mixed content, it is better to
     *     flag this in the define and simplify the runtime checking
     *     algorithm
     *
     * @param payload  the interleave definition
     * @param data  a Relax-NG parser context
     * @param name  the definition name
     */
    static void
    xmlRelaxNGComputeInterleaves(void *payload, void *data,
                                 const xmlChar * name ATTRIBUTE_UNUSED)
    {
        xmlRelaxNGDefinePtr def = (xmlRelaxNGDefinePtr) payload;
        xmlRelaxNGParserCtxtPtr ctxt = (xmlRelaxNGParserCtxtPtr) data;
        xmlRelaxNGDefinePtr cur, *tmp;
    
        xmlRelaxNGPartitionPtr partitions = NULL;
        xmlRelaxNGInterleaveGroupPtr *groups = NULL;
        xmlRelaxNGInterleaveGroupPtr group;
        int i, j, ret, res;
        int nbgroups = 0;
        int nbchild = 0;
        int is_mixed = 0;
        int is_determinist = 1;
    
        /*
         * Don't run that check in case of error. Infinite recursion
         * becomes possible.
         */
        if (ctxt->nbErrors != 0)
            return;
    
        cur = def->content;
        while (cur != NULL) {
            nbchild++;
            cur = cur->next;
        }
    
        groups = (xmlRelaxNGInterleaveGroupPtr *)
            xmlMalloc(nbchild * sizeof(xmlRelaxNGInterleaveGroupPtr));
        if (groups == NULL)
            goto error;
        cur = def->content;
        while (cur != NULL) {
            groups[nbgroups] = (xmlRelaxNGInterleaveGroupPtr)
                xmlMalloc(sizeof(xmlRelaxNGInterleaveGroup));
            if (groups[nbgroups] == NULL)
                goto error;
            if (cur->type == XML_RELAXNG_TEXT)
                is_mixed++;
            groups[nbgroups]->rule = cur;
            groups[nbgroups]->defs = xmlRelaxNGGetElements(ctxt, cur, 2);
            groups[nbgroups]->attrs = xmlRelaxNGGetElements(ctxt, cur, 1);
            nbgroups++;
            cur = cur->next;
        }
    
        /*
         * Let's check that all rules makes a partitions according to 7.4
         */
        partitions = (xmlRelaxNGPartitionPtr)
            xmlMalloc(sizeof(xmlRelaxNGPartition));
        if (partitions == NULL)
            goto error;
        memset(partitions, 0, sizeof(xmlRelaxNGPartition));
        partitions->nbgroups = nbgroups;
        partitions->triage = xmlHashCreate(nbgroups);
        for (i = 0; i < nbgroups; i++) {
            group = groups[i];
            for (j = i + 1; j < nbgroups; j++) {
                if (groups[j] == NULL)
                    continue;
    
                ret = xmlRelaxNGCompareElemDefLists(ctxt, group->defs,
                                                    groups[j]->defs);
                if (ret == 0) {
                    xmlRngPErr(ctxt, def->node, XML_RNGP_ELEM_TEXT_CONFLICT,
                               "Element or text conflicts in interleave\n",
                               NULL, NULL);
                }
                ret = xmlRelaxNGCompareElemDefLists(ctxt, group->attrs,
                                                    groups[j]->attrs);
                if (ret == 0) {
                    xmlRngPErr(ctxt, def->node, XML_RNGP_ATTR_CONFLICT,
                               "Attributes conflicts in interleave\n", NULL,
                               NULL);
                }
            }
            tmp = group->defs;
            if ((tmp != NULL) && (*tmp != NULL)) {
                while (*tmp != NULL) {
                    if ((*tmp)->type == XML_RELAXNG_TEXT) {
                        res = xmlHashAddEntry2(partitions->triage,
                                               BAD_CAST "#text", NULL,
                                               XML_INT_TO_PTR(i + 1));
                        if (res != 0)
                            is_determinist = -1;
                    } else if (((*tmp)->type == XML_RELAXNG_ELEMENT) &&
                               ((*tmp)->name != NULL)) {
                        if (((*tmp)->ns == NULL) || ((*tmp)->ns[0] == 0))
                            res = xmlHashAddEntry2(partitions->triage,
                                                   (*tmp)->name, NULL,
                                                   XML_INT_TO_PTR(i + 1));
                        else
                            res = xmlHashAddEntry2(partitions->triage,
                                                   (*tmp)->name, (*tmp)->ns,
                                                   XML_INT_TO_PTR(i + 1));
                        if (res != 0)
                            is_determinist = -1;
                    } else if ((*tmp)->type == XML_RELAXNG_ELEMENT) {
                        if (((*tmp)->ns == NULL) || ((*tmp)->ns[0] == 0))
                            res = xmlHashAddEntry2(partitions->triage,
                                                   BAD_CAST "#any", NULL,
                                                   XML_INT_TO_PTR(i + 1));
                        else
                            res = xmlHashAddEntry2(partitions->triage,
                                                   BAD_CAST "#any", (*tmp)->ns,
                                                   XML_INT_TO_PTR(i + 1));
                        if ((*tmp)->nameClass != NULL)
                            is_determinist = 2;
                        if (res != 0)
                            is_determinist = -1;
                    } else {
                        is_determinist = -1;
                    }
                    tmp++;
                }
            } else {
                is_determinist = 0;
            }
        }
        partitions->groups = groups;
    
        /*
         * and save the partition list back in the def
         */
        def->data = partitions;
        if (is_mixed != 0)
            def->dflags |= IS_MIXED;
        if (is_determinist == 1)
            partitions->flags = IS_DETERMINIST;
        if (is_determinist == 2)
            partitions->flags = IS_DETERMINIST | IS_NEEDCHECK;
        return;
    
      error:
        xmlRngPErrMemory(ctxt);
        if (groups != NULL) {
            for (i = 0; i < nbgroups; i++)
                if (groups[i] != NULL) {
                    if (groups[i]->defs != NULL)
                        xmlFree(groups[i]->defs);
                    xmlFree(groups[i]);
                }
            xmlFree(groups);
        }
        xmlRelaxNGFreePartition(partitions);
    }
    
    /**
     * parse the content of a RelaxNG interleave node.
     *
     * @param ctxt  a Relax-NG parser context
     * @param node  the data node.
     * @returns the definition pointer or NULL in case of error
     */
    static xmlRelaxNGDefinePtr
    xmlRelaxNGParseInterleave(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
    {
        xmlRelaxNGDefinePtr def = NULL;
        xmlRelaxNGDefinePtr last = NULL, cur;
        xmlNodePtr child;
    
        def = xmlRelaxNGNewDefine(ctxt, node);
        if (def == NULL) {
            return (NULL);
        }
        def->type = XML_RELAXNG_INTERLEAVE;
    
        if (ctxt->interleaves == NULL)
            ctxt->interleaves = xmlHashCreate(10);
        if (ctxt->interleaves == NULL) {
            xmlRngPErrMemory(ctxt);
        } else {
            char name[32];
    
            snprintf(name, 32, "interleave%d", ctxt->nbInterleaves++);
            if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST name, def) < 0) {
                xmlRngPErr(ctxt, node, XML_RNGP_INTERLEAVE_ADD,
                           "Failed to add %s to hash table\n",
    		       (const xmlChar *) name, NULL);
            }
        }
        child = node->children;
        if (child == NULL) {
            xmlRngPErr(ctxt, node, XML_RNGP_INTERLEAVE_NO_CONTENT,
                       "Element interleave is empty\n", NULL, NULL);
        }
        while (child != NULL) {
            if (IS_RELAXNG(child, "element")) {
                cur = xmlRelaxNGParseElement(ctxt, child);
            } else {
                cur = xmlRelaxNGParsePattern(ctxt, child);
            }
            if (cur != NULL) {
                cur->parent = def;
                if (last == NULL) {
                    def->content = last = cur;
                } else {
                    last->next = cur;
                    last = cur;
                }
            }
            child = child->next;
        }
    
        return (def);
    }
    
    /**
     * Integrate the content of an include node in the current grammar
     *
     * @param ctxt  a Relax-NG parser context
     * @param node  the include node
     * @returns 0 in case of success or -1 in case of error
     */
    static int
    xmlRelaxNGParseInclude(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
    {
        xmlRelaxNGIncludePtr incl;
        xmlNodePtr root;
        int ret = 0, tmp;
    
        incl = node->psvi;
        if (incl == NULL) {
            xmlRngPErr(ctxt, node, XML_RNGP_INCLUDE_EMPTY,
                       "Include node has no data\n", NULL, NULL);
            return (-1);
        }
        root = xmlDocGetRootElement(incl->doc);
        if (root == NULL) {
            xmlRngPErr(ctxt, node, XML_RNGP_EMPTY, "Include document is empty\n",
                       NULL, NULL);
            return (-1);
        }
        if (!xmlStrEqual(root->name, BAD_CAST "grammar")) {
            xmlRngPErr(ctxt, node, XML_RNGP_GRAMMAR_MISSING,
                       "Include document root is not a grammar\n", NULL, NULL);
            return (-1);
        }
    
        /*
         * Merge the definition from both the include and the internal list
         */
        if (root->children != NULL) {
            tmp = xmlRelaxNGParseGrammarContent(ctxt, root->children);
            if (tmp != 0)
                ret = -1;
        }
        if (node->children != NULL) {
            tmp = xmlRelaxNGParseGrammarContent(ctxt, node->children);
            if (tmp != 0)
                ret = -1;
        }
        return (ret);
    }
    
    /**
     * parse the content of a RelaxNG define element node.
     *
     * @param ctxt  a Relax-NG parser context
     * @param node  the define node
     * @returns 0 in case of success or -1 in case of error
     */
    static int
    xmlRelaxNGParseDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
    {
        xmlChar *name;
        int ret = 0, tmp;
        xmlRelaxNGDefinePtr def;
        const xmlChar *olddefine;
    
        name = xmlGetProp(node, BAD_CAST "name");
        if (name == NULL) {
            xmlRngPErr(ctxt, node, XML_RNGP_DEFINE_NAME_MISSING,
                       "define has no name\n", NULL, NULL);
        } else {
            xmlRelaxNGNormExtSpace(name);
            if (xmlValidateNCName(name, 0)) {
                xmlRngPErr(ctxt, node, XML_RNGP_INVALID_DEFINE_NAME,
                           "define name '%s' is not an NCName\n", name, NULL);
            }
            def = xmlRelaxNGNewDefine(ctxt, node);
            if (def == NULL) {
                xmlFree(name);
                return (-1);
            }
            def->type = XML_RELAXNG_DEF;
            def->name = name;
            if (node->children == NULL) {
                xmlRngPErr(ctxt, node, XML_RNGP_DEFINE_EMPTY,
                           "define has no children\n", NULL, NULL);
            } else {
                olddefine = ctxt->define;
                ctxt->define = name;
                def->content =
                    xmlRelaxNGParsePatterns(ctxt, node->children, 0);
                ctxt->define = olddefine;
            }
            if (ctxt->grammar->defs == NULL)
                ctxt->grammar->defs = xmlHashCreate(10);
            if (ctxt->grammar->defs == NULL) {
                xmlRngPErr(ctxt, node, XML_RNGP_DEFINE_CREATE_FAILED,
                           "Could not create definition hash\n", NULL, NULL);
                ret = -1;
            } else {
                tmp = xmlHashAddEntry(ctxt->grammar->defs, name, def);
                if (tmp < 0) {
                    xmlRelaxNGDefinePtr prev;
    
                    prev = xmlHashLookup(ctxt->grammar->defs, name);
                    if (prev == NULL) {
                        xmlRngPErr(ctxt, node, XML_RNGP_DEFINE_CREATE_FAILED,
                                   "Internal error on define aggregation of %s\n",
                                   name, NULL);
                        ret = -1;
                    } else {
                        while (prev->nextHash != NULL)
                            prev = prev->nextHash;
                        prev->nextHash = def;
                    }
                }
            }
        }
        return (ret);
    }
    
    /**
     * Import import one references into the current grammar
     *
     * @param payload  the parser context
     * @param data  the current grammar
     * @param name  the reference name
     */
    static void
    xmlRelaxNGParseImportRef(void *payload, void *data, const xmlChar *name) {
        xmlRelaxNGParserCtxtPtr ctxt = (xmlRelaxNGParserCtxtPtr) data;
        xmlRelaxNGDefinePtr def = (xmlRelaxNGDefinePtr) payload;
        int tmp;
    
        def->dflags |= IS_EXTERNAL_REF;
    
        tmp = xmlHashAddEntry(ctxt->grammar->refs, name, def);
        if (tmp < 0) {
            xmlRelaxNGDefinePtr prev;
    
            prev = (xmlRelaxNGDefinePtr)
                xmlHashLookup(ctxt->grammar->refs, def->name);
            if (prev == NULL) {
                if (def->name != NULL) {
                    xmlRngPErr(ctxt, NULL, XML_RNGP_REF_CREATE_FAILED,
                               "Error refs definitions '%s'\n",
                               def->name, NULL);
                } else {
                    xmlRngPErr(ctxt, NULL, XML_RNGP_REF_CREATE_FAILED,
                               "Error refs definitions\n",
                               NULL, NULL);
                }
            } else {
                def->nextHash = prev->nextHash;
                prev->nextHash = def;
            }
        }
    }
    
    /**
     * Import references from the subgrammar into the current grammar
     *
     * @param ctxt  the parser context
     * @param grammar  the sub grammar
     * @returns 0 in case of success, -1 in case of failure
     */
    static int
    xmlRelaxNGParseImportRefs(xmlRelaxNGParserCtxtPtr ctxt,
                              xmlRelaxNGGrammarPtr grammar) {
        if ((ctxt == NULL) || (grammar == NULL) || (ctxt->grammar == NULL))
            return(-1);
        if (grammar->refs == NULL)
            return(0);
        if (ctxt->grammar->refs == NULL)
            ctxt->grammar->refs = xmlHashCreate(10);
        if (ctxt->grammar->refs == NULL) {
            xmlRngPErr(ctxt, NULL, XML_RNGP_REF_CREATE_FAILED,
                       "Could not create references hash\n", NULL, NULL);
            return(-1);
        }
        xmlHashScan(grammar->refs, xmlRelaxNGParseImportRef, ctxt);
        return(0);
    }
    
    /**
     * Process and compile an externalRef node
     *
     * @param ctxt  the parser context
     * @param node  the externalRef node
     * @returns the xmlRelaxNGDefinePtr or NULL in case of error
     */
    static xmlRelaxNGDefinePtr
    xmlRelaxNGProcessExternalRef(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
    {
        xmlRelaxNGDocumentPtr docu;
        xmlNodePtr root, tmp;
        xmlChar *ns;
        int newNs = 0, oldflags;
        xmlRelaxNGDefinePtr def;
    
        docu = node->psvi;
        if (docu != NULL) {
            def = xmlRelaxNGNewDefine(ctxt, node);
            if (def == NULL)
                return (NULL);
            def->type = XML_RELAXNG_EXTERNALREF;
    
            if (docu->content == NULL) {
                /*
                 * Then do the parsing for good
                 */
                root = xmlDocGetRootElement(docu->doc);
                if (root == NULL) {
                    xmlRngPErr(ctxt, node, XML_RNGP_EXTERNALREF_EMTPY,
                               "xmlRelaxNGParse: %s is empty\n", ctxt->URL,
                               NULL);
                    return (NULL);
                }
                /*
                 * ns transmission rules
                 */
                ns = xmlGetProp(root, BAD_CAST "ns");
                if (ns == NULL) {
                    tmp = node;
                    while ((tmp != NULL) && (tmp->type == XML_ELEMENT_NODE)) {
                        ns = xmlGetProp(tmp, BAD_CAST "ns");
                        if (ns != NULL) {
                            break;
                        }
                        tmp = tmp->parent;
                    }
                    if (ns != NULL) {
                        xmlSetProp(root, BAD_CAST "ns", ns);
                        newNs = 1;
                        xmlFree(ns);
                    }
                } else {
                    xmlFree(ns);
                }
    
                /*
                 * Parsing to get a precompiled schemas.
                 */
                oldflags = ctxt->flags;
                ctxt->flags |= XML_RELAXNG_IN_EXTERNALREF;
                docu->schema = xmlRelaxNGParseDocument(ctxt, root);
                ctxt->flags = oldflags;
                if ((docu->schema != NULL) &&
                    (docu->schema->topgrammar != NULL)) {
                    docu->content = docu->schema->topgrammar->start;
                    if (docu->schema->topgrammar->refs)
                        xmlRelaxNGParseImportRefs(ctxt, docu->schema->topgrammar);
                }
    
                /*
                 * the externalRef may be reused in a different ns context
                 */
                if (newNs == 1) {
                    xmlUnsetProp(root, BAD_CAST "ns");
                }
            }
            def->content = docu->content;
        } else {
            def = NULL;
        }
        return (def);
    }
    
    /**
     * parse the content of a RelaxNG pattern node.
     *
     * @param ctxt  a Relax-NG parser context
     * @param node  the pattern node.
     * @returns the definition pointer or NULL in case of error or if no
     *     pattern is generated.
     */
    static xmlRelaxNGDefinePtr
    xmlRelaxNGParsePattern(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
    {
        xmlRelaxNGDefinePtr def = NULL;
    
        if (node == NULL) {
            return (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;
            if (node->children != NULL) {
                xmlRngPErr(ctxt, node, XML_RNGP_EMPTY_NOT_EMPTY,
                           "empty: had a child node\n", NULL, NULL);
            }
        } else if (IS_RELAXNG(node, "text")) {
            def = xmlRelaxNGNewDefine(ctxt, node);
            if (def == NULL)
                return (NULL);
            def->type = XML_RELAXNG_TEXT;
            if (node->children != NULL) {
                xmlRngPErr(ctxt, node, XML_RNGP_TEXT_HAS_CHILD,
                           "text: had a child node\n", NULL, NULL);
            }
        } else if (IS_RELAXNG(node, "zeroOrMore")) {
            def = xmlRelaxNGNewDefine(ctxt, node);
            if (def == NULL)
                return (NULL);
            def->type = XML_RELAXNG_ZEROORMORE;
            if (node->children == NULL) {
                xmlRngPErr(ctxt, node, XML_RNGP_EMPTY_CONSTRUCT,
                           "Element %s is empty\n", node->name, NULL);
            } else {
                def->content =
                    xmlRelaxNGParsePatterns(ctxt, node->children, 1);
            }
        } else if (IS_RELAXNG(node, "oneOrMore")) {
            def = xmlRelaxNGNewDefine(ctxt, node);
            if (def == NULL)
                return (NULL);
            def->type = XML_RELAXNG_ONEORMORE;
            if (node->children == NULL) {
                xmlRngPErr(ctxt, node, XML_RNGP_EMPTY_CONSTRUCT,
                           "Element %s is empty\n", node->name, NULL);
            } else {
                def->content =
                    xmlRelaxNGParsePatterns(ctxt, node->children, 1);
            }
        } else if (IS_RELAXNG(node, "optional")) {
            def = xmlRelaxNGNewDefine(ctxt, node);
            if (def == NULL)
                return (NULL);
            def->type = XML_RELAXNG_OPTIONAL;
            if (node->children == NULL) {
                xmlRngPErr(ctxt, node, XML_RNGP_EMPTY_CONSTRUCT,
                           "Element %s is empty\n", node->name, NULL);
            } else {
                def->content =
                    xmlRelaxNGParsePatterns(ctxt, node->children, 1);
            }
        } else if (IS_RELAXNG(node, "choice")) {
            def = xmlRelaxNGNewDefine(ctxt, node);
            if (def == NULL)
                return (NULL);
            def->type = XML_RELAXNG_CHOICE;
            if (node->children == NULL) {
                xmlRngPErr(ctxt, node, XML_RNGP_EMPTY_CONSTRUCT,
                           "Element %s is empty\n", node->name, NULL);
            } else {
                def->content =
                    xmlRelaxNGParsePatterns(ctxt, node->children, 0);
            }
        } else if (IS_RELAXNG(node, "group")) {
            def = xmlRelaxNGNewDefine(ctxt, node);
            if (def == NULL)
                return (NULL);
            def->type = XML_RELAXNG_GROUP;
            if (node->children == NULL) {
                xmlRngPErr(ctxt, node, XML_RNGP_EMPTY_CONSTRUCT,
                           "Element %s is empty\n", node->name, NULL);
            } else {
                def->content =
                    xmlRelaxNGParsePatterns(ctxt, node->children, 0);
            }
        } 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) {
                xmlRngPErr(ctxt, node, XML_RNGP_REF_NO_NAME, "ref has no name\n",
                           NULL, NULL);
            } else {
                xmlRelaxNGNormExtSpace(def->name);
                if (xmlValidateNCName(def->name, 0)) {
                    xmlRngPErr(ctxt, node, XML_RNGP_REF_NAME_INVALID,
                               "ref name '%s' is not an NCName\n", def->name,
                               NULL);
                }
            }
            if (node->children != NULL) {
                xmlRngPErr(ctxt, node, XML_RNGP_REF_NOT_EMPTY, "ref is not empty\n",
                           NULL, NULL);
            }
            if (ctxt->grammar->refs == NULL)
                ctxt->grammar->refs = xmlHashCreate(10);
            if (ctxt->grammar->refs == NULL) {
                xmlRngPErr(ctxt, node, XML_RNGP_REF_CREATE_FAILED,
                           "Could not create references hash\n", NULL, NULL);
                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 (def->name != NULL) {
    		        xmlRngPErr(ctxt, node, XML_RNGP_REF_CREATE_FAILED,
    				   "Error refs definitions '%s'\n",
    				   def->name, NULL);
                        } else {
    		        xmlRngPErr(ctxt, node, XML_RNGP_REF_CREATE_FAILED,
    				   "Error refs definitions\n",
    				   NULL, NULL);
                        }
                        def = NULL;
                    } else {
                        def->nextHash = prev->nextHash;
                        prev->nextHash = def;
                    }
                }
            }
        } else if (IS_RELAXNG(node, "data")) {
            def = xmlRelaxNGParseData(ctxt, node);
        } else if (IS_RELAXNG(node, "value")) {
            def = xmlRelaxNGParseValue(ctxt, node);
        } else if (IS_RELAXNG(node, "list")) {
            def = xmlRelaxNGNewDefine(ctxt, node);
            if (def == NULL)
                return (NULL);
            def->type = XML_RELAXNG_LIST;
            if (node->children == NULL) {
                xmlRngPErr(ctxt, node, XML_RNGP_EMPTY_CONSTRUCT,
                           "Element %s is empty\n", node->name, NULL);
            } else {
                def->content =
                    xmlRelaxNGParsePatterns(ctxt, node->children, 0);
            }
        } else if (IS_RELAXNG(node, "interleave")) {
            def = xmlRelaxNGParseInterleave(ctxt, node);
        } else if (IS_RELAXNG(node, "externalRef")) {
            def = xmlRelaxNGProcessExternalRef(ctxt, node);
        } else if (IS_RELAXNG(node, "notAllowed")) {
            def = xmlRelaxNGNewDefine(ctxt, node);
            if (def == NULL)
                return (NULL);
            def->type = XML_RELAXNG_NOT_ALLOWED;
            if (node->children != NULL) {
                xmlRngPErr(ctxt, node, XML_RNGP_NOTALLOWED_NOT_EMPTY,
                           "xmlRelaxNGParse: notAllowed element is not empty\n",
                           NULL, NULL);
            }
        } else if (IS_RELAXNG(node, "grammar")) {
            xmlRelaxNGGrammarPtr grammar, old;
            xmlRelaxNGGrammarPtr oldparent;
    
            oldparent = ctxt->parentgrammar;
            old = ctxt->grammar;
            ctxt->parentgrammar = old;
            grammar = xmlRelaxNGParseGrammar(ctxt, node->children);
            if (old != NULL) {
                ctxt->grammar = old;
                ctxt->parentgrammar = oldparent;
    #if 0
                if (grammar != NULL) {
                    grammar->next = old->next;
                    old->next = grammar;
                }
    #endif
            }
            if (grammar != NULL)
                def = grammar->start;
            else
                def = NULL;
        } else if (IS_RELAXNG(node, "parentRef")) {
            if (ctxt->parentgrammar == NULL) {
                xmlRngPErr(ctxt, node, XML_RNGP_PARENTREF_NO_PARENT,
                           "Use of parentRef without a parent grammar\n", NULL,
                           NULL);
                return (NULL);
            }
            def = xmlRelaxNGNewDefine(ctxt, node);
            if (def == NULL)
                return (NULL);
            def->type = XML_RELAXNG_PARENTREF;
            def->name = xmlGetProp(node, BAD_CAST "name");
            if (def->name == NULL) {
                xmlRngPErr(ctxt, node, XML_RNGP_PARENTREF_NO_NAME,
                           "parentRef has no name\n", NULL, NULL);
            } else {
                xmlRelaxNGNormExtSpace(def->name);
                if (xmlValidateNCName(def->name, 0)) {
                    xmlRngPErr(ctxt, node, XML_RNGP_PARENTREF_NAME_INVALID,
                               "parentRef name '%s' is not an NCName\n",
                               def->name, NULL);
                }
            }
            if (node->children != NULL) {
                xmlRngPErr(ctxt, node, XML_RNGP_PARENTREF_NOT_EMPTY,
                           "parentRef is not empty\n", NULL, NULL);
            }
            if (ctxt->parentgrammar->refs == NULL)
                ctxt->parentgrammar->refs = xmlHashCreate(10);
            if (ctxt->parentgrammar->refs == NULL) {
                xmlRngPErr(ctxt, node, XML_RNGP_PARENTREF_CREATE_FAILED,
                           "Could not create references hash\n", NULL, NULL);
                def = NULL;
            } else if (def->name != NULL) {
                int tmp;
    
                tmp =
                    xmlHashAddEntry(ctxt->parentgrammar->refs, def->name, def);
                if (tmp < 0) {
                    xmlRelaxNGDefinePtr prev;
    
                    prev = (xmlRelaxNGDefinePtr)
                        xmlHashLookup(ctxt->parentgrammar->refs, def->name);
                    if (prev == NULL) {
                        xmlRngPErr(ctxt, node, XML_RNGP_PARENTREF_CREATE_FAILED,
                                   "Internal error parentRef definitions '%s'\n",
                                   def->name, NULL);
                        def = NULL;
                    } else {
                        def->nextHash = prev->nextHash;
                        prev->nextHash = def;
                    }
                }
            }
        } else if (IS_RELAXNG(node, "mixed")) {
            if (node->children == NULL) {
                xmlRngPErr(ctxt, node, XML_RNGP_EMPTY_CONSTRUCT, "Mixed is empty\n",
                           NULL, NULL);
                def = NULL;
            } else {
                def = xmlRelaxNGParseInterleave(ctxt, node);
                if (def != NULL) {
                    xmlRelaxNGDefinePtr tmp;
    
                    if ((def->content != NULL) && (def->content->next != NULL)) {
                        tmp = xmlRelaxNGNewDefine(ctxt, node);
                        if (tmp != NULL) {
                            tmp->type = XML_RELAXNG_GROUP;
                            tmp->content = def->content;
                            def->content = tmp;
                        }
                    }
    
                    tmp = xmlRelaxNGNewDefine(ctxt, node);
                    if (tmp == NULL)
                        return (def);
                    tmp->type = XML_RELAXNG_TEXT;
                    tmp->next = def->content;
                    def->content = tmp;
                }
            }
        } else {
            xmlRngPErr(ctxt, node, XML_RNGP_UNKNOWN_CONSTRUCT,
                       "Unexpected node %s is not a pattern\n", node->name,
                       NULL);
            def = NULL;
        }
        return (def);
    }
    
    /**
     * parse the content of a RelaxNG attribute node.
     *
     * @param ctxt  a Relax-NG parser context
     * @param node  the element node
     * @returns the definition pointer or NULL in case of error.
     */
    static xmlRelaxNGDefinePtr
    xmlRelaxNGParseAttribute(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
    {
        xmlRelaxNGDefinePtr ret, cur;
        xmlNodePtr child;
        int old_flags;
    
        ret = xmlRelaxNGNewDefine(ctxt, node);
        if (ret == NULL)
            return (NULL);
        ret->type = XML_RELAXNG_ATTRIBUTE;
        ret->parent = ctxt->def;
        child = node->children;
        if (child == NULL) {
            xmlRngPErr(ctxt, node, XML_RNGP_ATTRIBUTE_EMPTY,
                       "xmlRelaxNGParseattribute: attribute has no children\n",
                       NULL, NULL);
            return (ret);
        }
        old_flags = ctxt->flags;
        ctxt->flags |= XML_RELAXNG_IN_ATTRIBUTE;
        cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
        if (cur != NULL)
            child = child->next;
    
        if (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_PARENTREF:
                    case XML_RELAXNG_EXTERNALREF:
                    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:
                    case XML_RELAXNG_ATTRIBUTE:
                        ret->content = cur;
                        cur->parent = ret;
                        break;
                    case XML_RELAXNG_START:
                    case XML_RELAXNG_PARAM:
                    case XML_RELAXNG_EXCEPT:
                        xmlRngPErr(ctxt, node, XML_RNGP_ATTRIBUTE_CONTENT,
                                   "attribute has invalid content\n", NULL,
                                   NULL);
                        break;
                    case XML_RELAXNG_NOOP:
                        xmlRngPErr(ctxt, node, XML_RNGP_ATTRIBUTE_NOOP,
                                   "RNG Internal error, noop found in attribute\n",
                                   NULL, NULL);
                        break;
                }
            }
            child = child->next;
        }
        if (child != NULL) {
            xmlRngPErr(ctxt, node, XML_RNGP_ATTRIBUTE_CHILDREN,
                       "attribute has multiple children\n", NULL, NULL);
        }
        ctxt->flags = old_flags;
        return (ret);
    }
    
    /**
     * parse the content of a RelaxNG nameClass node.
     *
     * @param ctxt  a Relax-NG parser context
     * @param node  the except node
     * @param attr  1 if within an attribute, 0 if within an element
     * @returns the definition pointer or NULL in case of error.
     */
    static xmlRelaxNGDefinePtr
    xmlRelaxNGParseExceptNameClass(xmlRelaxNGParserCtxtPtr ctxt,
                                   xmlNodePtr node, int attr)
    {
        xmlRelaxNGDefinePtr ret, cur, last = NULL;
        xmlNodePtr child;
    
        if (!IS_RELAXNG(node, "except")) {
            xmlRngPErr(ctxt, node, XML_RNGP_EXCEPT_MISSING,
                       "Expecting an except node\n", NULL, NULL);
            return (NULL);
        }
        if (node->next != NULL) {
            xmlRngPErr(ctxt, node, XML_RNGP_EXCEPT_MULTIPLE,
                       "exceptNameClass allows only a single except node\n",
                       NULL, NULL);
        }
        if (node->children == NULL) {
            xmlRngPErr(ctxt, node, XML_RNGP_EXCEPT_EMPTY, "except has no content\n",
                       NULL, NULL);
            return (NULL);
        }
    
        ret = xmlRelaxNGNewDefine(ctxt, node);
        if (ret == NULL)
            return (NULL);
        ret->type = XML_RELAXNG_EXCEPT;
        child = node->children;
        while (child != NULL) {
            cur = xmlRelaxNGNewDefine(ctxt, child);
            if (cur == NULL)
                break;
            if (attr)
                cur->type = XML_RELAXNG_ATTRIBUTE;
            else
                cur->type = XML_RELAXNG_ELEMENT;
    
            if (xmlRelaxNGParseNameClass(ctxt, child, cur) != NULL) {
                if (last == NULL) {
                    ret->content = cur;
                } else {
                    last->next = cur;
                }
                last = cur;
            }
            child = child->next;
        }
    
        return (ret);
    }
    
    /**
     * parse the content of a RelaxNG nameClass node.
     *
     * @param ctxt  a Relax-NG parser context
     * @param node  the nameClass node
     * @param def  the current definition
     * @returns the definition pointer or NULL in case of error.
     */
    static xmlRelaxNGDefinePtr
    xmlRelaxNGParseNameClass(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
                             xmlRelaxNGDefinePtr def)
    {
        xmlRelaxNGDefinePtr ret, tmp;
        xmlChar *val;
    
        ret = def;
        if ((IS_RELAXNG(node, "name")) || (IS_RELAXNG(node, "anyName")) ||
            (IS_RELAXNG(node, "nsName"))) {
            if ((def->type != XML_RELAXNG_ELEMENT) &&
                (def->type != XML_RELAXNG_ATTRIBUTE)) {
                ret = xmlRelaxNGNewDefine(ctxt, node);
                if (ret == NULL)
                    return (NULL);
                ret->parent = def;
                if (ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE)
                    ret->type = XML_RELAXNG_ATTRIBUTE;
                else
                    ret->type = XML_RELAXNG_ELEMENT;
            }
        }
        if (IS_RELAXNG(node, "name")) {
            val = xmlNodeGetContent(node);
            xmlRelaxNGNormExtSpace(val);
            if (xmlValidateNCName(val, 0)) {
    	    if (node->parent != NULL)
    		xmlRngPErr(ctxt, node, XML_RNGP_ELEMENT_NAME,
    			   "Element %s name '%s' is not an NCName\n",
    			   node->parent->name, val);
    	    else
    		xmlRngPErr(ctxt, node, XML_RNGP_ELEMENT_NAME,
    			   "name '%s' is not an NCName\n",
    			   val, NULL);
            }
            ret->name = val;
            val = xmlGetProp(node, BAD_CAST "ns");
            ret->ns = val;
            if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
                (val != NULL) &&
                (xmlStrEqual(val, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
    	    xmlRngPErr(ctxt, node, XML_RNGP_XML_NS,
                            "Attribute with namespace '%s' is not allowed\n",
                            val, NULL);
            }
            if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
                (val != NULL) &&
                (val[0] == 0) && (xmlStrEqual(ret->name, BAD_CAST "xmlns"))) {
    	    xmlRngPErr(ctxt, node, XML_RNGP_XMLNS_NAME,
                           "Attribute with QName 'xmlns' is not allowed\n",
                           val, NULL);
            }
        } else if (IS_RELAXNG(node, "anyName")) {
            ret->name = NULL;
            ret->ns = NULL;
            if (node->children != NULL) {
                ret->nameClass =
                    xmlRelaxNGParseExceptNameClass(ctxt, node->children,
                                                   (def->type ==
                                                    XML_RELAXNG_ATTRIBUTE));
            }
        } else if (IS_RELAXNG(node, "nsName")) {
            ret->name = NULL;
            ret->ns = xmlGetProp(node, BAD_CAST "ns");
            if (ret->ns == NULL) {
                xmlRngPErr(ctxt, node, XML_RNGP_NSNAME_NO_NS,
                           "nsName has no ns attribute\n", NULL, NULL);
            }
            if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
                (ret->ns != NULL) &&
                (xmlStrEqual
                 (ret->ns, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
                xmlRngPErr(ctxt, node, XML_RNGP_XML_NS,
                           "Attribute with namespace '%s' is not allowed\n",
                           ret->ns, NULL);
            }
            if (node->children != NULL) {
                ret->nameClass =
                    xmlRelaxNGParseExceptNameClass(ctxt, node->children,
                                                   (def->type ==
                                                    XML_RELAXNG_ATTRIBUTE));
            }
        } else if (IS_RELAXNG(node, "choice")) {
            xmlNodePtr child;
            xmlRelaxNGDefinePtr last = NULL;
    
            if (def->type == XML_RELAXNG_CHOICE) {
                ret = def;
            } else {
                ret = xmlRelaxNGNewDefine(ctxt, node);
                if (ret == NULL)
                    return (NULL);
                ret->parent = def;
                ret->type = XML_RELAXNG_CHOICE;
            }
    
            if (node->children == NULL) {
                xmlRngPErr(ctxt, node, XML_RNGP_CHOICE_EMPTY,
                           "Element choice is empty\n", NULL, NULL);
            } else {
    
                child = node->children;
                while (child != NULL) {
                    tmp = xmlRelaxNGParseNameClass(ctxt, child, ret);
                    if (tmp != NULL) {
                        if (last == NULL) {
                            last = tmp;
                        } else if (tmp != ret) {
                            last->next = tmp;
                            last = tmp;
                        }
                    }
                    child = child->next;
                }
            }
        } else {
            xmlRngPErr(ctxt, node, XML_RNGP_CHOICE_CONTENT,
                       "expecting name, anyName, nsName or choice : got %s\n",
                       (node == NULL ? (const xmlChar *) "nothing" : node->name),
    		   NULL);
            return (NULL);
        }
        if (ret != def) {
            if (def->nameClass == NULL) {
                def->nameClass = ret;
            } else {
                tmp = def->nameClass;
                while (tmp->next != NULL) {
                    tmp = tmp->next;
                }
                tmp->next = ret;
            }
        }
        return (ret);
    }
    
    /**
     * parse the content of a RelaxNG element node.
     *
     * @param ctxt  a Relax-NG parser context
     * @param node  the 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;
        const xmlChar *olddefine;
    
        ret = xmlRelaxNGNewDefine(ctxt, node);
        if (ret == NULL)
            return (NULL);
        ret->type = XML_RELAXNG_ELEMENT;
        ret->parent = ctxt->def;
        child = node->children;
        if (child == NULL) {
            xmlRngPErr(ctxt, node, XML_RNGP_ELEMENT_EMPTY,
                       "xmlRelaxNGParseElement: element has no children\n",
                       NULL, NULL);
            return (ret);
        }
        cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
        if (cur != NULL)
            child = child->next;
    
        if (child == NULL) {
            xmlRngPErr(ctxt, node, XML_RNGP_ELEMENT_NO_CONTENT,
                       "xmlRelaxNGParseElement: element has no content\n",
                       NULL, NULL);
            return (ret);
        }
        olddefine = ctxt->define;
        ctxt->define = NULL;
        last = NULL;
        while (child != NULL) {
            cur = xmlRelaxNGParsePattern(ctxt, child);
            if (cur != NULL) {
                cur->parent = ret;
                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_PARENTREF:
                    case XML_RELAXNG_EXTERNALREF:
                    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;
                    case XML_RELAXNG_START:
                        xmlRngPErr(ctxt, node, XML_RNGP_ELEMENT_CONTENT,
                                   "RNG Internal error, start found in element\n",
                                   NULL, NULL);
                        break;
                    case XML_RELAXNG_PARAM:
                        xmlRngPErr(ctxt, node, XML_RNGP_ELEMENT_CONTENT,
                                   "RNG Internal error, param found in element\n",
                                   NULL, NULL);
                        break;
                    case XML_RELAXNG_EXCEPT:
                        xmlRngPErr(ctxt, node, XML_RNGP_ELEMENT_CONTENT,
                                   "RNG Internal error, except found in element\n",
                                   NULL, NULL);
                        break;
                    case XML_RELAXNG_NOOP:
                        xmlRngPErr(ctxt, node, XML_RNGP_ELEMENT_CONTENT,
                                   "RNG Internal error, noop found in element\n",
                                   NULL, NULL);
                        break;
                }
            }
            child = child->next;
        }
        ctxt->define = olddefine;
        return (ret);
    }
    
    /**
     * parse the content of a RelaxNG start node.
     *
     * @param ctxt  a Relax-NG parser context
     * @param nodes  list of nodes
     * @param group  use an implicit `<group>` for elements
     * @returns the definition pointer or NULL in case of error.
     */
    static xmlRelaxNGDefinePtr
    xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes,
                            int group)
    {
        xmlRelaxNGDefinePtr def = NULL, last = NULL, cur, parent;
    
        parent = ctxt->def;
        while (nodes != NULL) {
            if (IS_RELAXNG(nodes, "element")) {
                cur = xmlRelaxNGParseElement(ctxt, nodes);
                if (cur == NULL)
                    return (NULL);
                if (def == NULL) {
                    def = last = cur;
                } else {
                    if ((group == 1) && (def->type == XML_RELAXNG_ELEMENT) &&
                        (def == last)) {
                        def = xmlRelaxNGNewDefine(ctxt, nodes);
                        if (def == NULL)
                            return (NULL);
                        def->type = XML_RELAXNG_GROUP;
                        def->content = last;
                    }
                    last->next = cur;
                    last = cur;
                }
                cur->parent = parent;
            } else {
                cur = xmlRelaxNGParsePattern(ctxt, nodes);
                if (cur != NULL) {
                    if (def == NULL) {
                        def = last = cur;
                    } else {
                        last->next = cur;
                        last = cur;
                    }
                }
            }
            nodes = nodes->next;
        }
        return (def);
    }
    
    /**
     * parse the content of a RelaxNG start node.
     *
     * @param ctxt  a Relax-NG parser context
     * @param nodes  start children nodes
     * @returns 0 in case of success, -1 in case of error
     */
    static int
    xmlRelaxNGParseStart(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes)
    {
        int ret = 0;
        xmlRelaxNGDefinePtr def = NULL, last;
    
        if (nodes == NULL) {
            xmlRngPErr(ctxt, nodes, XML_RNGP_START_EMPTY, "start has no children\n",
                       NULL, NULL);
            return (-1);
        }
        if (IS_RELAXNG(nodes, "empty")) {
            def = xmlRelaxNGNewDefine(ctxt, nodes);
            if (def == NULL)
                return (-1);
            def->type = XML_RELAXNG_EMPTY;
            if (nodes->children != NULL) {
                xmlRngPErr(ctxt, nodes, XML_RNGP_EMPTY_CONTENT,
                           "element empty is not empty\n", NULL, NULL);
            }
        } else if (IS_RELAXNG(nodes, "notAllowed")) {
            def = xmlRelaxNGNewDefine(ctxt, nodes);
            if (def == NULL)
                return (-1);
            def->type = XML_RELAXNG_NOT_ALLOWED;
            if (nodes->children != NULL) {
                xmlRngPErr(ctxt, nodes, XML_RNGP_NOTALLOWED_NOT_EMPTY,
                           "element notAllowed is not empty\n", NULL, NULL);
            }
        } else {
            def = xmlRelaxNGParsePatterns(ctxt, nodes, 1);
        }
        if (ctxt->grammar->start != NULL) {
            last = ctxt->grammar->start;
            while (last->next != NULL)
                last = last->next;
            last->next = def;
        } else {
            ctxt->grammar->start = def;
        }
        nodes = nodes->next;
        if (nodes != NULL) {
            xmlRngPErr(ctxt, nodes, XML_RNGP_START_CONTENT,
                       "start more than one children\n", NULL, NULL);
            return (-1);
        }
        return (ret);
    }
    
    /**
     * parse the content of a RelaxNG grammar node.
     *
     * @param ctxt  a Relax-NG parser context
     * @param nodes  grammar children nodes
     * @returns 0 in case of success, -1 in case of error
     */
    static int
    xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt,
                                  xmlNodePtr nodes)
    {
        int ret = 0, tmp;
    
        if (nodes == NULL) {
            xmlRngPErr(ctxt, nodes, XML_RNGP_GRAMMAR_EMPTY,
                       "grammar has no children\n", NULL, NULL);
            return (-1);
        }
        while (nodes != NULL) {
            if (IS_RELAXNG(nodes, "start")) {
                if (nodes->children == NULL) {
                    xmlRngPErr(ctxt, nodes, XML_RNGP_START_EMPTY,
                               "start has no children\n", NULL, NULL);
                } else {
                    tmp = xmlRelaxNGParseStart(ctxt, nodes->children);
                    if (tmp != 0)
                        ret = -1;
                }
            } else if (IS_RELAXNG(nodes, "define")) {
                tmp = xmlRelaxNGParseDefine(ctxt, nodes);
                if (tmp != 0)
                    ret = -1;
            } else if (IS_RELAXNG(nodes, "include")) {
                tmp = xmlRelaxNGParseInclude(ctxt, nodes);
                if (tmp != 0)
                    ret = -1;
            } else {
                xmlRngPErr(ctxt, nodes, XML_RNGP_GRAMMAR_CONTENT,
                           "grammar has unexpected child %s\n", nodes->name,
                           NULL);
                ret = -1;
            }
            nodes = nodes->next;
        }
        return (ret);
    }
    
    /**
     * Applies the 4.17. combine attribute rule for all the define
     * element of a given grammar using the same name.
     *
     * @param payload  the ref
     * @param data  a Relax-NG parser context
     * @param name  the name associated to the defines
     */
    static void
    xmlRelaxNGCheckReference(void *payload, void *data, const xmlChar * name)
    {
        xmlRelaxNGDefinePtr ref = (xmlRelaxNGDefinePtr) payload;
        xmlRelaxNGParserCtxtPtr ctxt = (xmlRelaxNGParserCtxtPtr) data;
        xmlRelaxNGGrammarPtr grammar;
        xmlRelaxNGDefinePtr def, cur;
    
        /*
         * Those rules don't apply to imported ref from xmlRelaxNGParseImportRef
         */
        if (ref->dflags & IS_EXTERNAL_REF)
            return;
    
        grammar = ctxt->grammar;
        if (grammar == NULL) {
            xmlRngPErr(ctxt, ref->node, XML_ERR_INTERNAL_ERROR,
                       "Internal error: no grammar in CheckReference %s\n",
                       name, NULL);
            return;
        }
        if (ref->content != NULL) {
            xmlRngPErr(ctxt, ref->node, XML_ERR_INTERNAL_ERROR,
                       "Internal error: reference has content in CheckReference %s\n",
                       name, NULL);
            return;
        }
        if (grammar->defs != NULL) {
            def = xmlHashLookup(grammar->defs, name);
            if (def != NULL) {
                cur = ref;
                while (cur != NULL) {
                    cur->content = def;
                    cur = cur->nextHash;
                }
            } else {
                xmlRngPErr(ctxt, ref->node, XML_RNGP_REF_NO_DEF,
                           "Reference %s has no matching definition\n", name,
                           NULL);
            }
        } else {
            xmlRngPErr(ctxt, ref->node, XML_RNGP_REF_NO_DEF,
                       "Reference %s has no matching definition\n", name,
                       NULL);
        }
    }
    
    /**
     * Applies the 4.17. combine attribute rule for all the define
     * element of a given grammar using the same name.
     *
     * @param payload  the define(s) list
     * @param data  a Relax-NG parser context
     * @param name  the name associated to the defines
     */
    static void
    xmlRelaxNGCheckCombine(void *payload, void *data, const xmlChar * name)
    {
        xmlRelaxNGDefinePtr define = (xmlRelaxNGDefinePtr) payload;
        xmlRelaxNGParserCtxtPtr ctxt = (xmlRelaxNGParserCtxtPtr) data;
        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) {
                        xmlRngPErr(ctxt, define->node, XML_RNGP_DEF_CHOICE_AND_INTERLEAVE,
                                   "Defines for %s use both 'choice' and 'interleave'\n",
                                   name, NULL);
                    }
                } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
                    if (choiceOrInterleave == -1)
                        choiceOrInterleave = 0;
                    else if (choiceOrInterleave == 1) {
                        xmlRngPErr(ctxt, define->node, XML_RNGP_DEF_CHOICE_AND_INTERLEAVE,
                                   "Defines for %s use both 'choice' and 'interleave'\n",
                                   name, NULL);
                    }
                } else {
                    xmlRngPErr(ctxt, define->node, XML_RNGP_UNKNOWN_COMBINE,
                               "Defines for %s use unknown combine value '%s''\n",
                               name, combine);
                }
                xmlFree(combine);
            } else {
                if (missing == 0)
                    missing = 1;
                else {
                    xmlRngPErr(ctxt, define->node, XML_RNGP_NEED_COMBINE,
                               "Some defines for %s needs the combine attribute\n",
                               name, NULL);
                }
            }
    
            cur = cur->nextHash;
        }
        if (choiceOrInterleave == -1)
            choiceOrInterleave = 0;
        cur = xmlRelaxNGNewDefine(ctxt, define->node);
        if (cur == NULL)
            return;
        if (choiceOrInterleave == 0)
            cur->type = XML_RELAXNG_INTERLEAVE;
        else
            cur->type = XML_RELAXNG_CHOICE;
        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 = cur;
            tmp = tmp->nextHash;
        }
        define->content = cur;
        if (choiceOrInterleave == 0) {
            if (ctxt->interleaves == NULL)
                ctxt->interleaves = xmlHashCreate(10);
            if (ctxt->interleaves == NULL) {
                xmlRngPErr(ctxt, define->node, XML_RNGP_INTERLEAVE_CREATE_FAILED,
                           "Failed to create interleaves hash table\n", NULL,
                           NULL);
            } else {
                char tmpname[32];
    
                snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
                if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) <
                    0) {
                    xmlRngPErr(ctxt, define->node, XML_RNGP_INTERLEAVE_CREATE_FAILED,
                               "Failed to add %s to hash table\n",
    			   (const xmlChar *) tmpname, NULL);
                }
            }
        }
    }
    
    /**
     * Applies the 4.17. combine rule for all the start
     * element of a given grammar.
     *
     * @param ctxt  a Relax-NG parser context
     * @param grammar  the grammar
     */
    static void
    xmlRelaxNGCombineStart(xmlRelaxNGParserCtxtPtr ctxt,
                           xmlRelaxNGGrammarPtr grammar)
    {
        xmlRelaxNGDefinePtr starts;
        xmlChar *combine;
        int choiceOrInterleave = -1;
        int missing = 0;
        xmlRelaxNGDefinePtr cur;
    
        starts = grammar->start;
        if ((starts == NULL) || (starts->next == NULL))
            return;
        cur = starts;
        while (cur != NULL) {
            if ((cur->node == NULL) || (cur->node->parent == NULL) ||
                (!xmlStrEqual(cur->node->parent->name, BAD_CAST "start"))) {
                combine = NULL;
                xmlRngPErr(ctxt, cur->node, XML_RNGP_START_MISSING,
                           "Internal error: start element not found\n", NULL,
                           NULL);
            } else {
                combine = xmlGetProp(cur->node->parent, BAD_CAST "combine");
            }
    
            if (combine != NULL) {
                if (xmlStrEqual(combine, BAD_CAST "choice")) {
                    if (choiceOrInterleave == -1)
                        choiceOrInterleave = 1;
                    else if (choiceOrInterleave == 0) {
                        xmlRngPErr(ctxt, cur->node, XML_RNGP_START_CHOICE_AND_INTERLEAVE,
                                   "<start> use both 'choice' and 'interleave'\n",
                                   NULL, NULL);
                    }
                } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
                    if (choiceOrInterleave == -1)
                        choiceOrInterleave = 0;
                    else if (choiceOrInterleave == 1) {
                        xmlRngPErr(ctxt, cur->node, XML_RNGP_START_CHOICE_AND_INTERLEAVE,
                                   "<start> use both 'choice' and 'interleave'\n",
                                   NULL, NULL);
                    }
                } else {
                    xmlRngPErr(ctxt, cur->node, XML_RNGP_UNKNOWN_COMBINE,
                               "<start> uses unknown combine value '%s''\n",
                               combine, NULL);
                }
                xmlFree(combine);
            } else {
                if (missing == 0)
                    missing = 1;
                else {
                    xmlRngPErr(ctxt, cur->node, XML_RNGP_NEED_COMBINE,
                               "Some <start> element miss the combine attribute\n",
                               NULL, NULL);
                }
            }
    
            cur = cur->next;
        }
        if (choiceOrInterleave == -1)
            choiceOrInterleave = 0;
        cur = xmlRelaxNGNewDefine(ctxt, starts->node);
        if (cur == NULL)
            return;
        if (choiceOrInterleave == 0)
            cur->type = XML_RELAXNG_INTERLEAVE;
        else
            cur->type = XML_RELAXNG_CHOICE;
        cur->content = grammar->start;
        grammar->start = cur;
        if (choiceOrInterleave == 0) {
            if (ctxt->interleaves == NULL)
                ctxt->interleaves = xmlHashCreate(10);
            if (ctxt->interleaves == NULL) {
                xmlRngPErr(ctxt, cur->node, XML_RNGP_INTERLEAVE_CREATE_FAILED,
                           "Failed to create interleaves hash table\n", NULL,
                           NULL);
            } else {
                char tmpname[32];
    
                snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
                if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) <
                    0) {
                    xmlRngPErr(ctxt, cur->node, XML_RNGP_INTERLEAVE_CREATE_FAILED,
                               "Failed to add %s to hash table\n",
    			   (const xmlChar *) tmpname, NULL);
                }
            }
        }
    }
    
    /**
     * Check for cycles.
     *
     * @param ctxt  a Relax-NG parser context
     * @param cur  grammar children nodes
     * @param depth  the counter
     * @returns 0 if check passed, and -1 in case of error
     */
    static int
    xmlRelaxNGCheckCycles(xmlRelaxNGParserCtxtPtr ctxt,
                          xmlRelaxNGDefinePtr cur, int depth)
    {
        int ret = 0;
    
        while ((ret == 0) && (cur != NULL)) {
            if ((cur->type == XML_RELAXNG_REF) ||
                (cur->type == XML_RELAXNG_PARENTREF)) {
                if (cur->depth == -1) {
                    cur->depth = depth;
                    ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth);
                    cur->depth = -2;
                } else if (depth == cur->depth) {
                    xmlRngPErr(ctxt, cur->node, XML_RNGP_REF_CYCLE,
                               "Detected a cycle in %s references\n",
                               cur->name, NULL);
                    return (-1);
                }
            } else if (cur->type == XML_RELAXNG_ELEMENT) {
                ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth + 1);
            } else {
                ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth);
            }
            cur = cur->next;
        }
        return (ret);
    }
    
    /**
     * Try to unlink a definition. If not possible make it a NOOP
     *
     * @param ctxt  a Relax-NG parser context
     * @param cur  the definition to unlink
     * @param parent  the parent definition
     * @param prev  the previous sibling definition
     * @returns the new prev definition
     */
    static xmlRelaxNGDefinePtr
    xmlRelaxNGTryUnlink(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
                        xmlRelaxNGDefinePtr cur,
                        xmlRelaxNGDefinePtr parent, xmlRelaxNGDefinePtr prev)
    {
        if (prev != NULL) {
            prev->next = cur->next;
        } else {
            if (parent != NULL) {
                if (parent->content == cur)
                    parent->content = cur->next;
                else if (parent->attrs == cur)
                    parent->attrs = cur->next;
                else if (parent->nameClass == cur)
                    parent->nameClass = cur->next;
            } else {
                cur->type = XML_RELAXNG_NOOP;
                prev = cur;
            }
        }
        return (prev);
    }
    
    /**
     * Check for simplification of empty and notAllowed
     *
     * @param ctxt  a Relax-NG parser context
     * @param cur  grammar children nodes
     * @param parent  parent
     */
    static void
    xmlRelaxNGSimplify(xmlRelaxNGParserCtxtPtr ctxt,
                       xmlRelaxNGDefinePtr cur, xmlRelaxNGDefinePtr parent)
    {
        xmlRelaxNGDefinePtr prev = NULL;
    
        while (cur != NULL) {
            if ((cur->type == XML_RELAXNG_REF) ||
                (cur->type == XML_RELAXNG_PARENTREF)) {
                if (cur->depth != -3) {
                    cur->depth = -3;
                    xmlRelaxNGSimplify(ctxt, cur->content, cur);
                }
            } else if (cur->type == XML_RELAXNG_NOT_ALLOWED) {
                cur->parent = parent;
                if ((parent != NULL) &&
                    ((parent->type == XML_RELAXNG_ATTRIBUTE) ||
                     (parent->type == XML_RELAXNG_LIST) ||
                     (parent->type == XML_RELAXNG_GROUP) ||
                     (parent->type == XML_RELAXNG_INTERLEAVE) ||
                     (parent->type == XML_RELAXNG_ONEORMORE) ||
                     (parent->type == XML_RELAXNG_ZEROORMORE))) {
                    parent->type = XML_RELAXNG_NOT_ALLOWED;
                    break;
                }
                if ((parent != NULL) && (parent->type == XML_RELAXNG_CHOICE)) {
                    prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
                } else
                    prev = cur;
            } else if (cur->type == XML_RELAXNG_EMPTY) {
                cur->parent = parent;
                if ((parent != NULL) &&
                    ((parent->type == XML_RELAXNG_ONEORMORE) ||
                     (parent->type == XML_RELAXNG_ZEROORMORE))) {
                    parent->type = XML_RELAXNG_EMPTY;
                    break;
                }
                if ((parent != NULL) &&
                    ((parent->type == XML_RELAXNG_GROUP) ||
                     (parent->type == XML_RELAXNG_INTERLEAVE))) {
                    prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
                } else
                    prev = cur;
            } else {
                cur->parent = parent;
                if (cur->content != NULL)
                    xmlRelaxNGSimplify(ctxt, cur->content, cur);
                if ((cur->type != XML_RELAXNG_VALUE) && (cur->attrs != NULL))
                    xmlRelaxNGSimplify(ctxt, cur->attrs, cur);
                if (cur->nameClass != NULL)
                    xmlRelaxNGSimplify(ctxt, cur->nameClass, cur);
                /*
                 * On Elements, try to move attribute only generating rules on
                 * the attrs rules.
                 */
                if (cur->type == XML_RELAXNG_ELEMENT) {
                    int attronly;
                    xmlRelaxNGDefinePtr tmp, pre;
    
                    while (cur->content != NULL) {
                        attronly =
                            xmlRelaxNGGenerateAttributes(ctxt, cur->content);
                        if (attronly == 1) {
                            /*
                             * migrate cur->content to attrs
                             */
                            tmp = cur->content;
                            cur->content = tmp->next;
                            tmp->next = cur->attrs;
                            cur->attrs = tmp;
                        } else {
                            /*
                             * cur->content can generate elements or text
                             */
                            break;
                        }
                    }
                    pre = cur->content;
                    while ((pre != NULL) && (pre->next != NULL)) {
                        tmp = pre->next;
                        attronly = xmlRelaxNGGenerateAttributes(ctxt, tmp);
                        if (attronly == 1) {
                            /*
                             * migrate tmp to attrs
                             */
                            pre->next = tmp->next;
                            tmp->next = cur->attrs;
                            cur->attrs = tmp;
                        } else {
                            pre = tmp;
                        }
                    }
                }
                /*
                 * This may result in a simplification
                 */
                if ((cur->type == XML_RELAXNG_GROUP) ||
                    (cur->type == XML_RELAXNG_INTERLEAVE)) {
                    if (cur->content == NULL)
                        cur->type = XML_RELAXNG_EMPTY;
                    else if (cur->content->next == NULL) {
                        if ((parent == NULL) && (prev == NULL)) {
                            cur->type = XML_RELAXNG_NOOP;
                        } else if (prev == NULL) {
                            parent->content = cur->content;
                            cur->content->next = cur->next;
                            cur = cur->content;
                        } else {
                            cur->content->next = cur->next;
                            prev->next = cur->content;
                            cur = cur->content;
                        }
                    }
                }
                /*
                 * the current node may have been transformed back
                 */
                if ((cur->type == XML_RELAXNG_EXCEPT) &&
                    (cur->content != NULL) &&
                    (cur->content->type == XML_RELAXNG_NOT_ALLOWED)) {
                    prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
                } else if (cur->type == XML_RELAXNG_NOT_ALLOWED) {
                    if ((parent != NULL) &&
                        ((parent->type == XML_RELAXNG_ATTRIBUTE) ||
                         (parent->type == XML_RELAXNG_LIST) ||
                         (parent->type == XML_RELAXNG_GROUP) ||
                         (parent->type == XML_RELAXNG_INTERLEAVE) ||
                         (parent->type == XML_RELAXNG_ONEORMORE) ||
                         (parent->type == XML_RELAXNG_ZEROORMORE))) {
                        parent->type = XML_RELAXNG_NOT_ALLOWED;
                        break;
                    }
                    if ((parent != NULL) &&
                        (parent->type == XML_RELAXNG_CHOICE)) {
                        prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
                    } else
                        prev = cur;
                } else if (cur->type == XML_RELAXNG_EMPTY) {
                    if ((parent != NULL) &&
                        ((parent->type == XML_RELAXNG_ONEORMORE) ||
                         (parent->type == XML_RELAXNG_ZEROORMORE))) {
                        parent->type = XML_RELAXNG_EMPTY;
                        break;
                    }
                    if ((parent != NULL) &&
                        ((parent->type == XML_RELAXNG_GROUP) ||
                         (parent->type == XML_RELAXNG_INTERLEAVE) ||
                         (parent->type == XML_RELAXNG_CHOICE))) {
                        prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
                    } else
                        prev = cur;
                } else {
                    prev = cur;
                }
            }
            cur = cur->next;
        }
    }
    
    /**
     * Try to group 2 content types
     *
     * @param ct1  the first content type
     * @param ct2  the second content type
     * @returns the content type
     */
    static xmlRelaxNGContentType
    xmlRelaxNGGroupContentType(xmlRelaxNGContentType ct1,
                               xmlRelaxNGContentType ct2)
    {
        if ((ct1 == XML_RELAXNG_CONTENT_ERROR) ||
            (ct2 == XML_RELAXNG_CONTENT_ERROR))
            return (XML_RELAXNG_CONTENT_ERROR);
        if (ct1 == XML_RELAXNG_CONTENT_EMPTY)
            return (ct2);
        if (ct2 == XML_RELAXNG_CONTENT_EMPTY)
            return (ct1);
        if ((ct1 == XML_RELAXNG_CONTENT_COMPLEX) &&
            (ct2 == XML_RELAXNG_CONTENT_COMPLEX))
            return (XML_RELAXNG_CONTENT_COMPLEX);
        return (XML_RELAXNG_CONTENT_ERROR);
    }
    
    /**
     * Compute the max content-type
     *
     * @param ct1  the first content type
     * @param ct2  the second content type
     * @returns the content type
     */
    static xmlRelaxNGContentType
    xmlRelaxNGMaxContentType(xmlRelaxNGContentType ct1,
                             xmlRelaxNGContentType ct2)
    {
        if ((ct1 == XML_RELAXNG_CONTENT_ERROR) ||
            (ct2 == XML_RELAXNG_CONTENT_ERROR))
            return (XML_RELAXNG_CONTENT_ERROR);
        if ((ct1 == XML_RELAXNG_CONTENT_SIMPLE) ||
            (ct2 == XML_RELAXNG_CONTENT_SIMPLE))
            return (XML_RELAXNG_CONTENT_SIMPLE);
        if ((ct1 == XML_RELAXNG_CONTENT_COMPLEX) ||
            (ct2 == XML_RELAXNG_CONTENT_COMPLEX))
            return (XML_RELAXNG_CONTENT_COMPLEX);
        return (XML_RELAXNG_CONTENT_EMPTY);
    }
    
    /**
     * Check for rules in section 7.1 and 7.2
     *
     * @param ctxt  a Relax-NG parser context
     * @param cur  the current definition
     * @param flags  some accumulated flags
     * @param ptype  the parent type
     * @returns the content type of `cur`
     */
    static xmlRelaxNGContentType
    xmlRelaxNGCheckRules(xmlRelaxNGParserCtxtPtr ctxt,
                         xmlRelaxNGDefinePtr cur, int flags,
                         xmlRelaxNGType ptype)
    {
        int nflags;
        xmlRelaxNGContentType ret, tmp, val = XML_RELAXNG_CONTENT_EMPTY;
    
        while (cur != NULL) {
            ret = XML_RELAXNG_CONTENT_EMPTY;
            if ((cur->type == XML_RELAXNG_REF) ||
                (cur->type == XML_RELAXNG_PARENTREF)) {
               /*
                * This should actually be caught by list//element(ref) at the
                * element boundaries, c.f. Bug #159968 local refs are dropped
                * in step 4.19.
                */
    #if 0
                if (flags & XML_RELAXNG_IN_LIST) {
                    xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_LIST_REF,
                               "Found forbidden pattern list//ref\n", NULL,
                               NULL);
                }
    #endif
                if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
                    xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_DATA_EXCEPT_REF,
                               "Found forbidden pattern data/except//ref\n",
                               NULL, NULL);
                }
                if (cur->content == NULL) {
                    if (cur->type == XML_RELAXNG_PARENTREF)
                        xmlRngPErr(ctxt, cur->node, XML_RNGP_REF_NO_DEF,
                                   "Internal found no define for parent refs\n",
                                   NULL, NULL);
                    else
                        xmlRngPErr(ctxt, cur->node, XML_RNGP_REF_NO_DEF,
                                   "Internal found no define for ref %s\n",
                                   (cur->name ? cur->name: BAD_CAST "null"), NULL);
                }
                if (cur->depth > -4) {
                    cur->depth = -4;
                    ret = xmlRelaxNGCheckRules(ctxt, cur->content,
                                               flags, cur->type);
                    cur->depth = ret - 15;
                } else if (cur->depth == -4) {
                    ret = XML_RELAXNG_CONTENT_COMPLEX;
                } else {
                    ret = (xmlRelaxNGContentType) (cur->depth + 15);
                }
            } else if (cur->type == XML_RELAXNG_ELEMENT) {
                /*
                 * The 7.3 Attribute derivation rule for groups is plugged there
                 */
                xmlRelaxNGCheckGroupAttrs(ctxt, cur);
                if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
                    xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_DATA_EXCEPT_ELEM,
                               "Found forbidden pattern data/except//element(ref)\n",
                               NULL, NULL);
                }
                if (flags & XML_RELAXNG_IN_LIST) {
                    xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_LIST_ELEM,
                               "Found forbidden pattern list//element(ref)\n",
                               NULL, NULL);
                }
                if (flags & XML_RELAXNG_IN_ATTRIBUTE) {
                    xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_ATTR_ELEM,
                               "Found forbidden pattern attribute//element(ref)\n",
                               NULL, NULL);
                }
                if (flags & XML_RELAXNG_IN_ATTRIBUTE) {
                    xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_ATTR_ELEM,
                               "Found forbidden pattern attribute//element(ref)\n",
                               NULL, NULL);
                }
                /*
                 * reset since in the simple form elements are only child
                 * of grammar/define
                 */
                nflags = 0;
                ret =
                    xmlRelaxNGCheckRules(ctxt, cur->attrs, nflags, cur->type);
                if (ret != XML_RELAXNG_CONTENT_EMPTY) {
                    xmlRngPErr(ctxt, cur->node, XML_RNGP_ELEM_CONTENT_EMPTY,
                               "Element %s attributes have a content type error\n",
                               cur->name, NULL);
                }
                ret =
                    xmlRelaxNGCheckRules(ctxt, cur->content, nflags,
                                         cur->type);
                if (ret == XML_RELAXNG_CONTENT_ERROR) {
                    xmlRngPErr(ctxt, cur->node, XML_RNGP_ELEM_CONTENT_ERROR,
                               "Element %s has a content type error\n",
                               cur->name, NULL);
                } else {
                    ret = XML_RELAXNG_CONTENT_COMPLEX;
                }
            } else if (cur->type == XML_RELAXNG_ATTRIBUTE) {
                if (flags & XML_RELAXNG_IN_ATTRIBUTE) {
                    xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_ATTR_ATTR,
                               "Found forbidden pattern attribute//attribute\n",
                               NULL, NULL);
                }
                if (flags & XML_RELAXNG_IN_LIST) {
                    xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_LIST_ATTR,
                               "Found forbidden pattern list//attribute\n",
                               NULL, NULL);
                }
                if (flags & XML_RELAXNG_IN_OOMGROUP) {
                    xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_ONEMORE_GROUP_ATTR,
                               "Found forbidden pattern oneOrMore//group//attribute\n",
                               NULL, NULL);
                }
                if (flags & XML_RELAXNG_IN_OOMINTERLEAVE) {
                    xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_ONEMORE_INTERLEAVE_ATTR,
                               "Found forbidden pattern oneOrMore//interleave//attribute\n",
                               NULL, NULL);
                }
                if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
                    xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_DATA_EXCEPT_ATTR,
                               "Found forbidden pattern data/except//attribute\n",
                               NULL, NULL);
                }
                if (flags & XML_RELAXNG_IN_START) {
                    xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_START_ATTR,
                               "Found forbidden pattern start//attribute\n",
                               NULL, NULL);
                }
                if ((!(flags & XML_RELAXNG_IN_ONEORMORE))
                    && cur->name == NULL
                    /* following is checking alternative name class readiness
                       in case it went the "choice" route */
                    && cur->nameClass == NULL) {
                    if (cur->ns == NULL) {
                        xmlRngPErr(ctxt, cur->node, XML_RNGP_ANYNAME_ATTR_ANCESTOR,
                                   "Found anyName attribute without oneOrMore ancestor\n",
                                   NULL, NULL);
                    } else {
                        xmlRngPErr(ctxt, cur->node, XML_RNGP_NSNAME_ATTR_ANCESTOR,
                                   "Found nsName attribute without oneOrMore ancestor\n",
                                   NULL, NULL);
                    }
                }
                nflags = flags | XML_RELAXNG_IN_ATTRIBUTE;
                xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
                ret = XML_RELAXNG_CONTENT_EMPTY;
            } else if ((cur->type == XML_RELAXNG_ONEORMORE) ||
                       (cur->type == XML_RELAXNG_ZEROORMORE)) {
                if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
                    xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_DATA_EXCEPT_ONEMORE,
                               "Found forbidden pattern data/except//oneOrMore\n",
                               NULL, NULL);
                }
                if (flags & XML_RELAXNG_IN_START) {
                    xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_START_ONEMORE,
                               "Found forbidden pattern start//oneOrMore\n",
                               NULL, NULL);
                }
                nflags = flags | XML_RELAXNG_IN_ONEORMORE;
                ret =
                    xmlRelaxNGCheckRules(ctxt, cur->content, nflags,
                                         cur->type);
                ret = xmlRelaxNGGroupContentType(ret, ret);
            } else if (cur->type == XML_RELAXNG_LIST) {
                if (flags & XML_RELAXNG_IN_LIST) {
                    xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_LIST_LIST,
                               "Found forbidden pattern list//list\n", NULL,
                               NULL);
                }
                if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
                    xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_DATA_EXCEPT_LIST,
                               "Found forbidden pattern data/except//list\n",
                               NULL, NULL);
                }
                if (flags & XML_RELAXNG_IN_START) {
                    xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_START_LIST,
                               "Found forbidden pattern start//list\n", NULL,
                               NULL);
                }
                nflags = flags | XML_RELAXNG_IN_LIST;
                ret =
                    xmlRelaxNGCheckRules(ctxt, cur->content, nflags,
                                         cur->type);
            } else if (cur->type == XML_RELAXNG_GROUP) {
                if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
                    xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_DATA_EXCEPT_GROUP,
                               "Found forbidden pattern data/except//group\n",
                               NULL, NULL);
                }
                if (flags & XML_RELAXNG_IN_START) {
                    xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_START_GROUP,
                               "Found forbidden pattern start//group\n", NULL,
                               NULL);
                }
                if (flags & XML_RELAXNG_IN_ONEORMORE)
                    nflags = flags | XML_RELAXNG_IN_OOMGROUP;
                else
                    nflags = flags;
                ret =
                    xmlRelaxNGCheckRules(ctxt, cur->content, nflags,
                                         cur->type);
                /*
                 * The 7.3 Attribute derivation rule for groups is plugged there
                 */
                xmlRelaxNGCheckGroupAttrs(ctxt, cur);
            } else if (cur->type == XML_RELAXNG_INTERLEAVE) {
                if (flags & XML_RELAXNG_IN_LIST) {
                    xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_LIST_INTERLEAVE,
                               "Found forbidden pattern list//interleave\n",
                               NULL, NULL);
                }
                if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
                    xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_DATA_EXCEPT_INTERLEAVE,
                               "Found forbidden pattern data/except//interleave\n",
                               NULL, NULL);
                }
                if (flags & XML_RELAXNG_IN_START) {
                    xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_DATA_EXCEPT_INTERLEAVE,
                               "Found forbidden pattern start//interleave\n",
                               NULL, NULL);
                }
                if (flags & XML_RELAXNG_IN_ONEORMORE)
                    nflags = flags | XML_RELAXNG_IN_OOMINTERLEAVE;
                else
                    nflags = flags;
                ret =
                    xmlRelaxNGCheckRules(ctxt, cur->content, nflags,
                                         cur->type);
            } else if (cur->type == XML_RELAXNG_EXCEPT) {
                if ((cur->parent != NULL) &&
                    (cur->parent->type == XML_RELAXNG_DATATYPE))
                    nflags = flags | XML_RELAXNG_IN_DATAEXCEPT;
                else
                    nflags = flags;
                ret =
                    xmlRelaxNGCheckRules(ctxt, cur->content, nflags,
                                         cur->type);
            } else if (cur->type == XML_RELAXNG_DATATYPE) {
                if (flags & XML_RELAXNG_IN_START) {
                    xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_START_DATA,
                               "Found forbidden pattern start//data\n", NULL,
                               NULL);
                }
                xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type);
                ret = XML_RELAXNG_CONTENT_SIMPLE;
            } else if (cur->type == XML_RELAXNG_VALUE) {
                if (flags & XML_RELAXNG_IN_START) {
                    xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_START_VALUE,
                               "Found forbidden pattern start//value\n", NULL,
                               NULL);
                }
                xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type);
                ret = XML_RELAXNG_CONTENT_SIMPLE;
            } else if (cur->type == XML_RELAXNG_TEXT) {
                if (flags & XML_RELAXNG_IN_LIST) {
                    xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_LIST_TEXT,
                               "Found forbidden pattern list//text\n", NULL,
                               NULL);
                }
                if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
                    xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_DATA_EXCEPT_TEXT,
                               "Found forbidden pattern data/except//text\n",
                               NULL, NULL);
                }
                if (flags & XML_RELAXNG_IN_START) {
                    xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_START_TEXT,
                               "Found forbidden pattern start//text\n", NULL,
                               NULL);
                }
                ret = XML_RELAXNG_CONTENT_COMPLEX;
            } else if (cur->type == XML_RELAXNG_EMPTY) {
                if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
                    xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_DATA_EXCEPT_EMPTY,
                               "Found forbidden pattern data/except//empty\n",
                               NULL, NULL);
                }
                if (flags & XML_RELAXNG_IN_START) {
                    xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_START_EMPTY,
                               "Found forbidden pattern start//empty\n", NULL,
                               NULL);
                }
                ret = XML_RELAXNG_CONTENT_EMPTY;
            } else if (cur->type == XML_RELAXNG_CHOICE) {
                xmlRelaxNGCheckChoiceDeterminism(ctxt, cur);
                ret =
                    xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type);
            } else {
                ret =
                    xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type);
            }
            cur = cur->next;
            if (ptype == XML_RELAXNG_GROUP) {
                val = xmlRelaxNGGroupContentType(val, ret);
            } else if (ptype == XML_RELAXNG_INTERLEAVE) {
                /*
                 * TODO: scan complain that tmp is never used, seems on purpose
                 *       need double-checking
                 */
                tmp = xmlRelaxNGGroupContentType(val, ret);
                if (tmp != XML_RELAXNG_CONTENT_ERROR)
                    tmp = xmlRelaxNGMaxContentType(val, ret);
            } else if (ptype == XML_RELAXNG_CHOICE) {
                val = xmlRelaxNGMaxContentType(val, ret);
            } else if (ptype == XML_RELAXNG_LIST) {
                val = XML_RELAXNG_CONTENT_SIMPLE;
            } else if (ptype == XML_RELAXNG_EXCEPT) {
                if (ret == XML_RELAXNG_CONTENT_ERROR)
                    val = XML_RELAXNG_CONTENT_ERROR;
                else
                    val = XML_RELAXNG_CONTENT_SIMPLE;
            } else {
                val = xmlRelaxNGGroupContentType(val, ret);
            }
    
        }
        return (val);
    }
    
    /**
     * parse a Relax-NG `<grammar>` node
     *
     * @param ctxt  a Relax-NG parser context
     * @param nodes  grammar children nodes
     * @returns the internal xmlRelaxNGGrammarPtr built or
     *         NULL in case of error
     */
    static xmlRelaxNGGrammarPtr
    xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes)
    {
        xmlRelaxNGGrammarPtr ret, tmp, old;
    
        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;
        if (ctxt->grammar == NULL) {
            xmlRngPErr(ctxt, nodes, XML_RNGP_GRAMMAR_CONTENT,
                       "Failed to parse <grammar> content\n", NULL, NULL);
        } else if (ctxt->grammar->start == NULL) {
            xmlRngPErr(ctxt, nodes, XML_RNGP_GRAMMAR_NO_START,
                       "Element <grammar> has no <start>\n", NULL, NULL);
        }
    
        /*
         * Apply 4.17 merging rules to defines and starts
         */
        xmlRelaxNGCombineStart(ctxt, ret);
        if (ret->defs != NULL) {
            xmlHashScan(ret->defs, xmlRelaxNGCheckCombine, ctxt);
        }
    
        /*
         * link together defines and refs in this grammar
         */
        if (ret->refs != NULL) {
            xmlHashScan(ret->refs, xmlRelaxNGCheckReference, ctxt);
        }
    
    
        /* @@@@ */
    
        ctxt->grammar = old;
        return (ret);
    }
    
    /**
     * parse a Relax-NG definition resource and build an internal
     * xmlRelaxNG structure which can be used to validate instances.
     *
     * @param ctxt  a Relax-NG parser context
     * @param node  the root node of the RelaxNG schema
     * @returns the internal XML RelaxNG structure built or
     *         NULL in case of error
     */
    static xmlRelaxNGPtr
    xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
    {
        xmlRelaxNGPtr schema = NULL;
        const xmlChar *olddefine;
        xmlRelaxNGGrammarPtr old;
    
        if ((ctxt == NULL) || (node == NULL))
            return (NULL);
    
        schema = xmlRelaxNGNewRelaxNG(ctxt);
        if (schema == NULL)
            return (NULL);
    
        olddefine = ctxt->define;
        ctxt->define = NULL;
        if (IS_RELAXNG(node, "grammar")) {
            schema->topgrammar = xmlRelaxNGParseGrammar(ctxt, node->children);
            if (schema->topgrammar == NULL) {
                xmlRelaxNGFree(schema);
                return (NULL);
            }
        } else {
            xmlRelaxNGGrammarPtr tmp, ret;
    
            schema->topgrammar = ret = xmlRelaxNGNewGrammar(ctxt);
            if (schema->topgrammar == NULL) {
                xmlRelaxNGFree(schema);
                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;
            xmlRelaxNGParseStart(ctxt, node);
            if (old != NULL)
                ctxt->grammar = old;
        }
        ctxt->define = olddefine;
        if (schema->topgrammar->start != NULL) {
            xmlRelaxNGCheckCycles(ctxt, schema->topgrammar->start, 0);
            if ((ctxt->flags & XML_RELAXNG_IN_EXTERNALREF) == 0) {
                xmlRelaxNGSimplify(ctxt, schema->topgrammar->start, NULL);
                while ((schema->topgrammar->start != NULL) &&
                       (schema->topgrammar->start->type == XML_RELAXNG_NOOP) &&
                       (schema->topgrammar->start->next != NULL))
                    schema->topgrammar->start =
                        schema->topgrammar->start->content;
                xmlRelaxNGCheckRules(ctxt, schema->topgrammar->start,
                                     XML_RELAXNG_IN_START, XML_RELAXNG_NOOP);
            }
        }
    
        return (schema);
    }
    
    /************************************************************************
     *									*
     *			Reading RelaxNGs				*
     *									*
     ************************************************************************/
    
    /**
     * Create an XML RelaxNGs parse context for that file/resource expected
     * to contain an XML RelaxNGs file.
     *
     * @param URL  the location of the schema
     * @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) {
            xmlRngPErrMemory(NULL);
            return (NULL);
        }
        memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
        ret->URL = xmlStrdup((const xmlChar *) URL);
        return (ret);
    }
    
    /**
     * Create an XML RelaxNGs parse context for that memory buffer expected
     * to contain an XML RelaxNGs file.
     *
     * @param buffer  a pointer to a char array containing the schemas
     * @param size  the size of the array
     * @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) {
            xmlRngPErrMemory(NULL);
            return (NULL);
        }
        memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
        ret->buffer = buffer;
        ret->size = size;
        return (ret);
    }
    
    /**
     * Create an XML RelaxNGs parser context for that document.
     * Note: since the process of compiling a RelaxNG schemas modifies the
     *       document, the `doc` parameter is duplicated internally.
     *
     * @param doc  a preparsed document tree
     * @returns the parser context or NULL in case of error
     */
    xmlRelaxNGParserCtxtPtr
    xmlRelaxNGNewDocParserCtxt(xmlDocPtr doc)
    {
        xmlRelaxNGParserCtxtPtr ret;
        xmlDocPtr copy;
    
        if (doc == NULL)
            return (NULL);
        copy = xmlCopyDoc(doc, 1);
        if (copy == NULL)
            return (NULL);
    
        ret =
            (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
        if (ret == NULL) {
            xmlRngPErrMemory(NULL);
            xmlFreeDoc(copy);
            return (NULL);
        }
        memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
        ret->document = copy;
        ret->freedoc = 1;
        ret->userData = xmlGenericErrorContext;
        return (ret);
    }
    
    /**
     * Free the resources associated to the schema parser context
     *
     * @param ctxt  the schema parser context
     */
    void
    xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxtPtr ctxt)
    {
        if (ctxt == NULL)
            return;
        if (ctxt->URL != NULL)
            xmlFree(ctxt->URL);
        if (ctxt->doc != NULL)
            xmlRelaxNGFreeDocument(ctxt->doc);
        if (ctxt->interleaves != NULL)
            xmlHashFree(ctxt->interleaves, NULL);
        if (ctxt->documents != NULL)
            xmlRelaxNGFreeDocumentList(ctxt->documents);
        if (ctxt->includes != NULL)
            xmlRelaxNGFreeIncludeList(ctxt->includes);
        if (ctxt->docTab != NULL)
            xmlFree(ctxt->docTab);
        if (ctxt->incTab != NULL)
            xmlFree(ctxt->incTab);
        if (ctxt->defTab != NULL) {
            int i;
    
            for (i = 0; i < ctxt->defNr; i++)
                xmlRelaxNGFreeDefine(ctxt->defTab[i]);
            xmlFree(ctxt->defTab);
        }
        if ((ctxt->document != NULL) && (ctxt->freedoc))
            xmlFreeDoc(ctxt->document);
        xmlFree(ctxt);
    }
    
    /**
     * Removes the leading and ending spaces of the value
     * The string is modified "in situ"
     *
     * @param value  a value
     */
    static void
    xmlRelaxNGNormExtSpace(xmlChar * value)
    {
        xmlChar *start = value;
        xmlChar *cur = value;
    
        if (value == NULL)
            return;
    
        while (IS_BLANK_CH(*cur))
            cur++;
        if (cur == start) {
            do {
                while ((*cur != 0) && (!IS_BLANK_CH(*cur)))
                    cur++;
                if (*cur == 0)
                    return;
                start = cur;
                while (IS_BLANK_CH(*cur))
                    cur++;
                if (*cur == 0) {
                    *start = 0;
                    return;
                }
            } while (1);
        } else {
            do {
                while ((*cur != 0) && (!IS_BLANK_CH(*cur)))
                    *start++ = *cur++;
                if (*cur == 0) {
                    *start = 0;
                    return;
                }
                /* don't try to normalize the inner spaces */
                while (IS_BLANK_CH(*cur))
                    cur++;
                if (*cur == 0) {
                    *start = 0;
                    return;
                }
                *start++ = *cur++;
            } while (1);
        }
    }
    
    /**
     * Check all the attributes on the given node
     *
     * @param ctxt  a Relax-NG parser context
     * @param node  a Relax-NG node
     */
    static void
    xmlRelaxNGCleanupAttributes(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
    {
        xmlAttrPtr cur, next;
    
        cur = node->properties;
        while (cur != NULL) {
            next = cur->next;
            if ((cur->ns == NULL) ||
                (xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
                if (xmlStrEqual(cur->name, BAD_CAST "name")) {
                    if ((!xmlStrEqual(node->name, BAD_CAST "element")) &&
                        (!xmlStrEqual(node->name, BAD_CAST "attribute")) &&
                        (!xmlStrEqual(node->name, BAD_CAST "ref")) &&
                        (!xmlStrEqual(node->name, BAD_CAST "parentRef")) &&
                        (!xmlStrEqual(node->name, BAD_CAST "param")) &&
                        (!xmlStrEqual(node->name, BAD_CAST "define"))) {
                        xmlRngPErr(ctxt, node, XML_RNGP_FORBIDDEN_ATTRIBUTE,
                                   "Attribute %s is not allowed on %s\n",
                                   cur->name, node->name);
                    }
                } else if (xmlStrEqual(cur->name, BAD_CAST "type")) {
                    if ((!xmlStrEqual(node->name, BAD_CAST "value")) &&
                        (!xmlStrEqual(node->name, BAD_CAST "data"))) {
                        xmlRngPErr(ctxt, node, XML_RNGP_FORBIDDEN_ATTRIBUTE,
                                   "Attribute %s is not allowed on %s\n",
                                   cur->name, node->name);
                    }
                } else if (xmlStrEqual(cur->name, BAD_CAST "href")) {
                    if ((!xmlStrEqual(node->name, BAD_CAST "externalRef")) &&
                        (!xmlStrEqual(node->name, BAD_CAST "include"))) {
                        xmlRngPErr(ctxt, node, XML_RNGP_FORBIDDEN_ATTRIBUTE,
                                   "Attribute %s is not allowed on %s\n",
                                   cur->name, node->name);
                    }
                } else if (xmlStrEqual(cur->name, BAD_CAST "combine")) {
                    if ((!xmlStrEqual(node->name, BAD_CAST "start")) &&
                        (!xmlStrEqual(node->name, BAD_CAST "define"))) {
                        xmlRngPErr(ctxt, node, XML_RNGP_FORBIDDEN_ATTRIBUTE,
                                   "Attribute %s is not allowed on %s\n",
                                   cur->name, node->name);
                    }
                } else if (xmlStrEqual(cur->name, BAD_CAST "datatypeLibrary")) {
                    xmlChar *val;
                    xmlURIPtr uri;
    
                    val = xmlNodeListGetString(node->doc, cur->children, 1);
                    if (val != NULL) {
                        if (val[0] != 0) {
                            uri = xmlParseURI((const char *) val);
                            if (uri == NULL) {
                                xmlRngPErr(ctxt, node, XML_RNGP_INVALID_URI,
                                           "Attribute %s contains invalid URI %s\n",
                                           cur->name, val);
                            } else {
                                if (uri->scheme == NULL) {
                                    xmlRngPErr(ctxt, node, XML_RNGP_URI_NOT_ABSOLUTE,
                                               "Attribute %s URI %s is not absolute\n",
                                               cur->name, val);
                                }
                                if (uri->fragment != NULL) {
                                    xmlRngPErr(ctxt, node, XML_RNGP_URI_FRAGMENT,
                                               "Attribute %s URI %s has a fragment ID\n",
                                               cur->name, val);
                                }
                                xmlFreeURI(uri);
                            }
                        }
                        xmlFree(val);
                    }
                } else if (!xmlStrEqual(cur->name, BAD_CAST "ns")) {
                    xmlRngPErr(ctxt, node, XML_RNGP_UNKNOWN_ATTRIBUTE,
                               "Unknown attribute %s on %s\n", cur->name,
                               node->name);
                }
            }
            cur = next;
        }
    }
    
    /**
     * Cleanup the subtree from unwanted nodes for parsing, resolve
     * Include and externalRef lookups.
     *
     * @param ctxt  a Relax-NG parser context
     * @param root  an xmlNodePtr subtree
     */
    static void
    xmlRelaxNGCleanupTree(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr root)
    {
        xmlNodePtr cur, delete;
    
        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))) {
                    if ((cur->parent != NULL) &&
                        (cur->parent->type == XML_ELEMENT_NODE) &&
                        ((xmlStrEqual(cur->parent->name, BAD_CAST "name")) ||
                         (xmlStrEqual(cur->parent->name, BAD_CAST "value")) ||
                         (xmlStrEqual(cur->parent->name, BAD_CAST "param")))) {
                        xmlRngPErr(ctxt, cur, XML_RNGP_FOREIGN_ELEMENT,
                                   "element %s doesn't allow foreign elements\n",
                                   cur->parent->name, NULL);
                    }
                    delete = cur;
                    goto skip_children;
                } else {
                    xmlRelaxNGCleanupAttributes(ctxt, cur);
                    if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
                        xmlChar *href, *ns, *base, *URL;
                        xmlRelaxNGDocumentPtr docu;
                        xmlNodePtr tmp;
    		    xmlURIPtr uri;
    
                        ns = xmlGetProp(cur, BAD_CAST "ns");
                        if (ns == NULL) {
                            tmp = cur->parent;
                            while ((tmp != NULL) &&
                                   (tmp->type == XML_ELEMENT_NODE)) {
                                ns = xmlGetProp(tmp, BAD_CAST "ns");
                                if (ns != NULL)
                                    break;
                                tmp = tmp->parent;
                            }
                        }
                        href = xmlGetProp(cur, BAD_CAST "href");
                        if (href == NULL) {
                            xmlRngPErr(ctxt, cur, XML_RNGP_MISSING_HREF,
                                       "xmlRelaxNGParse: externalRef has no href attribute\n",
                                       NULL, NULL);
                            if (ns != NULL)
                                xmlFree(ns);
                            delete = cur;
                            goto skip_children;
                        }
    		    uri = xmlParseURI((const char *) href);
    		    if (uri == NULL) {
                            xmlRngPErr(ctxt, cur, XML_RNGP_HREF_ERROR,
                                       "Incorrect URI for externalRef %s\n",
                                       href, NULL);
                            if (ns != NULL)
                                xmlFree(ns);
                            if (href != NULL)
                                xmlFree(href);
                            delete = cur;
                            goto skip_children;
    		    }
    		    if (uri->fragment != NULL) {
                            xmlRngPErr(ctxt, cur, XML_RNGP_HREF_ERROR,
    			       "Fragment forbidden in URI for externalRef %s\n",
                                       href, NULL);
                            if (ns != NULL)
                                xmlFree(ns);
    		        xmlFreeURI(uri);
                            if (href != NULL)
                                xmlFree(href);
                            delete = cur;
                            goto skip_children;
    		    }
    		    xmlFreeURI(uri);
                        base = xmlNodeGetBase(cur->doc, cur);
                        URL = xmlBuildURI(href, base);
                        if (URL == NULL) {
                            xmlRngPErr(ctxt, cur, XML_RNGP_HREF_ERROR,
                                       "Failed to compute URL for externalRef %s\n",
                                       href, NULL);
                            if (ns != NULL)
                                xmlFree(ns);
                            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);
                        docu = xmlRelaxNGLoadExternalRef(ctxt, URL, ns);
                        if (docu == NULL) {
                            xmlRngPErr(ctxt, cur, XML_RNGP_EXTERNAL_REF_FAILURE,
                                       "Failed to load externalRef %s\n", URL,
                                       NULL);
                            if (ns != NULL)
                                xmlFree(ns);
                            xmlFree(URL);
                            delete = cur;
                            goto skip_children;
                        }
                        if (ns != NULL)
                            xmlFree(ns);
                        xmlFree(URL);
                        cur->psvi = docu;
                    } else if (xmlStrEqual(cur->name, BAD_CAST "include")) {
                        xmlChar *href, *ns, *base, *URL;
                        xmlRelaxNGIncludePtr incl;
                        xmlNodePtr tmp;
    
                        href = xmlGetProp(cur, BAD_CAST "href");
                        if (href == NULL) {
                            xmlRngPErr(ctxt, cur, XML_RNGP_MISSING_HREF,
                                       "xmlRelaxNGParse: include has no href attribute\n",
                                       NULL, NULL);
                            delete = cur;
                            goto skip_children;
                        }
                        base = xmlNodeGetBase(cur->doc, cur);
                        URL = xmlBuildURI(href, base);
                        if (URL == NULL) {
                            xmlRngPErr(ctxt, cur, XML_RNGP_HREF_ERROR,
                                       "Failed to compute URL for include %s\n",
                                       href, NULL);
                            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);
                        ns = xmlGetProp(cur, BAD_CAST "ns");
                        if (ns == NULL) {
                            tmp = cur->parent;
                            while ((tmp != NULL) &&
                                   (tmp->type == XML_ELEMENT_NODE)) {
                                ns = xmlGetProp(tmp, BAD_CAST "ns");
                                if (ns != NULL)
                                    break;
                                tmp = tmp->parent;
                            }
                        }
                        incl = xmlRelaxNGLoadInclude(ctxt, URL, cur, ns);
                        if (ns != NULL)
                            xmlFree(ns);
                        if (incl == NULL) {
                            xmlRngPErr(ctxt, cur, XML_RNGP_INCLUDE_FAILURE,
                                       "Failed to load include %s\n", URL,
                                       NULL);
                            xmlFree(URL);
                            delete = cur;
                            goto skip_children;
                        }
                        xmlFree(URL);
                        cur->psvi = incl;
                    } else if ((xmlStrEqual(cur->name, BAD_CAST "element")) ||
                               (xmlStrEqual(cur->name, BAD_CAST "attribute")))
                    {
                        xmlChar *name, *ns;
                        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 = xmlNewDocNode(cur->doc, cur->ns,
    			                         BAD_CAST "name", NULL);
                                if (node != NULL) {
                                    xmlAddPrevSibling(cur->children, node);
                                    text = xmlNewDocText(node->doc, name);
                                    xmlAddChild(node, text);
                                    text = node;
                                }
                            }
                            if (text == NULL) {
                                xmlRngPErr(ctxt, cur, XML_RNGP_CREATE_FAILURE,
                                           "Failed to create a name %s element\n",
                                           name, NULL);
                            }
                            xmlUnsetProp(cur, BAD_CAST "name");
                            xmlFree(name);
                            ns = xmlGetProp(cur, BAD_CAST "ns");
                            if (ns != NULL) {
                                if (text != NULL) {
                                    xmlSetProp(text, BAD_CAST "ns", ns);
                                    /* xmlUnsetProp(cur, BAD_CAST "ns"); */
                                }
                                xmlFree(ns);
                            } else if (xmlStrEqual(cur->name,
                                                   BAD_CAST "attribute")) {
                                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) {
                                        xmlRngPErr(ctxt, cur,
                                                   XML_RNGP_PREFIX_UNDEFINED,
                                                   "xmlRelaxNGParse: no namespace for prefix %s\n",
                                                   prefix, NULL);
                                    } else {
                                        xmlSetProp(cur, BAD_CAST "ns",
                                                   ns->href);
                                        xmlNodeSetContent(cur, local);
                                    }
                                    xmlFree(local);
                                    xmlFree(prefix);
                                }
                                xmlFree(name);
                            }
                        }
                        /*
                         * 4.16
                         */
                        if (xmlStrEqual(cur->name, BAD_CAST "nsName")) {
                            if (ctxt->flags & XML_RELAXNG_IN_NSEXCEPT) {
                                xmlRngPErr(ctxt, cur,
                                           XML_RNGP_PAT_NSNAME_EXCEPT_NSNAME,
                                           "Found nsName/except//nsName forbidden construct\n",
                                           NULL, NULL);
                            }
                        }
                    } else if ((xmlStrEqual(cur->name, BAD_CAST "except")) &&
                               (cur != root)) {
                        int oldflags = ctxt->flags;
    
                        /*
                         * 4.16
                         */
                        if ((cur->parent != NULL) &&
                            (xmlStrEqual
                             (cur->parent->name, BAD_CAST "anyName"))) {
                            ctxt->flags |= XML_RELAXNG_IN_ANYEXCEPT;
                            xmlRelaxNGCleanupTree(ctxt, cur);
                            ctxt->flags = oldflags;
                            goto skip_children;
                        } else if ((cur->parent != NULL) &&
                                   (xmlStrEqual
                                    (cur->parent->name, BAD_CAST "nsName"))) {
                            ctxt->flags |= XML_RELAXNG_IN_NSEXCEPT;
                            xmlRelaxNGCleanupTree(ctxt, cur);
                            ctxt->flags = oldflags;
                            goto skip_children;
                        }
                    } else if (xmlStrEqual(cur->name, BAD_CAST "anyName")) {
                        /*
                         * 4.16
                         */
                        if (ctxt->flags & XML_RELAXNG_IN_ANYEXCEPT) {
                            xmlRngPErr(ctxt, cur,
                                       XML_RNGP_PAT_ANYNAME_EXCEPT_ANYNAME,
                                       "Found anyName/except//anyName forbidden construct\n",
                                       NULL, NULL);
                        } else if (ctxt->flags & XML_RELAXNG_IN_NSEXCEPT) {
                            xmlRngPErr(ctxt, cur,
                                       XML_RNGP_PAT_NSNAME_EXCEPT_ANYNAME,
                                       "Found nsName/except//anyName forbidden construct\n",
                                       NULL, NULL);
                        }
                    }
                    /*
                     * This is not an else since "include" is transformed
                     * into a div
                     */
                    if (xmlStrEqual(cur->name, BAD_CAST "div")) {
                        xmlChar *ns;
                        xmlNodePtr child, ins, tmp;
    
                        /*
                         * implements rule 4.11
                         */
    
                        ns = xmlGetProp(cur, BAD_CAST "ns");
    
                        child = cur->children;
                        ins = cur;
                        while (child != NULL) {
                            if (ns != NULL) {
                                if (!xmlHasProp(child, BAD_CAST "ns")) {
                                    xmlSetProp(child, BAD_CAST "ns", ns);
                                }
                            }
                            tmp = child->next;
                            xmlUnlinkNode(child);
                            ins = xmlAddNextSibling(ins, child);
                            child = tmp;
                        }
                        if (ns != NULL)
                            xmlFree(ns);
    		    /*
    		     * Since we are about to delete cur, if its nsDef is non-NULL we
    		     * need to preserve it (it contains the ns definitions for the
    		     * children we just moved).  We'll just stick it on to the end
    		     * of cur->parent's list, since it's never going to be re-serialized
    		     * (bug 143738).
    		     */
    		    if ((cur->nsDef != NULL) && (cur->parent != NULL)) {
    			xmlNsPtr parDef = (xmlNsPtr)&cur->parent->nsDef;
    			while (parDef->next != NULL)
    			    parDef = parDef->next;
    			parDef->next = cur->nsDef;
    			cur->nsDef = NULL;
    		    }
                        delete = cur;
                        goto skip_children;
                    }
                }
            }
            /*
             * Simplification 4.2 whitespaces
             */
            else if ((cur->type == XML_TEXT_NODE) ||
                     (cur->type == XML_CDATA_SECTION_NODE)) {
                if (IS_BLANK_NODE(cur)) {
                    if ((cur->parent != NULL) &&
    		    (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 {
                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;
        }
    }
    
    /**
     * Cleanup the document from unwanted nodes for parsing, resolve
     * Include and externalRef lookups.
     *
     * @param ctxt  a Relax-NG parser context
     * @param doc  an xmldocPtr document pointer
     * @returns the cleaned up document or NULL in case of error
     */
    static xmlDocPtr
    xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt, xmlDocPtr doc)
    {
        xmlNodePtr root;
    
        /*
         * Extract the root
         */
        root = xmlDocGetRootElement(doc);
        if (root == NULL) {
            xmlRngPErr(ctxt, (xmlNodePtr) doc, XML_RNGP_EMPTY, "xmlRelaxNGParse: %s is empty\n",
                       ctxt->URL, NULL);
            return (NULL);
        }
        xmlRelaxNGCleanupTree(ctxt, root);
        return (doc);
    }
    
    /**
     * parse a schema definition resource and build an internal
     * XML Schema structure which can be used to validate instances.
     *
     * @param ctxt  a Relax-NG parser context
     * @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;
    
        xmlRelaxNGInitTypes();
    
        if (ctxt == NULL)
            return (NULL);
    
        /*
         * First step is to parse the input document into an DOM/Infoset
         */
        if (ctxt->URL != NULL) {
            doc = xmlRelaxReadFile(ctxt, (const char *) ctxt->URL);
            if (doc == NULL) {
                xmlRngPErr(ctxt, NULL, XML_RNGP_PARSE_ERROR,
                           "xmlRelaxNGParse: could not load %s\n", ctxt->URL,
                           NULL);
                return (NULL);
            }
        } else if (ctxt->buffer != NULL) {
            doc = xmlRelaxReadMemory(ctxt, ctxt->buffer, ctxt->size);
            if (doc == NULL) {
                xmlRngPErr(ctxt, NULL, XML_RNGP_PARSE_ERROR,
                           "xmlRelaxNGParse: could not parse schemas\n", NULL,
                           NULL);
                return (NULL);
            }
            doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
            ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
        } else if (ctxt->document != NULL) {
            doc = ctxt->document;
        } else {
            xmlRngPErr(ctxt, NULL, XML_RNGP_EMPTY,
                       "xmlRelaxNGParse: nothing to parse\n", NULL, NULL);
            return (NULL);
        }
        ctxt->document = doc;
    
        /*
         * Some preprocessing of the document content
         */
        doc = xmlRelaxNGCleanupDoc(ctxt, doc);
        if (doc == NULL) {
            xmlFreeDoc(ctxt->document);
            ctxt->document = NULL;
            return (NULL);
        }
    
        /*
         * Then do the parsing for good
         */
        root = xmlDocGetRootElement(doc);
        if (root == NULL) {
            xmlRngPErr(ctxt, (xmlNodePtr) doc,
    	           XML_RNGP_EMPTY, "xmlRelaxNGParse: %s is empty\n",
                       (ctxt->URL ? ctxt->URL : BAD_CAST "schemas"), NULL);
    
            xmlFreeDoc(ctxt->document);
            ctxt->document = NULL;
            return (NULL);
        }
        ret = xmlRelaxNGParseDocument(ctxt, root);
        if (ret == NULL) {
            xmlFreeDoc(ctxt->document);
            ctxt->document = NULL;
            return (NULL);
        }
    
        /*
         * Check the ref/defines links
         */
        /*
         * try to preprocess interleaves
         */
        if (ctxt->interleaves != NULL) {
            xmlHashScan(ctxt->interleaves, xmlRelaxNGComputeInterleaves, ctxt);
        }
    
        /*
         * if there was a parsing error return NULL
         */
        if (ctxt->nbErrors > 0) {
            xmlRelaxNGFree(ret);
            ctxt->document = NULL;
            xmlFreeDoc(doc);
            return (NULL);
        }
    
        /*
         * try to compile (parts of) the schemas
         */
        if ((ret->topgrammar != NULL) && (ret->topgrammar->start != NULL)) {
            if (ret->topgrammar->start->type != XML_RELAXNG_START) {
                xmlRelaxNGDefinePtr def;
    
                def = xmlRelaxNGNewDefine(ctxt, NULL);
                if (def != NULL) {
                    def->type = XML_RELAXNG_START;
                    def->content = ret->topgrammar->start;
                    ret->topgrammar->start = def;
                }
            }
            xmlRelaxNGTryCompile(ctxt, ret->topgrammar->start);
        }
    
        /*
         * Transfer the pointer for cleanup at the schema level.
         */
        ret->doc = doc;
        ctxt->document = NULL;
        ret->documents = ctxt->documents;
        ctxt->documents = NULL;
    
        ret->includes = ctxt->includes;
        ctxt->includes = NULL;
        ret->defNr = ctxt->defNr;
        ret->defTab = ctxt->defTab;
        ctxt->defTab = NULL;
        if (ctxt->idref == 1)
            ret->idref = 1;
    
        return (ret);
    }
    
    /**
     * Set the callback functions used to handle errors for a validation context
     *
     * @deprecated Use xmlRelaxNGSetParserStructuredErrors().
     *
     * @param ctxt  a Relax-NG validation context
     * @param err  the error callback
     * @param warn  the warning callback
     * @param ctx  contextual data for the callbacks
     */
    void
    xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
                              xmlRelaxNGValidityErrorFunc err,
                              xmlRelaxNGValidityWarningFunc warn, void *ctx)
    {
        if (ctxt == NULL)
            return;
        ctxt->error = err;
        ctxt->warning = warn;
        ctxt->serror = NULL;
        ctxt->userData = ctx;
    }
    
    /**
     * Get the callback information used to handle errors for a validation context
     *
     * @param ctxt  a Relax-NG validation context
     * @param err  the error callback result
     * @param warn  the warning callback result
     * @param ctx  contextual data for the callbacks result
     * @returns -1 in case of failure, 0 otherwise.
     */
    int
    xmlRelaxNGGetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
                              xmlRelaxNGValidityErrorFunc * err,
                              xmlRelaxNGValidityWarningFunc * warn, void **ctx)
    {
        if (ctxt == NULL)
            return (-1);
        if (err != NULL)
            *err = ctxt->error;
        if (warn != NULL)
            *warn = ctxt->warning;
        if (ctx != NULL)
            *ctx = ctxt->userData;
        return (0);
    }
    
    /**
     * Set the callback functions used to handle errors for a parsing context
     *
     * @param ctxt  a Relax-NG parser context
     * @param serror  the error callback
     * @param ctx  contextual data for the callbacks
     */
    void
    xmlRelaxNGSetParserStructuredErrors(xmlRelaxNGParserCtxtPtr ctxt,
    				    xmlStructuredErrorFunc serror,
    				    void *ctx)
    {
        if (ctxt == NULL)
            return;
        ctxt->serror = serror;
        ctxt->error = NULL;
        ctxt->warning = NULL;
        ctxt->userData = ctx;
    }
    
    /**
     * Set the callback function used to load external resources.
     *
     * @param ctxt  a Relax-NG parser context
     * @param loader  the callback
     * @param vctxt  contextual data for the callbacks
     */
    void
    xmlRelaxNGSetResourceLoader(xmlRelaxNGParserCtxtPtr ctxt,
                                xmlResourceLoader loader, void *vctxt) {
        if (ctxt == NULL)
            return;
        ctxt->resourceLoader = loader;
        ctxt->resourceCtxt = vctxt;
    }
    
    #ifdef LIBXML_OUTPUT_ENABLED
    
    /************************************************************************
     *									*
     *			Dump back a compiled form			*
     *									*
     ************************************************************************/
    static void xmlRelaxNGDumpDefine(FILE * output,
                                     xmlRelaxNGDefinePtr define);
    
    /**
     * Dump a RelaxNG structure back
     *
     * @param output  the file output
     * @param defines  a list of define structures
     */
    static void
    xmlRelaxNGDumpDefines(FILE * output, xmlRelaxNGDefinePtr defines)
    {
        while (defines != NULL) {
            xmlRelaxNGDumpDefine(output, defines);
            defines = defines->next;
        }
    }
    
    /**
     * Dump a RelaxNG structure back
     *
     * @param output  the file output
     * @param define  a define structure
     */
    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_PARENTREF:
                fprintf(output, "<parentRef");
                if (define->name != NULL)
                    fprintf(output, " name=\"%s\"", define->name);
                fprintf(output, ">\n");
                xmlRelaxNGDumpDefines(output, define->content);
                fprintf(output, "</parentRef>\n");
                break;
            case XML_RELAXNG_EXTERNALREF:
                fprintf(output, "<externalRef>");
                xmlRelaxNGDumpDefines(output, define->content);
                fprintf(output, "</externalRef>\n");
                break;
            case XML_RELAXNG_DATATYPE:
            case XML_RELAXNG_VALUE:
                /* TODO */
                break;
            case XML_RELAXNG_START:
            case XML_RELAXNG_EXCEPT:
            case XML_RELAXNG_PARAM:
                /* TODO */
                break;
            case XML_RELAXNG_NOOP:
                xmlRelaxNGDumpDefines(output, define->content);
                break;
        }
    }
    
    /**
     * Dump a RelaxNG structure back
     *
     * @param output  the file output
     * @param grammar  a grammar structure
     * @param top  is this a top grammar
     */
    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");
    }
    
    /**
     * Dump a RelaxNG structure back
     *
     * @param output  the file output
     * @param schema  a schema structure
     */
    void
    xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema)
    {
        if (output == NULL)
            return;
        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);
    }
    
    /**
     * Dump the transformed RelaxNG tree.
     *
     * @param output  the file output
     * @param schema  a schema structure
     */
    void
    xmlRelaxNGDumpTree(FILE * output, xmlRelaxNGPtr schema)
    {
        if (output == NULL)
            return;
        if (schema == NULL) {
            fprintf(output, "RelaxNG empty or failed to compile\n");
            return;
        }
        if (schema->doc == NULL) {
            fprintf(output, "no document\n");
        } else {
            xmlDocDump(output, schema->doc);
        }
    }
    #endif /* LIBXML_OUTPUT_ENABLED */
    
    /************************************************************************
     *									*
     *		Validation of compiled content				*
     *									*
     ************************************************************************/
    static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
                                            xmlRelaxNGDefinePtr define);
    
    /**
     * Handle the callback and if needed validate the element children.
     *
     * @param exec  the regular expression instance
     * @param token  the token which matched
     * @param transdata  callback data, the define for the subelement if available
     * @param inputdata  callback data, the Relax NG validation context
     */
    static void
    xmlRelaxNGValidateCompiledCallback(xmlRegExecCtxtPtr exec ATTRIBUTE_UNUSED,
                                       const xmlChar * token,
                                       void *transdata, void *inputdata)
    {
        xmlRelaxNGValidCtxtPtr ctxt = (xmlRelaxNGValidCtxtPtr) inputdata;
        xmlRelaxNGDefinePtr define = (xmlRelaxNGDefinePtr) transdata;
        int ret;
    
        if (ctxt == NULL) {
            xmlRngVErr(ctxt, NULL, XML_ERR_INTERNAL_ERROR,
                       "callback on %s missing context\n", token, NULL);
            return;
        }
        if (define == NULL) {
            if (token[0] == '#')
                return;
            xmlRngVErr(ctxt, NULL, XML_ERR_INTERNAL_ERROR,
                       "callback on %s missing define\n", token, NULL);
            if ((ctxt != NULL) && (ctxt->errNo == XML_RELAXNG_OK))
                ctxt->errNo = XML_RELAXNG_ERR_INTERNAL;
            return;
        }
        if (define->type != XML_RELAXNG_ELEMENT) {
            xmlRngVErr(ctxt, NULL, XML_ERR_INTERNAL_ERROR,
                       "callback on %s define is not element\n", token, NULL);
            if (ctxt->errNo == XML_RELAXNG_OK)
                ctxt->errNo = XML_RELAXNG_ERR_INTERNAL;
            return;
        }
        ret = xmlRelaxNGValidateDefinition(ctxt, define);
        if (ret != 0)
            ctxt->perr = ret;
    }
    
    /**
     * Validate the content model of an element or start using the regexp
     *
     * @param ctxt  the RelaxNG validation context
     * @param regexp  the regular expression as compiled
     * @param content  list of children to test against the regexp
     * @returns 0 in case of success, -1 in case of error.
     */
    static int
    xmlRelaxNGValidateCompiledContent(xmlRelaxNGValidCtxtPtr ctxt,
                                      xmlRegexpPtr regexp, xmlNodePtr content)
    {
        xmlRegExecCtxtPtr exec;
        xmlNodePtr cur;
        int ret = 0;
        int oldperr;
    
        if ((ctxt == NULL) || (regexp == NULL))
            return (-1);
        oldperr = ctxt->perr;
        exec = xmlRegNewExecCtxt(regexp,
                                 xmlRelaxNGValidateCompiledCallback, ctxt);
        ctxt->perr = 0;
        cur = content;
        while (cur != NULL) {
            ctxt->state->seq = cur;
            switch (cur->type) {
                case XML_TEXT_NODE:
                case XML_CDATA_SECTION_NODE:
                    if (xmlIsBlankNode(cur))
                        break;
                    ret = xmlRegExecPushString(exec, BAD_CAST "#text", ctxt);
                    if (ret < 0) {
                        VALID_ERR2(XML_RELAXNG_ERR_TEXTWRONG,
                                   cur->parent->name);
                    }
                    break;
                case XML_ELEMENT_NODE:
                    if (cur->ns != NULL) {
                        ret = xmlRegExecPushString2(exec, cur->name,
                                                    cur->ns->href, ctxt);
                    } else {
                        ret = xmlRegExecPushString(exec, cur->name, ctxt);
                    }
                    if (ret < 0) {
                        VALID_ERR2(XML_RELAXNG_ERR_ELEMWRONG, cur->name);
                    }
                    break;
                default:
                    break;
            }
            if (ret < 0)
                break;
            /*
             * Switch to next element
             */
            cur = cur->next;
        }
        ret = xmlRegExecPushString(exec, NULL, NULL);
        if (ret == 1) {
            ret = 0;
            ctxt->state->seq = NULL;
        } else if (ret == 0) {
            /*
             * TODO: get some of the names needed to exit the current state of exec
             */
            VALID_ERR2(XML_RELAXNG_ERR_NOELEM, BAD_CAST "");
            ret = -1;
            if ((ctxt->flags & FLAGS_IGNORABLE) == 0)
                xmlRelaxNGDumpValidError(ctxt);
        } else {
            ret = -1;
        }
        xmlRegFreeExecCtxt(exec);
        /*
         * There might be content model errors outside of the pure
         * regexp validation, e.g. for attribute values.
         */
        if ((ret == 0) && (ctxt->perr != 0)) {
            ret = ctxt->perr;
        }
        ctxt->perr = oldperr;
        return (ret);
    }
    
    /************************************************************************
     *									*
     *		Progressive validation of when possible			*
     *									*
     ************************************************************************/
    static int xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt,
                                               xmlRelaxNGDefinePtr defines);
    static int xmlRelaxNGValidateElementEnd(xmlRelaxNGValidCtxtPtr ctxt,
                                            int dolog);
    static void xmlRelaxNGLogBestError(xmlRelaxNGValidCtxtPtr ctxt);
    
    /**
     * Push a new regexp for the current node content model on the stack
     *
     * @param ctxt  the validation context
     * @param exec  the regexp runtime for the new content model
     * @returns 0 in case of success and -1 in case of error.
     */
    static int
    xmlRelaxNGElemPush(xmlRelaxNGValidCtxtPtr ctxt, xmlRegExecCtxtPtr exec)
    {
        if (ctxt->elemTab == NULL) {
            ctxt->elemMax = 10;
            ctxt->elemTab = (xmlRegExecCtxtPtr *) xmlMalloc(ctxt->elemMax *
                                                            sizeof
                                                            (xmlRegExecCtxtPtr));
            if (ctxt->elemTab == NULL) {
                xmlRngVErrMemory(ctxt);
                return (-1);
            }
        }
        if (ctxt->elemNr >= ctxt->elemMax) {
            ctxt->elemMax *= 2;
            ctxt->elemTab = (xmlRegExecCtxtPtr *) xmlRealloc(ctxt->elemTab,
                                                             ctxt->elemMax *
                                                             sizeof
                                                             (xmlRegExecCtxtPtr));
            if (ctxt->elemTab == NULL) {
                xmlRngVErrMemory(ctxt);
                return (-1);
            }
        }
        ctxt->elemTab[ctxt->elemNr++] = exec;
        ctxt->elem = exec;
        return (0);
    }
    
    /**
     * Pop the regexp of the current node content model from the stack
     *
     * @param ctxt  the validation context
     * @returns the exec or NULL if empty
     */
    static xmlRegExecCtxtPtr
    xmlRelaxNGElemPop(xmlRelaxNGValidCtxtPtr ctxt)
    {
        xmlRegExecCtxtPtr ret;
    
        if (ctxt->elemNr <= 0)
            return (NULL);
        ctxt->elemNr--;
        ret = ctxt->elemTab[ctxt->elemNr];
        ctxt->elemTab[ctxt->elemNr] = NULL;
        if (ctxt->elemNr > 0)
            ctxt->elem = ctxt->elemTab[ctxt->elemNr - 1];
        else
            ctxt->elem = NULL;
        return (ret);
    }
    
    /**
     * Handle the callback and if needed validate the element children.
     * some of the in/out information are passed via the context in `inputdata`.
     *
     * @param exec  the regular expression instance
     * @param token  the token which matched
     * @param transdata  callback data, the define for the subelement if available
     * @param inputdata  callback data, the Relax NG validation context
     */
    static void
    xmlRelaxNGValidateProgressiveCallback(xmlRegExecCtxtPtr exec
                                          ATTRIBUTE_UNUSED,
                                          const xmlChar * token,
                                          void *transdata, void *inputdata)
    {
        xmlRelaxNGValidCtxtPtr ctxt = (xmlRelaxNGValidCtxtPtr) inputdata;
        xmlRelaxNGDefinePtr define = (xmlRelaxNGDefinePtr) transdata;
        xmlRelaxNGValidStatePtr state, oldstate;
        xmlNodePtr node;
        int ret = 0, oldflags;
    
        if (ctxt == NULL) {
            xmlRngVErr(ctxt, NULL, XML_ERR_INTERNAL_ERROR,
                       "callback on %s missing context\n", token, NULL);
            return;
        }
        node = ctxt->pnode;
        ctxt->pstate = 1;
        if (define == NULL) {
            if (token[0] == '#')
                return;
            xmlRngVErr(ctxt, NULL, XML_ERR_INTERNAL_ERROR,
                       "callback on %s missing define\n", token, NULL);
            if ((ctxt != NULL) && (ctxt->errNo == XML_RELAXNG_OK))
                ctxt->errNo = XML_RELAXNG_ERR_INTERNAL;
            ctxt->pstate = -1;
            return;
        }
        if (define->type != XML_RELAXNG_ELEMENT) {
            xmlRngVErr(ctxt, NULL, XML_ERR_INTERNAL_ERROR,
                       "callback on %s define is not element\n", token, NULL);
            if (ctxt->errNo == XML_RELAXNG_OK)
                ctxt->errNo = XML_RELAXNG_ERR_INTERNAL;
            ctxt->pstate = -1;
            return;
        }
        if (node->type != XML_ELEMENT_NODE) {
            VALID_ERR(XML_RELAXNG_ERR_NOTELEM);
            if ((ctxt->flags & FLAGS_IGNORABLE) == 0)
                xmlRelaxNGDumpValidError(ctxt);
            ctxt->pstate = -1;
            return;
        }
        if (define->contModel == NULL) {
            /*
             * this node cannot be validated in a streamable fashion
             */
            ctxt->pstate = 0;
            ctxt->pdef = define;
            return;
        }
        exec = xmlRegNewExecCtxt(define->contModel,
                                 xmlRelaxNGValidateProgressiveCallback, ctxt);
        if (exec == NULL) {
            ctxt->pstate = -1;
            return;
        }
        xmlRelaxNGElemPush(ctxt, exec);
    
        /*
         * Validate the attributes part of the content.
         */
        state = xmlRelaxNGNewValidState(ctxt, node);
        if (state == NULL) {
            ctxt->pstate = -1;
            return;
        }
        oldstate = ctxt->state;
        ctxt->state = state;
        if (define->attrs != NULL) {
            ret = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
            if (ret != 0) {
                ctxt->pstate = -1;
                VALID_ERR2(XML_RELAXNG_ERR_ATTRVALID, node->name);
            }
        }
        if (ctxt->state != NULL) {
            ctxt->state->seq = NULL;
            ret = xmlRelaxNGValidateElementEnd(ctxt, 1);
            if (ret != 0) {
                ctxt->pstate = -1;
            }
            xmlRelaxNGFreeValidState(ctxt, ctxt->state);
        } else if (ctxt->states != NULL) {
            int tmp = -1, i;
    
            oldflags = ctxt->flags;
    
            for (i = 0; i < ctxt->states->nbState; i++) {
                state = ctxt->states->tabState[i];
                ctxt->state = state;
                ctxt->state->seq = NULL;
    
                if (xmlRelaxNGValidateElementEnd(ctxt, 0) == 0) {
                    tmp = 0;
                    break;
                }
            }
            if (tmp != 0) {
                /*
                 * validation error, log the message for the "best" one
                 */
                ctxt->flags |= FLAGS_IGNORABLE;
                xmlRelaxNGLogBestError(ctxt);
            }
            for (i = 0; i < ctxt->states->nbState; i++) {
                xmlRelaxNGFreeValidState(ctxt, ctxt->states->tabState[i]);
            }
            xmlRelaxNGFreeStates(ctxt, ctxt->states);
            ctxt->states = NULL;
            if ((ret == 0) && (tmp == -1))
                ctxt->pstate = -1;
            ctxt->flags = oldflags;
        }
        if (ctxt->pstate == -1) {
            if ((ctxt->flags & FLAGS_IGNORABLE) == 0) {
                xmlRelaxNGDumpValidError(ctxt);
            }
        }
        ctxt->state = oldstate;
    }
    
    /**
     * Push a new element start on the RelaxNG validation stack.
     *
     * @param ctxt  the validation context
     * @param doc  a document instance
     * @param elem  an element instance
     * @returns 1 if no validation problem was found or 0 if validating the
     *         element requires a full node, and -1 in case of error.
     */
    int
    xmlRelaxNGValidatePushElement(xmlRelaxNGValidCtxtPtr ctxt,
                                  xmlDocPtr doc ATTRIBUTE_UNUSED,
                                  xmlNodePtr elem)
    {
        int ret = 1;
    
        if ((ctxt == NULL) || (elem == NULL))
            return (-1);
    
        if (ctxt->elem == 0) {
            xmlRelaxNGPtr schema;
            xmlRelaxNGGrammarPtr grammar;
            xmlRegExecCtxtPtr exec;
            xmlRelaxNGDefinePtr define;
    
            schema = ctxt->schema;
            if (schema == NULL) {
                VALID_ERR(XML_RELAXNG_ERR_NOGRAMMAR);
                return (-1);
            }
            grammar = schema->topgrammar;
            if ((grammar == NULL) || (grammar->start == NULL)) {
                VALID_ERR(XML_RELAXNG_ERR_NOGRAMMAR);
                return (-1);
            }
            define = grammar->start;
            if (define->contModel == NULL) {
                ctxt->pdef = define;
                return (0);
            }
            exec = xmlRegNewExecCtxt(define->contModel,
                                     xmlRelaxNGValidateProgressiveCallback,
                                     ctxt);
            if (exec == NULL) {
                return (-1);
            }
            xmlRelaxNGElemPush(ctxt, exec);
        }
        ctxt->pnode = elem;
        ctxt->pstate = 0;
        if (elem->ns != NULL) {
            ret =
                xmlRegExecPushString2(ctxt->elem, elem->name, elem->ns->href,
                                      ctxt);
        } else {
            ret = xmlRegExecPushString(ctxt->elem, elem->name, ctxt);
        }
        if (ret < 0) {
            VALID_ERR2(XML_RELAXNG_ERR_ELEMWRONG, elem->name);
        } else {
            if (ctxt->pstate == 0)
                ret = 0;
            else if (ctxt->pstate < 0)
                ret = -1;
            else
                ret = 1;
        }
        return (ret);
    }
    
    /**
     * check the CData parsed for validation in the current stack
     *
     * @param ctxt  the RelaxNG validation context
     * @param data  some character data read
     * @param len  the length of the data
     * @returns 1 if no validation problem was found or -1 otherwise
     */
    int
    xmlRelaxNGValidatePushCData(xmlRelaxNGValidCtxtPtr ctxt,
                                const xmlChar * data, int len ATTRIBUTE_UNUSED)
    {
        int ret = 1;
    
        if ((ctxt == NULL) || (ctxt->elem == NULL) || (data == NULL))
            return (-1);
    
        while (*data != 0) {
            if (!IS_BLANK_CH(*data))
                break;
            data++;
        }
        if (*data == 0)
            return (1);
    
        ret = xmlRegExecPushString(ctxt->elem, BAD_CAST "#text", ctxt);
        if (ret < 0) {
            VALID_ERR2(XML_RELAXNG_ERR_TEXTWRONG, BAD_CAST " TODO ");
    
            return (-1);
        }
        return (1);
    }
    
    /**
     * Pop the element end from the RelaxNG validation stack.
     *
     * @param ctxt  the RelaxNG validation context
     * @param doc  a document instance
     * @param elem  an element instance
     * @returns 1 if no validation problem was found or 0 otherwise
     */
    int
    xmlRelaxNGValidatePopElement(xmlRelaxNGValidCtxtPtr ctxt,
                                 xmlDocPtr doc ATTRIBUTE_UNUSED,
                                 xmlNodePtr elem)
    {
        int ret;
        xmlRegExecCtxtPtr exec;
    
        if ((ctxt == NULL) || (ctxt->elem == NULL) || (elem == NULL))
            return (-1);
        /*
         * verify that we reached a terminal state of the content model.
         */
        exec = xmlRelaxNGElemPop(ctxt);
        ret = xmlRegExecPushString(exec, NULL, NULL);
        if (ret == 0) {
            /*
             * TODO: get some of the names needed to exit the current state of exec
             */
            VALID_ERR2(XML_RELAXNG_ERR_NOELEM, BAD_CAST "");
            ret = -1;
        } else if (ret < 0) {
            ret = -1;
        } else {
            ret = 1;
        }
        xmlRegFreeExecCtxt(exec);
        return (ret);
    }
    
    /**
     * Validate a full subtree when xmlRelaxNGValidatePushElement() returned
     * 0 and the content of the node has been expanded.
     *
     * @param ctxt  the validation context
     * @param doc  a document instance
     * @param elem  an element instance
     * @returns 1 if no validation problem was found or -1 in case of error.
     */
    int
    xmlRelaxNGValidateFullElement(xmlRelaxNGValidCtxtPtr ctxt,
                                  xmlDocPtr doc ATTRIBUTE_UNUSED,
                                  xmlNodePtr elem)
    {
        int ret;
        xmlRelaxNGValidStatePtr state;
    
        if ((ctxt == NULL) || (ctxt->pdef == NULL) || (elem == NULL))
            return (-1);
        state = xmlRelaxNGNewValidState(ctxt, elem->parent);
        if (state == NULL) {
            return (-1);
        }
        state->seq = elem;
        ctxt->state = state;
        ctxt->errNo = XML_RELAXNG_OK;
        ret = xmlRelaxNGValidateDefinition(ctxt, ctxt->pdef);
        if ((ret != 0) || (ctxt->errNo != XML_RELAXNG_OK))
            ret = -1;
        else
            ret = 1;
        xmlRelaxNGFreeValidState(ctxt, ctxt->state);
        ctxt->state = NULL;
        return (ret);
    }
    
    /************************************************************************
     *									*
     *		Generic interpreted validation implementation		*
     *									*
     ************************************************************************/
    static int xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
                                       xmlRelaxNGDefinePtr define);
    
    /**
     * Skip ignorable nodes in that context
     *
     * @param ctxt  a schema validation context
     * @param node  the top node.
     * @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_PI_NODE) ||
    	    (node->type == XML_XINCLUDE_START) ||
    	    (node->type == XML_XINCLUDE_END) ||
                (((node->type == XML_TEXT_NODE) ||
                  (node->type == XML_CDATA_SECTION_NODE)) &&
                 ((ctxt->flags & FLAGS_MIXED_CONTENT) ||
                  (IS_BLANK_NODE(node)))))) {
            node = node->next;
        }
        return (node);
    }
    
    /**
     * Implements the  normalizeWhiteSpace( s ) function from
     * section 6.2.9 of the spec
     *
     * @param ctxt  a schema validation context
     * @param str  the string to normalize
     * @returns the new string or NULL in case of error.
     */
    static xmlChar *
    xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar * str)
    {
        xmlChar *ret, *p;
        const xmlChar *tmp;
        int len;
    
        if (str == NULL)
            return (NULL);
        tmp = str;
        while (*tmp != 0)
            tmp++;
        len = tmp - str;
    
        ret = xmlMalloc(len + 1);
        if (ret == NULL) {
            xmlRngVErrMemory(ctxt);
            return (NULL);
        }
        p = ret;
        while (IS_BLANK_CH(*str))
            str++;
        while (*str != 0) {
            if (IS_BLANK_CH(*str)) {
                while (IS_BLANK_CH(*str))
                    str++;
                if (*str == 0)
                    break;
                *p++ = ' ';
            } else
                *p++ = *str++;
        }
        *p = 0;
        return (ret);
    }
    
    /**
     * Validate the given value against the datatype
     *
     * @param ctxt  a Relax-NG validation context
     * @param value  the string value
     * @param define  the datatype definition
     * @param node  the node
     * @returns 0 if the validation succeeded or an error code.
     */
    static int
    xmlRelaxNGValidateDatatype(xmlRelaxNGValidCtxtPtr ctxt,
                               const xmlChar * value,
                               xmlRelaxNGDefinePtr define, xmlNodePtr node)
    {
        int ret, tmp;
        xmlRelaxNGTypeLibraryPtr lib;
        void *result = NULL;
        xmlRelaxNGDefinePtr cur;
    
        if ((define == NULL) || (define->data == NULL)) {
            return (-1);
        }
        lib = (xmlRelaxNGTypeLibraryPtr) define->data;
        if (lib->check != NULL) {
            if ((define->attrs != NULL) &&
                (define->attrs->type == XML_RELAXNG_PARAM)) {
                ret =
                    lib->check(lib->data, define->name, value, &result, node);
            } else {
                ret = lib->check(lib->data, define->name, value, NULL, node);
            }
        } else
            ret = -1;
        if (ret < 0) {
            VALID_ERR2(XML_RELAXNG_ERR_TYPE, define->name);
            if ((result != NULL) && (lib != NULL) && (lib->freef != NULL))
                lib->freef(lib->data, result);
            return (-1);
        } else if (ret == 1) {
            ret = 0;
        } else if (ret == 2) {
            VALID_ERR2P(XML_RELAXNG_ERR_DUPID, value);
        } else {
            VALID_ERR3P(XML_RELAXNG_ERR_TYPEVAL, define->name, value);
            ret = -1;
        }
        cur = define->attrs;
        while ((ret == 0) && (cur != NULL) && (cur->type == XML_RELAXNG_PARAM)) {
            if (lib->facet != NULL) {
                tmp = lib->facet(lib->data, define->name, cur->name,
                                 cur->value, value, result);
                if (tmp != 0)
                    ret = -1;
            }
            cur = cur->next;
        }
        if ((ret == 0) && (define->content != NULL)) {
            const xmlChar *oldvalue, *oldendvalue;
    
            oldvalue = ctxt->state->value;
            oldendvalue = ctxt->state->endvalue;
            ctxt->state->value = (xmlChar *) value;
            ctxt->state->endvalue = NULL;
            ret = xmlRelaxNGValidateValue(ctxt, define->content);
            ctxt->state->value = (xmlChar *) oldvalue;
            ctxt->state->endvalue = (xmlChar *) oldendvalue;
        }
        if ((result != NULL) && (lib != NULL) && (lib->freef != NULL))
            lib->freef(lib->data, result);
        return (ret);
    }
    
    /**
     * Skip to the next value when validating within a list
     *
     * @param ctxt  a Relax-NG validation context
     * @returns 0 if the operation succeeded or an error code.
     */
    static int
    xmlRelaxNGNextValue(xmlRelaxNGValidCtxtPtr ctxt)
    {
        xmlChar *cur;
    
        cur = ctxt->state->value;
        if ((cur == NULL) || (ctxt->state->endvalue == NULL)) {
            ctxt->state->value = NULL;
            ctxt->state->endvalue = NULL;
            return (0);
        }
        while (*cur != 0)
            cur++;
        while ((cur != ctxt->state->endvalue) && (*cur == 0))
            cur++;
        if (cur == ctxt->state->endvalue)
            ctxt->state->value = NULL;
        else
            ctxt->state->value = cur;
        return (0);
    }
    
    /**
     * Validate the given set of definitions for the current value
     *
     * @param ctxt  a Relax-NG validation context
     * @param defines  the list of definitions to verify
     * @returns 0 if the validation succeeded or an error code.
     */
    static int
    xmlRelaxNGValidateValueList(xmlRelaxNGValidCtxtPtr ctxt,
                                xmlRelaxNGDefinePtr defines)
    {
        int ret = 0;
    
        while (defines != NULL) {
            ret = xmlRelaxNGValidateValue(ctxt, defines);
            if (ret != 0)
                break;
            defines = defines->next;
        }
        return (ret);
    }
    
    /**
     * Validate the given definition for the current value
     *
     * @param ctxt  a Relax-NG validation context
     * @param define  the definition to verify
     * @returns 0 if the validation succeeded or an error code.
     */
    static int
    xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
                            xmlRelaxNGDefinePtr define)
    {
        int ret = 0, oldflags;
        xmlChar *value;
    
        value = ctxt->state->value;
        switch (define->type) {
            case XML_RELAXNG_EMPTY:{
                    if ((value != NULL) && (value[0] != 0)) {
                        int idx = 0;
    
                        while (IS_BLANK_CH(value[idx]))
                            idx++;
                        if (value[idx] != 0)
                            ret = -1;
                    }
                    break;
                }
            case XML_RELAXNG_TEXT:
                break;
            case XML_RELAXNG_VALUE:{
                    if (!xmlStrEqual(value, define->value)) {
                        if (define->name != NULL) {
                            xmlRelaxNGTypeLibraryPtr lib;
    
                            lib = (xmlRelaxNGTypeLibraryPtr) define->data;
                            if ((lib != NULL) && (lib->comp != NULL)) {
                                ret = lib->comp(lib->data, define->name,
                                                define->value, define->node,
                                                (void *) define->attrs,
                                                value, ctxt->state->node);
                            } else
                                ret = -1;
                            if (ret < 0) {
                                VALID_ERR2(XML_RELAXNG_ERR_TYPECMP,
                                           define->name);
                                return (-1);
                            } else if (ret == 1) {
                                ret = 0;
                            } else {
                                ret = -1;
                            }
                        } else {
                            xmlChar *nval, *nvalue;
    
                            /*
                             * TODO: trivial optimizations are possible by
                             * computing at compile-time
                             */
                            nval = xmlRelaxNGNormalize(ctxt, define->value);
                            nvalue = xmlRelaxNGNormalize(ctxt, value);
    
                            if ((nval == NULL) || (nvalue == NULL) ||
                                (!xmlStrEqual(nval, nvalue)))
                                ret = -1;
                            if (nval != NULL)
                                xmlFree(nval);
                            if (nvalue != NULL)
                                xmlFree(nvalue);
                        }
                    }
                    if (ret == 0)
                        xmlRelaxNGNextValue(ctxt);
                    break;
                }
            case XML_RELAXNG_DATATYPE:{
                    ret = xmlRelaxNGValidateDatatype(ctxt, value, define,
                                                     ctxt->state->seq);
                    if (ret == 0)
                        xmlRelaxNGNextValue(ctxt);
    
                    break;
                }
            case XML_RELAXNG_CHOICE:{
                    xmlRelaxNGDefinePtr list = define->content;
                    xmlChar *oldvalue;
    
                    oldflags = ctxt->flags;
                    ctxt->flags |= FLAGS_IGNORABLE;
    
                    oldvalue = ctxt->state->value;
                    while (list != NULL) {
                        ret = xmlRelaxNGValidateValue(ctxt, list);
                        if (ret == 0) {
                            break;
                        }
                        ctxt->state->value = oldvalue;
                        list = list->next;
                    }
                    ctxt->flags = oldflags;
                    if (ret != 0) {
                        if ((ctxt->flags & FLAGS_IGNORABLE) == 0)
                            xmlRelaxNGDumpValidError(ctxt);
                    } else {
                        if (ctxt->errNr > 0)
                            xmlRelaxNGPopErrors(ctxt, 0);
                    }
                    break;
                }
            case XML_RELAXNG_LIST:{
                    xmlRelaxNGDefinePtr list = define->content;
                    xmlChar *oldvalue, *oldend, *val, *cur;
    
                    oldvalue = ctxt->state->value;
                    oldend = ctxt->state->endvalue;
    
                    val = xmlStrdup(oldvalue);
                    if (val == NULL) {
                        val = xmlStrdup(BAD_CAST "");
                    }
                    if (val == NULL) {
                        VALID_ERR(XML_RELAXNG_ERR_NOSTATE);
                        return (-1);
                    }
                    cur = val;
                    while (*cur != 0) {
                        if (IS_BLANK_CH(*cur)) {
                            *cur = 0;
                            cur++;
                            while (IS_BLANK_CH(*cur))
                                *cur++ = 0;
                        } else
                            cur++;
                    }
                    ctxt->state->endvalue = cur;
                    cur = val;
                    while ((*cur == 0) && (cur != ctxt->state->endvalue))
                        cur++;
    
                    ctxt->state->value = cur;
    
                    while (list != NULL) {
                        if (ctxt->state->value == ctxt->state->endvalue)
                            ctxt->state->value = NULL;
                        ret = xmlRelaxNGValidateValue(ctxt, list);
                        if (ret != 0) {
                            break;
                        }
                        list = list->next;
                    }
    
                    if ((ret == 0) && (ctxt->state->value != NULL) &&
                        (ctxt->state->value != ctxt->state->endvalue)) {
                        VALID_ERR2(XML_RELAXNG_ERR_LISTEXTRA,
                                   ctxt->state->value);
                        ret = -1;
                    }
                    xmlFree(val);
                    ctxt->state->value = oldvalue;
                    ctxt->state->endvalue = oldend;
                    break;
                }
            case XML_RELAXNG_ONEORMORE:
                ret = xmlRelaxNGValidateValueList(ctxt, define->content);
                if (ret != 0) {
                    break;
                }
                /* Falls through. */
            case XML_RELAXNG_ZEROORMORE:{
                    xmlChar *cur, *temp;
    
                    if ((ctxt->state->value == NULL) ||
                        (*ctxt->state->value == 0)) {
                        ret = 0;
                        break;
                    }
                    oldflags = ctxt->flags;
                    ctxt->flags |= FLAGS_IGNORABLE;
                    cur = ctxt->state->value;
                    temp = NULL;
                    while ((cur != NULL) && (cur != ctxt->state->endvalue) &&
                           (temp != cur)) {
                        temp = cur;
                        ret =
                            xmlRelaxNGValidateValueList(ctxt, define->content);
                        if (ret != 0) {
                            ctxt->state->value = temp;
                            ret = 0;
                            break;
                        }
                        cur = ctxt->state->value;
                    }
                    ctxt->flags = oldflags;
    		if (ctxt->errNr > 0)
    		    xmlRelaxNGPopErrors(ctxt, 0);
                    break;
                }
            case XML_RELAXNG_OPTIONAL:{
                    xmlChar *temp;
    
                    if ((ctxt->state->value == NULL) ||
                        (*ctxt->state->value == 0)) {
                        ret = 0;
                        break;
                    }
                    oldflags = ctxt->flags;
                    ctxt->flags |= FLAGS_IGNORABLE;
                    temp = ctxt->state->value;
                    ret = xmlRelaxNGValidateValue(ctxt, define->content);
                    ctxt->flags = oldflags;
                    if (ret != 0) {
                        ctxt->state->value = temp;
                        if (ctxt->errNr > 0)
                            xmlRelaxNGPopErrors(ctxt, 0);
                        ret = 0;
                        break;
                    }
    		if (ctxt->errNr > 0)
    		    xmlRelaxNGPopErrors(ctxt, 0);
                    break;
                }
            case XML_RELAXNG_EXCEPT:{
                    xmlRelaxNGDefinePtr list;
    
                    list = define->content;
                    while (list != NULL) {
                        ret = xmlRelaxNGValidateValue(ctxt, list);
                        if (ret == 0) {
                            ret = -1;
                            break;
                        } else
                            ret = 0;
                        list = list->next;
                    }
                    break;
                }
            case XML_RELAXNG_DEF:
            case XML_RELAXNG_GROUP:{
                    xmlRelaxNGDefinePtr list;
    
                    list = define->content;
                    while (list != NULL) {
                        ret = xmlRelaxNGValidateValue(ctxt, list);
                        if (ret != 0) {
                            ret = -1;
                            break;
                        } else
                            ret = 0;
                        list = list->next;
                    }
                    break;
                }
            case XML_RELAXNG_REF:
            case XML_RELAXNG_PARENTREF:
    	    if (define->content == NULL) {
                    VALID_ERR(XML_RELAXNG_ERR_NODEFINE);
                    ret = -1;
    	    } else {
                    ret = xmlRelaxNGValidateValue(ctxt, define->content);
                }
                break;
            default:
                /* TODO */
                ret = -1;
        }
        return (ret);
    }
    
    /**
     * Validate the given definitions for the current value
     *
     * @param ctxt  a Relax-NG validation context
     * @param defines  the list of definitions to verify
     * @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);
    }
    
    /**
     * Check if the attribute matches the definition nameClass
     *
     * @param ctxt  a Relax-NG validation context
     * @param define  the definition to check
     * @param prop  the attribute
     * @returns 1 if the attribute matches, 0 if no, or -1 in case of error
     */
    static int
    xmlRelaxNGAttributeMatch(xmlRelaxNGValidCtxtPtr ctxt,
                             xmlRelaxNGDefinePtr define, xmlAttrPtr prop)
    {
        int ret;
    
        if (define->name != NULL) {
            if (!xmlStrEqual(define->name, prop->name))
                return (0);
        }
        if (define->ns != NULL) {
            if (define->ns[0] == 0) {
                if (prop->ns != NULL)
                    return (0);
            } else {
                if ((prop->ns == NULL) ||
                    (!xmlStrEqual(define->ns, prop->ns->href)))
                    return (0);
            }
        }
        if (define->nameClass == NULL)
            return (1);
        define = define->nameClass;
        if (define->type == XML_RELAXNG_EXCEPT) {
            xmlRelaxNGDefinePtr list;
    
            list = define->content;
            while (list != NULL) {
                ret = xmlRelaxNGAttributeMatch(ctxt, list, prop);
                if (ret == 1)
                    return (0);
                if (ret < 0)
                    return (ret);
                list = list->next;
            }
        } else if (define->type == XML_RELAXNG_CHOICE) {
            xmlRelaxNGDefinePtr list;
    
            list = define->nameClass;
            while (list != NULL) {
                ret = xmlRelaxNGAttributeMatch(ctxt, list, prop);
                if (ret == 1)
                    return (1);
                if (ret < 0)
                    return (ret);
                list = list->next;
            }
            return (0);
        } else {
            /* TODO */
            return (0);
        }
        return (1);
    }
    
    /**
     * Validate the given attribute definition for that node
     *
     * @param ctxt  a Relax-NG validation context
     * @param define  the definition to verify
     * @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;
        xmlNodePtr oldseq;
    
        if (ctxt->state->nbAttrLeft <= 0)
            return (-1);
        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;
                oldseq = ctxt->state->seq;
                ctxt->state->seq = (xmlNodePtr) prop;
                ctxt->state->value = value;
                ctxt->state->endvalue = NULL;
                ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
                if (ctxt->state->value != NULL)
                    value = ctxt->state->value;
                if (value != NULL)
                    xmlFree(value);
                ctxt->state->value = oldvalue;
                ctxt->state->seq = oldseq;
                if (ret == 0) {
                    /*
                     * flag the attribute as processed
                     */
                    ctxt->state->attrs[i] = NULL;
                    ctxt->state->nbAttrLeft--;
                }
            } else {
                ret = -1;
            }
        } else {
            for (i = 0; i < ctxt->state->nbAttrs; i++) {
                tmp = ctxt->state->attrs[i];
                if ((tmp != NULL) &&
                    (xmlRelaxNGAttributeMatch(ctxt, define, tmp) == 1)) {
                    prop = tmp;
                    break;
                }
            }
            if (prop != NULL) {
                value = xmlNodeListGetString(prop->doc, prop->children, 1);
                oldvalue = ctxt->state->value;
                oldseq = ctxt->state->seq;
                ctxt->state->seq = (xmlNodePtr) prop;
                ctxt->state->value = value;
                ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
                if (ctxt->state->value != NULL)
                    value = ctxt->state->value;
                if (value != NULL)
                    xmlFree(value);
                ctxt->state->value = oldvalue;
                ctxt->state->seq = oldseq;
                if (ret == 0) {
                    /*
                     * flag the attribute as processed
                     */
                    ctxt->state->attrs[i] = NULL;
                    ctxt->state->nbAttrLeft--;
                }
            } else {
                ret = -1;
            }
        }
    
        return (ret);
    }
    
    /**
     * Validate the given node against the list of attribute definitions
     *
     * @param ctxt  a Relax-NG validation context
     * @param defines  the list of definition to verify
     * @returns 0 if the validation succeeded or an error code.
     */
    static int
    xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt,
                                    xmlRelaxNGDefinePtr defines)
    {
        int ret = 0, res;
        int needmore = 0;
        xmlRelaxNGDefinePtr cur;
    
        cur = defines;
        while (cur != NULL) {
            if (cur->type == XML_RELAXNG_ATTRIBUTE) {
                if (xmlRelaxNGValidateAttribute(ctxt, cur) != 0)
                    ret = -1;
            } else
                needmore = 1;
            cur = cur->next;
        }
        if (!needmore)
            return (ret);
        cur = defines;
        while (cur != NULL) {
            if (cur->type != XML_RELAXNG_ATTRIBUTE) {
                if ((ctxt->state != NULL) || (ctxt->states != NULL)) {
                    res = xmlRelaxNGValidateDefinition(ctxt, cur);
                    if (res < 0)
                        ret = -1;
                } else {
                    VALID_ERR(XML_RELAXNG_ERR_NOSTATE);
                    return (-1);
                }
                if (res == -1)      /* continues on -2 */
                    break;
            }
            cur = cur->next;
        }
    
        return (ret);
    }
    
    /**
     * Check if a node can be matched by one of the definitions
     *
     * @param node  the node
     * @param list  a NULL terminated array of definitions
     * @returns 1 if matches 0 otherwise
     */
    static int
    xmlRelaxNGNodeMatchesList(xmlNodePtr node, xmlRelaxNGDefinePtr * list)
    {
        xmlRelaxNGDefinePtr cur;
        int i = 0, tmp;
    
        if ((node == NULL) || (list == NULL))
            return (0);
    
        cur = list[i++];
        while (cur != NULL) {
            if ((node->type == XML_ELEMENT_NODE) &&
                (cur->type == XML_RELAXNG_ELEMENT)) {
                tmp = xmlRelaxNGElementMatch(NULL, cur, node);
                if (tmp == 1)
                    return (1);
            } else if (((node->type == XML_TEXT_NODE) ||
                        (node->type == XML_CDATA_SECTION_NODE)) &&
                       ((cur->type == XML_RELAXNG_DATATYPE) ||
    		    (cur->type == XML_RELAXNG_LIST) ||
                        (cur->type == XML_RELAXNG_TEXT) ||
                        (cur->type == XML_RELAXNG_VALUE))) {
                return (1);
            }
            cur = list[i++];
        }
        return (0);
    }
    
    /**
     * Validate an interleave definition for a node.
     *
     * @param ctxt  a Relax-NG validation context
     * @param define  the definition to verify
     * @returns 0 if the validation succeeded or an error code.
     */
    static int
    xmlRelaxNGValidateInterleave(xmlRelaxNGValidCtxtPtr ctxt,
                                 xmlRelaxNGDefinePtr define)
    {
        int ret = 0, i, nbgroups;
        int errNr = ctxt->errNr;
        int oldflags;
    
        xmlRelaxNGValidStatePtr oldstate;
        xmlRelaxNGPartitionPtr partitions;
        xmlRelaxNGInterleaveGroupPtr group = NULL;
        xmlNodePtr cur, start, last = NULL, lastchg = NULL, lastelem;
        xmlNodePtr *list = NULL, *lasts = NULL;
    
        if (define->data != NULL) {
            partitions = (xmlRelaxNGPartitionPtr) define->data;
            nbgroups = partitions->nbgroups;
        } else {
            VALID_ERR(XML_RELAXNG_ERR_INTERNODATA);
            return (-1);
        }
        /*
         * Optimizations for MIXED
         */
        oldflags = ctxt->flags;
        if (define->dflags & IS_MIXED) {
            ctxt->flags |= FLAGS_MIXED_CONTENT;
            if (nbgroups == 2) {
                /*
                 * this is a pure <mixed> case
                 */
                if (ctxt->state != NULL)
                    ctxt->state->seq = xmlRelaxNGSkipIgnored(ctxt,
                                                             ctxt->state->seq);
                if (partitions->groups[0]->rule->type == XML_RELAXNG_TEXT)
                    ret = xmlRelaxNGValidateDefinition(ctxt,
                                                       partitions->groups[1]->
                                                       rule);
                else
                    ret = xmlRelaxNGValidateDefinition(ctxt,
                                                       partitions->groups[0]->
                                                       rule);
                if (ret == 0) {
                    if (ctxt->state != NULL)
                        ctxt->state->seq = xmlRelaxNGSkipIgnored(ctxt,
                                                                 ctxt->state->
                                                                 seq);
                }
                ctxt->flags = oldflags;
                return (ret);
            }
        }
    
        /*
         * Build arrays to store the first and last node of the chain
         * pertaining to each group
         */
        list = (xmlNodePtr *) xmlMalloc(nbgroups * sizeof(xmlNodePtr));
        if (list == NULL) {
            xmlRngVErrMemory(ctxt);
            return (-1);
        }
        memset(list, 0, nbgroups * sizeof(xmlNodePtr));
        lasts = (xmlNodePtr *) xmlMalloc(nbgroups * sizeof(xmlNodePtr));
        if (lasts == NULL) {
            xmlRngVErrMemory(ctxt);
            return (-1);
        }
        memset(lasts, 0, nbgroups * sizeof(xmlNodePtr));
    
        /*
         * Walk the sequence of children finding the right group and
         * sorting them in sequences.
         */
        cur = ctxt->state->seq;
        cur = xmlRelaxNGSkipIgnored(ctxt, cur);
        start = cur;
        while (cur != NULL) {
            ctxt->state->seq = cur;
            if ((partitions->triage != NULL) &&
                (partitions->flags & IS_DETERMINIST)) {
                void *tmp = NULL;
    
                if ((cur->type == XML_TEXT_NODE) ||
                    (cur->type == XML_CDATA_SECTION_NODE)) {
                    tmp = xmlHashLookup2(partitions->triage, BAD_CAST "#text",
                                         NULL);
                } else if (cur->type == XML_ELEMENT_NODE) {
                    if (cur->ns != NULL) {
                        tmp = xmlHashLookup2(partitions->triage, cur->name,
                                             cur->ns->href);
                        if (tmp == NULL)
                            tmp = xmlHashLookup2(partitions->triage,
                                                 BAD_CAST "#any",
                                                 cur->ns->href);
                    } else
                        tmp =
                            xmlHashLookup2(partitions->triage, cur->name,
                                           NULL);
                    if (tmp == NULL)
                        tmp =
                            xmlHashLookup2(partitions->triage, BAD_CAST "#any",
                                           NULL);
                }
    
                if (tmp == NULL) {
                    i = nbgroups;
                } else {
                    i = XML_PTR_TO_INT(tmp) - 1;
                    if (partitions->flags & IS_NEEDCHECK) {
                        group = partitions->groups[i];
                        if (!xmlRelaxNGNodeMatchesList(cur, group->defs))
                            i = nbgroups;
                    }
                }
            } else {
                for (i = 0; i < nbgroups; i++) {
                    group = partitions->groups[i];
                    if (group == NULL)
                        continue;
                    if (xmlRelaxNGNodeMatchesList(cur, group->defs))
                        break;
                }
            }
            /*
             * We break as soon as an element not matched is found
             */
            if (i >= nbgroups) {
                break;
            }
            if (lasts[i] != NULL) {
                lasts[i]->next = cur;
                lasts[i] = cur;
            } else {
                list[i] = cur;
                lasts[i] = cur;
            }
            if (cur->next != NULL)
                lastchg = cur->next;
            else
                lastchg = cur;
            cur = xmlRelaxNGSkipIgnored(ctxt, cur->next);
        }
        if (ret != 0) {
            VALID_ERR(XML_RELAXNG_ERR_INTERSEQ);
            ret = -1;
            goto done;
        }
        lastelem = cur;
        oldstate = ctxt->state;
        for (i = 0; i < nbgroups; i++) {
            ctxt->state = xmlRelaxNGCopyValidState(ctxt, oldstate);
    	if (ctxt->state == NULL) {
    	    ret = -1;
    	    break;
    	}
            group = partitions->groups[i];
            if (lasts[i] != NULL) {
                last = lasts[i]->next;
                lasts[i]->next = NULL;
            }
            ctxt->state->seq = list[i];
            ret = xmlRelaxNGValidateDefinition(ctxt, group->rule);
            if (ret != 0)
                break;
            if (ctxt->state != NULL) {
                cur = ctxt->state->seq;
                cur = xmlRelaxNGSkipIgnored(ctxt, cur);
                xmlRelaxNGFreeValidState(ctxt, oldstate);
                oldstate = ctxt->state;
                ctxt->state = NULL;
                if (cur != NULL
                        /* there's a nasty violation of context-free unambiguities,
                           since in open-name-class context, interleave in the
                           production shall finish without caring about anything
                           else that is OK to follow in that case -- it would
                           otherwise get marked as "extra content" and would
                           hence fail the validation, hence this perhaps
                           dirty attempt to rectify such a situation */
                        && (define->parent->type != XML_RELAXNG_DEF
                            || !xmlStrEqual(define->parent->name,
                                            (const xmlChar *) "open-name-class"))) {
                    VALID_ERR2(XML_RELAXNG_ERR_INTEREXTRA, cur->name);
                    ret = -1;
                    ctxt->state = oldstate;
                    goto done;
                }
            } else if (ctxt->states != NULL) {
                int j;
                int found = 0;
    	    int best = -1;
    	    int lowattr = -1;
    
    	    /*
    	     * PBM: what happen if there is attributes checks in the interleaves
    	     */
    
                for (j = 0; j < ctxt->states->nbState; j++) {
                    cur = ctxt->states->tabState[j]->seq;
                    cur = xmlRelaxNGSkipIgnored(ctxt, cur);
                    if (cur == NULL) {
    		    if (found == 0) {
    		        lowattr = ctxt->states->tabState[j]->nbAttrLeft;
    			best = j;
    		    }
                        found = 1;
    		    if (ctxt->states->tabState[j]->nbAttrLeft <= lowattr) {
    		        /* try  to keep the latest one to mach old heuristic */
    		        lowattr = ctxt->states->tabState[j]->nbAttrLeft;
    			best = j;
    		    }
                        if (lowattr == 0)
    		        break;
                    } else if (found == 0) {
                        if (lowattr == -1) {
    		        lowattr = ctxt->states->tabState[j]->nbAttrLeft;
    			best = j;
    		    } else
    		    if (ctxt->states->tabState[j]->nbAttrLeft <= lowattr)  {
    		        /* try  to keep the latest one to mach old heuristic */
    		        lowattr = ctxt->states->tabState[j]->nbAttrLeft;
    			best = j;
    		    }
    		}
                }
    	    /*
    	     * BIG PBM: here we pick only one restarting point :-(
    	     */
                if (ctxt->states->nbState > 0) {
                    xmlRelaxNGFreeValidState(ctxt, oldstate);
    		if (best != -1) {
    		    oldstate = ctxt->states->tabState[best];
    		    ctxt->states->tabState[best] = NULL;
    		} else {
    		    oldstate =
    			ctxt->states->tabState[ctxt->states->nbState - 1];
                        ctxt->states->tabState[ctxt->states->nbState - 1] = NULL;
                        ctxt->states->nbState--;
    		}
                }
                for (j = 0; j < ctxt->states->nbState ; j++) {
                    xmlRelaxNGFreeValidState(ctxt, ctxt->states->tabState[j]);
                }
                xmlRelaxNGFreeStates(ctxt, ctxt->states);
                ctxt->states = NULL;
                if (found == 0) {
                    if (cur == NULL) {
    		    VALID_ERR2(XML_RELAXNG_ERR_INTEREXTRA,
    			       (const xmlChar *) "noname");
                    } else {
                        VALID_ERR2(XML_RELAXNG_ERR_INTEREXTRA, cur->name);
                    }
                    ret = -1;
                    ctxt->state = oldstate;
                    goto done;
                }
            } else {
                ret = -1;
                break;
            }
            if (lasts[i] != NULL) {
                lasts[i]->next = last;
            }
        }
        if (ctxt->state != NULL)
            xmlRelaxNGFreeValidState(ctxt, ctxt->state);
        ctxt->state = oldstate;
        ctxt->state->seq = lastelem;
        if (ret != 0) {
            VALID_ERR(XML_RELAXNG_ERR_INTERSEQ);
            ret = -1;
            goto done;
        }
    
      done:
        ctxt->flags = oldflags;
        /*
         * builds the next links chain from the prev one
         */
        cur = lastchg;
        while (cur != NULL) {
            if ((cur == start) || (cur->prev == NULL))
                break;
            cur->prev->next = cur;
            cur = cur->prev;
        }
        if (ret == 0) {
            if (ctxt->errNr > errNr)
                xmlRelaxNGPopErrors(ctxt, errNr);
        }
    
        xmlFree(list);
        xmlFree(lasts);
        return (ret);
    }
    
    /**
     * Validate the given node content against the (list) of definitions
     *
     * @param ctxt  a Relax-NG validation context
     * @param defines  the list of definition to verify
     * @returns 0 if the validation succeeded or an error code.
     */
    static int
    xmlRelaxNGValidateDefinitionList(xmlRelaxNGValidCtxtPtr ctxt,
                                     xmlRelaxNGDefinePtr defines)
    {
        int ret = 0, res;
    
    
        if (defines == NULL) {
            VALID_ERR2(XML_RELAXNG_ERR_INTERNAL,
                       BAD_CAST "NULL definition list");
            return (-1);
        }
        while (defines != NULL) {
            if ((ctxt->state != NULL) || (ctxt->states != NULL)) {
                res = xmlRelaxNGValidateDefinition(ctxt, defines);
                if (res < 0)
                    ret = -1;
            } else {
                VALID_ERR(XML_RELAXNG_ERR_NOSTATE);
                return (-1);
            }
            if (res == -1)          /* continues on -2 */
                break;
            defines = defines->next;
        }
    
        return (ret);
    }
    
    /**
     * Check if the element matches the definition nameClass
     *
     * @param ctxt  a Relax-NG validation context
     * @param define  the definition to check
     * @param elem  the element
     * @returns 1 if the element matches, 0 if no, or -1 in case of error
     */
    static int
    xmlRelaxNGElementMatch(xmlRelaxNGValidCtxtPtr ctxt,
                           xmlRelaxNGDefinePtr define, xmlNodePtr elem)
    {
        int ret = 0, oldflags = 0;
    
        if (define->name != NULL) {
            if (!xmlStrEqual(elem->name, define->name)) {
                VALID_ERR3(XML_RELAXNG_ERR_ELEMNAME, define->name, elem->name);
                return (0);
            }
        }
        if ((define->ns != NULL) && (define->ns[0] != 0)) {
            if (elem->ns == NULL) {
                VALID_ERR2(XML_RELAXNG_ERR_ELEMNONS, elem->name);
                return (0);
            } else if (!xmlStrEqual(elem->ns->href, define->ns)) {
                VALID_ERR3(XML_RELAXNG_ERR_ELEMWRONGNS,
                           elem->name, define->ns);
                return (0);
            }
        } else if ((elem->ns != NULL) && (define->ns != NULL) &&
                   (define->name == NULL)) {
            VALID_ERR2(XML_RELAXNG_ERR_ELEMEXTRANS, elem->name);
            return (0);
        } else if ((elem->ns != NULL) && (define->name != NULL)) {
            VALID_ERR2(XML_RELAXNG_ERR_ELEMEXTRANS, define->name);
            return (0);
        }
    
        if (define->nameClass == NULL)
            return (1);
    
        define = define->nameClass;
        if (define->type == XML_RELAXNG_EXCEPT) {
            xmlRelaxNGDefinePtr list;
    
            if (ctxt != NULL) {
                oldflags = ctxt->flags;
                ctxt->flags |= FLAGS_IGNORABLE;
            }
    
            list = define->content;
            while (list != NULL) {
                ret = xmlRelaxNGElementMatch(ctxt, list, elem);
                if (ret == 1) {
                    if (ctxt != NULL)
                        ctxt->flags = oldflags;
                    return (0);
                }
                if (ret < 0) {
                    if (ctxt != NULL)
                        ctxt->flags = oldflags;
                    return (ret);
                }
                list = list->next;
            }
            ret = 1;
            if (ctxt != NULL) {
                ctxt->flags = oldflags;
            }
        } else if (define->type == XML_RELAXNG_CHOICE) {
            xmlRelaxNGDefinePtr list;
    
            if (ctxt != NULL) {
                oldflags = ctxt->flags;
                ctxt->flags |= FLAGS_IGNORABLE;
            }
    
            list = define->nameClass;
            while (list != NULL) {
                ret = xmlRelaxNGElementMatch(ctxt, list, elem);
                if (ret == 1) {
                    if (ctxt != NULL)
                        ctxt->flags = oldflags;
                    return (1);
                }
                if (ret < 0) {
                    if (ctxt != NULL)
                        ctxt->flags = oldflags;
                    return (ret);
                }
                list = list->next;
            }
            if (ctxt != NULL) {
                if (ret != 0) {
                    if ((ctxt->flags & FLAGS_IGNORABLE) == 0)
                        xmlRelaxNGDumpValidError(ctxt);
                } else {
                    if (ctxt->errNr > 0)
                        xmlRelaxNGPopErrors(ctxt, 0);
                }
            }
            ret = 0;
            if (ctxt != NULL) {
                ctxt->flags = oldflags;
            }
        } else {
            /* TODO */
            ret = -1;
        }
        return (ret);
    }
    
    /**
     * Find the "best" state in the ctxt->states list of states to report
     * errors about. I.e. a state with no element left in the child list
     * or the one with the less attributes left.
     * This is called only if a validation error was detected
     *
     * @param ctxt  a Relax-NG validation context
     * @returns the index of the "best" state or -1 in case of error
     */
    static int
    xmlRelaxNGBestState(xmlRelaxNGValidCtxtPtr ctxt)
    {
        xmlRelaxNGValidStatePtr state;
        int i, tmp;
        int best = -1;
        int value = 1000000;
    
        if ((ctxt == NULL) || (ctxt->states == NULL) ||
            (ctxt->states->nbState <= 0))
            return (-1);
    
        for (i = 0; i < ctxt->states->nbState; i++) {
            state = ctxt->states->tabState[i];
            if (state == NULL)
                continue;
            if (state->seq != NULL) {
                if ((best == -1) || (value > 100000)) {
                    value = 100000;
                    best = i;
                }
            } else {
                tmp = state->nbAttrLeft;
                if ((best == -1) || (value > tmp)) {
                    value = tmp;
                    best = i;
                }
            }
        }
        return (best);
    }
    
    /**
     * Find the "best" state in the ctxt->states list of states to report
     * errors about and log it.
     *
     * @param ctxt  a Relax-NG validation context
     */
    static void
    xmlRelaxNGLogBestError(xmlRelaxNGValidCtxtPtr ctxt)
    {
        int best;
    
        if ((ctxt == NULL) || (ctxt->states == NULL) ||
            (ctxt->states->nbState <= 0))
            return;
    
        best = xmlRelaxNGBestState(ctxt);
        if ((best >= 0) && (best < ctxt->states->nbState)) {
            ctxt->state = ctxt->states->tabState[best];
    
            xmlRelaxNGValidateElementEnd(ctxt, 1);
        }
    }
    
    /**
     * Validate the end of the element, implements check that
     * there is nothing left not consumed in the element content
     * or in the attribute list.
     *
     * @param ctxt  a Relax-NG validation context
     * @param dolog  indicate that error logging should be done
     * @returns 0 if the validation succeeded or an error code.
     */
    static int
    xmlRelaxNGValidateElementEnd(xmlRelaxNGValidCtxtPtr ctxt, int dolog)
    {
        int i;
        xmlRelaxNGValidStatePtr state;
    
        state = ctxt->state;
        if (state->seq != NULL) {
            state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq);
            if (state->seq != NULL) {
                if (dolog) {
                    VALID_ERR3(XML_RELAXNG_ERR_EXTRACONTENT,
                               state->node->name, state->seq->name);
                }
                return (-1);
            }
        }
        for (i = 0; i < state->nbAttrs; i++) {
            if (state->attrs[i] != NULL) {
                if (dolog) {
                    VALID_ERR3(XML_RELAXNG_ERR_INVALIDATTR,
                               state->attrs[i]->name, state->node->name);
                }
                return (-1 - i);
            }
        }
        return (0);
    }
    
    /**
     * Validate the current state against the definition
     *
     * @param ctxt  a Relax-NG validation context
     * @param define  the definition to verify
     * @returns 0 if the validation succeeded or an error code.
     */
    static int
    xmlRelaxNGValidateState(xmlRelaxNGValidCtxtPtr ctxt,
                            xmlRelaxNGDefinePtr define)
    {
        xmlNodePtr node;
        int ret = 0, i, tmp, oldflags, errNr;
        xmlRelaxNGValidStatePtr oldstate = NULL, state;
    
        if (define == NULL) {
            VALID_ERR(XML_RELAXNG_ERR_NODEFINE);
            return (-1);
        }
    
        if (ctxt->state != NULL) {
            node = ctxt->state->seq;
        } else {
            node = NULL;
        }
        ctxt->depth++;
        switch (define->type) {
            case XML_RELAXNG_EMPTY:
                ret = 0;
                break;
            case XML_RELAXNG_NOT_ALLOWED:
                ret = -1;
                break;
            case XML_RELAXNG_TEXT:
                while ((node != NULL) &&
                       ((node->type == XML_TEXT_NODE) ||
                        (node->type == XML_COMMENT_NODE) ||
                        (node->type == XML_PI_NODE) ||
                        (node->type == XML_CDATA_SECTION_NODE)))
                    node = node->next;
                ctxt->state->seq = node;
                break;
            case XML_RELAXNG_ELEMENT:
                errNr = ctxt->errNr;
                node = xmlRelaxNGSkipIgnored(ctxt, node);
                if (node == NULL) {
                    VALID_ERR2(XML_RELAXNG_ERR_NOELEM, define->name);
                    ret = -1;
                    if ((ctxt->flags & FLAGS_IGNORABLE) == 0)
                        xmlRelaxNGDumpValidError(ctxt);
                    break;
                }
                if (node->type != XML_ELEMENT_NODE) {
                    VALID_ERR(XML_RELAXNG_ERR_NOTELEM);
                    ret = -1;
                    if ((ctxt->flags & FLAGS_IGNORABLE) == 0)
                        xmlRelaxNGDumpValidError(ctxt);
                    break;
                }
                /*
                 * This node was already validated successfully against
                 * this definition.
                 */
                if (node->psvi == define) {
                    ctxt->state->seq = xmlRelaxNGSkipIgnored(ctxt, node->next);
                    if (ctxt->errNr > errNr)
                        xmlRelaxNGPopErrors(ctxt, errNr);
                    if (ctxt->errNr != 0) {
                        while ((ctxt->err != NULL) &&
                               (((ctxt->err->err == XML_RELAXNG_ERR_ELEMNAME)
                                 && (xmlStrEqual(ctxt->err->arg2, node->name)))
                                ||
                                ((ctxt->err->err ==
                                  XML_RELAXNG_ERR_ELEMEXTRANS)
                                 && (xmlStrEqual(ctxt->err->arg1, node->name)))
                                || (ctxt->err->err == XML_RELAXNG_ERR_NOELEM)
                                || (ctxt->err->err ==
                                    XML_RELAXNG_ERR_NOTELEM)))
                            xmlRelaxNGValidErrorPop(ctxt);
                    }
                    break;
                }
    
                ret = xmlRelaxNGElementMatch(ctxt, define, node);
                if (ret <= 0) {
                    ret = -1;
                    if ((ctxt->flags & FLAGS_IGNORABLE) == 0)
                        xmlRelaxNGDumpValidError(ctxt);
                    break;
                }
                ret = 0;
                if (ctxt->errNr != 0) {
                    if (ctxt->errNr > errNr)
                        xmlRelaxNGPopErrors(ctxt, errNr);
                    while ((ctxt->err != NULL) &&
                           (((ctxt->err->err == XML_RELAXNG_ERR_ELEMNAME) &&
                             (xmlStrEqual(ctxt->err->arg2, node->name))) ||
                            ((ctxt->err->err == XML_RELAXNG_ERR_ELEMEXTRANS) &&
                             (xmlStrEqual(ctxt->err->arg1, node->name))) ||
                            (ctxt->err->err == XML_RELAXNG_ERR_NOELEM) ||
                            (ctxt->err->err == XML_RELAXNG_ERR_NOTELEM)))
                        xmlRelaxNGValidErrorPop(ctxt);
                }
                errNr = ctxt->errNr;
    
                oldflags = ctxt->flags;
                if (ctxt->flags & FLAGS_MIXED_CONTENT) {
                    ctxt->flags -= FLAGS_MIXED_CONTENT;
                }
                state = xmlRelaxNGNewValidState(ctxt, node);
                if (state == NULL) {
                    ret = -1;
                    if ((ctxt->flags & FLAGS_IGNORABLE) == 0)
                        xmlRelaxNGDumpValidError(ctxt);
                    break;
                }
    
                oldstate = ctxt->state;
                ctxt->state = state;
                if (define->attrs != NULL) {
                    tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
                    if (tmp != 0) {
                        ret = -1;
                        VALID_ERR2(XML_RELAXNG_ERR_ATTRVALID, node->name);
                    }
                }
                if (define->contModel != NULL) {
                    xmlRelaxNGValidStatePtr nstate, tmpstate = ctxt->state;
                    xmlRelaxNGStatesPtr tmpstates = ctxt->states;
                    xmlNodePtr nseq;
    
                    nstate = xmlRelaxNGNewValidState(ctxt, node);
                    ctxt->state = nstate;
                    ctxt->states = NULL;
    
                    tmp = xmlRelaxNGValidateCompiledContent(ctxt,
                                                            define->contModel,
                                                            ctxt->state->seq);
                    nseq = ctxt->state->seq;
                    ctxt->state = tmpstate;
                    ctxt->states = tmpstates;
                    xmlRelaxNGFreeValidState(ctxt, nstate);
    
                    if (tmp != 0)
                        ret = -1;
    
                    if (ctxt->states != NULL) {
                        tmp = -1;
    
                        for (i = 0; i < ctxt->states->nbState; i++) {
                            state = ctxt->states->tabState[i];
                            ctxt->state = state;
                            ctxt->state->seq = nseq;
    
                            if (xmlRelaxNGValidateElementEnd(ctxt, 0) == 0) {
                                tmp = 0;
                                break;
                            }
                        }
                        if (tmp != 0) {
                            /*
                             * validation error, log the message for the "best" one
                             */
                            ctxt->flags |= FLAGS_IGNORABLE;
                            xmlRelaxNGLogBestError(ctxt);
                        }
                        for (i = 0; i < ctxt->states->nbState; i++) {
                            xmlRelaxNGFreeValidState(ctxt,
                                                     ctxt->states->
                                                     tabState[i]);
                        }
                        xmlRelaxNGFreeStates(ctxt, ctxt->states);
                        ctxt->flags = oldflags;
                        ctxt->states = NULL;
                        if ((ret == 0) && (tmp == -1))
                            ret = -1;
                    } else {
                        state = ctxt->state;
    		    if (ctxt->state != NULL)
    			ctxt->state->seq = nseq;
                        if (ret == 0)
                            ret = xmlRelaxNGValidateElementEnd(ctxt, 1);
                        xmlRelaxNGFreeValidState(ctxt, state);
                    }
                } else {
                    if (define->content != NULL) {
                        tmp = xmlRelaxNGValidateDefinitionList(ctxt,
                                                               define->
                                                               content);
                        if (tmp != 0) {
                            ret = -1;
                            if (ctxt->state == NULL) {
                                ctxt->state = oldstate;
                                VALID_ERR2(XML_RELAXNG_ERR_CONTENTVALID,
                                           node->name);
                                ctxt->state = NULL;
                            } else {
                                VALID_ERR2(XML_RELAXNG_ERR_CONTENTVALID,
                                           node->name);
                            }
    
                        }
                    }
                    if (ctxt->states != NULL) {
                        tmp = -1;
    
                        for (i = 0; i < ctxt->states->nbState; i++) {
                            state = ctxt->states->tabState[i];
                            ctxt->state = state;
    
                            if (xmlRelaxNGValidateElementEnd(ctxt, 0) == 0) {
                                tmp = 0;
                                break;
                            }
                        }
                        if (tmp != 0) {
                            /*
                             * validation error, log the message for the "best" one
                             */
                            ctxt->flags |= FLAGS_IGNORABLE;
                            xmlRelaxNGLogBestError(ctxt);
                        }
                        for (i = 0; i < ctxt->states->nbState; i++) {
                            xmlRelaxNGFreeValidState(ctxt,
                                                     ctxt->states->tabState[i]);
                            ctxt->states->tabState[i] = NULL;
                        }
                        xmlRelaxNGFreeStates(ctxt, ctxt->states);
                        ctxt->flags = oldflags;
                        ctxt->states = NULL;
                        if ((ret == 0) && (tmp == -1))
                            ret = -1;
                    } else {
                        state = ctxt->state;
                        if (ret == 0)
                            ret = xmlRelaxNGValidateElementEnd(ctxt, 1);
                        xmlRelaxNGFreeValidState(ctxt, state);
                    }
                }
                if (ret == 0) {
                    node->psvi = define;
                }
                ctxt->flags = oldflags;
                ctxt->state = oldstate;
                if (oldstate != NULL)
                    oldstate->seq = xmlRelaxNGSkipIgnored(ctxt, node->next);
                if (ret != 0) {
                    if ((ctxt->flags & FLAGS_IGNORABLE) == 0) {
                        xmlRelaxNGDumpValidError(ctxt);
                        ret = 0;
    #if 0
                    } else {
                        ret = -2;
    #endif
                    }
                } else {
                    if (ctxt->errNr > errNr)
                        xmlRelaxNGPopErrors(ctxt, errNr);
                }
    
                break;
            case XML_RELAXNG_OPTIONAL:{
                    errNr = ctxt->errNr;
                    oldflags = ctxt->flags;
                    ctxt->flags |= FLAGS_IGNORABLE;
                    oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
                    ret =
                        xmlRelaxNGValidateDefinitionList(ctxt,
                                                         define->content);
                    if (ret != 0) {
                        if (ctxt->state != NULL)
                            xmlRelaxNGFreeValidState(ctxt, ctxt->state);
                        ctxt->state = oldstate;
                        ctxt->flags = oldflags;
                        ret = 0;
                        if (ctxt->errNr > errNr)
                            xmlRelaxNGPopErrors(ctxt, errNr);
                        break;
                    }
                    if (ctxt->states != NULL) {
                        xmlRelaxNGAddStates(ctxt, ctxt->states, oldstate);
                    } else {
                        ctxt->states = xmlRelaxNGNewStates(ctxt, 1);
                        if (ctxt->states == NULL) {
                            xmlRelaxNGFreeValidState(ctxt, oldstate);
                            ctxt->flags = oldflags;
                            ret = -1;
                            if (ctxt->errNr > errNr)
                                xmlRelaxNGPopErrors(ctxt, errNr);
                            break;
                        }
                        xmlRelaxNGAddStates(ctxt, ctxt->states, oldstate);
                        xmlRelaxNGAddStates(ctxt, ctxt->states, ctxt->state);
                        ctxt->state = NULL;
                    }
                    ctxt->flags = oldflags;
                    ret = 0;
                    if (ctxt->errNr > errNr)
                        xmlRelaxNGPopErrors(ctxt, errNr);
                    break;
                }
            case XML_RELAXNG_ONEORMORE:
                errNr = ctxt->errNr;
                ret = xmlRelaxNGValidateDefinitionList(ctxt, define->content);
                if (ret != 0) {
                    break;
                }
                if (ctxt->errNr > errNr)
                    xmlRelaxNGPopErrors(ctxt, errNr);
                /* Falls through. */
            case XML_RELAXNG_ZEROORMORE:{
                    int progress;
                    xmlRelaxNGStatesPtr states = NULL, res = NULL;
                    int base, j;
    
                    errNr = ctxt->errNr;
                    res = xmlRelaxNGNewStates(ctxt, 1);
                    if (res == NULL) {
                        ret = -1;
                        break;
                    }
                    /*
                     * All the input states are also exit states
                     */
                    if (ctxt->state != NULL) {
                        xmlRelaxNGAddStates(ctxt, res,
                                            xmlRelaxNGCopyValidState(ctxt,
                                                                     ctxt->
                                                                     state));
                    } else {
                        for (j = 0; j < ctxt->states->nbState; j++) {
                            xmlRelaxNGAddStates(ctxt, res,
                                xmlRelaxNGCopyValidState(ctxt,
                                                ctxt->states->tabState[j]));
                        }
                    }
                    oldflags = ctxt->flags;
                    ctxt->flags |= FLAGS_IGNORABLE;
                    do {
                        progress = 0;
                        base = res->nbState;
    
                        if (ctxt->states != NULL) {
                            states = ctxt->states;
                            for (i = 0; i < states->nbState; i++) {
                                ctxt->state = states->tabState[i];
                                ctxt->states = NULL;
                                ret = xmlRelaxNGValidateDefinitionList(ctxt,
                                                                       define->
                                                                       content);
                                if (ret == 0) {
                                    if (ctxt->state != NULL) {
                                        tmp = xmlRelaxNGAddStates(ctxt, res,
                                                                  ctxt->state);
                                        ctxt->state = NULL;
                                        if (tmp == 1)
                                            progress = 1;
                                    } else if (ctxt->states != NULL) {
                                        for (j = 0; j < ctxt->states->nbState;
                                             j++) {
                                            tmp =
                                                xmlRelaxNGAddStates(ctxt, res,
                                                       ctxt->states->tabState[j]);
                                            if (tmp == 1)
                                                progress = 1;
                                        }
                                        xmlRelaxNGFreeStates(ctxt,
                                                             ctxt->states);
                                        ctxt->states = NULL;
                                    }
                                } else {
                                    if (ctxt->state != NULL) {
                                        xmlRelaxNGFreeValidState(ctxt,
                                                                 ctxt->state);
                                        ctxt->state = NULL;
                                    }
                                }
                            }
                        } else {
                            ret = xmlRelaxNGValidateDefinitionList(ctxt,
                                                                   define->
                                                                   content);
                            if (ret != 0) {
                                xmlRelaxNGFreeValidState(ctxt, ctxt->state);
                                ctxt->state = NULL;
                            } else {
                                base = res->nbState;
                                if (ctxt->state != NULL) {
                                    tmp = xmlRelaxNGAddStates(ctxt, res,
                                                              ctxt->state);
                                    ctxt->state = NULL;
                                    if (tmp == 1)
                                        progress = 1;
                                } else if (ctxt->states != NULL) {
                                    for (j = 0; j < ctxt->states->nbState; j++) {
                                        tmp = xmlRelaxNGAddStates(ctxt, res,
                                                   ctxt->states->tabState[j]);
                                        if (tmp == 1)
                                            progress = 1;
                                    }
                                    if (states == NULL) {
                                        states = ctxt->states;
                                    } else {
                                        xmlRelaxNGFreeStates(ctxt,
                                                             ctxt->states);
                                    }
                                    ctxt->states = NULL;
                                }
                            }
                        }
                        if (progress) {
                            /*
                             * Collect all the new nodes added at that step
                             * and make them the new node set
                             */
                            if (res->nbState - base == 1) {
                                ctxt->state = xmlRelaxNGCopyValidState(ctxt,
                                                                       res->
                                                                       tabState
                                                                       [base]);
                            } else {
                                if (states == NULL) {
                                    xmlRelaxNGNewStates(ctxt,
                                                        res->nbState - base);
    			        states = ctxt->states;
    				if (states == NULL) {
    				    progress = 0;
    				    break;
    				}
                                }
                                states->nbState = 0;
                                for (i = base; i < res->nbState; i++)
                                    xmlRelaxNGAddStates(ctxt, states,
                                                        xmlRelaxNGCopyValidState
                                                        (ctxt, res->tabState[i]));
                                ctxt->states = states;
                            }
                        }
                    } while (progress == 1);
                    if (states != NULL) {
                        xmlRelaxNGFreeStates(ctxt, states);
                    }
                    ctxt->states = res;
                    ctxt->flags = oldflags;
    #if 0
                    /*
                     * errors may have to be propagated back...
                     */
                    if (ctxt->errNr > errNr)
                        xmlRelaxNGPopErrors(ctxt, errNr);
    #endif
                    ret = 0;
                    break;
                }
            case XML_RELAXNG_CHOICE:{
                    xmlRelaxNGDefinePtr list = NULL;
                    xmlRelaxNGStatesPtr states = NULL;
    
                    node = xmlRelaxNGSkipIgnored(ctxt, node);
    
                    errNr = ctxt->errNr;
                    if ((define->dflags & IS_TRIABLE) && (define->data != NULL) &&
    		    (node != NULL)) {
    		    /*
    		     * node == NULL can't be optimized since IS_TRIABLE
    		     * doesn't account for choice which may lead to
    		     * only attributes.
    		     */
                        xmlHashTablePtr triage =
                            (xmlHashTablePtr) define->data;
    
                        /*
                         * Something we can optimize cleanly there is only one
                         * possible branch out !
                         */
                        if ((node->type == XML_TEXT_NODE) ||
                            (node->type == XML_CDATA_SECTION_NODE)) {
                            list =
                                xmlHashLookup2(triage, BAD_CAST "#text", NULL);
                        } else if (node->type == XML_ELEMENT_NODE) {
                            if (node->ns != NULL) {
                                list = xmlHashLookup2(triage, node->name,
                                                      node->ns->href);
                                if (list == NULL)
                                    list =
                                        xmlHashLookup2(triage, BAD_CAST "#any",
                                                       node->ns->href);
                            } else
                                list =
                                    xmlHashLookup2(triage, node->name, NULL);
                            if (list == NULL)
                                list =
                                    xmlHashLookup2(triage, BAD_CAST "#any",
                                                   NULL);
                        }
                        if (list == NULL) {
                            ret = -1;
    			VALID_ERR2(XML_RELAXNG_ERR_ELEMWRONG, node->name);
                            break;
                        }
                        ret = xmlRelaxNGValidateDefinition(ctxt, list);
                        if (ret == 0) {
                        }
                        break;
                    }
    
                    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) {
                            if (states == NULL) {
                                states = xmlRelaxNGNewStates(ctxt, 1);
                            }
                            if (ctxt->state != NULL) {
                                xmlRelaxNGAddStates(ctxt, states, ctxt->state);
                            } else if (ctxt->states != NULL) {
                                for (i = 0; i < ctxt->states->nbState; i++) {
                                    xmlRelaxNGAddStates(ctxt, states,
                                                        ctxt->states->
                                                        tabState[i]);
                                }
                                xmlRelaxNGFreeStates(ctxt, ctxt->states);
                                ctxt->states = NULL;
                            }
                        } else {
                            xmlRelaxNGFreeValidState(ctxt, ctxt->state);
                        }
                        ctxt->state = oldstate;
                        list = list->next;
                    }
                    if (states != NULL) {
                        xmlRelaxNGFreeValidState(ctxt, oldstate);
                        ctxt->states = states;
                        ctxt->state = NULL;
                        ret = 0;
                    } else {
                        ctxt->states = NULL;
                    }
                    ctxt->flags = oldflags;
                    if (ret != 0) {
                        if ((ctxt->flags & FLAGS_IGNORABLE) == 0) {
                            xmlRelaxNGDumpValidError(ctxt);
                        }
                    } else {
                        if (ctxt->errNr > errNr)
                            xmlRelaxNGPopErrors(ctxt, errNr);
                    }
                    break;
                }
            case XML_RELAXNG_DEF:
            case XML_RELAXNG_GROUP:
                ret = xmlRelaxNGValidateDefinitionList(ctxt, define->content);
                break;
            case XML_RELAXNG_INTERLEAVE:
                ret = xmlRelaxNGValidateInterleave(ctxt, define);
                break;
            case XML_RELAXNG_ATTRIBUTE:
                ret = xmlRelaxNGValidateAttribute(ctxt, define);
                break;
            case XML_RELAXNG_START:
            case XML_RELAXNG_NOOP:
            case XML_RELAXNG_REF:
            case XML_RELAXNG_EXTERNALREF:
            case XML_RELAXNG_PARENTREF:
                ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
                break;
            case XML_RELAXNG_DATATYPE:{
                    xmlNodePtr child;
                    xmlChar *content = NULL;
    
                    child = node;
                    while (child != NULL) {
                        if (child->type == XML_ELEMENT_NODE) {
                            VALID_ERR2(XML_RELAXNG_ERR_DATAELEM,
                                       node->parent->name);
                            ret = -1;
                            break;
                        } else if ((child->type == XML_TEXT_NODE) ||
                                   (child->type == XML_CDATA_SECTION_NODE)) {
                            content = xmlStrcat(content, child->content);
                        }
                        /* TODO: handle entities ... */
                        child = child->next;
                    }
                    if (ret == -1) {
                        if (content != NULL)
                            xmlFree(content);
                        break;
                    }
                    if (content == NULL) {
                        content = xmlStrdup(BAD_CAST "");
                        if (content == NULL) {
                            xmlRngVErrMemory(ctxt);
                            ret = -1;
                            break;
                        }
                    }
                    ret = xmlRelaxNGValidateDatatype(ctxt, content, define,
                                                     ctxt->state->seq);
                    if (ret == -1) {
                        VALID_ERR2(XML_RELAXNG_ERR_DATATYPE, define->name);
                    } else if (ret == 0) {
                        ctxt->state->seq = NULL;
                    }
                    if (content != NULL)
                        xmlFree(content);
                    break;
                }
            case XML_RELAXNG_VALUE:{
                    xmlChar *content = NULL;
                    xmlChar *oldvalue;
                    xmlNodePtr child;
    
                    child = node;
                    while (child != NULL) {
                        if (child->type == XML_ELEMENT_NODE) {
                            VALID_ERR2(XML_RELAXNG_ERR_VALELEM,
                                       node->parent->name);
                            ret = -1;
                            break;
                        } else if ((child->type == XML_TEXT_NODE) ||
                                   (child->type == XML_CDATA_SECTION_NODE)) {
                            content = xmlStrcat(content, child->content);
                        }
                        /* TODO: handle entities ... */
                        child = child->next;
                    }
                    if (ret == -1) {
                        if (content != NULL)
                            xmlFree(content);
                        break;
                    }
                    if (content == NULL) {
                        content = xmlStrdup(BAD_CAST "");
                        if (content == NULL) {
                            xmlRngVErrMemory(ctxt);
                            ret = -1;
                            break;
                        }
                    }
                    oldvalue = ctxt->state->value;
                    ctxt->state->value = content;
                    ret = xmlRelaxNGValidateValue(ctxt, define);
                    ctxt->state->value = oldvalue;
                    if (ret == -1) {
                        VALID_ERR2(XML_RELAXNG_ERR_VALUE, define->name);
                    } else if (ret == 0) {
                        ctxt->state->seq = NULL;
                    }
                    if (content != NULL)
                        xmlFree(content);
                    break;
                }
            case XML_RELAXNG_LIST:{
                    xmlChar *content;
                    xmlNodePtr child;
                    xmlChar *oldvalue, *oldendvalue;
                    int len;
    
                    /*
                     * Make sure it's only text nodes
                     */
    
                    content = NULL;
                    child = node;
                    while (child != NULL) {
                        if (child->type == XML_ELEMENT_NODE) {
                            VALID_ERR2(XML_RELAXNG_ERR_LISTELEM,
                                       node->parent->name);
                            ret = -1;
                            break;
                        } else if ((child->type == XML_TEXT_NODE) ||
                                   (child->type == XML_CDATA_SECTION_NODE)) {
                            content = xmlStrcat(content, child->content);
                        }
                        /* TODO: handle entities ... */
                        child = child->next;
                    }
                    if (ret == -1) {
                        if (content != NULL)
                            xmlFree(content);
                        break;
                    }
                    if (content == NULL) {
                        content = xmlStrdup(BAD_CAST "");
                        if (content == NULL) {
                            xmlRngVErrMemory(ctxt);
                            ret = -1;
                            break;
                        }
                    }
                    len = xmlStrlen(content);
                    oldvalue = ctxt->state->value;
                    oldendvalue = ctxt->state->endvalue;
                    ctxt->state->value = content;
                    ctxt->state->endvalue = content + len;
                    ret = xmlRelaxNGValidateValue(ctxt, define);
                    ctxt->state->value = oldvalue;
                    ctxt->state->endvalue = oldendvalue;
                    if (ret == -1) {
                        VALID_ERR(XML_RELAXNG_ERR_LIST);
                    } else if ((ret == 0) && (node != NULL)) {
                        ctxt->state->seq = node->next;
                    }
                    if (content != NULL)
                        xmlFree(content);
                    break;
                }
            case XML_RELAXNG_EXCEPT:
            case XML_RELAXNG_PARAM:
                /* TODO */
                ret = -1;
                break;
        }
        ctxt->depth--;
        return (ret);
    }
    
    /**
     * Validate the current node lists against the definition
     *
     * @param ctxt  a Relax-NG validation context
     * @param define  the definition to verify
     * @returns 0 if the validation succeeded or an error code.
     */
    static int
    xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
                                 xmlRelaxNGDefinePtr define)
    {
        xmlRelaxNGStatesPtr states, res;
        int i, j, k, ret, oldflags;
    
        /*
         * We should NOT have both ctxt->state and ctxt->states
         */
        if ((ctxt->state != NULL) && (ctxt->states != NULL)) {
            /* TODO */
            xmlRelaxNGFreeValidState(ctxt, ctxt->state);
            ctxt->state = NULL;
        }
    
        if ((ctxt->states == NULL) || (ctxt->states->nbState == 1)) {
            if (ctxt->states != NULL) {
                ctxt->state = ctxt->states->tabState[0];
                xmlRelaxNGFreeStates(ctxt, ctxt->states);
                ctxt->states = NULL;
            }
            ret = xmlRelaxNGValidateState(ctxt, define);
            if ((ctxt->state != NULL) && (ctxt->states != NULL)) {
                /* TODO */
                xmlRelaxNGFreeValidState(ctxt, ctxt->state);
                ctxt->state = NULL;
            }
            if ((ctxt->states != NULL) && (ctxt->states->nbState == 1)) {
                ctxt->state = ctxt->states->tabState[0];
                xmlRelaxNGFreeStates(ctxt, ctxt->states);
                ctxt->states = NULL;
            }
            return (ret);
        }
    
        states = ctxt->states;
        ctxt->states = NULL;
        res = NULL;
        j = 0;
        oldflags = ctxt->flags;
        ctxt->flags |= FLAGS_IGNORABLE;
        for (i = 0; i < states->nbState; i++) {
            ctxt->state = states->tabState[i];
            ctxt->states = NULL;
            ret = xmlRelaxNGValidateState(ctxt, define);
            /*
             * We should NOT have both ctxt->state and ctxt->states
             */
            if ((ctxt->state != NULL) && (ctxt->states != NULL)) {
                /* TODO */
                xmlRelaxNGFreeValidState(ctxt, ctxt->state);
                ctxt->state = NULL;
            }
            if (ret == 0) {
                if (ctxt->states == NULL) {
                    if (res != NULL) {
                        /* add the state to the container */
                        xmlRelaxNGAddStates(ctxt, res, ctxt->state);
                        ctxt->state = NULL;
                    } else {
                        /* add the state directly in states */
                        states->tabState[j++] = ctxt->state;
                        ctxt->state = NULL;
                    }
                } else {
                    if (res == NULL) {
                        /* make it the new container and copy other results */
                        res = ctxt->states;
                        ctxt->states = NULL;
                        for (k = 0; k < j; k++)
                            xmlRelaxNGAddStates(ctxt, res,
                                                states->tabState[k]);
                    } else {
                        /* add all the new results to res and reff the container */
                        for (k = 0; k < ctxt->states->nbState; k++)
                            xmlRelaxNGAddStates(ctxt, res,
                                                ctxt->states->tabState[k]);
                        xmlRelaxNGFreeStates(ctxt, ctxt->states);
                        ctxt->states = NULL;
                    }
                }
            } else {
                if (ctxt->state != NULL) {
                    xmlRelaxNGFreeValidState(ctxt, ctxt->state);
                    ctxt->state = NULL;
                } else if (ctxt->states != NULL) {
                    for (k = 0; k < ctxt->states->nbState; k++)
                        xmlRelaxNGFreeValidState(ctxt,
                                                 ctxt->states->tabState[k]);
                    xmlRelaxNGFreeStates(ctxt, ctxt->states);
                    ctxt->states = NULL;
                }
            }
        }
        ctxt->flags = oldflags;
        if (res != NULL) {
            xmlRelaxNGFreeStates(ctxt, states);
            ctxt->states = res;
            ret = 0;
        } else if (j > 1) {
            states->nbState = j;
            ctxt->states = states;
            ret = 0;
        } else if (j == 1) {
            ctxt->state = states->tabState[0];
            xmlRelaxNGFreeStates(ctxt, states);
            ret = 0;
        } else {
            ret = -1;
            xmlRelaxNGFreeStates(ctxt, states);
            if (ctxt->states != NULL) {
                xmlRelaxNGFreeStates(ctxt, ctxt->states);
                ctxt->states = NULL;
            }
        }
        if ((ctxt->state != NULL) && (ctxt->states != NULL)) {
            /* TODO */
            xmlRelaxNGFreeValidState(ctxt, ctxt->state);
            ctxt->state = NULL;
        }
        return (ret);
    }
    
    /**
     * Validate the given document
     *
     * @param ctxt  a Relax-NG validation context
     * @param doc  the 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;
        xmlNodePtr node;
    
        if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL))
            return (-1);
    
        ctxt->errNo = XML_RELAXNG_OK;
        schema = ctxt->schema;
        grammar = schema->topgrammar;
        if (grammar == NULL) {
            VALID_ERR(XML_RELAXNG_ERR_NOGRAMMAR);
            return (-1);
        }
        state = xmlRelaxNGNewValidState(ctxt, NULL);
        ctxt->state = state;
        ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start);
        if ((ctxt->state != NULL) && (state->seq != NULL)) {
            state = ctxt->state;
            node = state->seq;
            node = xmlRelaxNGSkipIgnored(ctxt, node);
            if (node != NULL) {
                if (ret != -1) {
                    VALID_ERR(XML_RELAXNG_ERR_EXTRADATA);
                    ret = -1;
                }
            }
        } else if (ctxt->states != NULL) {
            int i;
            int tmp = -1;
    
            for (i = 0; i < ctxt->states->nbState; i++) {
                state = ctxt->states->tabState[i];
                node = state->seq;
                node = xmlRelaxNGSkipIgnored(ctxt, node);
                if (node == NULL)
                    tmp = 0;
                xmlRelaxNGFreeValidState(ctxt, state);
            }
            if (tmp == -1) {
                if (ret != -1) {
                    VALID_ERR(XML_RELAXNG_ERR_EXTRADATA);
                    ret = -1;
                }
            }
        }
        if (ctxt->state != NULL) {
            xmlRelaxNGFreeValidState(ctxt, ctxt->state);
            ctxt->state = NULL;
        }
        if (ret != 0)
            xmlRelaxNGDumpValidError(ctxt);
    #ifdef LIBXML_VALID_ENABLED
        if (ctxt->idref == 1) {
            xmlValidCtxt vctxt;
    
            memset(&vctxt, 0, sizeof(xmlValidCtxt));
            vctxt.valid = 1;
    
            if (ctxt->error == NULL) {
                vctxt.error = xmlGenericError;
                vctxt.warning = xmlGenericError;
                vctxt.userData = xmlGenericErrorContext;
            } else {
                vctxt.error = ctxt->error;
                vctxt.warning = ctxt->warning;
                vctxt.userData = ctxt->userData;
            }
    
            if (xmlValidateDocumentFinal(&vctxt, doc) != 1)
                ret = -1;
        }
    #endif /* LIBXML_VALID_ENABLED */
        if ((ret == 0) && (ctxt->errNo != XML_RELAXNG_OK))
            ret = -1;
    
        return (ret);
    }
    
    /**
     * Call this routine to speed up XPath computation on static documents.
     * This stamps all the element nodes with the document order
     * Like for line information, the order is kept in the element->content
     * field, the value stored is actually - the node number (starting at -1)
     * to be able to differentiate from line numbers.
     *
     * @param node  an input element or document
     * @returns the number of elements found in the document or -1 in case
     *    of error.
     */
    static void
    xmlRelaxNGCleanPSVI(xmlNodePtr node) {
        xmlNodePtr cur;
    
        if ((node == NULL) ||
            ((node->type != XML_ELEMENT_NODE) &&
             (node->type != XML_DOCUMENT_NODE) &&
             (node->type != XML_HTML_DOCUMENT_NODE)))
    	return;
        if (node->type == XML_ELEMENT_NODE)
            node->psvi = NULL;
    
        cur = node->children;
        while (cur != NULL) {
    	if (cur->type == XML_ELEMENT_NODE) {
    	    cur->psvi = NULL;
    	    if (cur->children != NULL) {
    		cur = cur->children;
    		continue;
    	    }
    	}
    	if (cur->next != NULL) {
    	    cur = cur->next;
    	    continue;
    	}
    	do {
    	    cur = cur->parent;
    	    if (cur == NULL)
    		break;
    	    if (cur == node) {
    		cur = NULL;
    		break;
    	    }
    	    if (cur->next != NULL) {
    		cur = cur->next;
    		break;
    	    }
    	} while (cur != NULL);
        }
    }
    /************************************************************************
     *									*
     *			Validation interfaces				*
     *									*
     ************************************************************************/
    
    /**
     * Create an XML RelaxNGs validation context based on the given schema
     *
     * @param schema  a precompiled XML RelaxNGs
     * @returns the validation context or NULL in case of error
     */
    xmlRelaxNGValidCtxtPtr
    xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema)
    {
        xmlRelaxNGValidCtxtPtr ret;
    
        ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt));
        if (ret == NULL) {
            xmlRngVErrMemory(NULL);
            return (NULL);
        }
        memset(ret, 0, sizeof(xmlRelaxNGValidCtxt));
        ret->schema = schema;
        ret->errNr = 0;
        ret->errMax = 0;
        ret->err = NULL;
        ret->errTab = NULL;
        if (schema != NULL)
    	ret->idref = schema->idref;
        ret->states = NULL;
        ret->freeState = NULL;
        ret->freeStates = NULL;
        ret->errNo = XML_RELAXNG_OK;
        return (ret);
    }
    
    /**
     * Free the resources associated to the schema validation context
     *
     * @param ctxt  the schema validation context
     */
    void
    xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt)
    {
        int k;
    
        if (ctxt == NULL)
            return;
        if (ctxt->states != NULL)
            xmlRelaxNGFreeStates(NULL, ctxt->states);
        if (ctxt->freeState != NULL) {
            for (k = 0; k < ctxt->freeState->nbState; k++) {
                xmlRelaxNGFreeValidState(NULL, ctxt->freeState->tabState[k]);
            }
            xmlRelaxNGFreeStates(NULL, ctxt->freeState);
        }
        if (ctxt->freeStates != NULL) {
            for (k = 0; k < ctxt->freeStatesNr; k++) {
                xmlRelaxNGFreeStates(NULL, ctxt->freeStates[k]);
            }
            xmlFree(ctxt->freeStates);
        }
        if (ctxt->errTab != NULL)
            xmlFree(ctxt->errTab);
        if (ctxt->elemTab != NULL) {
            xmlRegExecCtxtPtr exec;
    
            exec = xmlRelaxNGElemPop(ctxt);
            while (exec != NULL) {
                xmlRegFreeExecCtxt(exec);
                exec = xmlRelaxNGElemPop(ctxt);
            }
            xmlFree(ctxt->elemTab);
        }
        xmlFree(ctxt);
    }
    
    /**
     * Set the error and warning callback information
     *
     * @deprecated Use xmlRelaxNGSetValidStructuredErrors().
     *
     * @param ctxt  a Relax-NG validation context
     * @param err  the error function
     * @param warn  the warning function
     * @param ctx  the functions context
     */
    void
    xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
                             xmlRelaxNGValidityErrorFunc err,
                             xmlRelaxNGValidityWarningFunc warn, void *ctx)
    {
        if (ctxt == NULL)
            return;
        ctxt->error = err;
        ctxt->warning = warn;
        ctxt->userData = ctx;
        ctxt->serror = NULL;
    }
    
    /**
     * Set the structured error callback
     *
     * @param ctxt  a Relax-NG validation context
     * @param serror  the structured error function
     * @param ctx  the functions context
     */
    void
    xmlRelaxNGSetValidStructuredErrors(xmlRelaxNGValidCtxtPtr ctxt,
                                       xmlStructuredErrorFunc serror, void *ctx)
    {
        if (ctxt == NULL)
            return;
        ctxt->serror = serror;
        ctxt->error = NULL;
        ctxt->warning = NULL;
        ctxt->userData = ctx;
    }
    
    /**
     * Get the error and warning callback information
     *
     * @param ctxt  a Relax-NG validation context
     * @param err  the error function result
     * @param warn  the warning function result
     * @param ctx  the functions context result
     * @returns -1 in case of error and 0 otherwise
     */
    int
    xmlRelaxNGGetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
                             xmlRelaxNGValidityErrorFunc * err,
                             xmlRelaxNGValidityWarningFunc * warn, void **ctx)
    {
        if (ctxt == NULL)
            return (-1);
        if (err != NULL)
            *err = ctxt->error;
        if (warn != NULL)
            *warn = ctxt->warning;
        if (ctx != NULL)
            *ctx = ctxt->userData;
        return (0);
    }
    
    /**
     * Validate a document tree in memory.
     *
     * @param ctxt  a Relax-NG validation context
     * @param doc  a parsed document tree
     * @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);
        /*
         * Remove all left PSVI
         */
        xmlRelaxNGCleanPSVI((xmlNodePtr) doc);
    
        /*
         * TODO: build error codes
         */
        if (ret == -1)
            return (1);
        return (ret);
    }
    
    #endif /* LIBXML_RELAXNG_ENABLED */