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 #ifdef LIBXML_OUTPUT_ENABLED
169 xmlOutputWriteCallback iowrite
; /* if using XML_SCHEMATRON_OUT_IO */
170 xmlOutputCloseCallback ioclose
;
174 /* error reporting data */
175 void *userData
; /* user specific data block */
176 xmlSchematronValidityErrorFunc error
;/* the callback in case of errors */
177 xmlSchematronValidityWarningFunc warning
;/* callback in case of warning */
178 xmlStructuredErrorFunc serror
; /* the structured function */
181 struct _xmlSchematronParserCtxt
{
185 int preserve
; /* Whether the doc should be freed */
189 xmlDictPtr dict
; /* dictionnary for interned string names */
193 xmlXPathContextPtr xctxt
; /* the XPath context used for compilation */
194 xmlSchematronPtr schema
;
196 int nbNamespaces
; /* number of namespaces in the array */
197 int maxNamespaces
; /* size of the array */
198 const xmlChar
**namespaces
; /* the array of namespaces */
200 int nbIncludes
; /* number of includes in the array */
201 int maxIncludes
; /* size of the array */
202 xmlNodePtr
*includes
; /* the array of includes */
204 /* error reporting data */
205 void *userData
; /* user specific data block */
206 xmlSchematronValidityErrorFunc error
;/* the callback in case of errors */
207 xmlSchematronValidityWarningFunc warning
;/* callback in case of warning */
208 xmlStructuredErrorFunc serror
; /* the structured function */
211 #define XML_STRON_CTXT_PARSER 1
212 #define XML_STRON_CTXT_VALIDATOR 2
214 /************************************************************************
218 ************************************************************************/
221 * xmlSchematronPErrMemory:
222 * @node: a context node
223 * @extra: extra informations
225 * Handle an out of memory condition
228 xmlSchematronPErrMemory(xmlSchematronParserCtxtPtr ctxt
,
229 const char *extra
, xmlNodePtr node
)
233 __xmlSimpleError(XML_FROM_SCHEMASP
, XML_ERR_NO_MEMORY
, node
, NULL
,
239 * @ctxt: the parsing context
240 * @node: the context node
241 * @error: the error code
242 * @msg: the error message
246 * Handle a parser error
249 xmlSchematronPErr(xmlSchematronParserCtxtPtr ctxt
, xmlNodePtr node
, int error
,
250 const char *msg
, const xmlChar
* str1
, const xmlChar
* str2
)
252 xmlGenericErrorFunc channel
= NULL
;
253 xmlStructuredErrorFunc schannel
= NULL
;
258 channel
= ctxt
->error
;
259 data
= ctxt
->userData
;
260 schannel
= ctxt
->serror
;
262 __xmlRaiseError(schannel
, channel
, data
, ctxt
, node
, XML_FROM_SCHEMASP
,
263 error
, XML_ERR_ERROR
, NULL
, 0,
264 (const char *) str1
, (const char *) str2
, NULL
, 0, 0,
269 * xmlSchematronVTypeErrMemory:
270 * @node: a context node
271 * @extra: extra informations
273 * Handle an out of memory condition
276 xmlSchematronVErrMemory(xmlSchematronValidCtxtPtr ctxt
,
277 const char *extra
, xmlNodePtr node
)
281 ctxt
->err
= XML_SCHEMAV_INTERNAL
;
283 __xmlSimpleError(XML_FROM_SCHEMASV
, XML_ERR_NO_MEMORY
, node
, NULL
,
287 /************************************************************************
289 * Parsing and compilation of the Schematrontrons *
291 ************************************************************************/
294 * xmlSchematronAddTest:
295 * @ctxt: the schema parsing context
296 * @type: the type of test
297 * @rule: the parent rule
298 * @node: the node hosting the test
299 * @test: the associated test
300 * @report: the associated report string
302 * Add a test to a schematron
304 * Returns the new pointer or NULL in case of error
306 static xmlSchematronTestPtr
307 xmlSchematronAddTest(xmlSchematronParserCtxtPtr ctxt
,
308 xmlSchematronTestType type
,
309 xmlSchematronRulePtr rule
,
310 xmlNodePtr node
, xmlChar
*test
, xmlChar
*report
)
312 xmlSchematronTestPtr ret
;
313 xmlXPathCompExprPtr comp
;
315 if ((ctxt
== NULL
) || (rule
== NULL
) || (node
== NULL
) ||
320 * try first to compile the test expression
322 comp
= xmlXPathCtxtCompile(ctxt
->xctxt
, test
);
324 xmlSchematronPErr(ctxt
, node
,
326 "Failed to compile test expression %s",
331 ret
= (xmlSchematronTestPtr
) xmlMalloc(sizeof(xmlSchematronTest
));
333 xmlSchematronPErrMemory(ctxt
, "allocating schema test", node
);
336 memset(ret
, 0, sizeof(xmlSchematronTest
));
341 ret
->report
= report
;
343 if (rule
->tests
== NULL
) {
346 xmlSchematronTestPtr prev
= rule
->tests
;
348 while (prev
->next
!= NULL
)
356 * xmlSchematronFreeTests:
357 * @tests: a list of tests
359 * Free a list of tests.
362 xmlSchematronFreeTests(xmlSchematronTestPtr tests
) {
363 xmlSchematronTestPtr next
;
365 while (tests
!= NULL
) {
367 if (tests
->test
!= NULL
)
368 xmlFree(tests
->test
);
369 if (tests
->comp
!= NULL
)
370 xmlXPathFreeCompExpr(tests
->comp
);
371 if (tests
->report
!= NULL
)
372 xmlFree(tests
->report
);
379 * xmlSchematronAddRule:
380 * @ctxt: the schema parsing context
381 * @schema: a schema structure
382 * @node: the node hosting the rule
383 * @context: the associated context string
384 * @report: the associated report string
386 * Add a rule to a schematron
388 * Returns the new pointer or NULL in case of error
390 static xmlSchematronRulePtr
391 xmlSchematronAddRule(xmlSchematronParserCtxtPtr ctxt
, xmlSchematronPtr schema
,
392 xmlSchematronPatternPtr pat
, xmlNodePtr node
,
393 xmlChar
*context
, xmlChar
*report
)
395 xmlSchematronRulePtr ret
;
396 xmlPatternPtr pattern
;
398 if ((ctxt
== NULL
) || (schema
== NULL
) || (node
== NULL
) ||
403 * Try first to compile the pattern
405 pattern
= xmlPatterncompile(context
, ctxt
->dict
, XML_PATTERN_XPATH
,
407 if (pattern
== NULL
) {
408 xmlSchematronPErr(ctxt
, node
,
410 "Failed to compile context expression %s",
414 ret
= (xmlSchematronRulePtr
) xmlMalloc(sizeof(xmlSchematronRule
));
416 xmlSchematronPErrMemory(ctxt
, "allocating schema rule", node
);
419 memset(ret
, 0, sizeof(xmlSchematronRule
));
421 ret
->context
= context
;
422 ret
->pattern
= pattern
;
423 ret
->report
= report
;
425 if (schema
->rules
== NULL
) {
428 xmlSchematronRulePtr prev
= schema
->rules
;
430 while (prev
->next
!= NULL
)
435 if (pat
->rules
== NULL
) {
438 xmlSchematronRulePtr prev
= pat
->rules
;
440 while (prev
->patnext
!= NULL
)
441 prev
= prev
->patnext
;
448 * xmlSchematronFreeRules:
449 * @rules: a list of rules
451 * Free a list of rules.
454 xmlSchematronFreeRules(xmlSchematronRulePtr rules
) {
455 xmlSchematronRulePtr next
;
457 while (rules
!= NULL
) {
460 xmlSchematronFreeTests(rules
->tests
);
461 if (rules
->context
!= NULL
)
462 xmlFree(rules
->context
);
464 xmlFreePattern(rules
->pattern
);
465 if (rules
->report
!= NULL
)
466 xmlFree(rules
->report
);
473 * xmlSchematronAddPattern:
474 * @ctxt: the schema parsing context
475 * @schema: a schema structure
476 * @node: the node hosting the pattern
477 * @id: the id or name of the pattern
479 * Add a pattern to a schematron
481 * Returns the new pointer or NULL in case of error
483 static xmlSchematronPatternPtr
484 xmlSchematronAddPattern(xmlSchematronParserCtxtPtr ctxt
,
485 xmlSchematronPtr schema
, xmlNodePtr node
, xmlChar
*name
)
487 xmlSchematronPatternPtr ret
;
489 if ((ctxt
== NULL
) || (schema
== NULL
) || (node
== NULL
) || (name
== NULL
))
492 ret
= (xmlSchematronPatternPtr
) xmlMalloc(sizeof(xmlSchematronPattern
));
494 xmlSchematronPErrMemory(ctxt
, "allocating schema pattern", node
);
497 memset(ret
, 0, sizeof(xmlSchematronPattern
));
500 if (schema
->patterns
== NULL
) {
501 schema
->patterns
= ret
;
503 xmlSchematronPatternPtr prev
= schema
->patterns
;
505 while (prev
->next
!= NULL
)
513 * xmlSchematronFreePatterns:
514 * @patterns: a list of patterns
516 * Free a list of patterns.
519 xmlSchematronFreePatterns(xmlSchematronPatternPtr patterns
) {
520 xmlSchematronPatternPtr next
;
522 while (patterns
!= NULL
) {
523 next
= patterns
->next
;
524 if (patterns
->name
!= NULL
)
525 xmlFree(patterns
->name
);
532 * xmlSchematronNewSchematron:
533 * @ctxt: a schema validation context
535 * Allocate a new Schematron structure.
537 * Returns the newly allocated structure or NULL in case or error
539 static xmlSchematronPtr
540 xmlSchematronNewSchematron(xmlSchematronParserCtxtPtr ctxt
)
542 xmlSchematronPtr ret
;
544 ret
= (xmlSchematronPtr
) xmlMalloc(sizeof(xmlSchematron
));
546 xmlSchematronPErrMemory(ctxt
, "allocating schema", NULL
);
549 memset(ret
, 0, sizeof(xmlSchematron
));
550 ret
->dict
= ctxt
->dict
;
551 xmlDictReference(ret
->dict
);
558 * @schema: a schema structure
560 * Deallocate a Schematron structure.
563 xmlSchematronFree(xmlSchematronPtr schema
)
568 if ((schema
->doc
!= NULL
) && (!(schema
->preserve
)))
569 xmlFreeDoc(schema
->doc
);
571 if (schema
->namespaces
!= NULL
)
572 xmlFree((char **) schema
->namespaces
);
574 xmlSchematronFreeRules(schema
->rules
);
575 xmlSchematronFreePatterns(schema
->patterns
);
576 xmlDictFree(schema
->dict
);
581 * xmlSchematronNewParserCtxt:
582 * @URL: the location of the schema
584 * Create an XML Schematrons parse context for that file/resource expected
585 * to contain an XML Schematrons file.
587 * Returns the parser context or NULL in case of error
589 xmlSchematronParserCtxtPtr
590 xmlSchematronNewParserCtxt(const char *URL
)
592 xmlSchematronParserCtxtPtr ret
;
598 (xmlSchematronParserCtxtPtr
)
599 xmlMalloc(sizeof(xmlSchematronParserCtxt
));
601 xmlSchematronPErrMemory(NULL
, "allocating schema parser context",
605 memset(ret
, 0, sizeof(xmlSchematronParserCtxt
));
606 ret
->type
= XML_STRON_CTXT_PARSER
;
607 ret
->dict
= xmlDictCreate();
608 ret
->URL
= xmlDictLookup(ret
->dict
, (const xmlChar
*) URL
, -1);
609 ret
->includes
= NULL
;
610 ret
->xctxt
= xmlXPathNewContext(NULL
);
611 if (ret
->xctxt
== NULL
) {
612 xmlSchematronPErrMemory(NULL
, "allocating schema parser XPath context",
614 xmlSchematronFreeParserCtxt(ret
);
617 ret
->xctxt
->flags
= XML_XPATH_CHECKNS
;
622 * xmlSchematronNewMemParserCtxt:
623 * @buffer: a pointer to a char array containing the schemas
624 * @size: the size of the array
626 * Create an XML Schematrons parse context for that memory buffer expected
627 * to contain an XML Schematrons file.
629 * Returns the parser context or NULL in case of error
631 xmlSchematronParserCtxtPtr
632 xmlSchematronNewMemParserCtxt(const char *buffer
, int size
)
634 xmlSchematronParserCtxtPtr ret
;
636 if ((buffer
== NULL
) || (size
<= 0))
640 (xmlSchematronParserCtxtPtr
)
641 xmlMalloc(sizeof(xmlSchematronParserCtxt
));
643 xmlSchematronPErrMemory(NULL
, "allocating schema parser context",
647 memset(ret
, 0, sizeof(xmlSchematronParserCtxt
));
648 ret
->buffer
= buffer
;
650 ret
->dict
= xmlDictCreate();
651 ret
->xctxt
= xmlXPathNewContext(NULL
);
652 if (ret
->xctxt
== NULL
) {
653 xmlSchematronPErrMemory(NULL
, "allocating schema parser XPath context",
655 xmlSchematronFreeParserCtxt(ret
);
662 * xmlSchematronNewDocParserCtxt:
663 * @doc: a preparsed document tree
665 * Create an XML Schematrons parse context for that document.
666 * NB. The document may be modified during the parsing process.
668 * Returns the parser context or NULL in case of error
670 xmlSchematronParserCtxtPtr
671 xmlSchematronNewDocParserCtxt(xmlDocPtr doc
)
673 xmlSchematronParserCtxtPtr ret
;
679 (xmlSchematronParserCtxtPtr
)
680 xmlMalloc(sizeof(xmlSchematronParserCtxt
));
682 xmlSchematronPErrMemory(NULL
, "allocating schema parser context",
686 memset(ret
, 0, sizeof(xmlSchematronParserCtxt
));
688 ret
->dict
= xmlDictCreate();
689 /* The application has responsibility for the document */
691 ret
->xctxt
= xmlXPathNewContext(doc
);
692 if (ret
->xctxt
== NULL
) {
693 xmlSchematronPErrMemory(NULL
, "allocating schema parser XPath context",
695 xmlSchematronFreeParserCtxt(ret
);
703 * xmlSchematronFreeParserCtxt:
704 * @ctxt: the schema parser context
706 * Free the resources associated to the schema parser context
709 xmlSchematronFreeParserCtxt(xmlSchematronParserCtxtPtr ctxt
)
713 if (ctxt
->doc
!= NULL
&& !ctxt
->preserve
)
714 xmlFreeDoc(ctxt
->doc
);
715 if (ctxt
->xctxt
!= NULL
) {
716 xmlXPathFreeContext(ctxt
->xctxt
);
718 if (ctxt
->namespaces
!= NULL
)
719 xmlFree((char **) ctxt
->namespaces
);
720 xmlDictFree(ctxt
->dict
);
726 * xmlSchematronPushInclude:
727 * @ctxt: the schema parser context
728 * @doc: the included document
729 * @cur: the current include node
731 * Add an included document
734 xmlSchematronPushInclude(xmlSchematronParserCtxtPtr ctxt
,
735 xmlDocPtr doc
, xmlNodePtr cur
)
737 if (ctxt
->includes
== NULL
) {
738 ctxt
->maxIncludes
= 10;
739 ctxt
->includes
= (xmlNodePtr
*)
740 xmlMalloc(ctxt
->maxIncludes
* 2 * sizeof(xmlNodePtr
));
741 if (ctxt
->includes
== NULL
) {
742 xmlSchematronPErrMemory(NULL
, "allocating parser includes",
746 ctxt
->nbIncludes
= 0;
747 } else if (ctxt
->nbIncludes
+ 2 >= ctxt
->maxIncludes
) {
751 xmlRealloc(ctxt
->includes
, ctxt
->maxIncludes
* 4 *
754 xmlSchematronPErrMemory(NULL
, "allocating parser includes",
758 ctxt
->includes
= tmp
;
759 ctxt
->maxIncludes
*= 2;
761 ctxt
->includes
[2 * ctxt
->nbIncludes
] = cur
;
762 ctxt
->includes
[2 * ctxt
->nbIncludes
+ 1] = (xmlNodePtr
) doc
;
767 * xmlSchematronPopInclude:
768 * @ctxt: the schema parser context
770 * Pop an include level. The included document is being freed
772 * Returns the node immediately following the include or NULL if the
773 * include list was empty.
776 xmlSchematronPopInclude(xmlSchematronParserCtxtPtr ctxt
)
781 if (ctxt
->nbIncludes
<= 0)
784 doc
= (xmlDocPtr
) ctxt
->includes
[2 * ctxt
->nbIncludes
+ 1];
785 ret
= ctxt
->includes
[2 * ctxt
->nbIncludes
];
790 return(xmlSchematronPopInclude(ctxt
));
796 * xmlSchematronAddNamespace:
797 * @ctxt: the schema parser context
798 * @prefix: the namespace prefix
799 * @ns: the namespace name
801 * Add a namespace definition in the context
804 xmlSchematronAddNamespace(xmlSchematronParserCtxtPtr ctxt
,
805 const xmlChar
*prefix
, const xmlChar
*ns
)
807 if (ctxt
->namespaces
== NULL
) {
808 ctxt
->maxNamespaces
= 10;
809 ctxt
->namespaces
= (const xmlChar
**)
810 xmlMalloc(ctxt
->maxNamespaces
* 2 * sizeof(const xmlChar
*));
811 if (ctxt
->namespaces
== NULL
) {
812 xmlSchematronPErrMemory(NULL
, "allocating parser namespaces",
816 ctxt
->nbNamespaces
= 0;
817 } else if (ctxt
->nbNamespaces
+ 2 >= ctxt
->maxNamespaces
) {
820 tmp
= (const xmlChar
**)
821 xmlRealloc((xmlChar
**) ctxt
->namespaces
, ctxt
->maxNamespaces
* 4 *
822 sizeof(const xmlChar
*));
824 xmlSchematronPErrMemory(NULL
, "allocating parser namespaces",
828 ctxt
->namespaces
= tmp
;
829 ctxt
->maxNamespaces
*= 2;
831 ctxt
->namespaces
[2 * ctxt
->nbNamespaces
] =
832 xmlDictLookup(ctxt
->dict
, ns
, -1);
833 ctxt
->namespaces
[2 * ctxt
->nbNamespaces
+ 1] =
834 xmlDictLookup(ctxt
->dict
, prefix
, -1);
835 ctxt
->nbNamespaces
++;
836 ctxt
->namespaces
[2 * ctxt
->nbNamespaces
] = NULL
;
837 ctxt
->namespaces
[2 * ctxt
->nbNamespaces
+ 1] = NULL
;
842 * xmlSchematronParseRule:
843 * @ctxt: a schema validation context
844 * @rule: the rule node
846 * parse a rule element
849 xmlSchematronParseRule(xmlSchematronParserCtxtPtr ctxt
,
850 xmlSchematronPatternPtr pattern
,
858 xmlSchematronRulePtr ruleptr
;
859 xmlSchematronTestPtr testptr
;
861 if ((ctxt
== NULL
) || (rule
== NULL
)) return;
863 context
= xmlGetNoNsProp(rule
, BAD_CAST
"context");
864 if (context
== NULL
) {
865 xmlSchematronPErr(ctxt
, rule
,
867 "rule has no context attribute",
870 } else if (context
[0] == 0) {
871 xmlSchematronPErr(ctxt
, rule
,
873 "rule has an empty context attribute",
878 ruleptr
= xmlSchematronAddRule(ctxt
, ctxt
->schema
, pattern
,
879 rule
, context
, NULL
);
880 if (ruleptr
== NULL
) {
886 cur
= rule
->children
;
887 NEXT_SCHEMATRON(cur
);
888 while (cur
!= NULL
) {
889 if (IS_SCHEMATRON(cur
, "assert")) {
891 test
= xmlGetNoNsProp(cur
, BAD_CAST
"test");
893 xmlSchematronPErr(ctxt
, cur
,
895 "assert has no test attribute",
897 } else if (test
[0] == 0) {
898 xmlSchematronPErr(ctxt
, cur
,
900 "assert has an empty test attribute",
904 /* TODO will need dynamic processing instead */
905 report
= xmlNodeGetContent(cur
);
907 testptr
= xmlSchematronAddTest(ctxt
, XML_SCHEMATRON_ASSERT
,
908 ruleptr
, cur
, test
, report
);
912 } else if (IS_SCHEMATRON(cur
, "report")) {
914 test
= xmlGetNoNsProp(cur
, BAD_CAST
"test");
916 xmlSchematronPErr(ctxt
, cur
,
918 "assert has no test attribute",
920 } else if (test
[0] == 0) {
921 xmlSchematronPErr(ctxt
, cur
,
923 "assert has an empty test attribute",
927 /* TODO will need dynamic processing instead */
928 report
= xmlNodeGetContent(cur
);
930 testptr
= xmlSchematronAddTest(ctxt
, XML_SCHEMATRON_REPORT
,
931 ruleptr
, cur
, test
, report
);
936 xmlSchematronPErr(ctxt
, cur
,
938 "Expecting an assert or a report element instead of %s",
942 NEXT_SCHEMATRON(cur
);
945 xmlSchematronPErr(ctxt
, rule
,
947 "rule has no assert nor report element", NULL
, NULL
);
952 * xmlSchematronParsePattern:
953 * @ctxt: a schema validation context
954 * @pat: the pattern node
956 * parse a pattern element
959 xmlSchematronParsePattern(xmlSchematronParserCtxtPtr ctxt
, xmlNodePtr pat
)
962 xmlSchematronPatternPtr pattern
;
966 if ((ctxt
== NULL
) || (pat
== NULL
)) return;
968 id
= xmlGetNoNsProp(pat
, BAD_CAST
"id");
970 id
= xmlGetNoNsProp(pat
, BAD_CAST
"name");
972 pattern
= xmlSchematronAddPattern(ctxt
, ctxt
->schema
, pat
, id
);
973 if (pattern
== NULL
) {
979 NEXT_SCHEMATRON(cur
);
980 while (cur
!= NULL
) {
981 if (IS_SCHEMATRON(cur
, "rule")) {
982 xmlSchematronParseRule(ctxt
, pattern
, cur
);
985 xmlSchematronPErr(ctxt
, cur
,
987 "Expecting a rule element instead of %s", cur
->name
, NULL
);
990 NEXT_SCHEMATRON(cur
);
993 xmlSchematronPErr(ctxt
, pat
,
995 "Pattern has no rule element", NULL
, NULL
);
1001 * xmlSchematronLoadInclude:
1002 * @ctxt: a schema validation context
1003 * @cur: the include element
1005 * Load the include document, Push the current pointer
1007 * Returns the updated node pointer
1010 xmlSchematronLoadInclude(xmlSchematronParserCtxtPtr ctxt
, xmlNodePtr cur
)
1012 xmlNodePtr ret
= NULL
;
1013 xmlDocPtr doc
= NULL
;
1014 xmlChar
*href
= NULL
;
1015 xmlChar
*base
= NULL
;
1016 xmlChar
*URI
= NULL
;
1018 if ((ctxt
== NULL
) || (cur
== NULL
))
1021 href
= xmlGetNoNsProp(cur
, BAD_CAST
"href");
1023 xmlSchematronPErr(ctxt
, cur
,
1025 "Include has no href attribute", NULL
, NULL
);
1029 /* do the URI base composition, load and find the root */
1030 base
= xmlNodeGetBase(cur
->doc
, cur
);
1031 URI
= xmlBuildURI(href
, base
);
1032 doc
= xmlReadFile((const char *) URI
, NULL
, SCHEMATRON_PARSE_OPTIONS
);
1034 xmlSchematronPErr(ctxt
, cur
,
1035 XML_SCHEMAP_FAILED_LOAD
,
1036 "could not load include '%s'.\n",
1040 ret
= xmlDocGetRootElement(doc
);
1042 xmlSchematronPErr(ctxt
, cur
,
1043 XML_SCHEMAP_FAILED_LOAD
,
1044 "could not find root from include '%s'.\n",
1049 /* Success, push the include for rollback on exit */
1050 xmlSchematronPushInclude(ctxt
, doc
, cur
);
1067 * xmlSchematronParse:
1068 * @ctxt: a schema validation context
1070 * parse a schema definition resource and build an internal
1071 * XML Shema struture which can be used to validate instances.
1073 * Returns the internal XML Schematron structure built from the resource or
1074 * NULL in case of error
1077 xmlSchematronParse(xmlSchematronParserCtxtPtr ctxt
)
1079 xmlSchematronPtr ret
= NULL
;
1081 xmlNodePtr root
, cur
;
1090 * First step is to parse the input document into an DOM/Infoset
1092 if (ctxt
->URL
!= NULL
) {
1093 doc
= xmlReadFile((const char *) ctxt
->URL
, NULL
,
1094 SCHEMATRON_PARSE_OPTIONS
);
1096 xmlSchematronPErr(ctxt
, NULL
,
1097 XML_SCHEMAP_FAILED_LOAD
,
1098 "xmlSchematronParse: could not load '%s'.\n",
1103 } else if (ctxt
->buffer
!= NULL
) {
1104 doc
= xmlReadMemory(ctxt
->buffer
, ctxt
->size
, NULL
, NULL
,
1105 SCHEMATRON_PARSE_OPTIONS
);
1107 xmlSchematronPErr(ctxt
, NULL
,
1108 XML_SCHEMAP_FAILED_PARSE
,
1109 "xmlSchematronParse: could not parse.\n",
1113 doc
->URL
= xmlStrdup(BAD_CAST
"in_memory_buffer");
1114 ctxt
->URL
= xmlDictLookup(ctxt
->dict
, BAD_CAST
"in_memory_buffer", -1);
1116 } else if (ctxt
->doc
!= NULL
) {
1121 xmlSchematronPErr(ctxt
, NULL
,
1122 XML_SCHEMAP_NOTHING_TO_PARSE
,
1123 "xmlSchematronParse: could not parse.\n",
1129 * Then extract the root and Schematron parse it
1131 root
= xmlDocGetRootElement(doc
);
1133 xmlSchematronPErr(ctxt
, (xmlNodePtr
) doc
,
1135 "The schema has no document element.\n", NULL
, NULL
);
1142 if (!IS_SCHEMATRON(root
, "schema")) {
1143 xmlSchematronPErr(ctxt
, root
,
1145 "The XML document '%s' is not a XML schematron document",
1149 ret
= xmlSchematronNewSchematron(ctxt
);
1155 * scan the schema elements
1157 cur
= root
->children
;
1158 NEXT_SCHEMATRON(cur
);
1159 if (IS_SCHEMATRON(cur
, "title")) {
1160 xmlChar
*title
= xmlNodeGetContent(cur
);
1161 if (title
!= NULL
) {
1162 ret
->title
= xmlDictLookup(ret
->dict
, title
, -1);
1166 NEXT_SCHEMATRON(cur
);
1168 while (IS_SCHEMATRON(cur
, "ns")) {
1169 xmlChar
*prefix
= xmlGetNoNsProp(cur
, BAD_CAST
"prefix");
1170 xmlChar
*uri
= xmlGetNoNsProp(cur
, BAD_CAST
"uri");
1171 if ((uri
== NULL
) || (uri
[0] == 0)) {
1172 xmlSchematronPErr(ctxt
, cur
,
1174 "ns element has no uri", NULL
, NULL
);
1176 if ((prefix
== NULL
) || (prefix
[0] == 0)) {
1177 xmlSchematronPErr(ctxt
, cur
,
1179 "ns element has no prefix", NULL
, NULL
);
1181 if ((prefix
) && (uri
)) {
1182 xmlXPathRegisterNs(ctxt
->xctxt
, prefix
, uri
);
1183 xmlSchematronAddNamespace(ctxt
, prefix
, uri
);
1191 NEXT_SCHEMATRON(cur
);
1193 while (cur
!= NULL
) {
1194 if (IS_SCHEMATRON(cur
, "pattern")) {
1195 xmlSchematronParsePattern(ctxt
, cur
);
1198 xmlSchematronPErr(ctxt
, cur
,
1200 "Expecting a pattern element instead of %s", cur
->name
, NULL
);
1203 NEXT_SCHEMATRON(cur
);
1205 if (ret
->nbPattern
== 0) {
1206 xmlSchematronPErr(ctxt
, root
,
1208 "The schematron document '%s' has no pattern",
1212 /* the original document must be kept for reporting */
1224 if (ctxt
->nberrors
!= 0) {
1225 xmlSchematronFree(ret
);
1228 ret
->namespaces
= ctxt
->namespaces
;
1229 ret
->nbNamespaces
= ctxt
->nbNamespaces
;
1230 ctxt
->namespaces
= NULL
;
1236 /************************************************************************
1238 * Schematrontron Reports handler *
1240 ************************************************************************/
1243 xmlSchematronGetNode(xmlSchematronValidCtxtPtr ctxt
,
1244 xmlNodePtr cur
, const xmlChar
*xpath
) {
1245 xmlNodePtr node
= NULL
;
1246 xmlXPathObjectPtr ret
;
1248 if ((ctxt
== NULL
) || (cur
== NULL
) || (xpath
== NULL
))
1251 ctxt
->xctxt
->doc
= cur
->doc
;
1252 ctxt
->xctxt
->node
= cur
;
1253 ret
= xmlXPathEval(xpath
, ctxt
->xctxt
);
1257 if ((ret
->type
== XPATH_NODESET
) &&
1258 (ret
->nodesetval
!= NULL
) && (ret
->nodesetval
->nodeNr
> 0))
1259 node
= ret
->nodesetval
->nodeTab
[0];
1261 xmlXPathFreeObject(ret
);
1266 * xmlSchematronReportOutput:
1267 * @ctxt: the validation context
1268 * @cur: the current node tested
1269 * @msg: the message output
1271 * Output part of the report to whatever channel the user selected
1274 xmlSchematronReportOutput(xmlSchematronValidCtxtPtr ctxt ATTRIBUTE_UNUSED
,
1275 xmlNodePtr cur ATTRIBUTE_UNUSED
,
1278 fprintf(stderr
, "%s", msg
);
1282 * xmlSchematronFormatReport:
1283 * @ctxt: the validation context
1284 * @test: the test node
1285 * @cur: the current node tested
1287 * Build the string being reported to the user.
1289 * Returns a report string or NULL in case of error. The string needs
1290 * to be deallocated by teh caller
1293 xmlSchematronFormatReport(xmlSchematronValidCtxtPtr ctxt
,
1294 xmlNodePtr test
, xmlNodePtr cur
) {
1295 xmlChar
*ret
= NULL
;
1296 xmlNodePtr child
, node
;
1298 if ((test
== NULL
) || (cur
== NULL
))
1301 child
= test
->children
;
1302 while (child
!= NULL
) {
1303 if ((child
->type
== XML_TEXT_NODE
) ||
1304 (child
->type
== XML_CDATA_SECTION_NODE
))
1305 ret
= xmlStrcat(ret
, child
->content
);
1306 else if (IS_SCHEMATRON(child
, "name")) {
1309 path
= xmlGetNoNsProp(child
, BAD_CAST
"path");
1313 node
= xmlSchematronGetNode(ctxt
, cur
, path
);
1319 if ((node
->ns
== NULL
) || (node
->ns
->prefix
== NULL
))
1320 ret
= xmlStrcat(ret
, node
->name
);
1322 ret
= xmlStrcat(ret
, node
->ns
->prefix
);
1323 ret
= xmlStrcat(ret
, BAD_CAST
":");
1324 ret
= xmlStrcat(ret
, node
->name
);
1327 child
= child
->next
;
1332 * remove superfluous \n
1335 int len
= xmlStrlen(ret
);
1340 if ((c
== ' ') || (c
== '\n') || (c
== '\r') || (c
== '\t')) {
1341 while ((c
== ' ') || (c
== '\n') ||
1342 (c
== '\r') || (c
== '\t')) {
1354 child
= child
->next
;
1360 * xmlSchematronReportSuccess:
1361 * @ctxt: the validation context
1362 * @test: the compiled test
1363 * @cur: the current node tested
1364 * @success: boolean value for the result
1366 * called from the validation engine when an assert or report test have
1370 xmlSchematronReportSuccess(xmlSchematronValidCtxtPtr ctxt
,
1371 xmlSchematronTestPtr test
, xmlNodePtr cur
, xmlSchematronPatternPtr pattern
, int success
) {
1372 if ((ctxt
== NULL
) || (cur
== NULL
) || (test
== NULL
))
1374 /* if quiet and not SVRL report only failures */
1375 if ((ctxt
->flags
& XML_SCHEMATRON_OUT_QUIET
) &&
1376 ((ctxt
->flags
& XML_SCHEMATRON_OUT_XML
) == 0) &&
1377 (test
->type
== XML_SCHEMATRON_REPORT
))
1379 if (ctxt
->flags
& XML_SCHEMATRON_OUT_XML
) {
1385 const xmlChar
*report
= NULL
;
1387 if (((test
->type
== XML_SCHEMATRON_REPORT
) & (!success
)) ||
1388 ((test
->type
== XML_SCHEMATRON_ASSERT
) & (success
)))
1390 line
= xmlGetLineNo(cur
);
1391 path
= xmlGetNodePath(cur
);
1393 path
= (xmlChar
*) cur
->name
;
1395 if ((test
->report
!= NULL
) && (test
->report
[0] != 0))
1396 report
= test
->report
;
1398 if (test
->node
!= NULL
)
1399 report
= xmlSchematronFormatReport(ctxt
, test
->node
, cur
);
1400 if (report
== NULL
) {
1401 if (test
->type
== XML_SCHEMATRON_ASSERT
) {
1402 report
= xmlStrdup((const xmlChar
*) "node failed assert");
1404 report
= xmlStrdup((const xmlChar
*) "node failed report");
1407 snprintf(msg
, 999, "%s line %ld: %s\n", (const char *) path
,
1408 line
, (const char *) report
);
1410 if (ctxt
->flags
& XML_SCHEMATRON_OUT_ERROR
) {
1411 xmlStructuredErrorFunc schannel
= NULL
;
1412 xmlGenericErrorFunc channel
= NULL
;
1416 if (ctxt
->serror
!= NULL
)
1417 schannel
= ctxt
->serror
;
1419 channel
= ctxt
->error
;
1420 data
= ctxt
->userData
;
1423 __xmlRaiseError(schannel
, channel
, data
,
1424 NULL
, cur
, XML_FROM_SCHEMATRONV
,
1425 (test
->type
== XML_SCHEMATRON_ASSERT
)?XML_SCHEMATRONV_ASSERT
:XML_SCHEMATRONV_REPORT
,
1426 XML_ERR_ERROR
, NULL
, line
,
1427 (pattern
== NULL
)?NULL
:((const char *) pattern
->name
),
1428 (const char *) path
,
1429 (const char *) report
, 0, 0,
1432 xmlSchematronReportOutput(ctxt
, cur
, &msg
[0]);
1435 xmlFree((char *) report
);
1437 if ((path
!= NULL
) && (path
!= (xmlChar
*) cur
->name
))
1443 * xmlSchematronReportPattern:
1444 * @ctxt: the validation context
1445 * @pattern: the current pattern
1447 * called from the validation engine when starting to check a pattern
1450 xmlSchematronReportPattern(xmlSchematronValidCtxtPtr ctxt
,
1451 xmlSchematronPatternPtr pattern
) {
1452 if ((ctxt
== NULL
) || (pattern
== NULL
))
1454 if ((ctxt
->flags
& XML_SCHEMATRON_OUT_QUIET
) || (ctxt
->flags
& XML_SCHEMATRON_OUT_ERROR
)) /* Error gives pattern name as part of error */
1456 if (ctxt
->flags
& XML_SCHEMATRON_OUT_XML
) {
1461 if (pattern
->name
== NULL
)
1463 snprintf(msg
, 999, "Pattern: %s\n", (const char *) pattern
->name
);
1464 xmlSchematronReportOutput(ctxt
, NULL
, &msg
[0]);
1469 /************************************************************************
1471 * Validation against a Schematrontron *
1473 ************************************************************************/
1476 * xmlSchematronSetValidStructuredErrors:
1477 * @ctxt: a Schematron validation context
1478 * @serror: the structured error function
1479 * @ctx: the functions context
1481 * Set the structured error callback
1484 xmlSchematronSetValidStructuredErrors(xmlSchematronValidCtxtPtr ctxt
,
1485 xmlStructuredErrorFunc serror
, void *ctx
)
1489 ctxt
->serror
= serror
;
1491 ctxt
->warning
= NULL
;
1492 ctxt
->userData
= ctx
;
1496 * xmlSchematronNewValidCtxt:
1497 * @schema: a precompiled XML Schematrons
1498 * @options: a set of xmlSchematronValidOptions
1500 * Create an XML Schematrons validation context based on the given schema.
1502 * Returns the validation context or NULL in case of error
1504 xmlSchematronValidCtxtPtr
1505 xmlSchematronNewValidCtxt(xmlSchematronPtr schema
, int options
)
1508 xmlSchematronValidCtxtPtr ret
;
1510 ret
= (xmlSchematronValidCtxtPtr
) xmlMalloc(sizeof(xmlSchematronValidCtxt
));
1512 xmlSchematronVErrMemory(NULL
, "allocating validation context",
1516 memset(ret
, 0, sizeof(xmlSchematronValidCtxt
));
1517 ret
->type
= XML_STRON_CTXT_VALIDATOR
;
1518 ret
->schema
= schema
;
1519 ret
->xctxt
= xmlXPathNewContext(NULL
);
1520 ret
->flags
= options
;
1521 if (ret
->xctxt
== NULL
) {
1522 xmlSchematronPErrMemory(NULL
, "allocating schema parser XPath context",
1524 xmlSchematronFreeValidCtxt(ret
);
1527 for (i
= 0;i
< schema
->nbNamespaces
;i
++) {
1528 if ((schema
->namespaces
[2 * i
] == NULL
) ||
1529 (schema
->namespaces
[2 * i
+ 1] == NULL
))
1531 xmlXPathRegisterNs(ret
->xctxt
, schema
->namespaces
[2 * i
+ 1],
1532 schema
->namespaces
[2 * i
]);
1538 * xmlSchematronFreeValidCtxt:
1539 * @ctxt: the schema validation context
1541 * Free the resources associated to the schema validation context
1544 xmlSchematronFreeValidCtxt(xmlSchematronValidCtxtPtr ctxt
)
1548 if (ctxt
->xctxt
!= NULL
)
1549 xmlXPathFreeContext(ctxt
->xctxt
);
1550 if (ctxt
->dict
!= NULL
)
1551 xmlDictFree(ctxt
->dict
);
1556 xmlSchematronNextNode(xmlNodePtr cur
) {
1557 if (cur
->children
!= NULL
) {
1559 * Do not descend on entities declarations
1561 if (cur
->children
->type
!= XML_ENTITY_DECL
) {
1562 cur
= cur
->children
;
1566 if (cur
->type
!= XML_DTD_NODE
)
1571 while (cur
->next
!= NULL
) {
1573 if ((cur
->type
!= XML_ENTITY_DECL
) &&
1574 (cur
->type
!= XML_DTD_NODE
))
1580 if (cur
== NULL
) break;
1581 if (cur
->type
== XML_DOCUMENT_NODE
) return(NULL
);
1582 if (cur
->next
!= NULL
) {
1586 } while (cur
!= NULL
);
1591 * xmlSchematronRunTest:
1592 * @ctxt: the schema validation context
1593 * @test: the current test
1594 * @instance: the document instace tree
1595 * @cur: the current node in the instance
1597 * Validate a rule against a tree instance at a given position
1599 * Returns 1 in case of success, 0 if error and -1 in case of internal error
1602 xmlSchematronRunTest(xmlSchematronValidCtxtPtr ctxt
,
1603 xmlSchematronTestPtr test
, xmlDocPtr instance
, xmlNodePtr cur
, xmlSchematronPatternPtr pattern
)
1605 xmlXPathObjectPtr ret
;
1609 ctxt
->xctxt
->doc
= instance
;
1610 ctxt
->xctxt
->node
= cur
;
1611 ret
= xmlXPathCompiledEval(test
->comp
, ctxt
->xctxt
);
1615 switch (ret
->type
) {
1616 case XPATH_XSLT_TREE
:
1618 if ((ret
->nodesetval
== NULL
) ||
1619 (ret
->nodesetval
->nodeNr
== 0))
1623 failed
= !ret
->boolval
;
1626 if ((xmlXPathIsNaN(ret
->floatval
)) ||
1627 (ret
->floatval
== 0.0))
1631 if ((ret
->stringval
== NULL
) ||
1632 (ret
->stringval
[0] == 0))
1635 case XPATH_UNDEFINED
:
1638 case XPATH_LOCATIONSET
:
1643 xmlXPathFreeObject(ret
);
1645 if ((failed
) && (test
->type
== XML_SCHEMATRON_ASSERT
))
1647 else if ((!failed
) && (test
->type
== XML_SCHEMATRON_REPORT
))
1650 xmlSchematronReportSuccess(ctxt
, test
, cur
, pattern
, !failed
);
1656 * xmlSchematronValidateDoc:
1657 * @ctxt: the schema validation context
1658 * @instance: the document instace tree
1660 * Validate a tree instance against the schematron
1662 * Returns 0 in case of success, -1 in case of internal error
1663 * and an error count otherwise.
1666 xmlSchematronValidateDoc(xmlSchematronValidCtxtPtr ctxt
, xmlDocPtr instance
)
1668 xmlNodePtr cur
, root
;
1669 xmlSchematronPatternPtr pattern
;
1670 xmlSchematronRulePtr rule
;
1671 xmlSchematronTestPtr test
;
1673 if ((ctxt
== NULL
) || (ctxt
->schema
== NULL
) ||
1674 (ctxt
->schema
->rules
== NULL
) || (instance
== NULL
))
1677 root
= xmlDocGetRootElement(instance
);
1683 if ((ctxt
->flags
& XML_SCHEMATRON_OUT_QUIET
) ||
1684 (ctxt
->flags
== 0)) {
1686 * we are just trying to assert the validity of the document,
1687 * speed primes over the output, run in a single pass
1690 while (cur
!= NULL
) {
1691 rule
= ctxt
->schema
->rules
;
1692 while (rule
!= NULL
) {
1693 if (xmlPatternMatch(rule
->pattern
, cur
) == 1) {
1695 while (test
!= NULL
) {
1696 xmlSchematronRunTest(ctxt
, test
, instance
, cur
, (xmlSchematronPatternPtr
)rule
->pattern
);
1703 cur
= xmlSchematronNextNode(cur
);
1707 * Process all contexts one at a time
1709 pattern
= ctxt
->schema
->patterns
;
1711 while (pattern
!= NULL
) {
1712 xmlSchematronReportPattern(ctxt
, pattern
);
1715 * TODO convert the pattern rule to a direct XPath and
1716 * compute directly instead of using the pattern matching
1717 * over the full document...
1718 * Check the exact semantic
1721 while (cur
!= NULL
) {
1722 rule
= pattern
->rules
;
1723 while (rule
!= NULL
) {
1724 if (xmlPatternMatch(rule
->pattern
, cur
) == 1) {
1726 while (test
!= NULL
) {
1727 xmlSchematronRunTest(ctxt
, test
, instance
, cur
, pattern
);
1731 rule
= rule
->patnext
;
1734 cur
= xmlSchematronNextNode(cur
);
1736 pattern
= pattern
->next
;
1739 return(ctxt
->nberrors
);
1748 xmlSchematronParserCtxtPtr pctxt
;
1749 xmlSchematronValidCtxtPtr vctxt
;
1750 xmlSchematronPtr schema
= NULL
;
1752 pctxt
= xmlSchematronNewParserCtxt("tst.sct");
1753 if (pctxt
== NULL
) {
1754 fprintf(stderr
, "failed to build schematron parser\n");
1756 schema
= xmlSchematronParse(pctxt
);
1757 if (schema
== NULL
) {
1758 fprintf(stderr
, "failed to compile schematron\n");
1760 xmlSchematronFreeParserCtxt(pctxt
);
1762 instance
= xmlReadFile("tst.sct", NULL
,
1763 XML_PARSE_NOENT
| XML_PARSE_NOCDATA
);
1764 if (instance
== NULL
) {
1765 fprintf(stderr
, "failed to parse instance\n");
1767 if ((schema
!= NULL
) && (instance
!= NULL
)) {
1768 vctxt
= xmlSchematronNewValidCtxt(schema
);
1769 if (vctxt
== NULL
) {
1770 fprintf(stderr
, "failed to build schematron validator\n");
1772 ret
= xmlSchematronValidateDoc(vctxt
, instance
);
1773 xmlSchematronFreeValidCtxt(vctxt
);
1776 xmlSchematronFree(schema
);
1777 xmlFreeDoc(instance
);
1785 #define bottom_schematron
1786 #include "elfgcchack.h"
1787 #endif /* LIBXML_SCHEMATRON_ENABLED */