This source file includes following definitions.
- xmlSchematronPErrMemory
- xmlSchematronPErr
- xmlSchematronVErrMemory
- xmlSchematronAddTest
- xmlSchematronFreeTests
- xmlSchematronAddRule
- xmlSchematronFreeRules
- xmlSchematronAddPattern
- xmlSchematronFreePatterns
- xmlSchematronNewSchematron
- xmlSchematronFree
- xmlSchematronNewParserCtxt
- xmlSchematronNewMemParserCtxt
- xmlSchematronNewDocParserCtxt
- xmlSchematronFreeParserCtxt
- xmlSchematronPushInclude
- xmlSchematronPopInclude
- xmlSchematronAddNamespace
- xmlSchematronParseRule
- xmlSchematronParsePattern
- xmlSchematronLoadInclude
- xmlSchematronParse
- xmlSchematronGetNode
- xmlSchematronReportOutput
- xmlSchematronFormatReport
- xmlSchematronReportSuccess
- xmlSchematronReportPattern
- xmlSchematronSetValidStructuredErrors
- xmlSchematronNewValidCtxt
- xmlSchematronFreeValidCtxt
- xmlSchematronNextNode
- xmlSchematronRunTest
- xmlSchematronValidateDoc
- main
#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; \
}
#define TODO \
xmlGenericError(xmlGenericErrorContext, \
"Unimplemented block at %s:%d\n", \
__FILE__, __LINE__);
typedef enum {
XML_SCHEMATRON_ASSERT=1,
XML_SCHEMATRON_REPORT=2
} xmlSchematronTestType;
typedef struct _xmlSchematronTest xmlSchematronTest;
typedef xmlSchematronTest *xmlSchematronTestPtr;
struct _xmlSchematronTest {
xmlSchematronTestPtr next;
xmlSchematronTestType type;
xmlNodePtr node;
xmlChar *test;
xmlXPathCompExprPtr comp;
xmlChar *report;
};
typedef struct _xmlSchematronRule xmlSchematronRule;
typedef xmlSchematronRule *xmlSchematronRulePtr;
struct _xmlSchematronRule {
xmlSchematronRulePtr next;
xmlSchematronRulePtr patnext;
xmlNodePtr node;
xmlChar *context;
xmlSchematronTestPtr tests;
xmlPatternPtr pattern;
xmlChar *report;
};
typedef struct _xmlSchematronPattern xmlSchematronPattern;
typedef xmlSchematronPattern *xmlSchematronPatternPtr;
struct _xmlSchematronPattern {
xmlSchematronPatternPtr next;
xmlSchematronRulePtr rules;
xmlChar *name;
};
struct _xmlSchematron {
const xmlChar *name;
int preserve;
xmlDocPtr doc;
int flags;
void *_private;
xmlDictPtr dict;
const xmlChar *title;
int nbNs;
int nbPattern;
xmlSchematronPatternPtr patterns;
xmlSchematronRulePtr rules;
int nbNamespaces;
int maxNamespaces;
const xmlChar **namespaces;
};
struct _xmlSchematronValidCtxt {
int type;
int flags;
xmlDictPtr dict;
int nberrors;
int err;
xmlSchematronPtr schema;
xmlXPathContextPtr xctxt;
FILE *outputFile;
xmlBufferPtr outputBuffer;
xmlOutputWriteCallback iowrite;
xmlOutputCloseCallback ioclose;
void *ioctx;
void *userData;
xmlSchematronValidityErrorFunc error;
xmlSchematronValidityWarningFunc warning;
xmlStructuredErrorFunc serror;
};
struct _xmlSchematronParserCtxt {
int type;
const xmlChar *URL;
xmlDocPtr doc;
int preserve;
const char *buffer;
int size;
xmlDictPtr dict;
int nberrors;
int err;
xmlXPathContextPtr xctxt;
xmlSchematronPtr schema;
int nbNamespaces;
int maxNamespaces;
const xmlChar **namespaces;
int nbIncludes;
int maxIncludes;
xmlNodePtr *includes;
void *userData;
xmlSchematronValidityErrorFunc error;
xmlSchematronValidityWarningFunc warning;
xmlStructuredErrorFunc serror;
};
#define XML_STRON_CTXT_PARSER 1
#define XML_STRON_CTXT_VALIDATOR 2
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);
}
static void
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);
}
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);
}
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);
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);
}
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;
}
}
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);
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);
}
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;
}
}
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);
}
static void
xmlSchematronFreePatterns(xmlSchematronPatternPtr patterns) {
xmlSchematronPatternPtr next;
while (patterns != NULL) {
next = patterns->next;
if (patterns->name != NULL)
xmlFree(patterns->name);
xmlFree(patterns);
patterns = next;
}
}
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);
}
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);
}
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);
}
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);
}
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();
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);
}
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
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++;
}
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
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;
}
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 {
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 {
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);
}
}
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
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);
}
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;
}
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
xmlSchematronPtr
xmlSchematronParse(xmlSchematronParserCtxtPtr ctxt)
{
xmlSchematronPtr ret = NULL;
xmlDocPtr doc;
xmlNodePtr root, cur;
int preserve = 0;
if (ctxt == NULL)
return (NULL);
ctxt->nberrors = 0;
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);
}
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;
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;
}
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);
}
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);
}
static void
xmlSchematronReportOutput(xmlSchematronValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
xmlNodePtr cur ATTRIBUTE_UNUSED,
const char *msg) {
fprintf(stderr, "%s", msg);
}
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;
}
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);
}
static void
xmlSchematronReportSuccess(xmlSchematronValidCtxtPtr ctxt,
xmlSchematronTestPtr test, xmlNodePtr cur, xmlSchematronPatternPtr pattern, int success) {
if ((ctxt == NULL) || (cur == NULL) || (test == NULL))
return;
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);
}
}
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))
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]);
}
}
void
xmlSchematronSetValidStructuredErrors(xmlSchematronValidCtxtPtr ctxt,
xmlStructuredErrorFunc serror, void *ctx)
{
if (ctxt == NULL)
return;
ctxt->serror = serror;
ctxt->error = NULL;
ctxt->warning = NULL;
ctxt->userData = ctx;
}
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);
}
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) {
if (cur->children->type != XML_ENTITY_DECL) {
cur = cur->children;
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);
}
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);
}
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)) {
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 {
pattern = ctxt->schema->patterns;
while (pattern != NULL) {
xmlSchematronReportPattern(ctxt, pattern);
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