Skip to content
Snippets Groups Projects
schematron.c 61.3 KiB
Newer Older
  • Learn to ignore specific revisions
  •  * 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;
    
    Oliver Diehl's avatar
    Oliver Diehl committed
    #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))))
    
    Oliver Diehl's avatar
    Oliver Diehl committed
    #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;                                               \
    
    Oliver Diehl's avatar
    Oliver Diehl committed
    #define TODO                                                            \
        xmlGenericError(xmlGenericErrorContext,                             \
                "Unimplemented block at %s:%d\n",                           \
    
    typedef enum {
        XML_SCHEMATRON_ASSERT=1,
        XML_SCHEMATRON_REPORT=2
    } xmlSchematronTestType;
    
    
     * _xmlSchematronLet:
     *
     * A Schematron let variable
     */
    typedef struct _xmlSchematronLet xmlSchematronLet;
    typedef xmlSchematronLet *xmlSchematronLetPtr;
    struct _xmlSchematronLet {
        xmlSchematronLetPtr next; /* the next let variable in the list */
        xmlChar *name;            /* the name of the variable */
        xmlXPathCompExprPtr comp; /* the compiled expression */
    };
    
    /**
    
     * _xmlSchematronTest:
     *
     * A Schematrons test, either an assert or a report
     */
    typedef struct _xmlSchematronTest xmlSchematronTest;
    typedef xmlSchematronTest *xmlSchematronTestPtr;
    struct _xmlSchematronTest {
    
    Oliver Diehl's avatar
    Oliver Diehl committed
        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 {
    
    Oliver Diehl's avatar
    Oliver Diehl committed
        xmlSchematronRulePtr next;  /* the next rule in the list */
    
        xmlSchematronRulePtr patnext;/* the next rule in the pattern list */
    
    Oliver Diehl's avatar
    Oliver Diehl committed
        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 */
    
        xmlSchematronLetPtr lets;   /* the list of let variables */
    
     * _xmlSchematronPattern:
     *
     * A Schematrons pattern
     */
    typedef struct _xmlSchematronPattern xmlSchematronPattern;
    typedef xmlSchematronPattern *xmlSchematronPatternPtr;
    struct _xmlSchematronPattern {
        xmlSchematronPatternPtr next;/* the next pattern in the list */
    
    Oliver Diehl's avatar
    Oliver Diehl committed
        xmlSchematronRulePtr rules; /* the list of rules */
        xmlChar *name;              /* the name of the pattern */
    
     * _xmlSchematron:
     *
     * A Schematrons definition
     */
    struct _xmlSchematron {
    
    Oliver Diehl's avatar
    Oliver Diehl committed
        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 */
    
    Oliver Diehl's avatar
    Oliver Diehl committed
        void *_private;             /* unused by the library */
        xmlDictPtr dict;            /* the dictionary used internally */
    
    Oliver Diehl's avatar
    Oliver Diehl committed
        const xmlChar *title;       /* the title if any */
    
    Oliver Diehl's avatar
    Oliver Diehl committed
        int nbNs;                   /* the number of namespaces */
    
    Oliver Diehl's avatar
    Oliver Diehl committed
        int nbPattern;              /* the number of patterns */
    
        xmlSchematronPatternPtr patterns;/* the patterns found */
    
    Oliver Diehl's avatar
    Oliver Diehl committed
        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;
    
    Oliver Diehl's avatar
    Oliver Diehl committed
        int flags;                  /* an or of xmlSchematronValidOptions */
    
    
        xmlDictPtr dict;
        int nberrors;
        int err;
    
        xmlSchematronPtr schema;
        xmlXPathContextPtr xctxt;
    
    
    Oliver Diehl's avatar
    Oliver Diehl committed
        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;
    
    
        /* 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 */
    
    Oliver Diehl's avatar
    Oliver Diehl committed
        xmlXPathContextPtr xctxt;   /* the XPath context used for compilation */
    
    Oliver Diehl's avatar
    Oliver Diehl committed
        int nbNamespaces;           /* number of namespaces in the array */
        int maxNamespaces;          /* size of the array */
        const xmlChar **namespaces; /* the array of namespaces */
    
    Oliver Diehl's avatar
    Oliver Diehl committed
        int nbIncludes;             /* number of includes in the array */
        int maxIncludes;            /* size of the array */
        xmlNodePtr *includes;       /* the array of includes */
    
        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
    
    /************************************************************************
    
    Oliver Diehl's avatar
    Oliver Diehl committed
     *                                                                      *
     *                      Error reporting                                 *
     *                                                                      *
    
     ************************************************************************/
    
    /**
     * xmlSchematronPErrMemory:
     * @node: a context node
    
    Nick Wellnhofer's avatar
    Nick Wellnhofer committed
     * @extra:  extra information
    
     *
     * 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
    
    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;
    
    Oliver Diehl's avatar
    Oliver Diehl committed
            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
    
    Nick Wellnhofer's avatar
    Nick Wellnhofer committed
     * @extra:  extra information
    
     *
     * 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);
    }
    
    /************************************************************************
    
    Oliver Diehl's avatar
    Oliver Diehl committed
     *                                                                      *
     *              Parsing and compilation of the Schematrontrons          *
     *                                                                      *
    
     ************************************************************************/
    
    /**
     * xmlSchematronAddTest:
     * @ctxt: the schema parsing context
    
     * @type:  the type of test
     * @rule:  the parent rule
    
     * @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,
    
                         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) {
    
    Oliver Diehl's avatar
    Oliver Diehl committed
            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->next = NULL;
        if (rule->tests == NULL) {
    
    Oliver Diehl's avatar
    Oliver Diehl committed
            rule->tests = ret;
    
        } else {
            xmlSchematronTestPtr prev = rule->tests;
    
    
    Oliver Diehl's avatar
    Oliver Diehl committed
            while (prev->next != NULL)
                 prev = prev->next;
    
        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;
    
    Oliver Diehl's avatar
    Oliver Diehl committed
            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;
    
     * xmlSchematronFreeLets:
     * @lets:  a list of let variables
     *
     * Free a list of let variables.
     */
    static void
    xmlSchematronFreeLets(xmlSchematronLetPtr lets) {
        xmlSchematronLetPtr next;
    
        while (lets != NULL) {
            next = lets->next;
            if (lets->name != NULL)
                xmlFree(lets->name);
            if (lets->comp != NULL)
                xmlXPathFreeCompExpr(lets->comp);
            xmlFree(lets);
            lets = 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,
    
    Oliver Diehl's avatar
    Oliver Diehl committed
                         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) {
    
    Oliver Diehl's avatar
    Oliver Diehl committed
            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->lets = NULL;
    
    Oliver Diehl's avatar
    Oliver Diehl committed
            schema->rules = ret;
    
        } else {
            xmlSchematronRulePtr prev = schema->rules;
    
    
    Oliver Diehl's avatar
    Oliver Diehl committed
            while (prev->next != NULL)
                 prev = prev->next;
    
            prev->next = ret;
        }
        ret->patnext = NULL;
        if (pat->rules == NULL) {
    
    Oliver Diehl's avatar
    Oliver Diehl committed
            pat->rules = ret;
    
        } else {
            xmlSchematronRulePtr prev = pat->rules;
    
    
    Oliver Diehl's avatar
    Oliver Diehl committed
            while (prev->patnext != NULL)
                 prev = prev->patnext;
    
        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;
    
    Oliver Diehl's avatar
    Oliver Diehl committed
            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);
    
            if (rules->lets != NULL)
                xmlSchematronFreeLets(rules->lets);
    
    Oliver Diehl's avatar
    Oliver Diehl committed
            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) {
    
    Oliver Diehl's avatar
    Oliver Diehl committed
            schema->patterns = ret;
    
        } else {
            xmlSchematronPatternPtr prev = schema->patterns;
    
    
    Oliver Diehl's avatar
    Oliver Diehl committed
            while (prev->next != NULL)
                 prev = prev->next;
    
        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;
    
    Oliver Diehl's avatar
    Oliver Diehl committed
            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->xctxt = xmlXPathNewContext(NULL);
        if (ret->xctxt == NULL) {
            xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",
                                    NULL);
    
    Oliver Diehl's avatar
    Oliver Diehl committed
            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);
    
    Oliver Diehl's avatar
    Oliver Diehl committed
            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);
    
    Oliver Diehl's avatar
    Oliver Diehl committed
            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);
    
    /**
     * 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 *)
    
    Oliver Diehl's avatar
    Oliver Diehl committed
                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;
    
    
    Oliver Diehl's avatar
    Oliver Diehl committed
            tmp = (xmlNodePtr *)
                xmlRealloc(ctxt->includes, ctxt->maxIncludes * 4 *
                           sizeof(xmlNodePtr));
            if (tmp == NULL) {
                xmlSchematronPErrMemory(NULL, "allocating parser includes",
                                        NULL);
                return;
            }
    
    Oliver Diehl's avatar
    Oliver Diehl committed
            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)
    
    Oliver Diehl's avatar
    Oliver Diehl committed
            ret = ret->next;
    
        if (ret == NULL)
            return(xmlSchematronPopInclude(ctxt));
        return(ret);
    }
    
    
    /**
     * 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 **)
    
    Oliver Diehl's avatar
    Oliver Diehl committed
                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;
    
    
    Oliver Diehl's avatar
    Oliver Diehl committed
            tmp = (const xmlChar **)
                xmlRealloc((xmlChar **) ctxt->namespaces, ctxt->maxNamespaces * 4 *
                           sizeof(const xmlChar *));
            if (tmp == NULL) {
                xmlSchematronPErrMemory(NULL, "allocating parser namespaces",
                                        NULL);
                return;
            }
    
    Oliver Diehl's avatar
    Oliver Diehl committed
            ctxt->maxNamespaces *= 2;
    
        ctxt->namespaces[2 * ctxt->nbNamespaces] =
    
        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;
    
    }
    
    /**
    
    Oliver Diehl's avatar
    Oliver Diehl committed
     * xmlSchematronParseTestReportMsg:
     * @ctxt:  the schema parser context
     * @con:  the assert or report node
     *
     * Format the message content of the assert or report test
     */
    static void
    xmlSchematronParseTestReportMsg(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr con)
    {
        xmlNodePtr child;
        xmlXPathCompExprPtr comp;
    
        child = con->children;
        while (child != NULL) {
            if ((child->type == XML_TEXT_NODE) ||
                (child->type == XML_CDATA_SECTION_NODE))
                /* Do Nothing */
                {}
            else if (IS_SCHEMATRON(child, "name")) {
                /* Do Nothing */
            } else if (IS_SCHEMATRON(child, "value-of")) {
                xmlChar *select;
    
                select = xmlGetNoNsProp(child, BAD_CAST "select");
    
                if (select == NULL) {
                    xmlSchematronPErr(ctxt, child,
                                      XML_SCHEMAV_ATTRINVALID,
                                      "value-of has no select attribute",
                                      NULL, NULL);
                } else {
                    /*
                     * try first to compile the test expression
                     */
                    comp = xmlXPathCtxtCompile(ctxt->xctxt, select);
                    if (comp == NULL) {
                        xmlSchematronPErr(ctxt, child,
                                          XML_SCHEMAV_ATTRINVALID,
                                          "Failed to compile select expression %s",
                                          select, NULL);
                    }
                    xmlXPathFreeCompExpr(comp);
                }
                xmlFree(select);
            }
            child = child->next;
            continue;
        }
    }
    
    /**
    
     * xmlSchematronParseRule:
     * @ctxt:  a schema validation context
     * @rule:  the rule node
     *
     * parse a rule element
     */
    static void
    
    xmlSchematronParseRule(xmlSchematronParserCtxtPtr ctxt,
                           xmlSchematronPatternPtr pattern,
    
    Oliver Diehl's avatar
    Oliver Diehl committed
                           xmlNodePtr rule)
    
    {
        xmlNodePtr cur;
        int nbChecks = 0;
        xmlChar *test;
        xmlChar *context;
    
        xmlChar *name;
        xmlChar *value;
    
        xmlSchematronRulePtr ruleptr;
        xmlSchematronTestPtr testptr;
    
        if ((ctxt == NULL) || (rule == NULL)) return;
    
        context = xmlGetNoNsProp(rule, BAD_CAST "context");
        if (context == NULL) {
    
    Oliver Diehl's avatar
    Oliver Diehl committed
            xmlSchematronPErr(ctxt, rule,
                XML_SCHEMAP_NOROOT,
                "rule has no context attribute",
                NULL, NULL);
            return;
    
    Oliver Diehl's avatar
    Oliver Diehl committed
            xmlSchematronPErr(ctxt, rule,
                XML_SCHEMAP_NOROOT,
                "rule has an empty context attribute",
                NULL, NULL);
            xmlFree(context);
            return;
    
    Oliver Diehl's avatar
    Oliver Diehl committed
            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, "let")) {
                xmlXPathCompExprPtr var_comp;
                xmlSchematronLetPtr let;
    
                name = xmlGetNoNsProp(cur, BAD_CAST "name");
                if (name == NULL) {
                    xmlSchematronPErr(ctxt, cur,
                                      XML_SCHEMAP_NOROOT,
                                      "let has no name attribute",
                                      NULL, NULL);
                    return;
                } else if (name[0] == 0) {
                    xmlSchematronPErr(ctxt, cur,
                                      XML_SCHEMAP_NOROOT,
                                      "let has an empty name attribute",
                                      NULL, NULL);
                    xmlFree(name);
                    return;
                }
                value = xmlGetNoNsProp(cur, BAD_CAST "value");
                if (value == NULL) {