Edit

kc3-lang/libxml2/schematron.c

Branch :

  • Show log

    Commit

  • Author : Jared Yanovich
    Date : 2019-09-30 17:04:54
    Hash : 2a350ee9
    Message : Large batch of typo fixes Closes #109.

  • schematron.c
  • /*
     * schematron.c : implementation of the Schematron schema validity checking
     *
     * See Copyright for the status of this software.
     *
     * Daniel Veillard <daniel@veillard.com>
     */
    
    /*
     * TODO:
     * + double check the semantic, especially
     *        - multiple rules applying in a single pattern/node
     *        - the semantic of libxml2 patterns vs. XSLT production referenced
     *          by the spec.
     * + export of results in SVRL
     * + full parsing and coverage of the spec, conformance of the input to the
     *   spec
     * + divergences between the draft and the ISO proposed standard :-(
     * + hook and test include
     * + try and compare with the XSLT version
     */
    
    #define IN_LIBXML
    #include "libxml.h"
    
    #ifdef LIBXML_SCHEMATRON_ENABLED
    
    #include <string.h>
    #include <libxml/parser.h>
    #include <libxml/tree.h>
    #include <libxml/uri.h>
    #include <libxml/xpath.h>
    #include <libxml/xpathInternals.h>
    #include <libxml/pattern.h>
    #include <libxml/schematron.h>
    
    #define SCHEMATRON_PARSE_OPTIONS XML_PARSE_NOENT
    
    #define SCT_OLD_NS BAD_CAST "http://www.ascc.net/xml/schematron"
    
    #define XML_SCHEMATRON_NS BAD_CAST "http://purl.oclc.org/dsdl/schematron"
    
    
    static const xmlChar *xmlSchematronNs = XML_SCHEMATRON_NS;
    static const xmlChar *xmlOldSchematronNs = SCT_OLD_NS;
    
    #define IS_SCHEMATRON(node, elem)					\
       ((node != NULL) && (node->type == XML_ELEMENT_NODE ) &&		\
        (node->ns != NULL) &&						\
        (xmlStrEqual(node->name, (const xmlChar *) elem)) &&		\
        ((xmlStrEqual(node->ns->href, xmlSchematronNs)) ||			\
         (xmlStrEqual(node->ns->href, xmlOldSchematronNs))))
    
    #define NEXT_SCHEMATRON(node)						\
       while (node != NULL) {						\
           if ((node->type == XML_ELEMENT_NODE ) && (node->ns != NULL) &&	\
               ((xmlStrEqual(node->ns->href, xmlSchematronNs)) ||		\
    	    (xmlStrEqual(node->ns->href, xmlOldSchematronNs))))		\
    	   break;							\
           node = node->next;						\
       }
    
    /**
     * TODO:
     *
     * macro to flag unimplemented blocks
     */
    #define TODO								\
        xmlGenericError(xmlGenericErrorContext,				\
    	    "Unimplemented block at %s:%d\n",				\
                __FILE__, __LINE__);
    
    typedef enum {
        XML_SCHEMATRON_ASSERT=1,
        XML_SCHEMATRON_REPORT=2
    } xmlSchematronTestType;
    
    /**
     * _xmlSchematronTest:
     *
     * A Schematrons test, either an assert or a report
     */
    typedef struct _xmlSchematronTest xmlSchematronTest;
    typedef xmlSchematronTest *xmlSchematronTestPtr;
    struct _xmlSchematronTest {
        xmlSchematronTestPtr next;	/* the next test in the list */
        xmlSchematronTestType type;	/* the test type */
        xmlNodePtr node;		/* the node in the tree */
        xmlChar *test;		/* the expression to test */
        xmlXPathCompExprPtr comp;	/* the compiled expression */
        xmlChar *report;		/* the message to report */
    };
    
    /**
     * _xmlSchematronRule:
     *
     * A Schematrons rule
     */
    typedef struct _xmlSchematronRule xmlSchematronRule;
    typedef xmlSchematronRule *xmlSchematronRulePtr;
    struct _xmlSchematronRule {
        xmlSchematronRulePtr next;	/* the next rule in the list */
        xmlSchematronRulePtr patnext;/* the next rule in the pattern list */
        xmlNodePtr node;		/* the node in the tree */
        xmlChar *context;		/* the context evaluation rule */
        xmlSchematronTestPtr tests;	/* the list of tests */
        xmlPatternPtr pattern;	/* the compiled pattern associated */
        xmlChar *report;		/* the message to report */
    };
    
    /**
     * _xmlSchematronPattern:
     *
     * A Schematrons pattern
     */
    typedef struct _xmlSchematronPattern xmlSchematronPattern;
    typedef xmlSchematronPattern *xmlSchematronPatternPtr;
    struct _xmlSchematronPattern {
        xmlSchematronPatternPtr next;/* the next pattern in the list */
        xmlSchematronRulePtr rules;	/* the list of rules */
        xmlChar *name;		/* the name of the pattern */
    };
    
    /**
     * _xmlSchematron:
     *
     * A Schematrons definition
     */
    struct _xmlSchematron {
        const xmlChar *name;	/* schema name */
        int preserve;		/* was the document passed by the user */
        xmlDocPtr doc;		/* pointer to the parsed document */
        int flags;			/* specific to this schematron */
    
        void *_private;		/* unused by the library */
        xmlDictPtr dict;		/* the dictionary used internally */
    
        const xmlChar *title;	/* the title if any */
    
        int nbNs;			/* the number of namespaces */
    
        int nbPattern;		/* the number of patterns */
        xmlSchematronPatternPtr patterns;/* the patterns found */
        xmlSchematronRulePtr rules;	/* the rules gathered */
        int nbNamespaces;		/* number of namespaces in the array */
        int maxNamespaces;		/* size of the array */
        const xmlChar **namespaces;	/* the array of namespaces */
    };
    
    /**
     * xmlSchematronValidCtxt:
     *
     * A Schematrons validation context
     */
    struct _xmlSchematronValidCtxt {
        int type;
        int flags;			/* an or of xmlSchematronValidOptions */
    
        xmlDictPtr dict;
        int nberrors;
        int err;
    
        xmlSchematronPtr schema;
        xmlXPathContextPtr xctxt;
    
        FILE *outputFile;		/* if using XML_SCHEMATRON_OUT_FILE */
        xmlBufferPtr outputBuffer;	/* if using XML_SCHEMATRON_OUT_BUFFER */
    #ifdef LIBXML_OUTPUT_ENABLED
        xmlOutputWriteCallback iowrite; /* if using XML_SCHEMATRON_OUT_IO */
        xmlOutputCloseCallback  ioclose;
    #endif
        void *ioctx;
    
        /* error reporting data */
        void *userData;                      /* user specific data block */
        xmlSchematronValidityErrorFunc error;/* the callback in case of errors */
        xmlSchematronValidityWarningFunc warning;/* callback in case of warning */
        xmlStructuredErrorFunc serror;       /* the structured function */
    };
    
    struct _xmlSchematronParserCtxt {
        int type;
        const xmlChar *URL;
        xmlDocPtr doc;
        int preserve;               /* Whether the doc should be freed  */
        const char *buffer;
        int size;
    
        xmlDictPtr dict;            /* dictionary for interned string names */
    
        int nberrors;
        int err;
        xmlXPathContextPtr xctxt;	/* the XPath context used for compilation */
        xmlSchematronPtr schema;
    
        int nbNamespaces;		/* number of namespaces in the array */
        int maxNamespaces;		/* size of the array */
        const xmlChar **namespaces;	/* the array of namespaces */
    
        int nbIncludes;		/* number of includes in the array */
        int maxIncludes;		/* size of the array */
        xmlNodePtr *includes;	/* the array of includes */
    
        /* error reporting data */
        void *userData;                      /* user specific data block */
        xmlSchematronValidityErrorFunc error;/* the callback in case of errors */
        xmlSchematronValidityWarningFunc warning;/* callback in case of warning */
        xmlStructuredErrorFunc serror;       /* the structured function */
    };
    
    #define XML_STRON_CTXT_PARSER 1
    #define XML_STRON_CTXT_VALIDATOR 2
    
    /************************************************************************
     *									*
     *			Error reporting					*
     *									*
     ************************************************************************/
    
    /**
     * xmlSchematronPErrMemory:
     * @node: a context node
     * @extra:  extra informations
     *
     * Handle an out of memory condition
     */
    static void
    xmlSchematronPErrMemory(xmlSchematronParserCtxtPtr ctxt,
                            const char *extra, xmlNodePtr node)
    {
        if (ctxt != NULL)
            ctxt->nberrors++;
        __xmlSimpleError(XML_FROM_SCHEMASP, XML_ERR_NO_MEMORY, node, NULL,
                         extra);
    }
    
    /**
     * xmlSchematronPErr:
     * @ctxt: the parsing context
     * @node: the context node
     * @error: the error code
     * @msg: the error message
     * @str1: extra data
     * @str2: extra data
     *
     * Handle a parser error
     */
    static void LIBXML_ATTR_FORMAT(4,0)
    xmlSchematronPErr(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr node, int error,
                  const char *msg, const xmlChar * str1, const xmlChar * str2)
    {
        xmlGenericErrorFunc channel = NULL;
        xmlStructuredErrorFunc schannel = NULL;
        void *data = NULL;
    
        if (ctxt != NULL) {
            ctxt->nberrors++;
            channel = ctxt->error;
            data = ctxt->userData;
    	schannel = ctxt->serror;
        }
        __xmlRaiseError(schannel, channel, data, ctxt, node, XML_FROM_SCHEMASP,
                        error, XML_ERR_ERROR, NULL, 0,
                        (const char *) str1, (const char *) str2, NULL, 0, 0,
                        msg, str1, str2);
    }
    
    /**
     * xmlSchematronVTypeErrMemory:
     * @node: a context node
     * @extra:  extra informations
     *
     * Handle an out of memory condition
     */
    static void
    xmlSchematronVErrMemory(xmlSchematronValidCtxtPtr ctxt,
                            const char *extra, xmlNodePtr node)
    {
        if (ctxt != NULL) {
            ctxt->nberrors++;
            ctxt->err = XML_SCHEMAV_INTERNAL;
        }
        __xmlSimpleError(XML_FROM_SCHEMASV, XML_ERR_NO_MEMORY, node, NULL,
                         extra);
    }
    
    /************************************************************************
     *									*
     *		Parsing and compilation of the Schematrontrons		*
     *									*
     ************************************************************************/
    
    /**
     * xmlSchematronAddTest:
     * @ctxt: the schema parsing context
     * @type:  the type of test
     * @rule:  the parent rule
     * @node:  the node hosting the test
     * @test: the associated test
     * @report: the associated report string
     *
     * Add a test to a schematron
     *
     * Returns the new pointer or NULL in case of error
     */
    static xmlSchematronTestPtr
    xmlSchematronAddTest(xmlSchematronParserCtxtPtr ctxt,
                         xmlSchematronTestType type,
                         xmlSchematronRulePtr rule,
                         xmlNodePtr node, xmlChar *test, xmlChar *report)
    {
        xmlSchematronTestPtr ret;
        xmlXPathCompExprPtr comp;
    
        if ((ctxt == NULL) || (rule == NULL) || (node == NULL) ||
            (test == NULL))
            return(NULL);
    
        /*
         * try first to compile the test expression
         */
        comp = xmlXPathCtxtCompile(ctxt->xctxt, test);
        if (comp == NULL) {
    	xmlSchematronPErr(ctxt, node,
    	    XML_SCHEMAP_NOROOT,
    	    "Failed to compile test expression %s",
    	    test, NULL);
    	return(NULL);
        }
    
        ret = (xmlSchematronTestPtr) xmlMalloc(sizeof(xmlSchematronTest));
        if (ret == NULL) {
            xmlSchematronPErrMemory(ctxt, "allocating schema test", node);
            return (NULL);
        }
        memset(ret, 0, sizeof(xmlSchematronTest));
        ret->type = type;
        ret->node = node;
        ret->test = test;
        ret->comp = comp;
        ret->report = report;
        ret->next = NULL;
        if (rule->tests == NULL) {
    	rule->tests = ret;
        } else {
            xmlSchematronTestPtr prev = rule->tests;
    
    	while (prev->next != NULL)
    	     prev = prev->next;
            prev->next = ret;
        }
        return (ret);
    }
    
    /**
     * xmlSchematronFreeTests:
     * @tests:  a list of tests
     *
     * Free a list of tests.
     */
    static void
    xmlSchematronFreeTests(xmlSchematronTestPtr tests) {
        xmlSchematronTestPtr next;
    
        while (tests != NULL) {
            next = tests->next;
    	if (tests->test != NULL)
    	    xmlFree(tests->test);
    	if (tests->comp != NULL)
    	    xmlXPathFreeCompExpr(tests->comp);
    	if (tests->report != NULL)
    	    xmlFree(tests->report);
    	xmlFree(tests);
    	tests = next;
        }
    }
    
    /**
     * xmlSchematronAddRule:
     * @ctxt: the schema parsing context
     * @schema:  a schema structure
     * @node:  the node hosting the rule
     * @context: the associated context string
     * @report: the associated report string
     *
     * Add a rule to a schematron
     *
     * Returns the new pointer or NULL in case of error
     */
    static xmlSchematronRulePtr
    xmlSchematronAddRule(xmlSchematronParserCtxtPtr ctxt, xmlSchematronPtr schema,
                         xmlSchematronPatternPtr pat, xmlNodePtr node,
    		     xmlChar *context, xmlChar *report)
    {
        xmlSchematronRulePtr ret;
        xmlPatternPtr pattern;
    
        if ((ctxt == NULL) || (schema == NULL) || (node == NULL) ||
            (context == NULL))
            return(NULL);
    
        /*
         * Try first to compile the pattern
         */
        pattern = xmlPatterncompile(context, ctxt->dict, XML_PATTERN_XPATH,
                                    ctxt->namespaces);
        if (pattern == NULL) {
    	xmlSchematronPErr(ctxt, node,
    	    XML_SCHEMAP_NOROOT,
    	    "Failed to compile context expression %s",
    	    context, NULL);
        }
    
        ret = (xmlSchematronRulePtr) xmlMalloc(sizeof(xmlSchematronRule));
        if (ret == NULL) {
            xmlSchematronPErrMemory(ctxt, "allocating schema rule", node);
            return (NULL);
        }
        memset(ret, 0, sizeof(xmlSchematronRule));
        ret->node = node;
        ret->context = context;
        ret->pattern = pattern;
        ret->report = report;
        ret->next = NULL;
        if (schema->rules == NULL) {
    	schema->rules = ret;
        } else {
            xmlSchematronRulePtr prev = schema->rules;
    
    	while (prev->next != NULL)
    	     prev = prev->next;
            prev->next = ret;
        }
        ret->patnext = NULL;
        if (pat->rules == NULL) {
    	pat->rules = ret;
        } else {
            xmlSchematronRulePtr prev = pat->rules;
    
    	while (prev->patnext != NULL)
    	     prev = prev->patnext;
            prev->patnext = ret;
        }
        return (ret);
    }
    
    /**
     * xmlSchematronFreeRules:
     * @rules:  a list of rules
     *
     * Free a list of rules.
     */
    static void
    xmlSchematronFreeRules(xmlSchematronRulePtr rules) {
        xmlSchematronRulePtr next;
    
        while (rules != NULL) {
            next = rules->next;
    	if (rules->tests)
    	    xmlSchematronFreeTests(rules->tests);
    	if (rules->context != NULL)
    	    xmlFree(rules->context);
    	if (rules->pattern)
    	    xmlFreePattern(rules->pattern);
    	if (rules->report != NULL)
    	    xmlFree(rules->report);
    	xmlFree(rules);
    	rules = next;
        }
    }
    
    /**
     * xmlSchematronAddPattern:
     * @ctxt: the schema parsing context
     * @schema:  a schema structure
     * @node:  the node hosting the pattern
     * @id: the id or name of the pattern
     *
     * Add a pattern to a schematron
     *
     * Returns the new pointer or NULL in case of error
     */
    static xmlSchematronPatternPtr
    xmlSchematronAddPattern(xmlSchematronParserCtxtPtr ctxt,
                         xmlSchematronPtr schema, xmlNodePtr node, xmlChar *name)
    {
        xmlSchematronPatternPtr ret;
    
        if ((ctxt == NULL) || (schema == NULL) || (node == NULL) || (name == NULL))
            return(NULL);
    
        ret = (xmlSchematronPatternPtr) xmlMalloc(sizeof(xmlSchematronPattern));
        if (ret == NULL) {
            xmlSchematronPErrMemory(ctxt, "allocating schema pattern", node);
            return (NULL);
        }
        memset(ret, 0, sizeof(xmlSchematronPattern));
        ret->name = name;
        ret->next = NULL;
        if (schema->patterns == NULL) {
    	schema->patterns = ret;
        } else {
            xmlSchematronPatternPtr prev = schema->patterns;
    
    	while (prev->next != NULL)
    	     prev = prev->next;
            prev->next = ret;
        }
        return (ret);
    }
    
    /**
     * xmlSchematronFreePatterns:
     * @patterns:  a list of patterns
     *
     * Free a list of patterns.
     */
    static void
    xmlSchematronFreePatterns(xmlSchematronPatternPtr patterns) {
        xmlSchematronPatternPtr next;
    
        while (patterns != NULL) {
            next = patterns->next;
    	if (patterns->name != NULL)
    	    xmlFree(patterns->name);
    	xmlFree(patterns);
    	patterns = next;
        }
    }
    
    /**
     * xmlSchematronNewSchematron:
     * @ctxt:  a schema validation context
     *
     * Allocate a new Schematron structure.
     *
     * Returns the newly allocated structure or NULL in case or error
     */
    static xmlSchematronPtr
    xmlSchematronNewSchematron(xmlSchematronParserCtxtPtr ctxt)
    {
        xmlSchematronPtr ret;
    
        ret = (xmlSchematronPtr) xmlMalloc(sizeof(xmlSchematron));
        if (ret == NULL) {
            xmlSchematronPErrMemory(ctxt, "allocating schema", NULL);
            return (NULL);
        }
        memset(ret, 0, sizeof(xmlSchematron));
        ret->dict = ctxt->dict;
        xmlDictReference(ret->dict);
    
        return (ret);
    }
    
    /**
     * xmlSchematronFree:
     * @schema:  a schema structure
     *
     * Deallocate a Schematron structure.
     */
    void
    xmlSchematronFree(xmlSchematronPtr schema)
    {
        if (schema == NULL)
            return;
    
        if ((schema->doc != NULL) && (!(schema->preserve)))
            xmlFreeDoc(schema->doc);
    
        if (schema->namespaces != NULL)
            xmlFree((char **) schema->namespaces);
    
        xmlSchematronFreeRules(schema->rules);
        xmlSchematronFreePatterns(schema->patterns);
        xmlDictFree(schema->dict);
        xmlFree(schema);
    }
    
    /**
     * xmlSchematronNewParserCtxt:
     * @URL:  the location of the schema
     *
     * Create an XML Schematrons parse context for that file/resource expected
     * to contain an XML Schematrons file.
     *
     * Returns the parser context or NULL in case of error
     */
    xmlSchematronParserCtxtPtr
    xmlSchematronNewParserCtxt(const char *URL)
    {
        xmlSchematronParserCtxtPtr ret;
    
        if (URL == NULL)
            return (NULL);
    
        ret =
            (xmlSchematronParserCtxtPtr)
            xmlMalloc(sizeof(xmlSchematronParserCtxt));
        if (ret == NULL) {
            xmlSchematronPErrMemory(NULL, "allocating schema parser context",
                                    NULL);
            return (NULL);
        }
        memset(ret, 0, sizeof(xmlSchematronParserCtxt));
        ret->type = XML_STRON_CTXT_PARSER;
        ret->dict = xmlDictCreate();
        ret->URL = xmlDictLookup(ret->dict, (const xmlChar *) URL, -1);
        ret->includes = NULL;
        ret->xctxt = xmlXPathNewContext(NULL);
        if (ret->xctxt == NULL) {
            xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",
                                    NULL);
    	xmlSchematronFreeParserCtxt(ret);
            return (NULL);
        }
        ret->xctxt->flags = XML_XPATH_CHECKNS;
        return (ret);
    }
    
    /**
     * xmlSchematronNewMemParserCtxt:
     * @buffer:  a pointer to a char array containing the schemas
     * @size:  the size of the array
     *
     * Create an XML Schematrons parse context for that memory buffer expected
     * to contain an XML Schematrons file.
     *
     * Returns the parser context or NULL in case of error
     */
    xmlSchematronParserCtxtPtr
    xmlSchematronNewMemParserCtxt(const char *buffer, int size)
    {
        xmlSchematronParserCtxtPtr ret;
    
        if ((buffer == NULL) || (size <= 0))
            return (NULL);
    
        ret =
            (xmlSchematronParserCtxtPtr)
            xmlMalloc(sizeof(xmlSchematronParserCtxt));
        if (ret == NULL) {
            xmlSchematronPErrMemory(NULL, "allocating schema parser context",
                                    NULL);
            return (NULL);
        }
        memset(ret, 0, sizeof(xmlSchematronParserCtxt));
        ret->buffer = buffer;
        ret->size = size;
        ret->dict = xmlDictCreate();
        ret->xctxt = xmlXPathNewContext(NULL);
        if (ret->xctxt == NULL) {
            xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",
                                    NULL);
    	xmlSchematronFreeParserCtxt(ret);
            return (NULL);
        }
        return (ret);
    }
    
    /**
     * xmlSchematronNewDocParserCtxt:
     * @doc:  a preparsed document tree
     *
     * Create an XML Schematrons parse context for that document.
     * NB. The document may be modified during the parsing process.
     *
     * Returns the parser context or NULL in case of error
     */
    xmlSchematronParserCtxtPtr
    xmlSchematronNewDocParserCtxt(xmlDocPtr doc)
    {
        xmlSchematronParserCtxtPtr ret;
    
        if (doc == NULL)
            return (NULL);
    
        ret =
            (xmlSchematronParserCtxtPtr)
            xmlMalloc(sizeof(xmlSchematronParserCtxt));
        if (ret == NULL) {
            xmlSchematronPErrMemory(NULL, "allocating schema parser context",
                                    NULL);
            return (NULL);
        }
        memset(ret, 0, sizeof(xmlSchematronParserCtxt));
        ret->doc = doc;
        ret->dict = xmlDictCreate();
        /* The application has responsibility for the document */
        ret->preserve = 1;
        ret->xctxt = xmlXPathNewContext(doc);
        if (ret->xctxt == NULL) {
            xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",
                                    NULL);
    	xmlSchematronFreeParserCtxt(ret);
            return (NULL);
        }
    
        return (ret);
    }
    
    /**
     * xmlSchematronFreeParserCtxt:
     * @ctxt:  the schema parser context
     *
     * Free the resources associated to the schema parser context
     */
    void
    xmlSchematronFreeParserCtxt(xmlSchematronParserCtxtPtr ctxt)
    {
        if (ctxt == NULL)
            return;
        if (ctxt->doc != NULL && !ctxt->preserve)
            xmlFreeDoc(ctxt->doc);
        if (ctxt->xctxt != NULL) {
            xmlXPathFreeContext(ctxt->xctxt);
        }
        if (ctxt->namespaces != NULL)
            xmlFree((char **) ctxt->namespaces);
        xmlDictFree(ctxt->dict);
        xmlFree(ctxt);
    }
    
    #if 0
    /**
     * xmlSchematronPushInclude:
     * @ctxt:  the schema parser context
     * @doc:  the included document
     * @cur:  the current include node
     *
     * Add an included document
     */
    static void
    xmlSchematronPushInclude(xmlSchematronParserCtxtPtr ctxt,
                            xmlDocPtr doc, xmlNodePtr cur)
    {
        if (ctxt->includes == NULL) {
            ctxt->maxIncludes = 10;
            ctxt->includes = (xmlNodePtr *)
    	    xmlMalloc(ctxt->maxIncludes * 2 * sizeof(xmlNodePtr));
    	if (ctxt->includes == NULL) {
    	    xmlSchematronPErrMemory(NULL, "allocating parser includes",
    				    NULL);
    	    return;
    	}
            ctxt->nbIncludes = 0;
        } else if (ctxt->nbIncludes + 2 >= ctxt->maxIncludes) {
            xmlNodePtr *tmp;
    
    	tmp = (xmlNodePtr *)
    	    xmlRealloc(ctxt->includes, ctxt->maxIncludes * 4 *
    	               sizeof(xmlNodePtr));
    	if (tmp == NULL) {
    	    xmlSchematronPErrMemory(NULL, "allocating parser includes",
    				    NULL);
    	    return;
    	}
            ctxt->includes = tmp;
    	ctxt->maxIncludes *= 2;
        }
        ctxt->includes[2 * ctxt->nbIncludes] = cur;
        ctxt->includes[2 * ctxt->nbIncludes + 1] = (xmlNodePtr) doc;
        ctxt->nbIncludes++;
    }
    
    /**
     * xmlSchematronPopInclude:
     * @ctxt:  the schema parser context
     *
     * Pop an include level. The included document is being freed
     *
     * Returns the node immediately following the include or NULL if the
     *         include list was empty.
     */
    static xmlNodePtr
    xmlSchematronPopInclude(xmlSchematronParserCtxtPtr ctxt)
    {
        xmlDocPtr doc;
        xmlNodePtr ret;
    
        if (ctxt->nbIncludes <= 0)
            return(NULL);
        ctxt->nbIncludes--;
        doc = (xmlDocPtr) ctxt->includes[2 * ctxt->nbIncludes + 1];
        ret = ctxt->includes[2 * ctxt->nbIncludes];
        xmlFreeDoc(doc);
        if (ret != NULL)
    	ret = ret->next;
        if (ret == NULL)
            return(xmlSchematronPopInclude(ctxt));
        return(ret);
    }
    #endif
    
    /**
     * xmlSchematronAddNamespace:
     * @ctxt:  the schema parser context
     * @prefix:  the namespace prefix
     * @ns:  the namespace name
     *
     * Add a namespace definition in the context
     */
    static void
    xmlSchematronAddNamespace(xmlSchematronParserCtxtPtr ctxt,
                              const xmlChar *prefix, const xmlChar *ns)
    {
        if (ctxt->namespaces == NULL) {
            ctxt->maxNamespaces = 10;
            ctxt->namespaces = (const xmlChar **)
    	    xmlMalloc(ctxt->maxNamespaces * 2 * sizeof(const xmlChar *));
    	if (ctxt->namespaces == NULL) {
    	    xmlSchematronPErrMemory(NULL, "allocating parser namespaces",
    				    NULL);
    	    return;
    	}
            ctxt->nbNamespaces = 0;
        } else if (ctxt->nbNamespaces + 2 >= ctxt->maxNamespaces) {
            const xmlChar **tmp;
    
    	tmp = (const xmlChar **)
    	    xmlRealloc((xmlChar **) ctxt->namespaces, ctxt->maxNamespaces * 4 *
    	               sizeof(const xmlChar *));
    	if (tmp == NULL) {
    	    xmlSchematronPErrMemory(NULL, "allocating parser namespaces",
    				    NULL);
    	    return;
    	}
            ctxt->namespaces = tmp;
    	ctxt->maxNamespaces *= 2;
        }
        ctxt->namespaces[2 * ctxt->nbNamespaces] =
            xmlDictLookup(ctxt->dict, ns, -1);
        ctxt->namespaces[2 * ctxt->nbNamespaces + 1] =
            xmlDictLookup(ctxt->dict, prefix, -1);
        ctxt->nbNamespaces++;
        ctxt->namespaces[2 * ctxt->nbNamespaces] = NULL;
        ctxt->namespaces[2 * ctxt->nbNamespaces + 1] = NULL;
    
    }
    
    /**
     * xmlSchematronParseRule:
     * @ctxt:  a schema validation context
     * @rule:  the rule node
     *
     * parse a rule element
     */
    static void
    xmlSchematronParseRule(xmlSchematronParserCtxtPtr ctxt,
                           xmlSchematronPatternPtr pattern,
    		       xmlNodePtr rule)
    {
        xmlNodePtr cur;
        int nbChecks = 0;
        xmlChar *test;
        xmlChar *context;
        xmlChar *report;
        xmlSchematronRulePtr ruleptr;
        xmlSchematronTestPtr testptr;
    
        if ((ctxt == NULL) || (rule == NULL)) return;
    
        context = xmlGetNoNsProp(rule, BAD_CAST "context");
        if (context == NULL) {
    	xmlSchematronPErr(ctxt, rule,
    	    XML_SCHEMAP_NOROOT,
    	    "rule has no context attribute",
    	    NULL, NULL);
    	return;
        } else if (context[0] == 0) {
    	xmlSchematronPErr(ctxt, rule,
    	    XML_SCHEMAP_NOROOT,
    	    "rule has an empty context attribute",
    	    NULL, NULL);
    	xmlFree(context);
    	return;
        } else {
    	ruleptr = xmlSchematronAddRule(ctxt, ctxt->schema, pattern,
    	                               rule, context, NULL);
    	if (ruleptr == NULL) {
    	    xmlFree(context);
    	    return;
    	}
        }
    
        cur = rule->children;
        NEXT_SCHEMATRON(cur);
        while (cur != NULL) {
    	if (IS_SCHEMATRON(cur, "assert")) {
    	    nbChecks++;
    	    test = xmlGetNoNsProp(cur, BAD_CAST "test");
    	    if (test == NULL) {
    		xmlSchematronPErr(ctxt, cur,
    		    XML_SCHEMAP_NOROOT,
    		    "assert has no test attribute",
    		    NULL, NULL);
    	    } else if (test[0] == 0) {
    		xmlSchematronPErr(ctxt, cur,
    		    XML_SCHEMAP_NOROOT,
    		    "assert has an empty test attribute",
    		    NULL, NULL);
    		xmlFree(test);
    	    } else {
    		/* TODO will need dynamic processing instead */
    		report = xmlNodeGetContent(cur);
    
    		testptr = xmlSchematronAddTest(ctxt, XML_SCHEMATRON_ASSERT,
    		                               ruleptr, cur, test, report);
    		if (testptr == NULL)
    		    xmlFree(test);
    	    }
    	} else if (IS_SCHEMATRON(cur, "report")) {
    	    nbChecks++;
    	    test = xmlGetNoNsProp(cur, BAD_CAST "test");
    	    if (test == NULL) {
    		xmlSchematronPErr(ctxt, cur,
    		    XML_SCHEMAP_NOROOT,
    		    "assert has no test attribute",
    		    NULL, NULL);
    	    } else if (test[0] == 0) {
    		xmlSchematronPErr(ctxt, cur,
    		    XML_SCHEMAP_NOROOT,
    		    "assert has an empty test attribute",
    		    NULL, NULL);
    		xmlFree(test);
    	    } else {
    		/* TODO will need dynamic processing instead */
    		report = xmlNodeGetContent(cur);
    
    		testptr = xmlSchematronAddTest(ctxt, XML_SCHEMATRON_REPORT,
    		                               ruleptr, cur, test, report);
    		if (testptr == NULL)
    		    xmlFree(test);
    	    }
    	} else {
    	    xmlSchematronPErr(ctxt, cur,
    		XML_SCHEMAP_NOROOT,
    		"Expecting an assert or a report element instead of %s",
    		cur->name, NULL);
    	}
    	cur = cur->next;
    	NEXT_SCHEMATRON(cur);
        }
        if (nbChecks == 0) {
    	xmlSchematronPErr(ctxt, rule,
    	    XML_SCHEMAP_NOROOT,
    	    "rule has no assert nor report element", NULL, NULL);
        }
    }
    
    /**
     * xmlSchematronParsePattern:
     * @ctxt:  a schema validation context
     * @pat:  the pattern node
     *
     * parse a pattern element
     */
    static void
    xmlSchematronParsePattern(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr pat)
    {
        xmlNodePtr cur;
        xmlSchematronPatternPtr pattern;
        int nbRules = 0;
        xmlChar *id;
    
        if ((ctxt == NULL) || (pat == NULL)) return;
    
        id = xmlGetNoNsProp(pat, BAD_CAST "id");
        if (id == NULL) {
    	id = xmlGetNoNsProp(pat, BAD_CAST "name");
        }
        pattern = xmlSchematronAddPattern(ctxt, ctxt->schema, pat, id);
        if (pattern == NULL) {
    	if (id != NULL)
    	    xmlFree(id);
            return;
        }
        cur = pat->children;
        NEXT_SCHEMATRON(cur);
        while (cur != NULL) {
    	if (IS_SCHEMATRON(cur, "rule")) {
    	    xmlSchematronParseRule(ctxt, pattern, cur);
    	    nbRules++;
    	} else {
    	    xmlSchematronPErr(ctxt, cur,
    		XML_SCHEMAP_NOROOT,
    		"Expecting a rule element instead of %s", cur->name, NULL);
    	}
    	cur = cur->next;
    	NEXT_SCHEMATRON(cur);
        }
        if (nbRules == 0) {
    	xmlSchematronPErr(ctxt, pat,
    	    XML_SCHEMAP_NOROOT,
    	    "Pattern has no rule element", NULL, NULL);
        }
    }
    
    #if 0
    /**
     * xmlSchematronLoadInclude:
     * @ctxt:  a schema validation context
     * @cur:  the include element
     *
     * Load the include document, Push the current pointer
     *
     * Returns the updated node pointer
     */
    static xmlNodePtr
    xmlSchematronLoadInclude(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr cur)
    {
        xmlNodePtr ret = NULL;
        xmlDocPtr doc = NULL;
        xmlChar *href = NULL;
        xmlChar *base = NULL;
        xmlChar *URI = NULL;
    
        if ((ctxt == NULL) || (cur == NULL))
            return(NULL);
    
        href = xmlGetNoNsProp(cur, BAD_CAST "href");
        if (href == NULL) {
    	xmlSchematronPErr(ctxt, cur,
    	    XML_SCHEMAP_NOROOT,
    	    "Include has no href attribute", NULL, NULL);
    	return(cur->next);
        }
    
        /* do the URI base composition, load and find the root */
        base = xmlNodeGetBase(cur->doc, cur);
        URI = xmlBuildURI(href, base);
        doc = xmlReadFile((const char *) URI, NULL, SCHEMATRON_PARSE_OPTIONS);
        if (doc == NULL) {
    	xmlSchematronPErr(ctxt, cur,
    		      XML_SCHEMAP_FAILED_LOAD,
    		      "could not load include '%s'.\n",
    		      URI, NULL);
    	goto done;
        }
        ret = xmlDocGetRootElement(doc);
        if (ret == NULL) {
    	xmlSchematronPErr(ctxt, cur,
    		      XML_SCHEMAP_FAILED_LOAD,
    		      "could not find root from include '%s'.\n",
    		      URI, NULL);
    	goto done;
        }
    
        /* Success, push the include for rollback on exit */
        xmlSchematronPushInclude(ctxt, doc, cur);
    
    done:
        if (ret == NULL) {
            if (doc != NULL)
    	    xmlFreeDoc(doc);
        }
        xmlFree(href);
        if (base != NULL)
            xmlFree(base);
        if (URI != NULL)
            xmlFree(URI);
        return(ret);
    }
    #endif
    
    /**
     * xmlSchematronParse:
     * @ctxt:  a schema validation context
     *
     * parse a schema definition resource and build an internal
     * XML Schema structure which can be used to validate instances.
     *
     * Returns the internal XML Schematron structure built from the resource or
     *         NULL in case of error
     */
    xmlSchematronPtr
    xmlSchematronParse(xmlSchematronParserCtxtPtr ctxt)
    {
        xmlSchematronPtr ret = NULL;
        xmlDocPtr doc;
        xmlNodePtr root, cur;
        int preserve = 0;
    
        if (ctxt == NULL)
            return (NULL);
    
        ctxt->nberrors = 0;
    
        /*
         * First step is to parse the input document into an DOM/Infoset
         */
        if (ctxt->URL != NULL) {
            doc = xmlReadFile((const char *) ctxt->URL, NULL,
    	                  SCHEMATRON_PARSE_OPTIONS);
            if (doc == NULL) {
    	    xmlSchematronPErr(ctxt, NULL,
    			  XML_SCHEMAP_FAILED_LOAD,
                              "xmlSchematronParse: could not load '%s'.\n",
                              ctxt->URL, NULL);
                return (NULL);
            }
    	ctxt->preserve = 0;
        } else if (ctxt->buffer != NULL) {
            doc = xmlReadMemory(ctxt->buffer, ctxt->size, NULL, NULL,
    	                    SCHEMATRON_PARSE_OPTIONS);
            if (doc == NULL) {
    	    xmlSchematronPErr(ctxt, NULL,
    			  XML_SCHEMAP_FAILED_PARSE,
                              "xmlSchematronParse: could not parse.\n",
                              NULL, NULL);
                return (NULL);
            }
            doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
            ctxt->URL = xmlDictLookup(ctxt->dict, BAD_CAST "in_memory_buffer", -1);
    	ctxt->preserve = 0;
        } else if (ctxt->doc != NULL) {
            doc = ctxt->doc;
    	preserve = 1;
    	ctxt->preserve = 1;
        } else {
    	xmlSchematronPErr(ctxt, NULL,
    		      XML_SCHEMAP_NOTHING_TO_PARSE,
    		      "xmlSchematronParse: could not parse.\n",
    		      NULL, NULL);
            return (NULL);
        }
    
        /*
         * Then extract the root and Schematron parse it
         */
        root = xmlDocGetRootElement(doc);
        if (root == NULL) {
    	xmlSchematronPErr(ctxt, (xmlNodePtr) doc,
    		      XML_SCHEMAP_NOROOT,
    		      "The schema has no document element.\n", NULL, NULL);
    	if (!preserve) {
    	    xmlFreeDoc(doc);
    	}
            return (NULL);
        }
    
        if (!IS_SCHEMATRON(root, "schema")) {
    	xmlSchematronPErr(ctxt, root,
    	    XML_SCHEMAP_NOROOT,
    	    "The XML document '%s' is not a XML schematron document",
    	    ctxt->URL, NULL);
    	goto exit;
        }
        ret = xmlSchematronNewSchematron(ctxt);
        if (ret == NULL)
            goto exit;
        ctxt->schema = ret;
    
        /*
         * scan the schema elements
         */
        cur = root->children;
        NEXT_SCHEMATRON(cur);
        if (IS_SCHEMATRON(cur, "title")) {
            xmlChar *title = xmlNodeGetContent(cur);
    	if (title != NULL) {
    	    ret->title = xmlDictLookup(ret->dict, title, -1);
    	    xmlFree(title);
    	}
    	cur = cur->next;
    	NEXT_SCHEMATRON(cur);
        }
        while (IS_SCHEMATRON(cur, "ns")) {
            xmlChar *prefix = xmlGetNoNsProp(cur, BAD_CAST "prefix");
            xmlChar *uri = xmlGetNoNsProp(cur, BAD_CAST "uri");
    	if ((uri == NULL) || (uri[0] == 0)) {
    	    xmlSchematronPErr(ctxt, cur,
    		XML_SCHEMAP_NOROOT,
    		"ns element has no uri", NULL, NULL);
    	}
    	if ((prefix == NULL) || (prefix[0] == 0)) {
    	    xmlSchematronPErr(ctxt, cur,
    		XML_SCHEMAP_NOROOT,
    		"ns element has no prefix", NULL, NULL);
    	}
    	if ((prefix) && (uri)) {
    	    xmlXPathRegisterNs(ctxt->xctxt, prefix, uri);
    	    xmlSchematronAddNamespace(ctxt, prefix, uri);
    	    ret->nbNs++;
    	}
    	if (uri)
    	    xmlFree(uri);
    	if (prefix)
    	    xmlFree(prefix);
    	cur = cur->next;
    	NEXT_SCHEMATRON(cur);
        }
        while (cur != NULL) {
    	if (IS_SCHEMATRON(cur, "pattern")) {
    	    xmlSchematronParsePattern(ctxt, cur);
    	    ret->nbPattern++;
    	} else {
    	    xmlSchematronPErr(ctxt, cur,
    		XML_SCHEMAP_NOROOT,
    		"Expecting a pattern element instead of %s", cur->name, NULL);
    	}
    	cur = cur->next;
    	NEXT_SCHEMATRON(cur);
        }
        if (ret->nbPattern == 0) {
    	xmlSchematronPErr(ctxt, root,
    	    XML_SCHEMAP_NOROOT,
    	    "The schematron document '%s' has no pattern",
    	    ctxt->URL, NULL);
    	goto exit;
        }
        /* the original document must be kept for reporting */
        ret->doc = doc;
        if (preserve) {
    	    ret->preserve = 1;
        }
        preserve = 1;
    
    exit:
        if (!preserve) {
    	xmlFreeDoc(doc);
        }
        if (ret != NULL) {
    	if (ctxt->nberrors != 0) {
    	    xmlSchematronFree(ret);
    	    ret = NULL;
    	} else {
    	    ret->namespaces = ctxt->namespaces;
    	    ret->nbNamespaces = ctxt->nbNamespaces;
    	    ctxt->namespaces = NULL;
    	}
        }
        return (ret);
    }
    
    /************************************************************************
     *									*
     *		Schematrontron Reports handler				*
     *									*
     ************************************************************************/
    
    static xmlNodePtr
    xmlSchematronGetNode(xmlSchematronValidCtxtPtr ctxt,
                         xmlNodePtr cur, const xmlChar *xpath) {
        xmlNodePtr node = NULL;
        xmlXPathObjectPtr ret;
    
        if ((ctxt == NULL) || (cur == NULL) || (xpath == NULL))
            return(NULL);
    
        ctxt->xctxt->doc = cur->doc;
        ctxt->xctxt->node = cur;
        ret = xmlXPathEval(xpath, ctxt->xctxt);
        if (ret == NULL)
            return(NULL);
    
        if ((ret->type == XPATH_NODESET) &&
            (ret->nodesetval != NULL) && (ret->nodesetval->nodeNr > 0))
    	node = ret->nodesetval->nodeTab[0];
    
        xmlXPathFreeObject(ret);
        return(node);
    }
    
    /**
     * xmlSchematronReportOutput:
     * @ctxt: the validation context
     * @cur: the current node tested
     * @msg: the message output
     *
     * Output part of the report to whatever channel the user selected
     */
    static void
    xmlSchematronReportOutput(xmlSchematronValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
                              xmlNodePtr cur ATTRIBUTE_UNUSED,
                              const char *msg) {
        /* TODO */
        fprintf(stderr, "%s", msg);
    }
    
    /**
     * xmlSchematronFormatReport:
     * @ctxt:  the validation context
     * @test: the test node
     * @cur: the current node tested
     *
     * Build the string being reported to the user.
     *
     * Returns a report string or NULL in case of error. The string needs
     *         to be deallocated by the caller
     */
    static xmlChar *
    xmlSchematronFormatReport(xmlSchematronValidCtxtPtr ctxt,
    			  xmlNodePtr test, xmlNodePtr cur) {
        xmlChar *ret = NULL;
        xmlNodePtr child, node;
    
        if ((test == NULL) || (cur == NULL))
            return(ret);
    
        child = test->children;
        while (child != NULL) {
            if ((child->type == XML_TEXT_NODE) ||
    	    (child->type == XML_CDATA_SECTION_NODE))
    	    ret = xmlStrcat(ret, child->content);
    	else if (IS_SCHEMATRON(child, "name")) {
    	    xmlChar *path;
    
    	    path = xmlGetNoNsProp(child, BAD_CAST "path");
    
                node = cur;
    	    if (path != NULL) {
    	        node = xmlSchematronGetNode(ctxt, cur, path);
    		if (node == NULL)
    		    node = cur;
    		xmlFree(path);
    	    }
    
    	    if ((node->ns == NULL) || (node->ns->prefix == NULL))
    	        ret = xmlStrcat(ret, node->name);
    	    else {
    	        ret = xmlStrcat(ret, node->ns->prefix);
    	        ret = xmlStrcat(ret, BAD_CAST ":");
    	        ret = xmlStrcat(ret, node->name);
    	    }
    	} else {
    	    child = child->next;
    	    continue;
    	}
    
    	/*
    	 * remove superfluous \n
    	 */
    	if (ret != NULL) {
    	    int len = xmlStrlen(ret);
    	    xmlChar c;
    
    	    if (len > 0) {
    		c = ret[len - 1];
    		if ((c == ' ') || (c == '\n') || (c == '\r') || (c == '\t')) {
    		    while ((c == ' ') || (c == '\n') ||
    		           (c == '\r') || (c == '\t')) {
    			len--;
    			if (len == 0)
    			    break;
    			c = ret[len - 1];
    		    }
    		    ret[len] = ' ';
    		    ret[len + 1] = 0;
    		}
    	    }
    	}
    
            child = child->next;
        }
        return(ret);
    }
    
    /**
     * xmlSchematronReportSuccess:
     * @ctxt:  the validation context
     * @test: the compiled test
     * @cur: the current node tested
     * @success: boolean value for the result
     *
     * called from the validation engine when an assert or report test have
     * been done.
     */
    static void
    xmlSchematronReportSuccess(xmlSchematronValidCtxtPtr ctxt,
    		   xmlSchematronTestPtr test, xmlNodePtr cur, xmlSchematronPatternPtr pattern, int success) {
        if ((ctxt == NULL) || (cur == NULL) || (test == NULL))
            return;
        /* if quiet and not SVRL report only failures */
        if ((ctxt->flags & XML_SCHEMATRON_OUT_QUIET) &&
            ((ctxt->flags & XML_SCHEMATRON_OUT_XML) == 0) &&
    	(test->type == XML_SCHEMATRON_REPORT))
            return;
        if (ctxt->flags & XML_SCHEMATRON_OUT_XML) {
            TODO
        } else {
            xmlChar *path;
    	char msg[1000];
    	long line;
    	const xmlChar *report = NULL;
    
            if (((test->type == XML_SCHEMATRON_REPORT) & (!success)) ||
    	    ((test->type == XML_SCHEMATRON_ASSERT) & (success)))
    	    return;
    	line = xmlGetLineNo(cur);
    	path = xmlGetNodePath(cur);
    	if (path == NULL)
    	    path = (xmlChar *) cur->name;
    #if 0
    	if ((test->report != NULL) && (test->report[0] != 0))
    	    report = test->report;
    #endif
    	if (test->node != NULL)
                report = xmlSchematronFormatReport(ctxt, test->node, cur);
    	if (report == NULL) {
    	    if (test->type == XML_SCHEMATRON_ASSERT) {
                report = xmlStrdup((const xmlChar *) "node failed assert");
    	    } else {
                report = xmlStrdup((const xmlChar *) "node failed report");
    	    }
    	    }
    	    snprintf(msg, 999, "%s line %ld: %s\n", (const char *) path,
    		     line, (const char *) report);
    
        if (ctxt->flags & XML_SCHEMATRON_OUT_ERROR) {
            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;
    	}
    
            __xmlRaiseError(schannel, channel, data,
                            NULL, cur, XML_FROM_SCHEMATRONV,
                            (test->type == XML_SCHEMATRON_ASSERT)?XML_SCHEMATRONV_ASSERT:XML_SCHEMATRONV_REPORT,
                            XML_ERR_ERROR, NULL, line,
                            (pattern == NULL)?NULL:((const char *) pattern->name),
                            (const char *) path,
                            (const char *) report, 0, 0,
                            "%s", msg);
        } else {
    	xmlSchematronReportOutput(ctxt, cur, &msg[0]);
        }
    
        xmlFree((char *) report);
    
    	if ((path != NULL) && (path != (xmlChar *) cur->name))
    	    xmlFree(path);
        }
    }
    
    /**
     * xmlSchematronReportPattern:
     * @ctxt:  the validation context
     * @pattern: the current pattern
     *
     * called from the validation engine when starting to check a pattern
     */
    static void
    xmlSchematronReportPattern(xmlSchematronValidCtxtPtr ctxt,
    			   xmlSchematronPatternPtr pattern) {
        if ((ctxt == NULL) || (pattern == NULL))
            return;
        if ((ctxt->flags & XML_SCHEMATRON_OUT_QUIET) || (ctxt->flags & XML_SCHEMATRON_OUT_ERROR)) /* Error gives pattern name as part of error */
            return;
        if (ctxt->flags & XML_SCHEMATRON_OUT_XML) {
            TODO
        } else {
    	char msg[1000];
    
    	if (pattern->name == NULL)
    	    return;
    	snprintf(msg, 999, "Pattern: %s\n", (const char *) pattern->name);
    	xmlSchematronReportOutput(ctxt, NULL, &msg[0]);
        }
    }
    
    
    /************************************************************************
     *									*
     *		Validation against a Schematrontron				*
     *									*
     ************************************************************************/
    
    /**
     * xmlSchematronSetValidStructuredErrors:
     * @ctxt:  a Schematron validation context
     * @serror:  the structured error function
     * @ctx: the functions context
     *
     * Set the structured error callback
     */
    void
    xmlSchematronSetValidStructuredErrors(xmlSchematronValidCtxtPtr ctxt,
                                          xmlStructuredErrorFunc serror, void *ctx)
    {
        if (ctxt == NULL)
            return;
        ctxt->serror = serror;
        ctxt->error = NULL;
        ctxt->warning = NULL;
        ctxt->userData = ctx;
    }
    
    /**
     * xmlSchematronNewValidCtxt:
     * @schema:  a precompiled XML Schematrons
     * @options: a set of xmlSchematronValidOptions
     *
     * Create an XML Schematrons validation context based on the given schema.
     *
     * Returns the validation context or NULL in case of error
     */
    xmlSchematronValidCtxtPtr
    xmlSchematronNewValidCtxt(xmlSchematronPtr schema, int options)
    {
        int i;
        xmlSchematronValidCtxtPtr ret;
    
        ret = (xmlSchematronValidCtxtPtr) xmlMalloc(sizeof(xmlSchematronValidCtxt));
        if (ret == NULL) {
            xmlSchematronVErrMemory(NULL, "allocating validation context",
                                    NULL);
            return (NULL);
        }
        memset(ret, 0, sizeof(xmlSchematronValidCtxt));
        ret->type = XML_STRON_CTXT_VALIDATOR;
        ret->schema = schema;
        ret->xctxt = xmlXPathNewContext(NULL);
        ret->flags = options;
        if (ret->xctxt == NULL) {
            xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",
                                    NULL);
    	xmlSchematronFreeValidCtxt(ret);
            return (NULL);
        }
        for (i = 0;i < schema->nbNamespaces;i++) {
            if ((schema->namespaces[2 * i] == NULL) ||
                (schema->namespaces[2 * i + 1] == NULL))
    	    break;
    	xmlXPathRegisterNs(ret->xctxt, schema->namespaces[2 * i + 1],
    	                   schema->namespaces[2 * i]);
        }
        return (ret);
    }
    
    /**
     * xmlSchematronFreeValidCtxt:
     * @ctxt:  the schema validation context
     *
     * Free the resources associated to the schema validation context
     */
    void
    xmlSchematronFreeValidCtxt(xmlSchematronValidCtxtPtr ctxt)
    {
        if (ctxt == NULL)
            return;
        if (ctxt->xctxt != NULL)
            xmlXPathFreeContext(ctxt->xctxt);
        if (ctxt->dict != NULL)
            xmlDictFree(ctxt->dict);
        xmlFree(ctxt);
    }
    
    static xmlNodePtr
    xmlSchematronNextNode(xmlNodePtr cur) {
        if (cur->children != NULL) {
    	/*
    	 * Do not descend on entities declarations
    	 */
    	if (cur->children->type != XML_ENTITY_DECL) {
    	    cur = cur->children;
    	    /*
    	     * Skip DTDs
    	     */
    	    if (cur->type != XML_DTD_NODE)
    		return(cur);
    	}
        }
    
        while (cur->next != NULL) {
    	cur = cur->next;
    	if ((cur->type != XML_ENTITY_DECL) &&
    	    (cur->type != XML_DTD_NODE))
    	    return(cur);
        }
    
        do {
    	cur = cur->parent;
    	if (cur == NULL) break;
    	if (cur->type == XML_DOCUMENT_NODE) return(NULL);
    	if (cur->next != NULL) {
    	    cur = cur->next;
    	    return(cur);
    	}
        } while (cur != NULL);
        return(cur);
    }
    
    /**
     * xmlSchematronRunTest:
     * @ctxt:  the schema validation context
     * @test:  the current test
     * @instance:  the document instance tree
     * @cur:  the current node in the instance
     *
     * Validate a rule against a tree instance at a given position
     *
     * Returns 1 in case of success, 0 if error and -1 in case of internal error
     */
    static int
    xmlSchematronRunTest(xmlSchematronValidCtxtPtr ctxt,
         xmlSchematronTestPtr test, xmlDocPtr instance, xmlNodePtr cur, xmlSchematronPatternPtr pattern)
    {
        xmlXPathObjectPtr ret;
        int failed;
    
        failed = 0;
        ctxt->xctxt->doc = instance;
        ctxt->xctxt->node = cur;
        ret = xmlXPathCompiledEval(test->comp, ctxt->xctxt);
        if (ret == NULL) {
    	failed = 1;
        } else {
            switch (ret->type) {
    	    case XPATH_XSLT_TREE:
    	    case XPATH_NODESET:
    		if ((ret->nodesetval == NULL) ||
    		    (ret->nodesetval->nodeNr == 0))
    		    failed = 1;
    		break;
    	    case XPATH_BOOLEAN:
    		failed = !ret->boolval;
    		break;
    	    case XPATH_NUMBER:
    		if ((xmlXPathIsNaN(ret->floatval)) ||
    		    (ret->floatval == 0.0))
    		    failed = 1;
    		break;
    	    case XPATH_STRING:
    		if ((ret->stringval == NULL) ||
    		    (ret->stringval[0] == 0))
    		    failed = 1;
    		break;
    	    case XPATH_UNDEFINED:
    	    case XPATH_POINT:
    	    case XPATH_RANGE:
    	    case XPATH_LOCATIONSET:
    	    case XPATH_USERS:
    		failed = 1;
    		break;
    	}
    	xmlXPathFreeObject(ret);
        }
        if ((failed) && (test->type == XML_SCHEMATRON_ASSERT))
            ctxt->nberrors++;
        else if ((!failed) && (test->type == XML_SCHEMATRON_REPORT))
            ctxt->nberrors++;
    
        xmlSchematronReportSuccess(ctxt, test, cur, pattern, !failed);
    
        return(!failed);
    }
    
    /**
     * xmlSchematronValidateDoc:
     * @ctxt:  the schema validation context
     * @instance:  the document instance tree
     *
     * Validate a tree instance against the schematron
     *
     * Returns 0 in case of success, -1 in case of internal error
     *         and an error count otherwise.
     */
    int
    xmlSchematronValidateDoc(xmlSchematronValidCtxtPtr ctxt, xmlDocPtr instance)
    {
        xmlNodePtr cur, root;
        xmlSchematronPatternPtr pattern;
        xmlSchematronRulePtr rule;
        xmlSchematronTestPtr test;
    
        if ((ctxt == NULL) || (ctxt->schema == NULL) ||
            (ctxt->schema->rules == NULL) || (instance == NULL))
            return(-1);
        ctxt->nberrors = 0;
        root = xmlDocGetRootElement(instance);
        if (root == NULL) {
            TODO
    	ctxt->nberrors++;
    	return(1);
        }
        if ((ctxt->flags & XML_SCHEMATRON_OUT_QUIET) ||
            (ctxt->flags == 0)) {
    	/*
    	 * we are just trying to assert the validity of the document,
    	 * speed primes over the output, run in a single pass
    	 */
    	cur = root;
    	while (cur != NULL) {
    	    rule = ctxt->schema->rules;
    	    while (rule != NULL) {
    		if (xmlPatternMatch(rule->pattern, cur) == 1) {
    		    test = rule->tests;
    		    while (test != NULL) {
    			xmlSchematronRunTest(ctxt, test, instance, cur, (xmlSchematronPatternPtr)rule->pattern);
    			test = test->next;
    		    }
    		}
    		rule = rule->next;
    	    }
    
    	    cur = xmlSchematronNextNode(cur);
    	}
        } else {
            /*
    	 * Process all contexts one at a time
    	 */
    	pattern = ctxt->schema->patterns;
    
    	while (pattern != NULL) {
    	    xmlSchematronReportPattern(ctxt, pattern);
    
    	    /*
    	     * TODO convert the pattern rule to a direct XPath and
    	     * compute directly instead of using the pattern matching
    	     * over the full document...
    	     * Check the exact semantic
    	     */
    	    cur = root;
    	    while (cur != NULL) {
    		rule = pattern->rules;
    		while (rule != NULL) {
    		    if (xmlPatternMatch(rule->pattern, cur) == 1) {
    			test = rule->tests;
    			while (test != NULL) {
    			    xmlSchematronRunTest(ctxt, test, instance, cur, pattern);
    			    test = test->next;
    			}
    		    }
    		    rule = rule->patnext;
    		}
    
    		cur = xmlSchematronNextNode(cur);
    	    }
    	    pattern = pattern->next;
    	}
        }
        return(ctxt->nberrors);
    }
    
    #ifdef STANDALONE
    int
    main(void)
    {
        int ret;
        xmlDocPtr instance;
        xmlSchematronParserCtxtPtr pctxt;
        xmlSchematronValidCtxtPtr vctxt;
        xmlSchematronPtr schema = NULL;
    
        pctxt = xmlSchematronNewParserCtxt("tst.sct");
        if (pctxt == NULL) {
            fprintf(stderr, "failed to build schematron parser\n");
        } else {
            schema = xmlSchematronParse(pctxt);
    	if (schema == NULL) {
    	    fprintf(stderr, "failed to compile schematron\n");
    	}
    	xmlSchematronFreeParserCtxt(pctxt);
        }
        instance = xmlReadFile("tst.sct", NULL,
                               XML_PARSE_NOENT | XML_PARSE_NOCDATA);
        if (instance == NULL) {
    	fprintf(stderr, "failed to parse instance\n");
        }
        if ((schema != NULL) && (instance != NULL)) {
            vctxt = xmlSchematronNewValidCtxt(schema);
    	if (vctxt == NULL) {
    	    fprintf(stderr, "failed to build schematron validator\n");
    	} else {
    	    ret = xmlSchematronValidateDoc(vctxt, instance);
    	    xmlSchematronFreeValidCtxt(vctxt);
    	}
        }
        xmlSchematronFree(schema);
        xmlFreeDoc(instance);
    
        xmlCleanupParser();
        xmlMemoryDump();
    
        return (0);
    }
    #endif
    #define bottom_schematron
    #include "elfgcchack.h"
    #endif /* LIBXML_SCHEMATRON_ENABLED */