aboutsummaryrefslogtreecommitdiff
path: root/libxml2-2.9.10/schematron.c
diff options
context:
space:
mode:
Diffstat (limited to 'libxml2-2.9.10/schematron.c')
-rw-r--r--libxml2-2.9.10/schematron.c1787
1 files changed, 1787 insertions, 0 deletions
diff --git a/libxml2-2.9.10/schematron.c b/libxml2-2.9.10/schematron.c
new file mode 100644
index 0000000..258ce40
--- /dev/null
+++ b/libxml2-2.9.10/schematron.c
@@ -0,0 +1,1787 @@
+/*
+ * 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 */