2 * schematron.c : implementation of the Schematron schema validity checking
4 * See Copyright for the status of this software.
6 * Daniel Veillard <daniel@veillard.com>
11 * + double check the semantic, especially
12 * - multiple rules applying in a single pattern/node
13 * - the semantic of libxml2 patterns vs. XSLT production referenced
15 * + export of results in SVRL
16 * + full parsing and coverage of the spec, conformance of the input to the
18 * + divergences between the draft and the ISO proposed standard :-(
19 * + hook and test include
20 * + try and compare with the XSLT version
26 #ifdef LIBXML_SCHEMATRON_ENABLED
29 #include <libxml/parser.h>
30 #include <libxml/tree.h>
31 #include <libxml/uri.h>
32 #include <libxml/xpath.h>
33 #include <libxml/xpathInternals.h>
34 #include <libxml/pattern.h>
35 #include <libxml/schematron.h>
37 #define SCHEMATRON_PARSE_OPTIONS XML_PARSE_NOENT
39 #define SCT_OLD_NS BAD_CAST "http://www.ascc.net/xml/schematron"
41 #define XML_SCHEMATRON_NS BAD_CAST "http://purl.oclc.org/dsdl/schematron"
44 static const xmlChar
*xmlSchematronNs
= XML_SCHEMATRON_NS
;
45 static const xmlChar
*xmlOldSchematronNs
= SCT_OLD_NS
;
47 #define IS_SCHEMATRON(node, elem) \
48 ((node != NULL) && (node->type == XML_ELEMENT_NODE ) && \
49 (node->ns != NULL) && \
50 (xmlStrEqual(node->name, (const xmlChar *) elem)) && \
51 ((xmlStrEqual(node->ns->href, xmlSchematronNs)) || \
52 (xmlStrEqual(node->ns->href, xmlOldSchematronNs))))
54 #define NEXT_SCHEMATRON(node) \
55 while (node != NULL) { \
56 if ((node->type == XML_ELEMENT_NODE ) && (node->ns != NULL) && \
57 ((xmlStrEqual(node->ns->href, xmlSchematronNs)) || \
58 (xmlStrEqual(node->ns->href, xmlOldSchematronNs)))) \
66 * macro to flag unimplemented blocks
69 xmlGenericError(xmlGenericErrorContext, \
70 "Unimplemented block at %s:%d\n", \
74 XML_SCHEMATRON_ASSERT
=1,
75 XML_SCHEMATRON_REPORT
=2
76 } xmlSchematronTestType
;
81 * A Schematrons test, either an assert or a report
83 typedef struct _xmlSchematronTest xmlSchematronTest
;
84 typedef xmlSchematronTest
*xmlSchematronTestPtr
;
85 struct _xmlSchematronTest
{
86 xmlSchematronTestPtr next
; /* the next test in the list */
87 xmlSchematronTestType type
; /* the test type */
88 xmlNodePtr node
; /* the node in the tree */
89 xmlChar
*test
; /* the expression to test */
90 xmlXPathCompExprPtr comp
; /* the compiled expression */
91 xmlChar
*report
; /* the message to report */
99 typedef struct _xmlSchematronRule xmlSchematronRule
;
100 typedef xmlSchematronRule
*xmlSchematronRulePtr
;
101 struct _xmlSchematronRule
{
102 xmlSchematronRulePtr next
; /* the next rule in the list */
103 xmlSchematronRulePtr patnext
;/* the next rule in the pattern list */
104 xmlNodePtr node
; /* the node in the tree */
105 xmlChar
*context
; /* the context evaluation rule */
106 xmlSchematronTestPtr tests
; /* the list of tests */
107 xmlPatternPtr pattern
; /* the compiled pattern associated */
108 xmlChar
*report
; /* the message to report */
112 * _xmlSchematronPattern:
114 * A Schematrons pattern
116 typedef struct _xmlSchematronPattern xmlSchematronPattern
;
117 typedef xmlSchematronPattern
*xmlSchematronPatternPtr
;
118 struct _xmlSchematronPattern
{
119 xmlSchematronPatternPtr next
;/* the next pattern in the list */
120 xmlSchematronRulePtr rules
; /* the list of rules */
121 xmlChar
*name
; /* the name of the pattern */
127 * A Schematrons definition
129 struct _xmlSchematron
{
130 const xmlChar
*name
; /* schema name */
131 int preserve
; /* was the document passed by the user */
132 xmlDocPtr doc
; /* pointer to the parsed document */
133 int flags
; /* specific to this schematron */
135 void *_private
; /* unused by the library */
136 xmlDictPtr dict
; /* the dictionnary used internally */
138 const xmlChar
*title
; /* the title if any */
140 int nbNs
; /* the number of namespaces */
142 int nbPattern
; /* the number of patterns */
143 xmlSchematronPatternPtr patterns
;/* the patterns found */
144 xmlSchematronRulePtr rules
; /* the rules gathered */
145 int nbNamespaces
; /* number of namespaces in the array */
146 int maxNamespaces
; /* size of the array */
147 const xmlChar
**namespaces
; /* the array of namespaces */
151 * xmlSchematronValidCtxt:
153 * A Schematrons validation context
155 struct _xmlSchematronValidCtxt
{
157 int flags
; /* an or of xmlSchematronValidOptions */
163 xmlSchematronPtr schema
;
164 xmlXPathContextPtr xctxt
;
166 FILE *outputFile
; /* if using XML_SCHEMATRON_OUT_FILE */
167 xmlBufferPtr outputBuffer
; /* if using XML_SCHEMATRON_OUT_BUFFER */
168 xmlOutputWriteCallback iowrite
; /* if using XML_SCHEMATRON_OUT_IO */
169 xmlOutputCloseCallback ioclose
;
172 /* error reporting data */
173 void *userData
; /* user specific data block */
174 xmlSchematronValidityErrorFunc error
;/* the callback in case of errors */
175 xmlSchematronValidityWarningFunc warning
;/* callback in case of warning */
176 xmlStructuredErrorFunc serror
; /* the structured function */
179 struct _xmlSchematronParserCtxt
{
183 int preserve
; /* Whether the doc should be freed */
187 xmlDictPtr dict
; /* dictionnary for interned string names */
191 xmlXPathContextPtr xctxt
; /* the XPath context used for compilation */
192 xmlSchematronPtr schema
;
194 int nbNamespaces
; /* number of namespaces in the array */
195 int maxNamespaces
; /* size of the array */
196 const xmlChar
**namespaces
; /* the array of namespaces */
198 int nbIncludes
; /* number of includes in the array */
199 int maxIncludes
; /* size of the array */
200 xmlNodePtr
*includes
; /* the array of includes */
202 /* error reporting data */
203 void *userData
; /* user specific data block */
204 xmlSchematronValidityErrorFunc error
;/* the callback in case of errors */
205 xmlSchematronValidityWarningFunc warning
;/* callback in case of warning */
206 xmlStructuredErrorFunc serror
; /* the structured function */
209 #define XML_STRON_CTXT_PARSER 1
210 #define XML_STRON_CTXT_VALIDATOR 2
212 /************************************************************************
216 ************************************************************************/
219 * xmlSchematronPErrMemory:
220 * @node: a context node
221 * @extra: extra informations
223 * Handle an out of memory condition
226 xmlSchematronPErrMemory(xmlSchematronParserCtxtPtr ctxt
,
227 const char *extra
, xmlNodePtr node
)
231 __xmlSimpleError(XML_FROM_SCHEMASP
, XML_ERR_NO_MEMORY
, node
, NULL
,
237 * @ctxt: the parsing context
238 * @node: the context node
239 * @error: the error code
240 * @msg: the error message
244 * Handle a parser error
247 xmlSchematronPErr(xmlSchematronParserCtxtPtr ctxt
, xmlNodePtr node
, int error
,
248 const char *msg
, const xmlChar
* str1
, const xmlChar
* str2
)
250 xmlGenericErrorFunc channel
= NULL
;
251 xmlStructuredErrorFunc schannel
= NULL
;
256 channel
= ctxt
->error
;
257 data
= ctxt
->userData
;
258 schannel
= ctxt
->serror
;
260 __xmlRaiseError(schannel
, channel
, data
, ctxt
, node
, XML_FROM_SCHEMASP
,
261 error
, XML_ERR_ERROR
, NULL
, 0,
262 (const char *) str1
, (const char *) str2
, NULL
, 0, 0,
267 * xmlSchematronVTypeErrMemory:
268 * @node: a context node
269 * @extra: extra informations
271 * Handle an out of memory condition
274 xmlSchematronVErrMemory(xmlSchematronValidCtxtPtr ctxt
,
275 const char *extra
, xmlNodePtr node
)
279 ctxt
->err
= XML_SCHEMAV_INTERNAL
;
281 __xmlSimpleError(XML_FROM_SCHEMASV
, XML_ERR_NO_MEMORY
, node
, NULL
,
285 /************************************************************************
287 * Parsing and compilation of the Schematrontrons *
289 ************************************************************************/
292 * xmlSchematronAddTest:
293 * @ctxt: the schema parsing context
294 * @type: the type of test
295 * @rule: the parent rule
296 * @node: the node hosting the test
297 * @test: the associated test
298 * @report: the associated report string
300 * Add a test to a schematron
302 * Returns the new pointer or NULL in case of error
304 static xmlSchematronTestPtr
305 xmlSchematronAddTest(xmlSchematronParserCtxtPtr ctxt
,
306 xmlSchematronTestType type
,
307 xmlSchematronRulePtr rule
,
308 xmlNodePtr node
, xmlChar
*test
, xmlChar
*report
)
310 xmlSchematronTestPtr ret
;
311 xmlXPathCompExprPtr comp
;
313 if ((ctxt
== NULL
) || (rule
== NULL
) || (node
== NULL
) ||
318 * try first to compile the test expression
320 comp
= xmlXPathCtxtCompile(ctxt
->xctxt
, test
);
322 xmlSchematronPErr(ctxt
, node
,
324 "Failed to compile test expression %s",
329 ret
= (xmlSchematronTestPtr
) xmlMalloc(sizeof(xmlSchematronTest
));
331 xmlSchematronPErrMemory(ctxt
, "allocating schema test", node
);
334 memset(ret
, 0, sizeof(xmlSchematronTest
));
339 ret
->report
= report
;
341 if (rule
->tests
== NULL
) {
344 xmlSchematronTestPtr prev
= rule
->tests
;
346 while (prev
->next
!= NULL
)
354 * xmlSchematronFreeTests:
355 * @tests: a list of tests
357 * Free a list of tests.
360 xmlSchematronFreeTests(xmlSchematronTestPtr tests
) {
361 xmlSchematronTestPtr next
;
363 while (tests
!= NULL
) {
365 if (tests
->test
!= NULL
)
366 xmlFree(tests
->test
);
367 if (tests
->comp
!= NULL
)
368 xmlXPathFreeCompExpr(tests
->comp
);
369 if (tests
->report
!= NULL
)
370 xmlFree(tests
->report
);
377 * xmlSchematronAddRule:
378 * @ctxt: the schema parsing context
379 * @schema: a schema structure
380 * @node: the node hosting the rule
381 * @context: the associated context string
382 * @report: the associated report string
384 * Add a rule to a schematron
386 * Returns the new pointer or NULL in case of error
388 static xmlSchematronRulePtr
389 xmlSchematronAddRule(xmlSchematronParserCtxtPtr ctxt
, xmlSchematronPtr schema
,
390 xmlSchematronPatternPtr pat
, xmlNodePtr node
,
391 xmlChar
*context
, xmlChar
*report
)
393 xmlSchematronRulePtr ret
;
394 xmlPatternPtr pattern
;
396 if ((ctxt
== NULL
) || (schema
== NULL
) || (node
== NULL
) ||
401 * Try first to compile the pattern
403 pattern
= xmlPatterncompile(context
, ctxt
->dict
, XML_PATTERN_XPATH
,
405 if (pattern
== NULL
) {
406 xmlSchematronPErr(ctxt
, node
,
408 "Failed to compile context expression %s",
412 ret
= (xmlSchematronRulePtr
) xmlMalloc(sizeof(xmlSchematronRule
));
414 xmlSchematronPErrMemory(ctxt
, "allocating schema rule", node
);
417 memset(ret
, 0, sizeof(xmlSchematronRule
));
419 ret
->context
= context
;
420 ret
->pattern
= pattern
;
421 ret
->report
= report
;
423 if (schema
->rules
== NULL
) {
426 xmlSchematronRulePtr prev
= schema
->rules
;
428 while (prev
->next
!= NULL
)
433 if (pat
->rules
== NULL
) {
436 xmlSchematronRulePtr prev
= pat
->rules
;
438 while (prev
->patnext
!= NULL
)
439 prev
= prev
->patnext
;
446 * xmlSchematronFreeRules:
447 * @rules: a list of rules
449 * Free a list of rules.
452 xmlSchematronFreeRules(xmlSchematronRulePtr rules
) {
453 xmlSchematronRulePtr next
;
455 while (rules
!= NULL
) {
458 xmlSchematronFreeTests(rules
->tests
);
459 if (rules
->context
!= NULL
)
460 xmlFree(rules
->context
);
462 xmlFreePattern(rules
->pattern
);
463 if (rules
->report
!= NULL
)
464 xmlFree(rules
->report
);
471 * xmlSchematronAddPattern:
472 * @ctxt: the schema parsing context
473 * @schema: a schema structure
474 * @node: the node hosting the pattern
475 * @id: the id or name of the pattern
477 * Add a pattern to a schematron
479 * Returns the new pointer or NULL in case of error
481 static xmlSchematronPatternPtr
482 xmlSchematronAddPattern(xmlSchematronParserCtxtPtr ctxt
,
483 xmlSchematronPtr schema
, xmlNodePtr node
, xmlChar
*name
)
485 xmlSchematronPatternPtr ret
;
487 if ((ctxt
== NULL
) || (schema
== NULL
) || (node
== NULL
) || (name
== NULL
))
490 ret
= (xmlSchematronPatternPtr
) xmlMalloc(sizeof(xmlSchematronPattern
));
492 xmlSchematronPErrMemory(ctxt
, "allocating schema pattern", node
);
495 memset(ret
, 0, sizeof(xmlSchematronPattern
));
498 if (schema
->patterns
== NULL
) {
499 schema
->patterns
= ret
;
501 xmlSchematronPatternPtr prev
= schema
->patterns
;
503 while (prev
->next
!= NULL
)
511 * xmlSchematronFreePatterns:
512 * @patterns: a list of patterns
514 * Free a list of patterns.
517 xmlSchematronFreePatterns(xmlSchematronPatternPtr patterns
) {
518 xmlSchematronPatternPtr next
;
520 while (patterns
!= NULL
) {
521 next
= patterns
->next
;
522 if (patterns
->name
!= NULL
)
523 xmlFree(patterns
->name
);
530 * xmlSchematronNewSchematron:
531 * @ctxt: a schema validation context
533 * Allocate a new Schematron structure.
535 * Returns the newly allocated structure or NULL in case or error
537 static xmlSchematronPtr
538 xmlSchematronNewSchematron(xmlSchematronParserCtxtPtr ctxt
)
540 xmlSchematronPtr ret
;
542 ret
= (xmlSchematronPtr
) xmlMalloc(sizeof(xmlSchematron
));
544 xmlSchematronPErrMemory(ctxt
, "allocating schema", NULL
);
547 memset(ret
, 0, sizeof(xmlSchematron
));
548 ret
->dict
= ctxt
->dict
;
549 xmlDictReference(ret
->dict
);
556 * @schema: a schema structure
558 * Deallocate a Schematron structure.
561 xmlSchematronFree(xmlSchematronPtr schema
)
566 if ((schema
->doc
!= NULL
) && (!(schema
->preserve
)))
567 xmlFreeDoc(schema
->doc
);
569 if (schema
->namespaces
!= NULL
)
570 xmlFree((char **) schema
->namespaces
);
572 xmlSchematronFreeRules(schema
->rules
);
573 xmlSchematronFreePatterns(schema
->patterns
);
574 xmlDictFree(schema
->dict
);
579 * xmlSchematronNewParserCtxt:
580 * @URL: the location of the schema
582 * Create an XML Schematrons parse context for that file/resource expected
583 * to contain an XML Schematrons file.
585 * Returns the parser context or NULL in case of error
587 xmlSchematronParserCtxtPtr
588 xmlSchematronNewParserCtxt(const char *URL
)
590 xmlSchematronParserCtxtPtr ret
;
596 (xmlSchematronParserCtxtPtr
)
597 xmlMalloc(sizeof(xmlSchematronParserCtxt
));
599 xmlSchematronPErrMemory(NULL
, "allocating schema parser context",
603 memset(ret
, 0, sizeof(xmlSchematronParserCtxt
));
604 ret
->type
= XML_STRON_CTXT_PARSER
;
605 ret
->dict
= xmlDictCreate();
606 ret
->URL
= xmlDictLookup(ret
->dict
, (const xmlChar
*) URL
, -1);
607 ret
->includes
= NULL
;
608 ret
->xctxt
= xmlXPathNewContext(NULL
);
609 if (ret
->xctxt
== NULL
) {
610 xmlSchematronPErrMemory(NULL
, "allocating schema parser XPath context",
612 xmlSchematronFreeParserCtxt(ret
);
615 ret
->xctxt
->flags
= XML_XPATH_CHECKNS
;
620 * xmlSchematronNewMemParserCtxt:
621 * @buffer: a pointer to a char array containing the schemas
622 * @size: the size of the array
624 * Create an XML Schematrons parse context for that memory buffer expected
625 * to contain an XML Schematrons file.
627 * Returns the parser context or NULL in case of error
629 xmlSchematronParserCtxtPtr
630 xmlSchematronNewMemParserCtxt(const char *buffer
, int size
)
632 xmlSchematronParserCtxtPtr ret
;
634 if ((buffer
== NULL
) || (size
<= 0))
638 (xmlSchematronParserCtxtPtr
)
639 xmlMalloc(sizeof(xmlSchematronParserCtxt
));
641 xmlSchematronPErrMemory(NULL
, "allocating schema parser context",
645 memset(ret
, 0, sizeof(xmlSchematronParserCtxt
));
646 ret
->buffer
= buffer
;
648 ret
->dict
= xmlDictCreate();
649 ret
->xctxt
= xmlXPathNewContext(NULL
);
650 if (ret
->xctxt
== NULL
) {
651 xmlSchematronPErrMemory(NULL
, "allocating schema parser XPath context",
653 xmlSchematronFreeParserCtxt(ret
);
660 * xmlSchematronNewDocParserCtxt:
661 * @doc: a preparsed document tree
663 * Create an XML Schematrons parse context for that document.
664 * NB. The document may be modified during the parsing process.
666 * Returns the parser context or NULL in case of error
668 xmlSchematronParserCtxtPtr
669 xmlSchematronNewDocParserCtxt(xmlDocPtr doc
)
671 xmlSchematronParserCtxtPtr ret
;
677 (xmlSchematronParserCtxtPtr
)
678 xmlMalloc(sizeof(xmlSchematronParserCtxt
));
680 xmlSchematronPErrMemory(NULL
, "allocating schema parser context",
684 memset(ret
, 0, sizeof(xmlSchematronParserCtxt
));
686 ret
->dict
= xmlDictCreate();
687 /* The application has responsibility for the document */
689 ret
->xctxt
= xmlXPathNewContext(doc
);
690 if (ret
->xctxt
== NULL
) {
691 xmlSchematronPErrMemory(NULL
, "allocating schema parser XPath context",
693 xmlSchematronFreeParserCtxt(ret
);
701 * xmlSchematronFreeParserCtxt:
702 * @ctxt: the schema parser context
704 * Free the resources associated to the schema parser context
707 xmlSchematronFreeParserCtxt(xmlSchematronParserCtxtPtr ctxt
)
711 if (ctxt
->doc
!= NULL
&& !ctxt
->preserve
)
712 xmlFreeDoc(ctxt
->doc
);
713 if (ctxt
->xctxt
!= NULL
) {
714 xmlXPathFreeContext(ctxt
->xctxt
);
716 if (ctxt
->namespaces
!= NULL
)
717 xmlFree((char **) ctxt
->namespaces
);
718 xmlDictFree(ctxt
->dict
);
724 * xmlSchematronPushInclude:
725 * @ctxt: the schema parser context
726 * @doc: the included document
727 * @cur: the current include node
729 * Add an included document
732 xmlSchematronPushInclude(xmlSchematronParserCtxtPtr ctxt
,
733 xmlDocPtr doc
, xmlNodePtr cur
)
735 if (ctxt
->includes
== NULL
) {
736 ctxt
->maxIncludes
= 10;
737 ctxt
->includes
= (xmlNodePtr
*)
738 xmlMalloc(ctxt
->maxIncludes
* 2 * sizeof(xmlNodePtr
));
739 if (ctxt
->includes
== NULL
) {
740 xmlSchematronPErrMemory(NULL
, "allocating parser includes",
744 ctxt
->nbIncludes
= 0;
745 } else if (ctxt
->nbIncludes
+ 2 >= ctxt
->maxIncludes
) {
749 xmlRealloc(ctxt
->includes
, ctxt
->maxIncludes
* 4 *
752 xmlSchematronPErrMemory(NULL
, "allocating parser includes",
756 ctxt
->includes
= tmp
;
757 ctxt
->maxIncludes
*= 2;
759 ctxt
->includes
[2 * ctxt
->nbIncludes
] = cur
;
760 ctxt
->includes
[2 * ctxt
->nbIncludes
+ 1] = (xmlNodePtr
) doc
;
765 * xmlSchematronPopInclude:
766 * @ctxt: the schema parser context
768 * Pop an include level. The included document is being freed
770 * Returns the node immediately following the include or NULL if the
771 * include list was empty.
774 xmlSchematronPopInclude(xmlSchematronParserCtxtPtr ctxt
)
779 if (ctxt
->nbIncludes
<= 0)
782 doc
= (xmlDocPtr
) ctxt
->includes
[2 * ctxt
->nbIncludes
+ 1];
783 ret
= ctxt
->includes
[2 * ctxt
->nbIncludes
];
788 return(xmlSchematronPopInclude(ctxt
));
794 * xmlSchematronAddNamespace:
795 * @ctxt: the schema parser context
796 * @prefix: the namespace prefix
797 * @ns: the namespace name
799 * Add a namespace definition in the context
802 xmlSchematronAddNamespace(xmlSchematronParserCtxtPtr ctxt
,
803 const xmlChar
*prefix
, const xmlChar
*ns
)
805 if (ctxt
->namespaces
== NULL
) {
806 ctxt
->maxNamespaces
= 10;
807 ctxt
->namespaces
= (const xmlChar
**)
808 xmlMalloc(ctxt
->maxNamespaces
* 2 * sizeof(const xmlChar
*));
809 if (ctxt
->namespaces
== NULL
) {
810 xmlSchematronPErrMemory(NULL
, "allocating parser namespaces",
814 ctxt
->nbNamespaces
= 0;
815 } else if (ctxt
->nbNamespaces
+ 2 >= ctxt
->maxNamespaces
) {
818 tmp
= (const xmlChar
**)
819 xmlRealloc((xmlChar
**) ctxt
->namespaces
, ctxt
->maxNamespaces
* 4 *
820 sizeof(const xmlChar
*));
822 xmlSchematronPErrMemory(NULL
, "allocating parser namespaces",
826 ctxt
->namespaces
= tmp
;
827 ctxt
->maxNamespaces
*= 2;
829 ctxt
->namespaces
[2 * ctxt
->nbNamespaces
] =
830 xmlDictLookup(ctxt
->dict
, ns
, -1);
831 ctxt
->namespaces
[2 * ctxt
->nbNamespaces
+ 1] =
832 xmlDictLookup(ctxt
->dict
, prefix
, -1);
833 ctxt
->nbNamespaces
++;
834 ctxt
->namespaces
[2 * ctxt
->nbNamespaces
] = NULL
;
835 ctxt
->namespaces
[2 * ctxt
->nbNamespaces
+ 1] = NULL
;
840 * xmlSchematronParseRule:
841 * @ctxt: a schema validation context
842 * @rule: the rule node
844 * parse a rule element
847 xmlSchematronParseRule(xmlSchematronParserCtxtPtr ctxt
,
848 xmlSchematronPatternPtr pattern
,
856 xmlSchematronRulePtr ruleptr
;
857 xmlSchematronTestPtr testptr
;
859 if ((ctxt
== NULL
) || (rule
== NULL
)) return;
861 context
= xmlGetNoNsProp(rule
, BAD_CAST
"context");
862 if (context
== NULL
) {
863 xmlSchematronPErr(ctxt
, rule
,
865 "rule has no context attribute",
868 } else if (context
[0] == 0) {
869 xmlSchematronPErr(ctxt
, rule
,
871 "rule has an empty context attribute",
876 ruleptr
= xmlSchematronAddRule(ctxt
, ctxt
->schema
, pattern
,
877 rule
, context
, NULL
);
878 if (ruleptr
== NULL
) {
884 cur
= rule
->children
;
885 NEXT_SCHEMATRON(cur
);
886 while (cur
!= NULL
) {
887 if (IS_SCHEMATRON(cur
, "assert")) {
889 test
= xmlGetNoNsProp(cur
, BAD_CAST
"test");
891 xmlSchematronPErr(ctxt
, cur
,
893 "assert has no test attribute",
895 } else if (test
[0] == 0) {
896 xmlSchematronPErr(ctxt
, cur
,
898 "assert has an empty test attribute",
902 /* TODO will need dynamic processing instead */
903 report
= xmlNodeGetContent(cur
);
905 testptr
= xmlSchematronAddTest(ctxt
, XML_SCHEMATRON_ASSERT
,
906 ruleptr
, cur
, test
, report
);
910 } else if (IS_SCHEMATRON(cur
, "report")) {
912 test
= xmlGetNoNsProp(cur
, BAD_CAST
"test");
914 xmlSchematronPErr(ctxt
, cur
,
916 "assert has no test attribute",
918 } else if (test
[0] == 0) {
919 xmlSchematronPErr(ctxt
, cur
,
921 "assert has an empty test attribute",
925 /* TODO will need dynamic processing instead */
926 report
= xmlNodeGetContent(cur
);
928 testptr
= xmlSchematronAddTest(ctxt
, XML_SCHEMATRON_REPORT
,
929 ruleptr
, cur
, test
, report
);
934 xmlSchematronPErr(ctxt
, cur
,
936 "Expecting an assert or a report element instead of %s",
940 NEXT_SCHEMATRON(cur
);
943 xmlSchematronPErr(ctxt
, rule
,
945 "rule has no assert nor report element", NULL
, NULL
);
950 * xmlSchematronParsePattern:
951 * @ctxt: a schema validation context
952 * @pat: the pattern node
954 * parse a pattern element
957 xmlSchematronParsePattern(xmlSchematronParserCtxtPtr ctxt
, xmlNodePtr pat
)
960 xmlSchematronPatternPtr pattern
;
964 if ((ctxt
== NULL
) || (pat
== NULL
)) return;
966 id
= xmlGetNoNsProp(pat
, BAD_CAST
"id");
968 id
= xmlGetNoNsProp(pat
, BAD_CAST
"name");
970 pattern
= xmlSchematronAddPattern(ctxt
, ctxt
->schema
, pat
, id
);
971 if (pattern
== NULL
) {
977 NEXT_SCHEMATRON(cur
);
978 while (cur
!= NULL
) {
979 if (IS_SCHEMATRON(cur
, "rule")) {
980 xmlSchematronParseRule(ctxt
, pattern
, cur
);
983 xmlSchematronPErr(ctxt
, cur
,
985 "Expecting a rule element instead of %s", cur
->name
, NULL
);
988 NEXT_SCHEMATRON(cur
);
991 xmlSchematronPErr(ctxt
, pat
,
993 "Pattern has no rule element", NULL
, NULL
);
999 * xmlSchematronLoadInclude:
1000 * @ctxt: a schema validation context
1001 * @cur: the include element
1003 * Load the include document, Push the current pointer
1005 * Returns the updated node pointer
1008 xmlSchematronLoadInclude(xmlSchematronParserCtxtPtr ctxt
, xmlNodePtr cur
)
1010 xmlNodePtr ret
= NULL
;
1011 xmlDocPtr doc
= NULL
;
1012 xmlChar
*href
= NULL
;
1013 xmlChar
*base
= NULL
;
1014 xmlChar
*URI
= NULL
;
1016 if ((ctxt
== NULL
) || (cur
== NULL
))
1019 href
= xmlGetNoNsProp(cur
, BAD_CAST
"href");
1021 xmlSchematronPErr(ctxt
, cur
,
1023 "Include has no href attribute", NULL
, NULL
);
1027 /* do the URI base composition, load and find the root */
1028 base
= xmlNodeGetBase(cur
->doc
, cur
);
1029 URI
= xmlBuildURI(href
, base
);
1030 doc
= xmlReadFile((const char *) URI
, NULL
, SCHEMATRON_PARSE_OPTIONS
);
1032 xmlSchematronPErr(ctxt
, cur
,
1033 XML_SCHEMAP_FAILED_LOAD
,
1034 "could not load include '%s'.\n",
1038 ret
= xmlDocGetRootElement(doc
);
1040 xmlSchematronPErr(ctxt
, cur
,
1041 XML_SCHEMAP_FAILED_LOAD
,
1042 "could not find root from include '%s'.\n",
1047 /* Success, push the include for rollback on exit */
1048 xmlSchematronPushInclude(ctxt
, doc
, cur
);
1065 * xmlSchematronParse:
1066 * @ctxt: a schema validation context
1068 * parse a schema definition resource and build an internal
1069 * XML Shema struture which can be used to validate instances.
1071 * Returns the internal XML Schematron structure built from the resource or
1072 * NULL in case of error
1075 xmlSchematronParse(xmlSchematronParserCtxtPtr ctxt
)
1077 xmlSchematronPtr ret
= NULL
;
1079 xmlNodePtr root
, cur
;
1088 * First step is to parse the input document into an DOM/Infoset
1090 if (ctxt
->URL
!= NULL
) {
1091 doc
= xmlReadFile((const char *) ctxt
->URL
, NULL
,
1092 SCHEMATRON_PARSE_OPTIONS
);
1094 xmlSchematronPErr(ctxt
, NULL
,
1095 XML_SCHEMAP_FAILED_LOAD
,
1096 "xmlSchematronParse: could not load '%s'.\n",
1101 } else if (ctxt
->buffer
!= NULL
) {
1102 doc
= xmlReadMemory(ctxt
->buffer
, ctxt
->size
, NULL
, NULL
,
1103 SCHEMATRON_PARSE_OPTIONS
);
1105 xmlSchematronPErr(ctxt
, NULL
,
1106 XML_SCHEMAP_FAILED_PARSE
,
1107 "xmlSchematronParse: could not parse.\n",
1111 doc
->URL
= xmlStrdup(BAD_CAST
"in_memory_buffer");
1112 ctxt
->URL
= xmlDictLookup(ctxt
->dict
, BAD_CAST
"in_memory_buffer", -1);
1114 } else if (ctxt
->doc
!= NULL
) {
1119 xmlSchematronPErr(ctxt
, NULL
,
1120 XML_SCHEMAP_NOTHING_TO_PARSE
,
1121 "xmlSchematronParse: could not parse.\n",
1127 * Then extract the root and Schematron parse it
1129 root
= xmlDocGetRootElement(doc
);
1131 xmlSchematronPErr(ctxt
, (xmlNodePtr
) doc
,
1133 "The schema has no document element.\n", NULL
, NULL
);
1140 if (!IS_SCHEMATRON(root
, "schema")) {
1141 xmlSchematronPErr(ctxt
, root
,
1143 "The XML document '%s' is not a XML schematron document",
1147 ret
= xmlSchematronNewSchematron(ctxt
);
1153 * scan the schema elements
1155 cur
= root
->children
;
1156 NEXT_SCHEMATRON(cur
);
1157 if (IS_SCHEMATRON(cur
, "title")) {
1158 xmlChar
*title
= xmlNodeGetContent(cur
);
1159 if (title
!= NULL
) {
1160 ret
->title
= xmlDictLookup(ret
->dict
, title
, -1);
1164 NEXT_SCHEMATRON(cur
);
1166 while (IS_SCHEMATRON(cur
, "ns")) {
1167 xmlChar
*prefix
= xmlGetNoNsProp(cur
, BAD_CAST
"prefix");
1168 xmlChar
*uri
= xmlGetNoNsProp(cur
, BAD_CAST
"uri");
1169 if ((uri
== NULL
) || (uri
[0] == 0)) {
1170 xmlSchematronPErr(ctxt
, cur
,
1172 "ns element has no uri", NULL
, NULL
);
1174 if ((prefix
== NULL
) || (prefix
[0] == 0)) {
1175 xmlSchematronPErr(ctxt
, cur
,
1177 "ns element has no prefix", NULL
, NULL
);
1179 if ((prefix
) && (uri
)) {
1180 xmlXPathRegisterNs(ctxt
->xctxt
, prefix
, uri
);
1181 xmlSchematronAddNamespace(ctxt
, prefix
, uri
);
1189 NEXT_SCHEMATRON(cur
);
1191 while (cur
!= NULL
) {
1192 if (IS_SCHEMATRON(cur
, "pattern")) {
1193 xmlSchematronParsePattern(ctxt
, cur
);
1196 xmlSchematronPErr(ctxt
, cur
,
1198 "Expecting a pattern element instead of %s", cur
->name
, NULL
);
1201 NEXT_SCHEMATRON(cur
);
1203 if (ret
->nbPattern
== 0) {
1204 xmlSchematronPErr(ctxt
, root
,
1206 "The schematron document '%s' has no pattern",
1210 /* the original document must be kept for reporting */
1222 if (ctxt
->nberrors
!= 0) {
1223 xmlSchematronFree(ret
);
1226 ret
->namespaces
= ctxt
->namespaces
;
1227 ret
->nbNamespaces
= ctxt
->nbNamespaces
;
1228 ctxt
->namespaces
= NULL
;
1234 /************************************************************************
1236 * Schematrontron Reports handler *
1238 ************************************************************************/
1241 xmlSchematronGetNode(xmlSchematronValidCtxtPtr ctxt
,
1242 xmlNodePtr cur
, const xmlChar
*xpath
) {
1243 xmlNodePtr node
= NULL
;
1244 xmlXPathObjectPtr ret
;
1246 if ((ctxt
== NULL
) || (cur
== NULL
) || (xpath
== NULL
))
1249 ctxt
->xctxt
->doc
= cur
->doc
;
1250 ctxt
->xctxt
->node
= cur
;
1251 ret
= xmlXPathEval(xpath
, ctxt
->xctxt
);
1255 if ((ret
->type
== XPATH_NODESET
) &&
1256 (ret
->nodesetval
!= NULL
) && (ret
->nodesetval
->nodeNr
> 0))
1257 node
= ret
->nodesetval
->nodeTab
[0];
1259 xmlXPathFreeObject(ret
);
1264 * xmlSchematronReportOutput:
1265 * @ctxt: the validation context
1266 * @cur: the current node tested
1267 * @msg: the message output
1269 * Output part of the report to whatever channel the user selected
1272 xmlSchematronReportOutput(xmlSchematronValidCtxtPtr ctxt ATTRIBUTE_UNUSED
,
1273 xmlNodePtr cur ATTRIBUTE_UNUSED
,
1276 fprintf(stderr
, "%s", msg
);
1280 * xmlSchematronFormatReport:
1281 * @ctxt: the validation context
1282 * @test: the test node
1283 * @cur: the current node tested
1285 * Build the string being reported to the user.
1287 * Returns a report string or NULL in case of error. The string needs
1288 * to be deallocated by teh caller
1291 xmlSchematronFormatReport(xmlSchematronValidCtxtPtr ctxt
,
1292 xmlNodePtr test
, xmlNodePtr cur
) {
1293 xmlChar
*ret
= NULL
;
1294 xmlNodePtr child
, node
;
1296 if ((test
== NULL
) || (cur
== NULL
))
1299 child
= test
->children
;
1300 while (child
!= NULL
) {
1301 if ((child
->type
== XML_TEXT_NODE
) ||
1302 (child
->type
== XML_CDATA_SECTION_NODE
))
1303 ret
= xmlStrcat(ret
, child
->content
);
1304 else if (IS_SCHEMATRON(child
, "name")) {
1307 path
= xmlGetNoNsProp(child
, BAD_CAST
"path");
1311 node
= xmlSchematronGetNode(ctxt
, cur
, path
);
1317 if ((node
->ns
== NULL
) || (node
->ns
->prefix
== NULL
))
1318 ret
= xmlStrcat(ret
, node
->name
);
1320 ret
= xmlStrcat(ret
, node
->ns
->prefix
);
1321 ret
= xmlStrcat(ret
, BAD_CAST
":");
1322 ret
= xmlStrcat(ret
, node
->name
);
1325 child
= child
->next
;
1330 * remove superfluous \n
1333 int len
= xmlStrlen(ret
);
1338 if ((c
== ' ') || (c
== '\n') || (c
== '\r') || (c
== '\t')) {
1339 while ((c
== ' ') || (c
== '\n') ||
1340 (c
== '\r') || (c
== '\t')) {
1352 child
= child
->next
;
1358 * xmlSchematronReportSuccess:
1359 * @ctxt: the validation context
1360 * @test: the compiled test
1361 * @cur: the current node tested
1362 * @success: boolean value for the result
1364 * called from the validation engine when an assert or report test have
1368 xmlSchematronReportSuccess(xmlSchematronValidCtxtPtr ctxt
,
1369 xmlSchematronTestPtr test
, xmlNodePtr cur
, xmlSchematronPatternPtr pattern
, int success
) {
1370 if ((ctxt
== NULL
) || (cur
== NULL
) || (test
== NULL
))
1372 /* if quiet and not SVRL report only failures */
1373 if ((ctxt
->flags
& XML_SCHEMATRON_OUT_QUIET
) &&
1374 ((ctxt
->flags
& XML_SCHEMATRON_OUT_XML
) == 0) &&
1375 (test
->type
== XML_SCHEMATRON_REPORT
))
1377 if (ctxt
->flags
& XML_SCHEMATRON_OUT_XML
) {
1383 const xmlChar
*report
= NULL
;
1385 if (((test
->type
== XML_SCHEMATRON_REPORT
) & (!success
)) ||
1386 ((test
->type
== XML_SCHEMATRON_ASSERT
) & (success
)))
1388 line
= xmlGetLineNo(cur
);
1389 path
= xmlGetNodePath(cur
);
1391 path
= (xmlChar
*) cur
->name
;
1393 if ((test
->report
!= NULL
) && (test
->report
[0] != 0))
1394 report
= test
->report
;
1396 if (test
->node
!= NULL
)
1397 report
= xmlSchematronFormatReport(ctxt
, test
->node
, cur
);
1398 if (report
== NULL
) {
1399 if (test
->type
== XML_SCHEMATRON_ASSERT
) {
1400 report
= xmlStrdup((const xmlChar
*) "node failed assert");
1402 report
= xmlStrdup((const xmlChar
*) "node failed report");
1405 snprintf(msg
, 999, "%s line %ld: %s\n", (const char *) path
,
1406 line
, (const char *) report
);
1408 if (ctxt
->flags
& XML_SCHEMATRON_OUT_ERROR
) {
1409 xmlStructuredErrorFunc schannel
= NULL
;
1410 xmlGenericErrorFunc channel
= NULL
;
1414 if (ctxt
->serror
!= NULL
)
1415 schannel
= ctxt
->serror
;
1417 channel
= ctxt
->error
;
1418 data
= ctxt
->userData
;
1421 __xmlRaiseError(schannel
, channel
, data
,
1422 NULL
, cur
, XML_FROM_SCHEMATRONV
,
1423 (test
->type
== XML_SCHEMATRON_ASSERT
)?XML_SCHEMATRONV_ASSERT
:XML_SCHEMATRONV_REPORT
,
1424 XML_ERR_ERROR
, NULL
, line
,
1425 (pattern
== NULL
)?NULL
:((const char *) pattern
->name
),
1426 (const char *) path
,
1427 (const char *) report
, 0, 0,
1430 xmlSchematronReportOutput(ctxt
, cur
, &msg
[0]);
1433 xmlFree((char *) report
);
1435 if ((path
!= NULL
) && (path
!= (xmlChar
*) cur
->name
))
1441 * xmlSchematronReportPattern:
1442 * @ctxt: the validation context
1443 * @pattern: the current pattern
1445 * called from the validation engine when starting to check a pattern
1448 xmlSchematronReportPattern(xmlSchematronValidCtxtPtr ctxt
,
1449 xmlSchematronPatternPtr pattern
) {
1450 if ((ctxt
== NULL
) || (pattern
== NULL
))
1452 if ((ctxt
->flags
& XML_SCHEMATRON_OUT_QUIET
) || (ctxt
->flags
& XML_SCHEMATRON_OUT_ERROR
)) /* Error gives pattern name as part of error */
1454 if (ctxt
->flags
& XML_SCHEMATRON_OUT_XML
) {
1459 if (pattern
->name
== NULL
)
1461 snprintf(msg
, 999, "Pattern: %s\n", (const char *) pattern
->name
);
1462 xmlSchematronReportOutput(ctxt
, NULL
, &msg
[0]);
1467 /************************************************************************
1469 * Validation against a Schematrontron *
1471 ************************************************************************/
1474 * xmlSchematronSetValidStructuredErrors:
1475 * @ctxt: a Schematron validation context
1476 * @serror: the structured error function
1477 * @ctx: the functions context
1479 * Set the structured error callback
1482 xmlSchematronSetValidStructuredErrors(xmlSchematronValidCtxtPtr ctxt
,
1483 xmlStructuredErrorFunc serror
, void *ctx
)
1487 ctxt
->serror
= serror
;
1489 ctxt
->warning
= NULL
;
1490 ctxt
->userData
= ctx
;
1494 * xmlSchematronNewValidCtxt:
1495 * @schema: a precompiled XML Schematrons
1496 * @options: a set of xmlSchematronValidOptions
1498 * Create an XML Schematrons validation context based on the given schema.
1500 * Returns the validation context or NULL in case of error
1502 xmlSchematronValidCtxtPtr
1503 xmlSchematronNewValidCtxt(xmlSchematronPtr schema
, int options
)
1506 xmlSchematronValidCtxtPtr ret
;
1508 ret
= (xmlSchematronValidCtxtPtr
) xmlMalloc(sizeof(xmlSchematronValidCtxt
));
1510 xmlSchematronVErrMemory(NULL
, "allocating validation context",
1514 memset(ret
, 0, sizeof(xmlSchematronValidCtxt
));
1515 ret
->type
= XML_STRON_CTXT_VALIDATOR
;
1516 ret
->schema
= schema
;
1517 ret
->xctxt
= xmlXPathNewContext(NULL
);
1518 ret
->flags
= options
;
1519 if (ret
->xctxt
== NULL
) {
1520 xmlSchematronPErrMemory(NULL
, "allocating schema parser XPath context",
1522 xmlSchematronFreeValidCtxt(ret
);
1525 for (i
= 0;i
< schema
->nbNamespaces
;i
++) {
1526 if ((schema
->namespaces
[2 * i
] == NULL
) ||
1527 (schema
->namespaces
[2 * i
+ 1] == NULL
))
1529 xmlXPathRegisterNs(ret
->xctxt
, schema
->namespaces
[2 * i
+ 1],
1530 schema
->namespaces
[2 * i
]);
1536 * xmlSchematronFreeValidCtxt:
1537 * @ctxt: the schema validation context
1539 * Free the resources associated to the schema validation context
1542 xmlSchematronFreeValidCtxt(xmlSchematronValidCtxtPtr ctxt
)
1546 if (ctxt
->xctxt
!= NULL
)
1547 xmlXPathFreeContext(ctxt
->xctxt
);
1548 if (ctxt
->dict
!= NULL
)
1549 xmlDictFree(ctxt
->dict
);
1554 xmlSchematronNextNode(xmlNodePtr cur
) {
1555 if (cur
->children
!= NULL
) {
1557 * Do not descend on entities declarations
1559 if (cur
->children
->type
!= XML_ENTITY_DECL
) {
1560 cur
= cur
->children
;
1564 if (cur
->type
!= XML_DTD_NODE
)
1569 while (cur
->next
!= NULL
) {
1571 if ((cur
->type
!= XML_ENTITY_DECL
) &&
1572 (cur
->type
!= XML_DTD_NODE
))
1578 if (cur
== NULL
) break;
1579 if (cur
->type
== XML_DOCUMENT_NODE
) return(NULL
);
1580 if (cur
->next
!= NULL
) {
1584 } while (cur
!= NULL
);
1589 * xmlSchematronRunTest:
1590 * @ctxt: the schema validation context
1591 * @test: the current test
1592 * @instance: the document instace tree
1593 * @cur: the current node in the instance
1595 * Validate a rule against a tree instance at a given position
1597 * Returns 1 in case of success, 0 if error and -1 in case of internal error
1600 xmlSchematronRunTest(xmlSchematronValidCtxtPtr ctxt
,
1601 xmlSchematronTestPtr test
, xmlDocPtr instance
, xmlNodePtr cur
, xmlSchematronPatternPtr pattern
)
1603 xmlXPathObjectPtr ret
;
1607 ctxt
->xctxt
->doc
= instance
;
1608 ctxt
->xctxt
->node
= cur
;
1609 ret
= xmlXPathCompiledEval(test
->comp
, ctxt
->xctxt
);
1613 switch (ret
->type
) {
1614 case XPATH_XSLT_TREE
:
1616 if ((ret
->nodesetval
== NULL
) ||
1617 (ret
->nodesetval
->nodeNr
== 0))
1621 failed
= !ret
->boolval
;
1624 if ((xmlXPathIsNaN(ret
->floatval
)) ||
1625 (ret
->floatval
== 0.0))
1629 if ((ret
->stringval
== NULL
) ||
1630 (ret
->stringval
[0] == 0))
1633 case XPATH_UNDEFINED
:
1636 case XPATH_LOCATIONSET
:
1641 xmlXPathFreeObject(ret
);
1643 if ((failed
) && (test
->type
== XML_SCHEMATRON_ASSERT
))
1645 else if ((!failed
) && (test
->type
== XML_SCHEMATRON_REPORT
))
1648 xmlSchematronReportSuccess(ctxt
, test
, cur
, pattern
, !failed
);
1654 * xmlSchematronValidateDoc:
1655 * @ctxt: the schema validation context
1656 * @instance: the document instace tree
1658 * Validate a tree instance against the schematron
1660 * Returns 0 in case of success, -1 in case of internal error
1661 * and an error count otherwise.
1664 xmlSchematronValidateDoc(xmlSchematronValidCtxtPtr ctxt
, xmlDocPtr instance
)
1666 xmlNodePtr cur
, root
;
1667 xmlSchematronPatternPtr pattern
;
1668 xmlSchematronRulePtr rule
;
1669 xmlSchematronTestPtr test
;
1671 if ((ctxt
== NULL
) || (ctxt
->schema
== NULL
) ||
1672 (ctxt
->schema
->rules
== NULL
) || (instance
== NULL
))
1675 root
= xmlDocGetRootElement(instance
);
1681 if ((ctxt
->flags
& XML_SCHEMATRON_OUT_QUIET
) ||
1682 (ctxt
->flags
== 0)) {
1684 * we are just trying to assert the validity of the document,
1685 * speed primes over the output, run in a single pass
1688 while (cur
!= NULL
) {
1689 rule
= ctxt
->schema
->rules
;
1690 while (rule
!= NULL
) {
1691 if (xmlPatternMatch(rule
->pattern
, cur
) == 1) {
1693 while (test
!= NULL
) {
1694 xmlSchematronRunTest(ctxt
, test
, instance
, cur
, (xmlSchematronPatternPtr
)rule
->pattern
);
1701 cur
= xmlSchematronNextNode(cur
);
1705 * Process all contexts one at a time
1707 pattern
= ctxt
->schema
->patterns
;
1709 while (pattern
!= NULL
) {
1710 xmlSchematronReportPattern(ctxt
, pattern
);
1713 * TODO convert the pattern rule to a direct XPath and
1714 * compute directly instead of using the pattern matching
1715 * over the full document...
1716 * Check the exact semantic
1719 while (cur
!= NULL
) {
1720 rule
= pattern
->rules
;
1721 while (rule
!= NULL
) {
1722 if (xmlPatternMatch(rule
->pattern
, cur
) == 1) {
1724 while (test
!= NULL
) {
1725 xmlSchematronRunTest(ctxt
, test
, instance
, cur
, pattern
);
1729 rule
= rule
->patnext
;
1732 cur
= xmlSchematronNextNode(cur
);
1734 pattern
= pattern
->next
;
1737 return(ctxt
->nberrors
);
1746 xmlSchematronParserCtxtPtr pctxt
;
1747 xmlSchematronValidCtxtPtr vctxt
;
1748 xmlSchematronPtr schema
= NULL
;
1750 pctxt
= xmlSchematronNewParserCtxt("tst.sct");
1751 if (pctxt
== NULL
) {
1752 fprintf(stderr
, "failed to build schematron parser\n");
1754 schema
= xmlSchematronParse(pctxt
);
1755 if (schema
== NULL
) {
1756 fprintf(stderr
, "failed to compile schematron\n");
1758 xmlSchematronFreeParserCtxt(pctxt
);
1760 instance
= xmlReadFile("tst.sct", NULL
,
1761 XML_PARSE_NOENT
| XML_PARSE_NOCDATA
);
1762 if (instance
== NULL
) {
1763 fprintf(stderr
, "failed to parse instance\n");
1765 if ((schema
!= NULL
) && (instance
!= NULL
)) {
1766 vctxt
= xmlSchematronNewValidCtxt(schema
);
1767 if (vctxt
== NULL
) {
1768 fprintf(stderr
, "failed to build schematron validator\n");
1770 ret
= xmlSchematronValidateDoc(vctxt
, instance
);
1771 xmlSchematronFreeValidCtxt(vctxt
);
1774 xmlSchematronFree(schema
);
1775 xmlFreeDoc(instance
);
1783 #define bottom_schematron
1784 #include "elfgcchack.h"
1785 #endif /* LIBXML_SCHEMATRON_ENABLED */