blob: dbf45439be903b7dfe78ca2b8c6f42724c42a8d2 [file] [log] [blame]
Daniel Veillarded6c5492005-07-23 15:00:22 +00001/*
2 * schemas.c : implementation of the Schematron schema validity checking
3 *
4 * See Copyright for the status of this software.
5 *
6 * Daniel Veillard <veillard@redhat.com>
7 */
8
9#define IN_LIBXML
10#include "libxml.h"
11
12#ifdef LIBXML_SCHEMATRON_ENABLED
13
14#include <string.h>
15#include <libxml/parser.h>
16#include <libxml/tree.h>
17#include <libxml/uri.h>
18#include <libxml/xpath.h>
19#include <libxml/xpathInternals.h>
20#include <libxml/pattern.h>
21#include <libxml/schematron.h>
22
23#define SCHEMATRON_PARSE_OPTIONS XML_PARSE_NOENT
24
25static const xmlChar *xmlSchematronNs = XML_SCHEMATRON_NS;
26
27#define IS_SCHEMATRON(node, elem) \
28 ((node != NULL) && (node->type == XML_ELEMENT_NODE ) && \
29 (node->ns != NULL) && \
30 (xmlStrEqual(node->name, (const xmlChar *) elem)) && \
31 (xmlStrEqual(node->ns->href, xmlSchematronNs)))
32
33#define NEXT_SCHEMATRON(node) \
34 while (node != NULL) { \
35 if ((node->type == XML_ELEMENT_NODE ) && (node->ns != NULL) && \
36 (xmlStrEqual(node->ns->href, xmlSchematronNs))) \
37 break; \
38 node = node->next; \
39 }
40
41/**
42 * TODO:
43 *
44 * macro to flag unimplemented blocks
45 */
46#define TODO \
47 xmlGenericError(xmlGenericErrorContext, \
48 "Unimplemented block at %s:%d\n", \
49 __FILE__, __LINE__);
50
51#define XML_SCHEMATRON_ASSERT 0
52#define XML_SCHEMATRON_REPORT 1
53/**
54 * _xmlSchematronTest:
55 *
56 * A Schematrons test, either an assert or a report
57 */
58typedef struct _xmlSchematronTest xmlSchematronTest;
59typedef xmlSchematronTest *xmlSchematronTestPtr;
60struct _xmlSchematronTest {
61 xmlSchematronTestPtr next; /* the next test in the list */
62 int type; /* 0 for assert, 1 for report */
63 xmlNodePtr node; /* the node in the tree */
64 xmlChar *test; /* the expression to test */
65 xmlXPathCompExprPtr comp; /* the compiled expression */
Daniel Veillardd4501d72005-07-24 14:27:16 +000066 xmlChar *report; /* the message to report */
Daniel Veillarded6c5492005-07-23 15:00:22 +000067};
68
69/**
70 * _xmlSchematronRule:
71 *
72 * A Schematrons rule
73 */
74typedef struct _xmlSchematronRule xmlSchematronRule;
75typedef xmlSchematronRule *xmlSchematronRulePtr;
76struct _xmlSchematronRule {
77 xmlSchematronRulePtr next; /* the next rule in the list */
78 xmlNodePtr node; /* the node in the tree */
79 xmlChar *context; /* the context evaluation rule */
80 xmlSchematronTestPtr tests; /* the list of tests */
81 xmlPatternPtr pattern; /* the compiled pattern associated */
Daniel Veillardd4501d72005-07-24 14:27:16 +000082 xmlChar *report; /* the message to report */
Daniel Veillarded6c5492005-07-23 15:00:22 +000083};
84
85/**
86 * _xmlSchematron:
87 *
88 * A Schematrons definition
89 */
90struct _xmlSchematron {
91 const xmlChar *name; /* schema name */
92 int preserve; /* was the document preserved by the user */
93 xmlDocPtr doc; /* pointer to the parsed document */
94 int flags; /* specific to this schematron */
95
96 void *_private; /* unused by the library */
97 xmlDictPtr dict; /* the dictionnary used internally */
98
99 const xmlChar *title; /* the title if any */
100
101 int nbNs; /* the number of namespaces */
102
103 int nbPattern; /* the number of patterns */
104 xmlSchematronRulePtr rules; /* the rules gathered */
105 int nbNamespaces; /* number of namespaces in the array */
106 int maxNamespaces; /* size of the array */
107 const xmlChar **namespaces; /* the array of namespaces */
108};
109
110/**
111 * xmlSchematronValidCtxt:
112 *
113 * A Schematrons validation context
114 */
115struct _xmlSchematronValidCtxt {
116 int type;
117 int flags; /* an or of xmlSchematronValidOptions */
118
119 xmlDictPtr dict;
120 int nberrors;
121 int err;
122
123 xmlSchematronPtr schema;
124 xmlXPathContextPtr xctxt;
125
126 FILE *outputFile; /* if using XML_SCHEMATRON_OUT_FILE */
127 xmlBufferPtr outputBuffer; /* if using XML_SCHEMATRON_OUT_BUFFER */
128 xmlOutputWriteCallback iowrite; /* if using XML_SCHEMATRON_OUT_IO */
129 xmlOutputCloseCallback ioclose;
130 void *ioctx;
131};
132
133struct _xmlSchematronParserCtxt {
134 int type;
135 const xmlChar *URL;
136 xmlDocPtr doc;
137 int preserve; /* Whether the doc should be freed */
138 const char *buffer;
139 int size;
140
141 xmlDictPtr dict; /* dictionnary for interned string names */
142
143 int nberrors;
144 int err;
145 xmlXPathContextPtr xctxt; /* the XPath context used for compilation */
146 xmlSchematronPtr schema;
147
148 int nbNamespaces; /* number of namespaces in the array */
149 int maxNamespaces; /* size of the array */
150 const xmlChar **namespaces; /* the array of namespaces */
151
152 int nbIncludes; /* number of includes in the array */
153 int maxIncludes; /* size of the array */
154 xmlNodePtr *includes; /* the array of includes */
155
156 /* error rreporting data */
157 void *userData; /* user specific data block */
158 xmlSchematronValidityErrorFunc error;/* the callback in case of errors */
159 xmlSchematronValidityWarningFunc warning;/* callback in case of warning */
160 xmlStructuredErrorFunc serror; /* the structured function */
161
162};
163
164#define XML_STRON_CTXT_PARSER 1
165#define XML_STRON_CTXT_VALIDATOR 2
166
167/************************************************************************
168 * *
169 * Error reporting *
170 * *
171 ************************************************************************/
172
173/**
174 * xmlSchematronPErrMemory:
175 * @node: a context node
176 * @extra: extra informations
177 *
178 * Handle an out of memory condition
179 */
180static void
181xmlSchematronPErrMemory(xmlSchematronParserCtxtPtr ctxt,
182 const char *extra, xmlNodePtr node)
183{
184 if (ctxt != NULL)
185 ctxt->nberrors++;
186 __xmlSimpleError(XML_FROM_SCHEMASP, XML_ERR_NO_MEMORY, node, NULL,
187 extra);
188}
189
190/**
191 * xmlSchematronPErr:
192 * @ctxt: the parsing context
193 * @node: the context node
194 * @error: the error code
195 * @msg: the error message
196 * @str1: extra data
197 * @str2: extra data
198 *
199 * Handle a parser error
200 */
201static void
202xmlSchematronPErr(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr node, int error,
203 const char *msg, const xmlChar * str1, const xmlChar * str2)
204{
205 xmlGenericErrorFunc channel = NULL;
206 xmlStructuredErrorFunc schannel = NULL;
207 void *data = NULL;
208
209 if (ctxt != NULL) {
210 ctxt->nberrors++;
211 channel = ctxt->error;
212 data = ctxt->userData;
213 schannel = ctxt->serror;
214 }
215 __xmlRaiseError(schannel, channel, data, ctxt, node, XML_FROM_SCHEMASP,
216 error, XML_ERR_ERROR, NULL, 0,
217 (const char *) str1, (const char *) str2, NULL, 0, 0,
218 msg, str1, str2);
219}
220
221/**
222 * xmlSchematronVTypeErrMemory:
223 * @node: a context node
224 * @extra: extra informations
225 *
226 * Handle an out of memory condition
227 */
228static void
229xmlSchematronVErrMemory(xmlSchematronValidCtxtPtr ctxt,
230 const char *extra, xmlNodePtr node)
231{
232 if (ctxt != NULL) {
233 ctxt->nberrors++;
234 ctxt->err = XML_SCHEMAV_INTERNAL;
235 }
236 __xmlSimpleError(XML_FROM_SCHEMASV, XML_ERR_NO_MEMORY, node, NULL,
237 extra);
238}
239
240/************************************************************************
241 * *
242 * Parsing and compilation of the Schematrontrons *
243 * *
244 ************************************************************************/
245
246/**
247 * xmlSchematronAddTest:
248 * @ctxt: the schema parsing context
249 * @schema: a schema structure
250 * @node: the node hosting the test
251 * @context: the associated context string
Daniel Veillardd4501d72005-07-24 14:27:16 +0000252 * @report: the associated report string
Daniel Veillarded6c5492005-07-23 15:00:22 +0000253 *
254 * Add a test to a schematron
255 *
256 * Returns the new pointer or NULL in case of error
257 */
258static xmlSchematronTestPtr
259xmlSchematronAddTest(xmlSchematronParserCtxtPtr ctxt, int type,
260 xmlSchematronRulePtr rule,
Daniel Veillardd4501d72005-07-24 14:27:16 +0000261 xmlNodePtr node, xmlChar *test, xmlChar *report)
Daniel Veillarded6c5492005-07-23 15:00:22 +0000262{
263 xmlSchematronTestPtr ret;
264 xmlXPathCompExprPtr comp;
265
266 if ((ctxt == NULL) || (rule == NULL) || (node == NULL) ||
267 (test == NULL))
268 return(NULL);
269
270 /*
271 * try first to compile the test expression
272 */
273 comp = xmlXPathCtxtCompile(ctxt->xctxt, test);
274 if (comp == NULL) {
275 xmlSchematronPErr(ctxt, node,
276 XML_SCHEMAP_NOROOT,
277 "Failed to compile test expression %s",
278 test, NULL);
279 return(NULL);
280 }
281
282 ret = (xmlSchematronTestPtr) xmlMalloc(sizeof(xmlSchematronTest));
283 if (ret == NULL) {
284 xmlSchematronPErrMemory(ctxt, "allocating schema test", node);
285 return (NULL);
286 }
287 memset(ret, 0, sizeof(xmlSchematronTest));
288 ret->type = type;
289 ret->node = node;
290 ret->test = test;
291 ret->comp = comp;
Daniel Veillardd4501d72005-07-24 14:27:16 +0000292 ret->report = report;
Daniel Veillarded6c5492005-07-23 15:00:22 +0000293 ret->next = rule->tests;
294 rule->tests = ret;
295 return (ret);
296}
297
298/**
299 * xmlSchematronFreeTests:
300 * @tests: a list of tests
301 *
302 * Free a list of tests.
303 */
304static void
305xmlSchematronFreeTests(xmlSchematronTestPtr tests) {
306 xmlSchematronTestPtr next;
307
308 while (tests != NULL) {
309 next = tests->next;
310 if (tests->test != NULL)
311 xmlFree(tests->test);
312 if (tests->comp != NULL)
313 xmlXPathFreeCompExpr(tests->comp);
Daniel Veillardd4501d72005-07-24 14:27:16 +0000314 if (tests->report != NULL)
315 xmlFree(tests->report);
Daniel Veillarded6c5492005-07-23 15:00:22 +0000316 xmlFree(tests);
317 tests = next;
318 }
319}
320
321/**
322 * xmlSchematronAddRule:
323 * @ctxt: the schema parsing context
324 * @schema: a schema structure
325 * @node: the node hosting the rule
326 * @context: the associated context string
Daniel Veillardd4501d72005-07-24 14:27:16 +0000327 * @report: the associated report string
Daniel Veillarded6c5492005-07-23 15:00:22 +0000328 *
329 * Add a rule to a schematron
330 *
331 * Returns the new pointer or NULL in case of error
332 */
333static xmlSchematronRulePtr
334xmlSchematronAddRule(xmlSchematronParserCtxtPtr ctxt, xmlSchematronPtr schema,
Daniel Veillardd4501d72005-07-24 14:27:16 +0000335 xmlNodePtr node, xmlChar *context, xmlChar *report)
Daniel Veillarded6c5492005-07-23 15:00:22 +0000336{
337 xmlSchematronRulePtr ret;
338 xmlPatternPtr pattern;
339
340 if ((ctxt == NULL) || (schema == NULL) || (node == NULL) ||
341 (context == NULL))
342 return(NULL);
343
344 /*
345 * Try first to compile the pattern
346 */
347 pattern = xmlPatterncompile(context, ctxt->dict, XML_PATTERN_XPATH,
348 ctxt->namespaces);
349 if (pattern == NULL) {
350 xmlSchematronPErr(ctxt, node,
351 XML_SCHEMAP_NOROOT,
352 "Failed to compile context expression %s",
353 context, NULL);
354 }
355
356 ret = (xmlSchematronRulePtr) xmlMalloc(sizeof(xmlSchematronRule));
357 if (ret == NULL) {
358 xmlSchematronPErrMemory(ctxt, "allocating schema rule", node);
359 return (NULL);
360 }
361 memset(ret, 0, sizeof(xmlSchematronRule));
362 ret->node = node;
363 ret->context = context;
364 ret->next = schema->rules;
365 ret->pattern = pattern;
Daniel Veillardd4501d72005-07-24 14:27:16 +0000366 ret->report = report;
Daniel Veillarded6c5492005-07-23 15:00:22 +0000367 schema->rules = ret;
368 return (ret);
369}
370
371/**
372 * xmlSchematronFreeRules:
373 * @rules: a list of rules
374 *
375 * Free a list of rules.
376 */
377static void
378xmlSchematronFreeRules(xmlSchematronRulePtr rules) {
379 xmlSchematronRulePtr next;
380
381 while (rules != NULL) {
382 next = rules->next;
383 if (rules->tests)
384 xmlSchematronFreeTests(rules->tests);
385 if (rules->context != NULL)
386 xmlFree(rules->context);
387 if (rules->pattern)
388 xmlFreePattern(rules->pattern);
Daniel Veillardd4501d72005-07-24 14:27:16 +0000389 if (rules->report != NULL)
390 xmlFree(rules->report);
Daniel Veillarded6c5492005-07-23 15:00:22 +0000391 xmlFree(rules);
392 rules = next;
393 }
394}
395
396/**
397 * xmlSchematronNewSchematron:
398 * @ctxt: a schema validation context
399 *
400 * Allocate a new Schematron structure.
401 *
402 * Returns the newly allocated structure or NULL in case or error
403 */
404static xmlSchematronPtr
405xmlSchematronNewSchematron(xmlSchematronParserCtxtPtr ctxt)
406{
407 xmlSchematronPtr ret;
408
409 ret = (xmlSchematronPtr) xmlMalloc(sizeof(xmlSchematron));
410 if (ret == NULL) {
411 xmlSchematronPErrMemory(ctxt, "allocating schema", NULL);
412 return (NULL);
413 }
414 memset(ret, 0, sizeof(xmlSchematron));
415 ret->dict = ctxt->dict;
416 xmlDictReference(ret->dict);
417
418 return (ret);
419}
420
421/**
422 * xmlSchematronFree:
423 * @schema: a schema structure
424 *
425 * Deallocate a Schematron structure.
426 */
427void
428xmlSchematronFree(xmlSchematronPtr schema)
429{
430 if (schema == NULL)
431 return;
432
433 if ((schema->doc != NULL) && (!(schema->preserve)))
434 xmlFreeDoc(schema->doc);
435
436 if (schema->namespaces != NULL)
437 xmlFree(schema->namespaces);
438
439 xmlSchematronFreeRules(schema->rules);
440 xmlDictFree(schema->dict);
441 xmlFree(schema);
442}
443
444/**
445 * xmlSchematronNewParserCtxt:
446 * @URL: the location of the schema
447 *
448 * Create an XML Schematrons parse context for that file/resource expected
449 * to contain an XML Schematrons file.
450 *
451 * Returns the parser context or NULL in case of error
452 */
453xmlSchematronParserCtxtPtr
454xmlSchematronNewParserCtxt(const char *URL)
455{
456 xmlSchematronParserCtxtPtr ret;
457
458 if (URL == NULL)
459 return (NULL);
460
461 ret =
462 (xmlSchematronParserCtxtPtr)
463 xmlMalloc(sizeof(xmlSchematronParserCtxt));
464 if (ret == NULL) {
465 xmlSchematronPErrMemory(NULL, "allocating schema parser context",
466 NULL);
467 return (NULL);
468 }
469 memset(ret, 0, sizeof(xmlSchematronParserCtxt));
470 ret->type = XML_STRON_CTXT_PARSER;
471 ret->dict = xmlDictCreate();
472 ret->URL = xmlDictLookup(ret->dict, (const xmlChar *) URL, -1);
473 ret->includes = 0;
474 ret->xctxt = xmlXPathNewContext(NULL);
475 if (ret->xctxt == NULL) {
476 xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",
477 NULL);
478 xmlSchematronFreeParserCtxt(ret);
479 return (NULL);
480 }
481 ret->xctxt->flags = XML_XPATH_CHECKNS;
482 return (ret);
483}
484
485/**
486 * xmlSchematronNewMemParserCtxt:
487 * @buffer: a pointer to a char array containing the schemas
488 * @size: the size of the array
489 *
490 * Create an XML Schematrons parse context for that memory buffer expected
491 * to contain an XML Schematrons file.
492 *
493 * Returns the parser context or NULL in case of error
494 */
495xmlSchematronParserCtxtPtr
496xmlSchematronNewMemParserCtxt(const char *buffer, int size)
497{
498 xmlSchematronParserCtxtPtr ret;
499
500 if ((buffer == NULL) || (size <= 0))
501 return (NULL);
502
503 ret =
504 (xmlSchematronParserCtxtPtr)
505 xmlMalloc(sizeof(xmlSchematronParserCtxt));
506 if (ret == NULL) {
507 xmlSchematronPErrMemory(NULL, "allocating schema parser context",
508 NULL);
509 return (NULL);
510 }
511 memset(ret, 0, sizeof(xmlSchematronParserCtxt));
512 ret->buffer = buffer;
513 ret->size = size;
514 ret->dict = xmlDictCreate();
515 ret->xctxt = xmlXPathNewContext(NULL);
516 if (ret->xctxt == NULL) {
517 xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",
518 NULL);
519 xmlSchematronFreeParserCtxt(ret);
520 return (NULL);
521 }
522 return (ret);
523}
524
525/**
526 * xmlSchematronNewDocParserCtxt:
527 * @doc: a preparsed document tree
528 *
529 * Create an XML Schematrons parse context for that document.
530 * NB. The document may be modified during the parsing process.
531 *
532 * Returns the parser context or NULL in case of error
533 */
534xmlSchematronParserCtxtPtr
535xmlSchematronNewDocParserCtxt(xmlDocPtr doc)
536{
537 xmlSchematronParserCtxtPtr ret;
538
539 if (doc == NULL)
540 return (NULL);
541
542 ret =
543 (xmlSchematronParserCtxtPtr)
544 xmlMalloc(sizeof(xmlSchematronParserCtxt));
545 if (ret == NULL) {
546 xmlSchematronPErrMemory(NULL, "allocating schema parser context",
547 NULL);
548 return (NULL);
549 }
550 memset(ret, 0, sizeof(xmlSchematronParserCtxt));
551 ret->doc = doc;
552 ret->dict = xmlDictCreate();
553 /* The application has responsibility for the document */
554 ret->preserve = 1;
555 ret->xctxt = xmlXPathNewContext(doc);
556 if (ret->xctxt == NULL) {
557 xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",
558 NULL);
559 xmlSchematronFreeParserCtxt(ret);
560 return (NULL);
561 }
562
563 return (ret);
564}
565
566/**
567 * xmlSchematronFreeParserCtxt:
568 * @ctxt: the schema parser context
569 *
570 * Free the resources associated to the schema parser context
571 */
572void
573xmlSchematronFreeParserCtxt(xmlSchematronParserCtxtPtr ctxt)
574{
575 if (ctxt == NULL)
576 return;
577 if (ctxt->doc != NULL && !ctxt->preserve)
578 xmlFreeDoc(ctxt->doc);
579 if (ctxt->xctxt != NULL) {
580 xmlXPathFreeContext(ctxt->xctxt);
581 }
582 if (ctxt->namespaces != NULL)
583 xmlFree(ctxt->namespaces);
584 xmlDictFree(ctxt->dict);
585 xmlFree(ctxt);
586}
587
588/**
589 * xmlSchematronPushInclude:
590 * @ctxt: the schema parser context
591 * @doc: the included document
592 * @cur: the current include node
593 *
594 * Add an included document
595 */
596static void
597xmlSchematronPushInclude(xmlSchematronParserCtxtPtr ctxt,
598 xmlDocPtr doc, xmlNodePtr cur)
599{
600 if (ctxt->includes == NULL) {
601 ctxt->maxIncludes = 10;
602 ctxt->includes = (xmlNodePtr *)
603 xmlMalloc(ctxt->maxIncludes * 2 * sizeof(xmlNodePtr));
604 if (ctxt->includes == NULL) {
605 xmlSchematronPErrMemory(NULL, "allocating parser includes",
606 NULL);
607 return;
608 }
609 ctxt->nbIncludes = 0;
610 } else if (ctxt->nbIncludes + 2 >= ctxt->maxIncludes) {
611 xmlNodePtr *tmp;
612
613 tmp = (xmlNodePtr *)
614 xmlRealloc(ctxt->includes, ctxt->maxIncludes * 4 *
615 sizeof(xmlNodePtr));
616 if (tmp == NULL) {
617 xmlSchematronPErrMemory(NULL, "allocating parser includes",
618 NULL);
619 return;
620 }
621 ctxt->includes = tmp;
622 ctxt->maxIncludes *= 2;
623 }
624 ctxt->includes[2 * ctxt->nbIncludes] = cur;
625 ctxt->includes[2 * ctxt->nbIncludes + 1] = (xmlNodePtr) doc;
626 ctxt->nbIncludes++;
627}
628
629/**
630 * xmlSchematronPopInclude:
631 * @ctxt: the schema parser context
632 *
633 * Pop an include level. The included document is being freed
634 *
635 * Returns the node immediately following the include or NULL if the
636 * include list was empty.
637 */
638static xmlNodePtr
639xmlSchematronPopInclude(xmlSchematronParserCtxtPtr ctxt)
640{
641 xmlDocPtr doc;
642 xmlNodePtr ret;
643
644 if (ctxt->nbIncludes <= 0)
645 return(NULL);
646 ctxt->nbIncludes--;
647 doc = (xmlDocPtr) ctxt->includes[2 * ctxt->nbIncludes + 1];
648 ret = ctxt->includes[2 * ctxt->nbIncludes];
649 xmlFreeDoc(doc);
650 if (ret != NULL)
651 ret = ret->next;
652 if (ret == NULL)
653 return(xmlSchematronPopInclude(ctxt));
654 return(ret);
655}
656
657/**
658 * xmlSchematronAddNamespace:
659 * @ctxt: the schema parser context
660 * @prefix: the namespace prefix
661 * @ns: the namespace name
662 *
663 * Add a namespace definition in the context
664 */
665static void
666xmlSchematronAddNamespace(xmlSchematronParserCtxtPtr ctxt,
667 const xmlChar *prefix, const xmlChar *ns)
668{
669 if (ctxt->namespaces == NULL) {
670 ctxt->maxNamespaces = 10;
671 ctxt->namespaces = (const xmlChar **)
672 xmlMalloc(ctxt->maxNamespaces * 2 * sizeof(const xmlChar *));
673 if (ctxt->namespaces == NULL) {
674 xmlSchematronPErrMemory(NULL, "allocating parser namespaces",
675 NULL);
676 return;
677 }
678 ctxt->nbNamespaces = 0;
679 } else if (ctxt->nbNamespaces + 2 >= ctxt->maxNamespaces) {
680 const xmlChar **tmp;
681
682 tmp = (const xmlChar **)
683 xmlRealloc(ctxt->namespaces, ctxt->maxNamespaces * 4 *
684 sizeof(const xmlChar *));
685 if (tmp == NULL) {
686 xmlSchematronPErrMemory(NULL, "allocating parser namespaces",
687 NULL);
688 return;
689 }
690 ctxt->namespaces = tmp;
691 ctxt->maxNamespaces *= 2;
692 }
693 ctxt->namespaces[2 * ctxt->nbNamespaces] =
694 xmlDictLookup(ctxt->dict, ns, -1);
695 ctxt->namespaces[2 * ctxt->nbNamespaces + 1] =
696 xmlDictLookup(ctxt->dict, prefix, -1);
697 ctxt->nbNamespaces++;
698 ctxt->namespaces[2 * ctxt->nbNamespaces] = NULL;
699 ctxt->namespaces[2 * ctxt->nbNamespaces + 1] = NULL;
700
701}
702
703/**
704 * xmlSchematronParseRule:
705 * @ctxt: a schema validation context
706 * @rule: the rule node
707 *
708 * parse a rule element
709 */
710static void
711xmlSchematronParseRule(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr rule)
712{
713 xmlNodePtr cur;
714 int nbChecks = 0;
715 xmlChar *test;
716 xmlChar *context;
Daniel Veillardd4501d72005-07-24 14:27:16 +0000717 xmlChar *report;
Daniel Veillarded6c5492005-07-23 15:00:22 +0000718 xmlSchematronRulePtr ruleptr;
719 xmlSchematronTestPtr testptr;
720
721 if ((ctxt == NULL) || (rule == NULL)) return;
722
723 context = xmlGetNoNsProp(rule, BAD_CAST "context");
724 if (context == NULL) {
725 xmlSchematronPErr(ctxt, rule,
726 XML_SCHEMAP_NOROOT,
727 "rule has no context attribute",
728 NULL, NULL);
729 return;
730 } else if (context[0] == 0) {
731 xmlSchematronPErr(ctxt, rule,
732 XML_SCHEMAP_NOROOT,
733 "rule has an empty context attribute",
734 NULL, NULL);
735 xmlFree(context);
736 return;
737 } else {
Daniel Veillardd4501d72005-07-24 14:27:16 +0000738 ruleptr = xmlSchematronAddRule(ctxt, ctxt->schema, rule, context, NULL);
Daniel Veillarded6c5492005-07-23 15:00:22 +0000739 if (ruleptr == NULL) {
740 xmlFree(context);
741 return;
742 }
743 }
744
745 cur = rule->children;
746 NEXT_SCHEMATRON(cur);
747 while (cur != NULL) {
748 if (IS_SCHEMATRON(cur, "assert")) {
749 nbChecks++;
750 test = xmlGetNoNsProp(cur, BAD_CAST "test");
751 if (test == NULL) {
752 xmlSchematronPErr(ctxt, cur,
753 XML_SCHEMAP_NOROOT,
754 "assert has no test attribute",
755 NULL, NULL);
756 } else if (test[0] == 0) {
757 xmlSchematronPErr(ctxt, cur,
758 XML_SCHEMAP_NOROOT,
759 "assert has an empty test attribute",
760 NULL, NULL);
761 xmlFree(test);
762 } else {
Daniel Veillardd4501d72005-07-24 14:27:16 +0000763 /* TODO will need dynamic processing instead */
764 report = xmlNodeGetContent(cur);
765
Daniel Veillarded6c5492005-07-23 15:00:22 +0000766 testptr = xmlSchematronAddTest(ctxt, XML_SCHEMATRON_ASSERT,
Daniel Veillardd4501d72005-07-24 14:27:16 +0000767 ruleptr, cur, test, report);
Daniel Veillarded6c5492005-07-23 15:00:22 +0000768 if (testptr == NULL)
769 xmlFree(test);
770 }
771 } else if (IS_SCHEMATRON(cur, "report")) {
772 nbChecks++;
773 test = xmlGetNoNsProp(cur, BAD_CAST "test");
774 if (test == NULL) {
775 xmlSchematronPErr(ctxt, cur,
776 XML_SCHEMAP_NOROOT,
777 "assert has no test attribute",
778 NULL, NULL);
779 } else if (test[0] == 0) {
780 xmlSchematronPErr(ctxt, cur,
781 XML_SCHEMAP_NOROOT,
782 "assert has an empty test attribute",
783 NULL, NULL);
784 xmlFree(test);
785 } else {
Daniel Veillardd4501d72005-07-24 14:27:16 +0000786 /* TODO will need dynamic processing instead */
787 report = xmlNodeGetContent(cur);
788
Daniel Veillarded6c5492005-07-23 15:00:22 +0000789 testptr = xmlSchematronAddTest(ctxt, XML_SCHEMATRON_REPORT,
Daniel Veillardd4501d72005-07-24 14:27:16 +0000790 ruleptr, cur, test, report);
Daniel Veillarded6c5492005-07-23 15:00:22 +0000791 if (testptr == NULL)
792 xmlFree(test);
793 }
794 } else {
795 xmlSchematronPErr(ctxt, cur,
796 XML_SCHEMAP_NOROOT,
797 "Expecting an assert or a report element instead of %s",
798 cur->name, NULL);
799 }
800 cur = cur->next;
801 NEXT_SCHEMATRON(cur);
802 }
803 if (nbChecks == 0) {
804 xmlSchematronPErr(ctxt, rule,
805 XML_SCHEMAP_NOROOT,
806 "rule has no assert nor report element", NULL, NULL);
807 }
808}
809
810/**
811 * xmlSchematronParsePattern:
812 * @ctxt: a schema validation context
813 * @pat: the pattern node
814 *
815 * parse a pattern element
816 */
817static void
818xmlSchematronParsePattern(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr pat)
819{
820 xmlNodePtr cur;
821 int nbRules = 0;
822
823 if ((ctxt == NULL) || (pat == NULL)) return;
824
825 cur = pat->children;
826 NEXT_SCHEMATRON(cur);
827 while (cur != NULL) {
828 if (IS_SCHEMATRON(cur, "rule")) {
829 xmlSchematronParseRule(ctxt, cur);
830 nbRules++;
831 } else {
832 xmlSchematronPErr(ctxt, cur,
833 XML_SCHEMAP_NOROOT,
834 "Expecting a rule element instead of %s", cur->name, NULL);
835 }
836 cur = cur->next;
837 NEXT_SCHEMATRON(cur);
838 }
839 if (nbRules == 0) {
840 xmlSchematronPErr(ctxt, pat,
841 XML_SCHEMAP_NOROOT,
842 "Pattern has no rule element", NULL, NULL);
843 }
844}
845
846/**
847 * xmlSchematronLoadInclude:
848 * @ctxt: a schema validation context
849 * @cur: the include element
850 *
851 * Load the include document, Push the current pointer
852 *
853 * Returns the updated node pointer
854 */
855static xmlNodePtr
856xmlSchematronLoadInclude(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr cur)
857{
858 xmlNodePtr ret = NULL;
859 xmlDocPtr doc = NULL;
860 xmlChar *href = NULL;
861 xmlChar *base = NULL;
862 xmlChar *URI = NULL;
863
864 if ((ctxt == NULL) || (cur == NULL))
865 return(NULL);
866
867 href = xmlGetNoNsProp(cur, BAD_CAST "href");
868 if (href == NULL) {
869 xmlSchematronPErr(ctxt, cur,
870 XML_SCHEMAP_NOROOT,
871 "Include has no href attribute", NULL, NULL);
872 return(cur->next);
873 }
874
875 /* do the URI base composition, load and find the root */
876 base = xmlNodeGetBase(cur->doc, cur);
877 URI = xmlBuildURI(href, base);
878 doc = xmlReadFile((const char *) URI, NULL, SCHEMATRON_PARSE_OPTIONS);
879 if (doc == NULL) {
880 xmlSchematronPErr(ctxt, cur,
881 XML_SCHEMAP_FAILED_LOAD,
882 "could not load include '%s'.\n",
883 URI, NULL);
884 goto done;
885 }
886 ret = xmlDocGetRootElement(doc);
887 if (ret == NULL) {
888 xmlSchematronPErr(ctxt, cur,
889 XML_SCHEMAP_FAILED_LOAD,
890 "could not find root from include '%s'.\n",
891 URI, NULL);
892 goto done;
893 }
894
895 /* Success, push the include for rollback on exit */
896 xmlSchematronPushInclude(ctxt, doc, cur);
897
898done:
899 if (ret == NULL) {
900 if (doc != NULL)
901 xmlFreeDoc(doc);
902 }
903 if (href == NULL)
904 xmlFree(href);
905 if (base == NULL)
906 xmlFree(base);
907 if (URI == NULL)
908 xmlFree(URI);
909 return(ret);
910}
911
912/**
913 * xmlSchematronParse:
914 * @ctxt: a schema validation context
915 *
916 * parse a schema definition resource and build an internal
917 * XML Shema struture which can be used to validate instances.
918 *
919 * Returns the internal XML Schematron structure built from the resource or
920 * NULL in case of error
921 */
922xmlSchematronPtr
923xmlSchematronParse(xmlSchematronParserCtxtPtr ctxt)
924{
925 xmlSchematronPtr ret = NULL;
926 xmlDocPtr doc;
927 xmlNodePtr root, cur;
928 int preserve = 0;
929
930 if (ctxt == NULL)
931 return (NULL);
932
933 ctxt->nberrors = 0;
934
935 /*
936 * First step is to parse the input document into an DOM/Infoset
937 */
938 if (ctxt->URL != NULL) {
939 doc = xmlReadFile((const char *) ctxt->URL, NULL,
940 SCHEMATRON_PARSE_OPTIONS);
941 if (doc == NULL) {
942 xmlSchematronPErr(ctxt, NULL,
943 XML_SCHEMAP_FAILED_LOAD,
944 "xmlSchematronParse: could not load '%s'.\n",
945 ctxt->URL, NULL);
946 return (NULL);
947 }
948 } else if (ctxt->buffer != NULL) {
949 doc = xmlReadMemory(ctxt->buffer, ctxt->size, NULL, NULL,
950 SCHEMATRON_PARSE_OPTIONS);
951 if (doc == NULL) {
952 xmlSchematronPErr(ctxt, NULL,
953 XML_SCHEMAP_FAILED_PARSE,
954 "xmlSchematronParse: could not parse.\n",
955 NULL, NULL);
956 return (NULL);
957 }
958 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
959 ctxt->URL = xmlDictLookup(ctxt->dict, BAD_CAST "in_memory_buffer", -1);
960 } else if (ctxt->doc != NULL) {
961 doc = ctxt->doc;
962 preserve = 1;
963 } else {
964 xmlSchematronPErr(ctxt, NULL,
965 XML_SCHEMAP_NOTHING_TO_PARSE,
966 "xmlSchematronParse: could not parse.\n",
967 NULL, NULL);
968 return (NULL);
969 }
970
971 /*
972 * Then extract the root and Schematron parse it
973 */
974 root = xmlDocGetRootElement(doc);
975 if (root == NULL) {
976 xmlSchematronPErr(ctxt, (xmlNodePtr) doc,
977 XML_SCHEMAP_NOROOT,
978 "The schema has no document element.\n", NULL, NULL);
979 if (!preserve) {
980 xmlFreeDoc(doc);
981 }
982 return (NULL);
983 }
984
985 if (!IS_SCHEMATRON(root, "schema")) {
986 xmlSchematronPErr(ctxt, root,
987 XML_SCHEMAP_NOROOT,
988 "The XML document '%s' is not a XML schematron document",
989 ctxt->URL, NULL);
990 goto exit;
991 }
992 ret = xmlSchematronNewSchematron(ctxt);
993 if (ret == NULL)
994 goto exit;
995 ctxt->schema = ret;
996
997 /*
998 * scan the schema elements
999 */
1000 cur = root->children;
1001 NEXT_SCHEMATRON(cur);
1002 if (IS_SCHEMATRON(cur, "title")) {
1003 xmlChar *title = xmlNodeGetContent(cur);
1004 if (title != NULL) {
1005 ret->title = xmlDictLookup(ret->dict, title, -1);
1006 xmlFree(title);
1007 }
1008 cur = cur->next;
1009 NEXT_SCHEMATRON(cur);
1010 }
1011 while (IS_SCHEMATRON(cur, "ns")) {
1012 xmlChar *prefix = xmlGetNoNsProp(cur, BAD_CAST "prefix");
1013 xmlChar *uri = xmlGetNoNsProp(cur, BAD_CAST "uri");
1014 if ((uri == NULL) || (uri[0] == 0)) {
1015 xmlSchematronPErr(ctxt, cur,
1016 XML_SCHEMAP_NOROOT,
1017 "ns element has no uri", NULL, NULL);
1018 }
1019 if ((prefix == NULL) || (prefix[0] == 0)) {
1020 xmlSchematronPErr(ctxt, cur,
1021 XML_SCHEMAP_NOROOT,
1022 "ns element has no prefix", NULL, NULL);
1023 }
1024 if ((prefix) && (uri)) {
1025 xmlXPathRegisterNs(ctxt->xctxt, prefix, uri);
1026 xmlSchematronAddNamespace(ctxt, prefix, uri);
1027 ret->nbNs++;
1028 }
1029 if (uri)
1030 xmlFree(uri);
1031 if (prefix)
1032 xmlFree(prefix);
1033 cur = cur->next;
1034 NEXT_SCHEMATRON(cur);
1035 }
1036 while (cur != NULL) {
1037 if (IS_SCHEMATRON(cur, "pattern")) {
1038 xmlSchematronParsePattern(ctxt, cur);
1039 ret->nbPattern++;
1040 } else {
1041 xmlSchematronPErr(ctxt, cur,
1042 XML_SCHEMAP_NOROOT,
1043 "Expecting a pattern element instead of %s", cur->name, NULL);
1044 }
1045 cur = cur->next;
1046 NEXT_SCHEMATRON(cur);
1047 }
1048 if (ret->nbPattern == 0) {
1049 xmlSchematronPErr(ctxt, root,
1050 XML_SCHEMAP_NOROOT,
1051 "The schematron document '%s' has no pattern",
1052 ctxt->URL, NULL);
1053 goto exit;
1054 }
1055
1056exit:
1057 if (!preserve) {
1058 xmlFreeDoc(doc);
1059 }
1060 if (ctxt->nberrors != 0) {
1061 xmlSchematronFree(ret);
1062 ret = NULL;
1063 } else {
1064 ret->namespaces = ctxt->namespaces;
1065 ret->nbNamespaces = ctxt->nbNamespaces;
1066 ctxt->namespaces = NULL;
1067 }
1068 return (ret);
1069}
1070
1071/************************************************************************
1072 * *
1073 * Schematrontron Reports handler *
1074 * *
1075 ************************************************************************/
1076
Daniel Veillardd4501d72005-07-24 14:27:16 +00001077/**
1078 * xmlSchematronReportOutput:
1079 * @ctxt: the validation context
1080 * @cur: the current node tested
1081 * @msg: the message output
1082 *
1083 * Output part of the report to whatever channel the user selected
1084 */
1085static void
1086xmlSchematronReportOutput(xmlSchematronValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
1087 xmlNodePtr cur ATTRIBUTE_UNUSED,
1088 const char *msg ATTRIBUTE_UNUSED) {
1089 /* TODO */
1090 fprintf(stderr, "%s", msg);
1091}
1092
1093/**
1094 * xmlSchematronReportSuccess:
1095 * @ctxt: the validation context
1096 * @test: the compiled test
1097 * @cur: the current node tested
1098 * @success: boolean value for the result
1099 *
1100 * called from the validation engine when an assert or report test have
1101 * been done.
1102 */
Daniel Veillarded6c5492005-07-23 15:00:22 +00001103static void
1104xmlSchematronReportSuccess(xmlSchematronValidCtxtPtr ctxt,
Daniel Veillardd4501d72005-07-24 14:27:16 +00001105 xmlSchematronTestPtr test, xmlNodePtr cur, int success) {
1106 if ((ctxt == NULL) || (cur == NULL) || (test == NULL))
1107 return;
1108 /* if quiet and not SVRL report only failures */
1109 if ((ctxt->flags & XML_SCHEMATRON_OUT_QUIET) &&
1110 ((ctxt->flags & XML_SCHEMATRON_OUT_XML) == 0) && (success))
1111 return;
1112 if (ctxt->flags & XML_SCHEMATRON_OUT_XML) {
1113 TODO
1114 } else {
1115 xmlChar *path;
1116 char msg[1000];
1117 long line;
1118 const xmlChar *report;
1119
1120 if (success)
1121 return;
1122 line = xmlGetLineNo(cur);
1123 path = xmlGetNodePath(cur);
1124 if (path == NULL)
1125 path = (xmlChar *) cur->name;
1126 if ((test->report != NULL) && (test->report[0] != 0))
1127 report = test->report;
1128 else if (test->type == XML_SCHEMATRON_ASSERT) {
1129 report = BAD_CAST "node failed assert";
1130 } else {
1131 report = BAD_CAST "node failed report";
1132 }
1133 snprintf(msg, 999, "%s line %ld:\n %s\n", (const char *) path,
1134 line, (const char *) report);
1135 xmlSchematronReportOutput(ctxt, cur, &msg[0]);
1136 if ((path != NULL) && (path != (xmlChar *) cur->name))
1137 xmlFree(path);
1138 }
Daniel Veillarded6c5492005-07-23 15:00:22 +00001139}
1140
1141/************************************************************************
1142 * *
1143 * Validation against a Schematrontron *
1144 * *
1145 ************************************************************************/
1146
1147/**
1148 * xmlSchematronNewValidCtxt:
1149 * @schema: a precompiled XML Schematrons
1150 * @options: a set of xmlSchematronValidOptions
1151 *
1152 * Create an XML Schematrons validation context based on the given schema.
1153 *
1154 * Returns the validation context or NULL in case of error
1155 */
1156xmlSchematronValidCtxtPtr
1157xmlSchematronNewValidCtxt(xmlSchematronPtr schema, int options)
1158{
1159 int i;
1160 xmlSchematronValidCtxtPtr ret;
1161
1162 ret =
1163 (xmlSchematronValidCtxtPtr)
1164 xmlMalloc(sizeof(xmlSchematronValidCtxt));
1165 if (ret == NULL) {
1166 xmlSchematronVErrMemory(NULL, "allocating validation context",
1167 NULL);
1168 return (NULL);
1169 }
1170 memset(ret, 0, sizeof(xmlSchematronValidCtxt));
1171 ret->type = XML_STRON_CTXT_VALIDATOR;
1172 ret->schema = schema;
1173 ret->xctxt = xmlXPathNewContext(NULL);
Daniel Veillardd4501d72005-07-24 14:27:16 +00001174 ret->flags = options;
Daniel Veillarded6c5492005-07-23 15:00:22 +00001175 if (ret->xctxt == NULL) {
1176 xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",
1177 NULL);
1178 xmlSchematronFreeValidCtxt(ret);
1179 return (NULL);
1180 }
1181 for (i = 0;i < schema->nbNamespaces;i++) {
1182 if ((schema->namespaces[2 * i] == NULL) ||
1183 (schema->namespaces[2 * i + 1] == NULL))
1184 break;
1185 xmlXPathRegisterNs(ret->xctxt, schema->namespaces[2 * i + 1],
1186 schema->namespaces[2 * i]);
1187 }
1188 return (ret);
1189}
1190
1191/**
1192 * xmlSchematronFreeValidCtxt:
1193 * @ctxt: the schema validation context
1194 *
1195 * Free the resources associated to the schema validation context
1196 */
1197void
1198xmlSchematronFreeValidCtxt(xmlSchematronValidCtxtPtr ctxt)
1199{
1200 if (ctxt == NULL)
1201 return;
1202 if (ctxt->xctxt != NULL)
1203 xmlXPathFreeContext(ctxt->xctxt);
1204 if (ctxt->dict != NULL)
1205 xmlDictFree(ctxt->dict);
1206 xmlFree(ctxt);
1207}
1208
1209static xmlNodePtr
1210xmlSchematronNextNode(xmlNodePtr cur) {
1211 if (cur->children != NULL) {
1212 /*
1213 * Do not descend on entities declarations
1214 */
1215 if (cur->children->type != XML_ENTITY_DECL) {
1216 cur = cur->children;
1217 /*
1218 * Skip DTDs
1219 */
1220 if (cur->type != XML_DTD_NODE)
1221 return(cur);
1222 }
1223 }
1224
1225 while (cur->next != NULL) {
1226 cur = cur->next;
1227 if ((cur->type != XML_ENTITY_DECL) &&
1228 (cur->type != XML_DTD_NODE))
1229 return(cur);
1230 }
1231
1232 do {
1233 cur = cur->parent;
1234 if (cur == NULL) return(NULL);
1235 if (cur->type == XML_DOCUMENT_NODE) return(NULL);
1236 if (cur->next != NULL) {
1237 cur = cur->next;
1238 return(cur);
1239 }
1240 } while (cur != NULL);
1241 return(cur);
1242}
1243
1244/**
1245 * xmlSchematronRunTest:
1246 * @ctxt: the schema validation context
1247 * @test: the current test
1248 * @instance: the document instace tree
1249 * @cur: the current node in the instance
1250 *
1251 * Validate a rule against a tree instance at a given position
1252 *
1253 * Returns 1 in case of success, 0 if error and -1 in case of internal error
1254 */
1255static int
1256xmlSchematronRunTest(xmlSchematronValidCtxtPtr ctxt,
1257 xmlSchematronTestPtr test, xmlDocPtr instance, xmlNodePtr cur)
1258{
1259 xmlXPathObjectPtr ret;
1260 int failed;
1261
1262 failed = 0;
1263 ctxt->xctxt->doc = instance;
1264 ctxt->xctxt->node = cur;
1265 ret = xmlXPathCompiledEval(test->comp, ctxt->xctxt);
1266 if (ret == NULL) {
1267 failed = 1;
1268 } else switch (ret->type) {
1269 case XPATH_XSLT_TREE:
1270 case XPATH_NODESET:
1271 if ((ret->nodesetval == NULL) ||
1272 (ret->nodesetval->nodeNr == 0))
1273 failed = 1;
1274 break;
1275 case XPATH_BOOLEAN:
1276 failed = !ret->boolval;
1277 break;
1278 case XPATH_NUMBER:
1279 if ((xmlXPathIsNaN(ret->floatval)) ||
1280 (ret->floatval == 0.0))
1281 failed = 1;
1282 break;
1283 case XPATH_STRING:
1284 if ((ret->stringval == NULL) ||
1285 (ret->stringval[0] == 0))
1286 failed = 1;
1287 break;
1288 case XPATH_UNDEFINED:
1289 case XPATH_POINT:
1290 case XPATH_RANGE:
1291 case XPATH_LOCATIONSET:
1292 case XPATH_USERS:
1293 failed = 1;
1294 break;
1295 }
Daniel Veillardd4501d72005-07-24 14:27:16 +00001296 if (failed)
1297 ctxt->nberrors++;
1298 xmlSchematronReportSuccess(ctxt, test, cur, !failed);
Daniel Veillarded6c5492005-07-23 15:00:22 +00001299
1300 return(!failed);
1301}
1302
1303/**
1304 * xmlSchematronValidateDoc:
1305 * @ctxt: the schema validation context
1306 * @instance: the document instace tree
1307 *
1308 * Validate a tree instance against the schematron
1309 *
1310 * Returns 0 in case of success, -1 in case of internal error
1311 * and an error count otherwise.
1312 */
1313int
1314xmlSchematronValidateDoc(xmlSchematronValidCtxtPtr ctxt, xmlDocPtr instance)
1315{
1316 xmlNodePtr cur;
1317 xmlSchematronRulePtr rule;
1318 xmlSchematronTestPtr test;
1319
1320 if ((ctxt == NULL) || (ctxt->schema == NULL) ||
1321 (ctxt->schema->rules == NULL) || (instance == NULL))
1322 return(-1);
1323 ctxt->nberrors = 0;
1324 cur = xmlDocGetRootElement(instance);
1325 while (cur != NULL) {
1326 rule = ctxt->schema->rules;
1327 while (rule != NULL) {
1328 if (xmlPatternMatch(rule->pattern, cur) == 1) {
Daniel Veillarded6c5492005-07-23 15:00:22 +00001329 test = rule->tests;
1330 while (test != NULL) {
1331 xmlSchematronRunTest(ctxt, test, instance, cur);
1332 test = test->next;
1333 }
1334 }
1335 rule = rule->next;
1336 }
1337
1338 cur = xmlSchematronNextNode(cur);
1339 }
1340 return(ctxt->nberrors);
1341}
1342
1343#ifdef STANDALONE
1344int
1345main(void)
1346{
1347 int ret;
1348 xmlDocPtr instance;
1349 xmlSchematronParserCtxtPtr pctxt;
1350 xmlSchematronValidCtxtPtr vctxt;
1351 xmlSchematronPtr schema = NULL;
1352
1353 pctxt = xmlSchematronNewParserCtxt("tst.sct");
1354 if (pctxt == NULL) {
1355 fprintf(stderr, "failed to build schematron parser\n");
1356 } else {
1357 schema = xmlSchematronParse(pctxt);
1358 if (schema == NULL) {
1359 fprintf(stderr, "failed to compile schematron\n");
1360 }
1361 xmlSchematronFreeParserCtxt(pctxt);
1362 }
1363 instance = xmlReadFile("tst.sct", NULL,
1364 XML_PARSE_NOENT | XML_PARSE_NOCDATA);
1365 if (instance == NULL) {
1366 fprintf(stderr, "failed to parse instance\n");
1367 }
1368 if ((schema != NULL) && (instance != NULL)) {
1369 vctxt = xmlSchematronNewValidCtxt(schema);
1370 if (vctxt == NULL) {
1371 fprintf(stderr, "failed to build schematron validator\n");
1372 } else {
1373 ret = xmlSchematronValidateDoc(vctxt, instance);
1374 xmlSchematronFreeValidCtxt(vctxt);
1375 }
1376 }
1377 xmlSchematronFree(schema);
1378 xmlFreeDoc(instance);
1379
1380 xmlCleanupParser();
1381 xmlMemoryDump();
1382
1383 return (0);
1384}
1385#endif
1386#endif /* LIBXML_SCHEMATRON_ENABLED */