1 /*
2 * schematron.c : implementation of the Schematron schema validity checking
3 *
4 * See Copyright for the status of this software.
5 *
6 * Daniel Veillard <[email protected]>
7 */
8
9 /*
10 * TODO:
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
14 * by the spec.
15 * + export of results in SVRL
16 * + full parsing and coverage of the spec, conformance of the input to the
17 * spec
18 * + divergences between the draft and the ISO proposed standard :-(
19 * + hook and test include
20 * + try and compare with the XSLT version
21 */
22
23 #define IN_LIBXML
24 #include "libxml.h"
25
26 #ifdef LIBXML_SCHEMATRON_ENABLED
27
28 #include <stdlib.h>
29 #include <string.h>
30 #include <libxml/parser.h>
31 #include <libxml/tree.h>
32 #include <libxml/uri.h>
33 #include <libxml/xpath.h>
34 #include <libxml/xpathInternals.h>
35 #include <libxml/pattern.h>
36 #include <libxml/schematron.h>
37
38 #include "private/error.h"
39
40 #define SCHEMATRON_PARSE_OPTIONS XML_PARSE_NOENT
41
42 #define SCT_OLD_NS BAD_CAST "http://www.ascc.net/xml/schematron"
43
44 #define XML_SCHEMATRON_NS BAD_CAST "http://purl.oclc.org/dsdl/schematron"
45
46
47 static const xmlChar *xmlSchematronNs = XML_SCHEMATRON_NS;
48 static const xmlChar *xmlOldSchematronNs = SCT_OLD_NS;
49
50 #define IS_SCHEMATRON(node, elem) \
51 ((node != NULL) && (node->type == XML_ELEMENT_NODE ) && \
52 (node->ns != NULL) && \
53 (xmlStrEqual(node->name, (const xmlChar *) elem)) && \
54 ((xmlStrEqual(node->ns->href, xmlSchematronNs)) || \
55 (xmlStrEqual(node->ns->href, xmlOldSchematronNs))))
56
57 #define NEXT_SCHEMATRON(node) \
58 while (node != NULL) { \
59 if ((node->type == XML_ELEMENT_NODE ) && (node->ns != NULL) && \
60 ((xmlStrEqual(node->ns->href, xmlSchematronNs)) || \
61 (xmlStrEqual(node->ns->href, xmlOldSchematronNs)))) \
62 break; \
63 node = node->next; \
64 }
65
66 typedef enum {
67 XML_SCHEMATRON_ASSERT=1,
68 XML_SCHEMATRON_REPORT=2
69 } xmlSchematronTestType;
70
71 /**
72 * _xmlSchematronLet:
73 *
74 * A Schematron let variable
75 */
76 typedef struct _xmlSchematronLet xmlSchematronLet;
77 typedef xmlSchematronLet *xmlSchematronLetPtr;
78 struct _xmlSchematronLet {
79 xmlSchematronLetPtr next; /* the next let variable in the list */
80 xmlChar *name; /* the name of the variable */
81 xmlXPathCompExprPtr comp; /* the compiled expression */
82 };
83
84 /**
85 * _xmlSchematronTest:
86 *
87 * A Schematrons test, either an assert or a report
88 */
89 typedef struct _xmlSchematronTest xmlSchematronTest;
90 typedef xmlSchematronTest *xmlSchematronTestPtr;
91 struct _xmlSchematronTest {
92 xmlSchematronTestPtr next; /* the next test in the list */
93 xmlSchematronTestType type; /* the test type */
94 xmlNodePtr node; /* the node in the tree */
95 xmlChar *test; /* the expression to test */
96 xmlXPathCompExprPtr comp; /* the compiled expression */
97 xmlChar *report; /* the message to report */
98 };
99
100 /**
101 * _xmlSchematronRule:
102 *
103 * A Schematrons rule
104 */
105 typedef struct _xmlSchematronRule xmlSchematronRule;
106 typedef xmlSchematronRule *xmlSchematronRulePtr;
107 struct _xmlSchematronRule {
108 xmlSchematronRulePtr next; /* the next rule in the list */
109 xmlSchematronRulePtr patnext;/* the next rule in the pattern list */
110 xmlNodePtr node; /* the node in the tree */
111 xmlChar *context; /* the context evaluation rule */
112 xmlSchematronTestPtr tests; /* the list of tests */
113 xmlPatternPtr pattern; /* the compiled pattern associated */
114 xmlChar *report; /* the message to report */
115 xmlSchematronLetPtr lets; /* the list of let variables */
116 };
117
118 /**
119 * _xmlSchematronPattern:
120 *
121 * A Schematrons pattern
122 */
123 typedef struct _xmlSchematronPattern xmlSchematronPattern;
124 typedef xmlSchematronPattern *xmlSchematronPatternPtr;
125 struct _xmlSchematronPattern {
126 xmlSchematronPatternPtr next;/* the next pattern in the list */
127 xmlSchematronRulePtr rules; /* the list of rules */
128 xmlChar *name; /* the name of the pattern */
129 };
130
131 /**
132 * _xmlSchematron:
133 *
134 * A Schematrons definition
135 */
136 struct _xmlSchematron {
137 const xmlChar *name; /* schema name */
138 int preserve; /* was the document passed by the user */
139 xmlDocPtr doc; /* pointer to the parsed document */
140 int flags; /* specific to this schematron */
141
142 void *_private; /* unused by the library */
143 xmlDictPtr dict; /* the dictionary used internally */
144
145 const xmlChar *title; /* the title if any */
146
147 int nbNs; /* the number of namespaces */
148
149 int nbPattern; /* the number of patterns */
150 xmlSchematronPatternPtr patterns;/* the patterns found */
151 xmlSchematronRulePtr rules; /* the rules gathered */
152 int nbNamespaces; /* number of namespaces in the array */
153 int maxNamespaces; /* size of the array */
154 const xmlChar **namespaces; /* the array of namespaces */
155 };
156
157 /**
158 * xmlSchematronValidCtxt:
159 *
160 * A Schematrons validation context
161 */
162 struct _xmlSchematronValidCtxt {
163 int type;
164 int flags; /* an or of xmlSchematronValidOptions */
165
166 xmlDictPtr dict;
167 int nberrors;
168 int err;
169
170 xmlSchematronPtr schema;
171 xmlXPathContextPtr xctxt;
172
173 FILE *outputFile; /* if using XML_SCHEMATRON_OUT_FILE */
174 xmlBufferPtr outputBuffer; /* if using XML_SCHEMATRON_OUT_BUFFER */
175 #ifdef LIBXML_OUTPUT_ENABLED
176 xmlOutputWriteCallback iowrite; /* if using XML_SCHEMATRON_OUT_IO */
177 xmlOutputCloseCallback ioclose;
178 #endif
179 void *ioctx;
180
181 /* error reporting data */
182 void *userData; /* user specific data block */
183 xmlSchematronValidityErrorFunc error;/* the callback in case of errors */
184 xmlSchematronValidityWarningFunc warning;/* callback in case of warning */
185 xmlStructuredErrorFunc serror; /* the structured function */
186 };
187
188 struct _xmlSchematronParserCtxt {
189 int type;
190 const xmlChar *URL;
191 xmlDocPtr doc;
192 int preserve; /* Whether the doc should be freed */
193 const char *buffer;
194 int size;
195
196 xmlDictPtr dict; /* dictionary for interned string names */
197
198 int nberrors;
199 int err;
200 xmlXPathContextPtr xctxt; /* the XPath context used for compilation */
201 xmlSchematronPtr schema;
202
203 int nbNamespaces; /* number of namespaces in the array */
204 int maxNamespaces; /* size of the array */
205 const xmlChar **namespaces; /* the array of namespaces */
206
207 int nbIncludes; /* number of includes in the array */
208 int maxIncludes; /* size of the array */
209 xmlNodePtr *includes; /* the array of includes */
210
211 /* error reporting data */
212 void *userData; /* user specific data block */
213 xmlSchematronValidityErrorFunc error;/* the callback in case of errors */
214 xmlSchematronValidityWarningFunc warning;/* callback in case of warning */
215 xmlStructuredErrorFunc serror; /* the structured function */
216 };
217
218 #define XML_STRON_CTXT_PARSER 1
219 #define XML_STRON_CTXT_VALIDATOR 2
220
221 /************************************************************************
222 * *
223 * Error reporting *
224 * *
225 ************************************************************************/
226
227 /**
228 * xmlSchematronPErrMemory:
229 * @node: a context node
230 * @extra: extra information
231 *
232 * Handle an out of memory condition
233 */
234 static void
xmlSchematronPErrMemory(xmlSchematronParserCtxtPtr ctxt)235 xmlSchematronPErrMemory(xmlSchematronParserCtxtPtr ctxt)
236 {
237 if (ctxt != NULL)
238 ctxt->nberrors++;
239 xmlRaiseMemoryError(NULL, NULL, NULL, XML_FROM_SCHEMASP, NULL);
240 }
241
242 /**
243 * xmlSchematronPErr:
244 * @ctxt: the parsing context
245 * @node: the context node
246 * @error: the error code
247 * @msg: the error message
248 * @str1: extra data
249 * @str2: extra data
250 *
251 * Handle a parser error
252 */
253 static void LIBXML_ATTR_FORMAT(4,0)
xmlSchematronPErr(xmlSchematronParserCtxtPtr ctxt,xmlNodePtr node,int error,const char * msg,const xmlChar * str1,const xmlChar * str2)254 xmlSchematronPErr(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr node, int error,
255 const char *msg, const xmlChar * str1, const xmlChar * str2)
256 {
257 xmlGenericErrorFunc channel = NULL;
258 xmlStructuredErrorFunc schannel = NULL;
259 void *data = NULL;
260 int res;
261
262 if (ctxt != NULL) {
263 ctxt->nberrors++;
264 channel = ctxt->error;
265 data = ctxt->userData;
266 schannel = ctxt->serror;
267 }
268
269 if ((channel == NULL) && (schannel == NULL)) {
270 channel = xmlGenericError;
271 data = xmlGenericErrorContext;
272 }
273
274 res = xmlRaiseError(schannel, channel, data, ctxt, node,
275 XML_FROM_SCHEMASP, error, XML_ERR_ERROR, NULL, 0,
276 (const char *) str1, (const char *) str2, NULL, 0, 0,
277 msg, str1, str2);
278 if (res < 0)
279 xmlSchematronPErrMemory(ctxt);
280 }
281
282 /**
283 * xmlSchematronVTypeErrMemory:
284 * @node: a context node
285 * @extra: extra information
286 *
287 * Handle an out of memory condition
288 */
289 static void
xmlSchematronVErrMemory(xmlSchematronValidCtxtPtr ctxt)290 xmlSchematronVErrMemory(xmlSchematronValidCtxtPtr ctxt)
291 {
292 if (ctxt != NULL) {
293 ctxt->nberrors++;
294 ctxt->err = XML_SCHEMAV_INTERNAL;
295 }
296 xmlRaiseMemoryError(NULL, NULL, NULL, XML_FROM_SCHEMASV, NULL);
297 }
298
299 /**
300 * xmlSchematronVErr:
301 * @ctxt: the parsing context
302 * @node: the context node
303 * @error: the error code
304 * @msg: the error message
305 * @str1: extra data
306 * @str2: extra data
307 *
308 * Handle a validation error
309 */
310 static void LIBXML_ATTR_FORMAT(3,0)
xmlSchematronVErr(xmlSchematronValidCtxtPtr ctxt,int error,const char * msg,const xmlChar * str1)311 xmlSchematronVErr(xmlSchematronValidCtxtPtr ctxt, int error,
312 const char *msg, const xmlChar * str1)
313 {
314 xmlGenericErrorFunc channel = NULL;
315 xmlStructuredErrorFunc schannel = NULL;
316 void *data = NULL;
317 int res;
318
319 if (ctxt != NULL) {
320 ctxt->nberrors++;
321 channel = ctxt->error;
322 data = ctxt->userData;
323 schannel = ctxt->serror;
324 }
325
326 if ((channel == NULL) && (schannel == NULL)) {
327 channel = xmlGenericError;
328 data = xmlGenericErrorContext;
329 }
330
331 res = xmlRaiseError(schannel, channel, data, ctxt, NULL,
332 XML_FROM_SCHEMASV, error, XML_ERR_ERROR, NULL, 0,
333 (const char *) str1, NULL, NULL, 0, 0,
334 msg, str1);
335 if (res < 0)
336 xmlSchematronVErrMemory(ctxt);
337 }
338
339 /************************************************************************
340 * *
341 * Parsing and compilation of the Schematrontrons *
342 * *
343 ************************************************************************/
344
345 /**
346 * xmlSchematronAddTest:
347 * @ctxt: the schema parsing context
348 * @type: the type of test
349 * @rule: the parent rule
350 * @node: the node hosting the test
351 * @test: the associated test
352 * @report: the associated report string
353 *
354 * Add a test to a schematron
355 *
356 * Returns the new pointer or NULL in case of error
357 */
358 static xmlSchematronTestPtr
xmlSchematronAddTest(xmlSchematronParserCtxtPtr ctxt,xmlSchematronTestType type,xmlSchematronRulePtr rule,xmlNodePtr node,xmlChar * test,xmlChar * report)359 xmlSchematronAddTest(xmlSchematronParserCtxtPtr ctxt,
360 xmlSchematronTestType type,
361 xmlSchematronRulePtr rule,
362 xmlNodePtr node, xmlChar *test, xmlChar *report)
363 {
364 xmlSchematronTestPtr ret;
365 xmlXPathCompExprPtr comp;
366
367 if ((ctxt == NULL) || (rule == NULL) || (node == NULL) ||
368 (test == NULL))
369 return(NULL);
370
371 /*
372 * try first to compile the test expression
373 */
374 comp = xmlXPathCtxtCompile(ctxt->xctxt, test);
375 if (comp == NULL) {
376 xmlSchematronPErr(ctxt, node,
377 XML_SCHEMAP_NOROOT,
378 "Failed to compile test expression %s",
379 test, NULL);
380 return(NULL);
381 }
382
383 ret = (xmlSchematronTestPtr) xmlMalloc(sizeof(xmlSchematronTest));
384 if (ret == NULL) {
385 xmlSchematronPErrMemory(ctxt);
386 return (NULL);
387 }
388 memset(ret, 0, sizeof(xmlSchematronTest));
389 ret->type = type;
390 ret->node = node;
391 ret->test = test;
392 ret->comp = comp;
393 ret->report = report;
394 ret->next = NULL;
395 if (rule->tests == NULL) {
396 rule->tests = ret;
397 } else {
398 xmlSchematronTestPtr prev = rule->tests;
399
400 while (prev->next != NULL)
401 prev = prev->next;
402 prev->next = ret;
403 }
404 return (ret);
405 }
406
407 /**
408 * xmlSchematronFreeTests:
409 * @tests: a list of tests
410 *
411 * Free a list of tests.
412 */
413 static void
xmlSchematronFreeTests(xmlSchematronTestPtr tests)414 xmlSchematronFreeTests(xmlSchematronTestPtr tests) {
415 xmlSchematronTestPtr next;
416
417 while (tests != NULL) {
418 next = tests->next;
419 if (tests->test != NULL)
420 xmlFree(tests->test);
421 if (tests->comp != NULL)
422 xmlXPathFreeCompExpr(tests->comp);
423 if (tests->report != NULL)
424 xmlFree(tests->report);
425 xmlFree(tests);
426 tests = next;
427 }
428 }
429
430 /**
431 * xmlSchematronFreeLets:
432 * @lets: a list of let variables
433 *
434 * Free a list of let variables.
435 */
436 static void
xmlSchematronFreeLets(xmlSchematronLetPtr lets)437 xmlSchematronFreeLets(xmlSchematronLetPtr lets) {
438 xmlSchematronLetPtr next;
439
440 while (lets != NULL) {
441 next = lets->next;
442 if (lets->name != NULL)
443 xmlFree(lets->name);
444 if (lets->comp != NULL)
445 xmlXPathFreeCompExpr(lets->comp);
446 xmlFree(lets);
447 lets = next;
448 }
449 }
450
451 /**
452 * xmlSchematronAddRule:
453 * @ctxt: the schema parsing context
454 * @schema: a schema structure
455 * @node: the node hosting the rule
456 * @context: the associated context string
457 * @report: the associated report string
458 *
459 * Add a rule to a schematron
460 *
461 * Returns the new pointer or NULL in case of error
462 */
463 static xmlSchematronRulePtr
xmlSchematronAddRule(xmlSchematronParserCtxtPtr ctxt,xmlSchematronPtr schema,xmlSchematronPatternPtr pat,xmlNodePtr node,xmlChar * context,xmlChar * report)464 xmlSchematronAddRule(xmlSchematronParserCtxtPtr ctxt, xmlSchematronPtr schema,
465 xmlSchematronPatternPtr pat, xmlNodePtr node,
466 xmlChar *context, xmlChar *report)
467 {
468 xmlSchematronRulePtr ret;
469 xmlPatternPtr pattern;
470
471 if ((ctxt == NULL) || (schema == NULL) || (node == NULL) ||
472 (context == NULL))
473 return(NULL);
474
475 /*
476 * Try first to compile the pattern
477 */
478 pattern = xmlPatterncompile(context, ctxt->dict, XML_PATTERN_XPATH,
479 ctxt->namespaces);
480 if (pattern == NULL) {
481 xmlSchematronPErr(ctxt, node,
482 XML_SCHEMAP_NOROOT,
483 "Failed to compile context expression %s",
484 context, NULL);
485 }
486
487 ret = (xmlSchematronRulePtr) xmlMalloc(sizeof(xmlSchematronRule));
488 if (ret == NULL) {
489 xmlSchematronPErrMemory(ctxt);
490 return (NULL);
491 }
492 memset(ret, 0, sizeof(xmlSchematronRule));
493 ret->node = node;
494 ret->context = context;
495 ret->pattern = pattern;
496 ret->report = report;
497 ret->next = NULL;
498 ret->lets = NULL;
499 if (schema->rules == NULL) {
500 schema->rules = ret;
501 } else {
502 xmlSchematronRulePtr prev = schema->rules;
503
504 while (prev->next != NULL)
505 prev = prev->next;
506 prev->next = ret;
507 }
508 ret->patnext = NULL;
509 if (pat->rules == NULL) {
510 pat->rules = ret;
511 } else {
512 xmlSchematronRulePtr prev = pat->rules;
513
514 while (prev->patnext != NULL)
515 prev = prev->patnext;
516 prev->patnext = ret;
517 }
518 return (ret);
519 }
520
521 /**
522 * xmlSchematronFreeRules:
523 * @rules: a list of rules
524 *
525 * Free a list of rules.
526 */
527 static void
xmlSchematronFreeRules(xmlSchematronRulePtr rules)528 xmlSchematronFreeRules(xmlSchematronRulePtr rules) {
529 xmlSchematronRulePtr next;
530
531 while (rules != NULL) {
532 next = rules->next;
533 if (rules->tests)
534 xmlSchematronFreeTests(rules->tests);
535 if (rules->context != NULL)
536 xmlFree(rules->context);
537 if (rules->pattern)
538 xmlFreePattern(rules->pattern);
539 if (rules->report != NULL)
540 xmlFree(rules->report);
541 if (rules->lets != NULL)
542 xmlSchematronFreeLets(rules->lets);
543 xmlFree(rules);
544 rules = next;
545 }
546 }
547
548 /**
549 * xmlSchematronAddPattern:
550 * @ctxt: the schema parsing context
551 * @schema: a schema structure
552 * @node: the node hosting the pattern
553 * @id: the id or name of the pattern
554 *
555 * Add a pattern to a schematron
556 *
557 * Returns the new pointer or NULL in case of error
558 */
559 static xmlSchematronPatternPtr
xmlSchematronAddPattern(xmlSchematronParserCtxtPtr ctxt,xmlSchematronPtr schema,xmlNodePtr node,xmlChar * name)560 xmlSchematronAddPattern(xmlSchematronParserCtxtPtr ctxt,
561 xmlSchematronPtr schema, xmlNodePtr node, xmlChar *name)
562 {
563 xmlSchematronPatternPtr ret;
564
565 if ((ctxt == NULL) || (schema == NULL) || (node == NULL) || (name == NULL))
566 return(NULL);
567
568 ret = (xmlSchematronPatternPtr) xmlMalloc(sizeof(xmlSchematronPattern));
569 if (ret == NULL) {
570 xmlSchematronPErrMemory(ctxt);
571 return (NULL);
572 }
573 memset(ret, 0, sizeof(xmlSchematronPattern));
574 ret->name = name;
575 ret->next = NULL;
576 if (schema->patterns == NULL) {
577 schema->patterns = ret;
578 } else {
579 xmlSchematronPatternPtr prev = schema->patterns;
580
581 while (prev->next != NULL)
582 prev = prev->next;
583 prev->next = ret;
584 }
585 return (ret);
586 }
587
588 /**
589 * xmlSchematronFreePatterns:
590 * @patterns: a list of patterns
591 *
592 * Free a list of patterns.
593 */
594 static void
xmlSchematronFreePatterns(xmlSchematronPatternPtr patterns)595 xmlSchematronFreePatterns(xmlSchematronPatternPtr patterns) {
596 xmlSchematronPatternPtr next;
597
598 while (patterns != NULL) {
599 next = patterns->next;
600 if (patterns->name != NULL)
601 xmlFree(patterns->name);
602 xmlFree(patterns);
603 patterns = next;
604 }
605 }
606
607 /**
608 * xmlSchematronNewSchematron:
609 * @ctxt: a schema validation context
610 *
611 * Allocate a new Schematron structure.
612 *
613 * Returns the newly allocated structure or NULL in case or error
614 */
615 static xmlSchematronPtr
xmlSchematronNewSchematron(xmlSchematronParserCtxtPtr ctxt)616 xmlSchematronNewSchematron(xmlSchematronParserCtxtPtr ctxt)
617 {
618 xmlSchematronPtr ret;
619
620 ret = (xmlSchematronPtr) xmlMalloc(sizeof(xmlSchematron));
621 if (ret == NULL) {
622 xmlSchematronPErrMemory(ctxt);
623 return (NULL);
624 }
625 memset(ret, 0, sizeof(xmlSchematron));
626 ret->dict = ctxt->dict;
627 xmlDictReference(ret->dict);
628
629 return (ret);
630 }
631
632 /**
633 * xmlSchematronFree:
634 * @schema: a schema structure
635 *
636 * Deallocate a Schematron structure.
637 */
638 void
xmlSchematronFree(xmlSchematronPtr schema)639 xmlSchematronFree(xmlSchematronPtr schema)
640 {
641 if (schema == NULL)
642 return;
643
644 if ((schema->doc != NULL) && (!(schema->preserve)))
645 xmlFreeDoc(schema->doc);
646
647 if (schema->namespaces != NULL)
648 xmlFree((char **) schema->namespaces);
649
650 xmlSchematronFreeRules(schema->rules);
651 xmlSchematronFreePatterns(schema->patterns);
652 xmlDictFree(schema->dict);
653 xmlFree(schema);
654 }
655
656 /**
657 * xmlSchematronNewParserCtxt:
658 * @URL: the location of the schema
659 *
660 * Create an XML Schematrons parse context for that file/resource expected
661 * to contain an XML Schematrons file.
662 *
663 * Returns the parser context or NULL in case of error
664 */
665 xmlSchematronParserCtxtPtr
xmlSchematronNewParserCtxt(const char * URL)666 xmlSchematronNewParserCtxt(const char *URL)
667 {
668 xmlSchematronParserCtxtPtr ret;
669
670 if (URL == NULL)
671 return (NULL);
672
673 ret =
674 (xmlSchematronParserCtxtPtr)
675 xmlMalloc(sizeof(xmlSchematronParserCtxt));
676 if (ret == NULL) {
677 xmlSchematronPErrMemory(NULL);
678 return (NULL);
679 }
680 memset(ret, 0, sizeof(xmlSchematronParserCtxt));
681 ret->type = XML_STRON_CTXT_PARSER;
682 ret->dict = xmlDictCreate();
683 ret->URL = xmlDictLookup(ret->dict, (const xmlChar *) URL, -1);
684 ret->includes = NULL;
685 ret->xctxt = xmlXPathNewContext(NULL);
686 if (ret->xctxt == NULL) {
687 xmlSchematronPErrMemory(NULL);
688 xmlSchematronFreeParserCtxt(ret);
689 return (NULL);
690 }
691 ret->xctxt->flags = XML_XPATH_CHECKNS;
692 return (ret);
693 }
694
695 /**
696 * xmlSchematronNewMemParserCtxt:
697 * @buffer: a pointer to a char array containing the schemas
698 * @size: the size of the array
699 *
700 * Create an XML Schematrons parse context for that memory buffer expected
701 * to contain an XML Schematrons file.
702 *
703 * Returns the parser context or NULL in case of error
704 */
705 xmlSchematronParserCtxtPtr
xmlSchematronNewMemParserCtxt(const char * buffer,int size)706 xmlSchematronNewMemParserCtxt(const char *buffer, int size)
707 {
708 xmlSchematronParserCtxtPtr ret;
709
710 if ((buffer == NULL) || (size <= 0))
711 return (NULL);
712
713 ret =
714 (xmlSchematronParserCtxtPtr)
715 xmlMalloc(sizeof(xmlSchematronParserCtxt));
716 if (ret == NULL) {
717 xmlSchematronPErrMemory(NULL);
718 return (NULL);
719 }
720 memset(ret, 0, sizeof(xmlSchematronParserCtxt));
721 ret->buffer = buffer;
722 ret->size = size;
723 ret->dict = xmlDictCreate();
724 ret->xctxt = xmlXPathNewContext(NULL);
725 if (ret->xctxt == NULL) {
726 xmlSchematronPErrMemory(NULL);
727 xmlSchematronFreeParserCtxt(ret);
728 return (NULL);
729 }
730 return (ret);
731 }
732
733 /**
734 * xmlSchematronNewDocParserCtxt:
735 * @doc: a preparsed document tree
736 *
737 * Create an XML Schematrons parse context for that document.
738 * NB. The document may be modified during the parsing process.
739 *
740 * Returns the parser context or NULL in case of error
741 */
742 xmlSchematronParserCtxtPtr
xmlSchematronNewDocParserCtxt(xmlDocPtr doc)743 xmlSchematronNewDocParserCtxt(xmlDocPtr doc)
744 {
745 xmlSchematronParserCtxtPtr ret;
746
747 if (doc == NULL)
748 return (NULL);
749
750 ret =
751 (xmlSchematronParserCtxtPtr)
752 xmlMalloc(sizeof(xmlSchematronParserCtxt));
753 if (ret == NULL) {
754 xmlSchematronPErrMemory(NULL);
755 return (NULL);
756 }
757 memset(ret, 0, sizeof(xmlSchematronParserCtxt));
758 ret->doc = doc;
759 ret->dict = xmlDictCreate();
760 /* The application has responsibility for the document */
761 ret->preserve = 1;
762 ret->xctxt = xmlXPathNewContext(doc);
763 if (ret->xctxt == NULL) {
764 xmlSchematronPErrMemory(NULL);
765 xmlSchematronFreeParserCtxt(ret);
766 return (NULL);
767 }
768
769 return (ret);
770 }
771
772 /**
773 * xmlSchematronFreeParserCtxt:
774 * @ctxt: the schema parser context
775 *
776 * Free the resources associated to the schema parser context
777 */
778 void
xmlSchematronFreeParserCtxt(xmlSchematronParserCtxtPtr ctxt)779 xmlSchematronFreeParserCtxt(xmlSchematronParserCtxtPtr ctxt)
780 {
781 if (ctxt == NULL)
782 return;
783 if (ctxt->doc != NULL && !ctxt->preserve)
784 xmlFreeDoc(ctxt->doc);
785 if (ctxt->xctxt != NULL) {
786 xmlXPathFreeContext(ctxt->xctxt);
787 }
788 if (ctxt->namespaces != NULL)
789 xmlFree((char **) ctxt->namespaces);
790 xmlDictFree(ctxt->dict);
791 xmlFree(ctxt);
792 }
793
794 #if 0
795 /**
796 * xmlSchematronPushInclude:
797 * @ctxt: the schema parser context
798 * @doc: the included document
799 * @cur: the current include node
800 *
801 * Add an included document
802 */
803 static void
804 xmlSchematronPushInclude(xmlSchematronParserCtxtPtr ctxt,
805 xmlDocPtr doc, xmlNodePtr cur)
806 {
807 if (ctxt->includes == NULL) {
808 ctxt->maxIncludes = 10;
809 ctxt->includes = (xmlNodePtr *)
810 xmlMalloc(ctxt->maxIncludes * 2 * sizeof(xmlNodePtr));
811 if (ctxt->includes == NULL) {
812 xmlSchematronPErrMemory(NULL);
813 return;
814 }
815 ctxt->nbIncludes = 0;
816 } else if (ctxt->nbIncludes + 2 >= ctxt->maxIncludes) {
817 xmlNodePtr *tmp;
818
819 tmp = (xmlNodePtr *)
820 xmlRealloc(ctxt->includes, ctxt->maxIncludes * 4 *
821 sizeof(xmlNodePtr));
822 if (tmp == NULL) {
823 xmlSchematronPErrMemory(NULL);
824 return;
825 }
826 ctxt->includes = tmp;
827 ctxt->maxIncludes *= 2;
828 }
829 ctxt->includes[2 * ctxt->nbIncludes] = cur;
830 ctxt->includes[2 * ctxt->nbIncludes + 1] = (xmlNodePtr) doc;
831 ctxt->nbIncludes++;
832 }
833
834 /**
835 * xmlSchematronPopInclude:
836 * @ctxt: the schema parser context
837 *
838 * Pop an include level. The included document is being freed
839 *
840 * Returns the node immediately following the include or NULL if the
841 * include list was empty.
842 */
843 static xmlNodePtr
844 xmlSchematronPopInclude(xmlSchematronParserCtxtPtr ctxt)
845 {
846 xmlDocPtr doc;
847 xmlNodePtr ret;
848
849 if (ctxt->nbIncludes <= 0)
850 return(NULL);
851 ctxt->nbIncludes--;
852 doc = (xmlDocPtr) ctxt->includes[2 * ctxt->nbIncludes + 1];
853 ret = ctxt->includes[2 * ctxt->nbIncludes];
854 xmlFreeDoc(doc);
855 if (ret != NULL)
856 ret = ret->next;
857 if (ret == NULL)
858 return(xmlSchematronPopInclude(ctxt));
859 return(ret);
860 }
861 #endif
862
863 /**
864 * xmlSchematronAddNamespace:
865 * @ctxt: the schema parser context
866 * @prefix: the namespace prefix
867 * @ns: the namespace name
868 *
869 * Add a namespace definition in the context
870 */
871 static void
xmlSchematronAddNamespace(xmlSchematronParserCtxtPtr ctxt,const xmlChar * prefix,const xmlChar * ns)872 xmlSchematronAddNamespace(xmlSchematronParserCtxtPtr ctxt,
873 const xmlChar *prefix, const xmlChar *ns)
874 {
875 if (ctxt->namespaces == NULL) {
876 ctxt->maxNamespaces = 10;
877 ctxt->namespaces = (const xmlChar **)
878 xmlMalloc(ctxt->maxNamespaces * 2 * sizeof(const xmlChar *));
879 if (ctxt->namespaces == NULL) {
880 xmlSchematronPErrMemory(NULL);
881 return;
882 }
883 ctxt->nbNamespaces = 0;
884 } else if (ctxt->nbNamespaces + 2 >= ctxt->maxNamespaces) {
885 const xmlChar **tmp;
886
887 tmp = (const xmlChar **)
888 xmlRealloc((xmlChar **) ctxt->namespaces, ctxt->maxNamespaces * 4 *
889 sizeof(const xmlChar *));
890 if (tmp == NULL) {
891 xmlSchematronPErrMemory(NULL);
892 return;
893 }
894 ctxt->namespaces = tmp;
895 ctxt->maxNamespaces *= 2;
896 }
897 ctxt->namespaces[2 * ctxt->nbNamespaces] =
898 xmlDictLookup(ctxt->dict, ns, -1);
899 ctxt->namespaces[2 * ctxt->nbNamespaces + 1] =
900 xmlDictLookup(ctxt->dict, prefix, -1);
901 ctxt->nbNamespaces++;
902 ctxt->namespaces[2 * ctxt->nbNamespaces] = NULL;
903 ctxt->namespaces[2 * ctxt->nbNamespaces + 1] = NULL;
904
905 }
906
907 /**
908 * xmlSchematronParseTestReportMsg:
909 * @ctxt: the schema parser context
910 * @con: the assert or report node
911 *
912 * Format the message content of the assert or report test
913 */
914 static void
xmlSchematronParseTestReportMsg(xmlSchematronParserCtxtPtr ctxt,xmlNodePtr con)915 xmlSchematronParseTestReportMsg(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr con)
916 {
917 xmlNodePtr child;
918 xmlXPathCompExprPtr comp;
919
920 child = con->children;
921 while (child != NULL) {
922 if ((child->type == XML_TEXT_NODE) ||
923 (child->type == XML_CDATA_SECTION_NODE))
924 /* Do Nothing */
925 {}
926 else if (IS_SCHEMATRON(child, "name")) {
927 /* Do Nothing */
928 } else if (IS_SCHEMATRON(child, "value-of")) {
929 xmlChar *select;
930
931 select = xmlGetNoNsProp(child, BAD_CAST "select");
932
933 if (select == NULL) {
934 xmlSchematronPErr(ctxt, child,
935 XML_SCHEMAV_ATTRINVALID,
936 "value-of has no select attribute",
937 NULL, NULL);
938 } else {
939 /*
940 * try first to compile the test expression
941 */
942 comp = xmlXPathCtxtCompile(ctxt->xctxt, select);
943 if (comp == NULL) {
944 xmlSchematronPErr(ctxt, child,
945 XML_SCHEMAV_ATTRINVALID,
946 "Failed to compile select expression %s",
947 select, NULL);
948 }
949 xmlXPathFreeCompExpr(comp);
950 }
951 xmlFree(select);
952 }
953 child = child->next;
954 }
955 }
956
957 /**
958 * xmlSchematronParseRule:
959 * @ctxt: a schema validation context
960 * @rule: the rule node
961 *
962 * parse a rule element
963 */
964 static void
xmlSchematronParseRule(xmlSchematronParserCtxtPtr ctxt,xmlSchematronPatternPtr pattern,xmlNodePtr rule)965 xmlSchematronParseRule(xmlSchematronParserCtxtPtr ctxt,
966 xmlSchematronPatternPtr pattern,
967 xmlNodePtr rule)
968 {
969 xmlNodePtr cur;
970 int nbChecks = 0;
971 xmlChar *test;
972 xmlChar *context;
973 xmlChar *report;
974 xmlChar *name;
975 xmlChar *value;
976 xmlSchematronRulePtr ruleptr;
977 xmlSchematronTestPtr testptr;
978
979 if ((ctxt == NULL) || (rule == NULL)) return;
980
981 context = xmlGetNoNsProp(rule, BAD_CAST "context");
982 if (context == NULL) {
983 xmlSchematronPErr(ctxt, rule,
984 XML_SCHEMAP_NOROOT,
985 "rule has no context attribute",
986 NULL, NULL);
987 return;
988 } else if (context[0] == 0) {
989 xmlSchematronPErr(ctxt, rule,
990 XML_SCHEMAP_NOROOT,
991 "rule has an empty context attribute",
992 NULL, NULL);
993 xmlFree(context);
994 return;
995 } else {
996 ruleptr = xmlSchematronAddRule(ctxt, ctxt->schema, pattern,
997 rule, context, NULL);
998 if (ruleptr == NULL) {
999 xmlFree(context);
1000 return;
1001 }
1002 }
1003
1004 cur = rule->children;
1005 NEXT_SCHEMATRON(cur);
1006 while (cur != NULL) {
1007 if (IS_SCHEMATRON(cur, "let")) {
1008 xmlXPathCompExprPtr var_comp;
1009 xmlSchematronLetPtr let;
1010
1011 name = xmlGetNoNsProp(cur, BAD_CAST "name");
1012 if (name == NULL) {
1013 xmlSchematronPErr(ctxt, cur,
1014 XML_SCHEMAP_NOROOT,
1015 "let has no name attribute",
1016 NULL, NULL);
1017 return;
1018 } else if (name[0] == 0) {
1019 xmlSchematronPErr(ctxt, cur,
1020 XML_SCHEMAP_NOROOT,
1021 "let has an empty name attribute",
1022 NULL, NULL);
1023 xmlFree(name);
1024 return;
1025 }
1026 value = xmlGetNoNsProp(cur, BAD_CAST "value");
1027 if (value == NULL) {
1028 xmlSchematronPErr(ctxt, cur,
1029 XML_SCHEMAP_NOROOT,
1030 "let has no value attribute",
1031 NULL, NULL);
1032 return;
1033 } else if (value[0] == 0) {
1034 xmlSchematronPErr(ctxt, cur,
1035 XML_SCHEMAP_NOROOT,
1036 "let has an empty value attribute",
1037 NULL, NULL);
1038 xmlFree(value);
1039 return;
1040 }
1041
1042 var_comp = xmlXPathCtxtCompile(ctxt->xctxt, value);
1043 if (var_comp == NULL) {
1044 xmlSchematronPErr(ctxt, cur,
1045 XML_SCHEMAP_NOROOT,
1046 "Failed to compile let expression %s",
1047 value, NULL);
1048 return;
1049 }
1050
1051 let = (xmlSchematronLetPtr) xmlMalloc(sizeof(xmlSchematronLet));
1052 let->name = name;
1053 let->comp = var_comp;
1054 let->next = NULL;
1055
1056 /* add new let variable to the beginning of the list */
1057 if (ruleptr->lets != NULL) {
1058 let->next = ruleptr->lets;
1059 }
1060 ruleptr->lets = let;
1061
1062 xmlFree(value);
1063 } else if (IS_SCHEMATRON(cur, "assert")) {
1064 nbChecks++;
1065 test = xmlGetNoNsProp(cur, BAD_CAST "test");
1066 if (test == NULL) {
1067 xmlSchematronPErr(ctxt, cur,
1068 XML_SCHEMAP_NOROOT,
1069 "assert has no test attribute",
1070 NULL, NULL);
1071 } else if (test[0] == 0) {
1072 xmlSchematronPErr(ctxt, cur,
1073 XML_SCHEMAP_NOROOT,
1074 "assert has an empty test attribute",
1075 NULL, NULL);
1076 xmlFree(test);
1077 } else {
1078 xmlSchematronParseTestReportMsg(ctxt, cur);
1079 report = xmlNodeGetContent(cur);
1080
1081 testptr = xmlSchematronAddTest(ctxt, XML_SCHEMATRON_ASSERT,
1082 ruleptr, cur, test, report);
1083 if (testptr == NULL)
1084 xmlFree(test);
1085 }
1086 } else if (IS_SCHEMATRON(cur, "report")) {
1087 nbChecks++;
1088 test = xmlGetNoNsProp(cur, BAD_CAST "test");
1089 if (test == NULL) {
1090 xmlSchematronPErr(ctxt, cur,
1091 XML_SCHEMAP_NOROOT,
1092 "assert has no test attribute",
1093 NULL, NULL);
1094 } else if (test[0] == 0) {
1095 xmlSchematronPErr(ctxt, cur,
1096 XML_SCHEMAP_NOROOT,
1097 "assert has an empty test attribute",
1098 NULL, NULL);
1099 xmlFree(test);
1100 } else {
1101 xmlSchematronParseTestReportMsg(ctxt, cur);
1102 report = xmlNodeGetContent(cur);
1103
1104 testptr = xmlSchematronAddTest(ctxt, XML_SCHEMATRON_REPORT,
1105 ruleptr, cur, test, report);
1106 if (testptr == NULL)
1107 xmlFree(test);
1108 }
1109 } else {
1110 xmlSchematronPErr(ctxt, cur,
1111 XML_SCHEMAP_NOROOT,
1112 "Expecting an assert or a report element instead of %s",
1113 cur->name, NULL);
1114 }
1115 cur = cur->next;
1116 NEXT_SCHEMATRON(cur);
1117 }
1118 if (nbChecks == 0) {
1119 xmlSchematronPErr(ctxt, rule,
1120 XML_SCHEMAP_NOROOT,
1121 "rule has no assert nor report element", NULL, NULL);
1122 }
1123 }
1124
1125 /**
1126 * xmlSchematronParsePattern:
1127 * @ctxt: a schema validation context
1128 * @pat: the pattern node
1129 *
1130 * parse a pattern element
1131 */
1132 static void
xmlSchematronParsePattern(xmlSchematronParserCtxtPtr ctxt,xmlNodePtr pat)1133 xmlSchematronParsePattern(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr pat)
1134 {
1135 xmlNodePtr cur;
1136 xmlSchematronPatternPtr pattern;
1137 int nbRules = 0;
1138 xmlChar *id;
1139
1140 if ((ctxt == NULL) || (pat == NULL)) return;
1141
1142 id = xmlGetNoNsProp(pat, BAD_CAST "id");
1143 if (id == NULL) {
1144 id = xmlGetNoNsProp(pat, BAD_CAST "name");
1145 }
1146 pattern = xmlSchematronAddPattern(ctxt, ctxt->schema, pat, id);
1147 if (pattern == NULL) {
1148 if (id != NULL)
1149 xmlFree(id);
1150 return;
1151 }
1152 cur = pat->children;
1153 NEXT_SCHEMATRON(cur);
1154 while (cur != NULL) {
1155 if (IS_SCHEMATRON(cur, "rule")) {
1156 xmlSchematronParseRule(ctxt, pattern, cur);
1157 nbRules++;
1158 } else {
1159 xmlSchematronPErr(ctxt, cur,
1160 XML_SCHEMAP_NOROOT,
1161 "Expecting a rule element instead of %s", cur->name, NULL);
1162 }
1163 cur = cur->next;
1164 NEXT_SCHEMATRON(cur);
1165 }
1166 if (nbRules == 0) {
1167 xmlSchematronPErr(ctxt, pat,
1168 XML_SCHEMAP_NOROOT,
1169 "Pattern has no rule element", NULL, NULL);
1170 }
1171 }
1172
1173 #if 0
1174 /**
1175 * xmlSchematronLoadInclude:
1176 * @ctxt: a schema validation context
1177 * @cur: the include element
1178 *
1179 * Load the include document, Push the current pointer
1180 *
1181 * Returns the updated node pointer
1182 */
1183 static xmlNodePtr
1184 xmlSchematronLoadInclude(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr cur)
1185 {
1186 xmlNodePtr ret = NULL;
1187 xmlDocPtr doc = NULL;
1188 xmlChar *href = NULL;
1189 xmlChar *base = NULL;
1190 xmlChar *URI = NULL;
1191
1192 if ((ctxt == NULL) || (cur == NULL))
1193 return(NULL);
1194
1195 href = xmlGetNoNsProp(cur, BAD_CAST "href");
1196 if (href == NULL) {
1197 xmlSchematronPErr(ctxt, cur,
1198 XML_SCHEMAP_NOROOT,
1199 "Include has no href attribute", NULL, NULL);
1200 return(cur->next);
1201 }
1202
1203 /* do the URI base composition, load and find the root */
1204 base = xmlNodeGetBase(cur->doc, cur);
1205 URI = xmlBuildURI(href, base);
1206 doc = xmlReadFile((const char *) URI, NULL, SCHEMATRON_PARSE_OPTIONS);
1207 if (doc == NULL) {
1208 xmlSchematronPErr(ctxt, cur,
1209 XML_SCHEMAP_FAILED_LOAD,
1210 "could not load include '%s'.\n",
1211 URI, NULL);
1212 goto done;
1213 }
1214 ret = xmlDocGetRootElement(doc);
1215 if (ret == NULL) {
1216 xmlSchematronPErr(ctxt, cur,
1217 XML_SCHEMAP_FAILED_LOAD,
1218 "could not find root from include '%s'.\n",
1219 URI, NULL);
1220 goto done;
1221 }
1222
1223 /* Success, push the include for rollback on exit */
1224 xmlSchematronPushInclude(ctxt, doc, cur);
1225
1226 done:
1227 if (ret == NULL) {
1228 if (doc != NULL)
1229 xmlFreeDoc(doc);
1230 }
1231 xmlFree(href);
1232 if (base != NULL)
1233 xmlFree(base);
1234 if (URI != NULL)
1235 xmlFree(URI);
1236 return(ret);
1237 }
1238 #endif
1239
1240 /**
1241 * xmlSchematronParse:
1242 * @ctxt: a schema validation context
1243 *
1244 * parse a schema definition resource and build an internal
1245 * XML Schema structure which can be used to validate instances.
1246 *
1247 * Returns the internal XML Schematron structure built from the resource or
1248 * NULL in case of error
1249 */
1250 xmlSchematronPtr
xmlSchematronParse(xmlSchematronParserCtxtPtr ctxt)1251 xmlSchematronParse(xmlSchematronParserCtxtPtr ctxt)
1252 {
1253 xmlSchematronPtr ret = NULL;
1254 xmlDocPtr doc;
1255 xmlNodePtr root, cur;
1256 int preserve = 0;
1257
1258 if (ctxt == NULL)
1259 return (NULL);
1260
1261 ctxt->nberrors = 0;
1262
1263 /*
1264 * First step is to parse the input document into an DOM/Infoset
1265 */
1266 if (ctxt->URL != NULL) {
1267 doc = xmlReadFile((const char *) ctxt->URL, NULL,
1268 SCHEMATRON_PARSE_OPTIONS);
1269 if (doc == NULL) {
1270 xmlSchematronPErr(ctxt, NULL,
1271 XML_SCHEMAP_FAILED_LOAD,
1272 "xmlSchematronParse: could not load '%s'.\n",
1273 ctxt->URL, NULL);
1274 return (NULL);
1275 }
1276 ctxt->preserve = 0;
1277 } else if (ctxt->buffer != NULL) {
1278 doc = xmlReadMemory(ctxt->buffer, ctxt->size, NULL, NULL,
1279 SCHEMATRON_PARSE_OPTIONS);
1280 if (doc == NULL) {
1281 xmlSchematronPErr(ctxt, NULL,
1282 XML_SCHEMAP_FAILED_PARSE,
1283 "xmlSchematronParse: could not parse.\n",
1284 NULL, NULL);
1285 return (NULL);
1286 }
1287 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
1288 ctxt->URL = xmlDictLookup(ctxt->dict, BAD_CAST "in_memory_buffer", -1);
1289 ctxt->preserve = 0;
1290 } else if (ctxt->doc != NULL) {
1291 doc = ctxt->doc;
1292 preserve = 1;
1293 ctxt->preserve = 1;
1294 } else {
1295 xmlSchematronPErr(ctxt, NULL,
1296 XML_SCHEMAP_NOTHING_TO_PARSE,
1297 "xmlSchematronParse: could not parse.\n",
1298 NULL, NULL);
1299 return (NULL);
1300 }
1301
1302 /*
1303 * Then extract the root and Schematron parse it
1304 */
1305 root = xmlDocGetRootElement(doc);
1306 if (root == NULL) {
1307 xmlSchematronPErr(ctxt, (xmlNodePtr) doc,
1308 XML_SCHEMAP_NOROOT,
1309 "The schema has no document element.\n", NULL, NULL);
1310 if (!preserve) {
1311 xmlFreeDoc(doc);
1312 }
1313 return (NULL);
1314 }
1315
1316 if (!IS_SCHEMATRON(root, "schema")) {
1317 xmlSchematronPErr(ctxt, root,
1318 XML_SCHEMAP_NOROOT,
1319 "The XML document '%s' is not a XML schematron document",
1320 ctxt->URL, NULL);
1321 goto exit;
1322 }
1323 ret = xmlSchematronNewSchematron(ctxt);
1324 if (ret == NULL)
1325 goto exit;
1326 ctxt->schema = ret;
1327
1328 /*
1329 * scan the schema elements
1330 */
1331 cur = root->children;
1332 NEXT_SCHEMATRON(cur);
1333 if (IS_SCHEMATRON(cur, "title")) {
1334 xmlChar *title = xmlNodeGetContent(cur);
1335 if (title != NULL) {
1336 ret->title = xmlDictLookup(ret->dict, title, -1);
1337 xmlFree(title);
1338 }
1339 cur = cur->next;
1340 NEXT_SCHEMATRON(cur);
1341 }
1342 while (IS_SCHEMATRON(cur, "ns")) {
1343 xmlChar *prefix = xmlGetNoNsProp(cur, BAD_CAST "prefix");
1344 xmlChar *uri = xmlGetNoNsProp(cur, BAD_CAST "uri");
1345 if ((uri == NULL) || (uri[0] == 0)) {
1346 xmlSchematronPErr(ctxt, cur,
1347 XML_SCHEMAP_NOROOT,
1348 "ns element has no uri", NULL, NULL);
1349 }
1350 if ((prefix == NULL) || (prefix[0] == 0)) {
1351 xmlSchematronPErr(ctxt, cur,
1352 XML_SCHEMAP_NOROOT,
1353 "ns element has no prefix", NULL, NULL);
1354 }
1355 if ((prefix) && (uri)) {
1356 xmlXPathRegisterNs(ctxt->xctxt, prefix, uri);
1357 xmlSchematronAddNamespace(ctxt, prefix, uri);
1358 ret->nbNs++;
1359 }
1360 if (uri)
1361 xmlFree(uri);
1362 if (prefix)
1363 xmlFree(prefix);
1364 cur = cur->next;
1365 NEXT_SCHEMATRON(cur);
1366 }
1367 while (cur != NULL) {
1368 if (IS_SCHEMATRON(cur, "pattern")) {
1369 xmlSchematronParsePattern(ctxt, cur);
1370 ret->nbPattern++;
1371 } else {
1372 xmlSchematronPErr(ctxt, cur,
1373 XML_SCHEMAP_NOROOT,
1374 "Expecting a pattern element instead of %s", cur->name, NULL);
1375 }
1376 cur = cur->next;
1377 NEXT_SCHEMATRON(cur);
1378 }
1379 if (ret->nbPattern == 0) {
1380 xmlSchematronPErr(ctxt, root,
1381 XML_SCHEMAP_NOROOT,
1382 "The schematron document '%s' has no pattern",
1383 ctxt->URL, NULL);
1384 goto exit;
1385 }
1386 /* the original document must be kept for reporting */
1387 ret->doc = doc;
1388 if (preserve) {
1389 ret->preserve = 1;
1390 }
1391 preserve = 1;
1392
1393 exit:
1394 if (!preserve) {
1395 xmlFreeDoc(doc);
1396 }
1397 if (ret != NULL) {
1398 if (ctxt->nberrors != 0) {
1399 xmlSchematronFree(ret);
1400 ret = NULL;
1401 } else {
1402 ret->namespaces = ctxt->namespaces;
1403 ret->nbNamespaces = ctxt->nbNamespaces;
1404 ctxt->namespaces = NULL;
1405 }
1406 }
1407 return (ret);
1408 }
1409
1410 /************************************************************************
1411 * *
1412 * Schematrontron Reports handler *
1413 * *
1414 ************************************************************************/
1415
1416 static xmlNodePtr
xmlSchematronGetNode(xmlSchematronValidCtxtPtr ctxt,xmlNodePtr cur,const xmlChar * xpath)1417 xmlSchematronGetNode(xmlSchematronValidCtxtPtr ctxt,
1418 xmlNodePtr cur, const xmlChar *xpath) {
1419 xmlNodePtr node = NULL;
1420 xmlXPathObjectPtr ret;
1421
1422 if ((ctxt == NULL) || (cur == NULL) || (xpath == NULL))
1423 return(NULL);
1424
1425 ctxt->xctxt->doc = cur->doc;
1426 ctxt->xctxt->node = cur;
1427 ret = xmlXPathEval(xpath, ctxt->xctxt);
1428 if (ret == NULL)
1429 return(NULL);
1430
1431 if ((ret->type == XPATH_NODESET) &&
1432 (ret->nodesetval != NULL) && (ret->nodesetval->nodeNr > 0))
1433 node = ret->nodesetval->nodeTab[0];
1434
1435 xmlXPathFreeObject(ret);
1436 return(node);
1437 }
1438
1439 /**
1440 * xmlSchematronReportOutput:
1441 * @ctxt: the validation context
1442 * @cur: the current node tested
1443 * @msg: the message output
1444 *
1445 * Output part of the report to whatever channel the user selected
1446 */
1447 static void
xmlSchematronReportOutput(xmlSchematronValidCtxtPtr ctxt ATTRIBUTE_UNUSED,xmlNodePtr cur ATTRIBUTE_UNUSED,const char * msg)1448 xmlSchematronReportOutput(xmlSchematronValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
1449 xmlNodePtr cur ATTRIBUTE_UNUSED,
1450 const char *msg) {
1451 /* TODO */
1452 xmlPrintErrorMessage("%s", msg);
1453 }
1454
1455 /**
1456 * xmlSchematronFormatReport:
1457 * @ctxt: the validation context
1458 * @test: the test node
1459 * @cur: the current node tested
1460 *
1461 * Build the string being reported to the user.
1462 *
1463 * Returns a report string or NULL in case of error. The string needs
1464 * to be deallocated by the caller
1465 */
1466 static xmlChar *
xmlSchematronFormatReport(xmlSchematronValidCtxtPtr ctxt,xmlNodePtr test,xmlNodePtr cur)1467 xmlSchematronFormatReport(xmlSchematronValidCtxtPtr ctxt,
1468 xmlNodePtr test, xmlNodePtr cur) {
1469 xmlChar *ret = NULL;
1470 xmlNodePtr child, node;
1471 xmlXPathCompExprPtr comp;
1472
1473 if ((test == NULL) || (cur == NULL))
1474 return(ret);
1475
1476 child = test->children;
1477 while (child != NULL) {
1478 if ((child->type == XML_TEXT_NODE) ||
1479 (child->type == XML_CDATA_SECTION_NODE))
1480 ret = xmlStrcat(ret, child->content);
1481 else if (IS_SCHEMATRON(child, "name")) {
1482 xmlChar *path;
1483
1484 path = xmlGetNoNsProp(child, BAD_CAST "path");
1485
1486 node = cur;
1487 if (path != NULL) {
1488 node = xmlSchematronGetNode(ctxt, cur, path);
1489 if (node == NULL)
1490 node = cur;
1491 xmlFree(path);
1492 }
1493
1494 if ((node->ns == NULL) || (node->ns->prefix == NULL))
1495 ret = xmlStrcat(ret, node->name);
1496 else {
1497 ret = xmlStrcat(ret, node->ns->prefix);
1498 ret = xmlStrcat(ret, BAD_CAST ":");
1499 ret = xmlStrcat(ret, node->name);
1500 }
1501 } else if (IS_SCHEMATRON(child, "value-of")) {
1502 xmlChar *select;
1503 xmlXPathObjectPtr eval;
1504
1505 select = xmlGetNoNsProp(child, BAD_CAST "select");
1506 comp = xmlXPathCtxtCompile(ctxt->xctxt, select);
1507 eval = xmlXPathCompiledEval(comp, ctxt->xctxt);
1508
1509 switch (eval->type) {
1510 case XPATH_NODESET: {
1511 int indx;
1512 xmlChar *spacer = BAD_CAST " ";
1513
1514 if (eval->nodesetval) {
1515 for (indx = 0; indx < eval->nodesetval->nodeNr; indx++) {
1516 if (indx > 0)
1517 ret = xmlStrcat(ret, spacer);
1518 ret = xmlStrcat(ret, eval->nodesetval->nodeTab[indx]->name);
1519 }
1520 }
1521 break;
1522 }
1523 case XPATH_BOOLEAN: {
1524 const char *str = eval->boolval ? "True" : "False";
1525 ret = xmlStrcat(ret, BAD_CAST str);
1526 break;
1527 }
1528 case XPATH_NUMBER: {
1529 xmlChar *buf;
1530 int size;
1531
1532 size = snprintf(NULL, 0, "%0g", eval->floatval);
1533 buf = (xmlChar *) xmlMalloc(size + 1);
1534 if (buf != NULL) {
1535 snprintf((char *) buf, size + 1, "%0g", eval->floatval);
1536 ret = xmlStrcat(ret, buf);
1537 xmlFree(buf);
1538 }
1539 break;
1540 }
1541 case XPATH_STRING:
1542 ret = xmlStrcat(ret, eval->stringval);
1543 break;
1544 default:
1545 xmlSchematronVErr(ctxt, XML_ERR_INTERNAL_ERROR,
1546 "Unsupported XPATH Type\n", NULL);
1547 }
1548 xmlXPathFreeObject(eval);
1549 xmlXPathFreeCompExpr(comp);
1550 xmlFree(select);
1551 } else {
1552 child = child->next;
1553 continue;
1554 }
1555
1556 /*
1557 * remove superfluous \n
1558 */
1559 if (ret != NULL) {
1560 int len = xmlStrlen(ret);
1561 xmlChar c;
1562
1563 if (len > 0) {
1564 c = ret[len - 1];
1565 if ((c == ' ') || (c == '\n') || (c == '\r') || (c == '\t')) {
1566 while ((c == ' ') || (c == '\n') ||
1567 (c == '\r') || (c == '\t')) {
1568 len--;
1569 if (len == 0)
1570 break;
1571 c = ret[len - 1];
1572 }
1573 ret[len] = ' ';
1574 ret[len + 1] = 0;
1575 }
1576 }
1577 }
1578
1579 child = child->next;
1580 }
1581 return(ret);
1582 }
1583
1584 /**
1585 * xmlSchematronReportSuccess:
1586 * @ctxt: the validation context
1587 * @test: the compiled test
1588 * @cur: the current node tested
1589 * @success: boolean value for the result
1590 *
1591 * called from the validation engine when an assert or report test have
1592 * been done.
1593 */
1594 static void
xmlSchematronReportSuccess(xmlSchematronValidCtxtPtr ctxt,xmlSchematronTestPtr test,xmlNodePtr cur,xmlSchematronPatternPtr pattern,int success)1595 xmlSchematronReportSuccess(xmlSchematronValidCtxtPtr ctxt,
1596 xmlSchematronTestPtr test, xmlNodePtr cur, xmlSchematronPatternPtr pattern, int success) {
1597 if ((ctxt == NULL) || (cur == NULL) || (test == NULL))
1598 return;
1599 /* if quiet and not SVRL report only failures */
1600 if ((ctxt->flags & XML_SCHEMATRON_OUT_QUIET) &&
1601 ((ctxt->flags & XML_SCHEMATRON_OUT_XML) == 0) &&
1602 (test->type == XML_SCHEMATRON_REPORT))
1603 return;
1604 if (ctxt->flags & XML_SCHEMATRON_OUT_XML) {
1605 /* TODO */
1606 } else {
1607 xmlChar *path;
1608 char msg[1000];
1609 long line;
1610 const xmlChar *report = NULL;
1611
1612 if (((test->type == XML_SCHEMATRON_REPORT) && (!success)) ||
1613 ((test->type == XML_SCHEMATRON_ASSERT) && (success)))
1614 return;
1615 line = xmlGetLineNo(cur);
1616 path = xmlGetNodePath(cur);
1617 if (path == NULL)
1618 path = (xmlChar *) cur->name;
1619 #if 0
1620 if ((test->report != NULL) && (test->report[0] != 0))
1621 report = test->report;
1622 #endif
1623 if (test->node != NULL)
1624 report = xmlSchematronFormatReport(ctxt, test->node, cur);
1625 if (report == NULL) {
1626 if (test->type == XML_SCHEMATRON_ASSERT) {
1627 report = xmlStrdup((const xmlChar *) "node failed assert");
1628 } else {
1629 report = xmlStrdup((const xmlChar *) "node failed report");
1630 }
1631 }
1632 snprintf(msg, 999, "%s line %ld: %s\n", (const char *) path,
1633 line, (const char *) report);
1634
1635 if (ctxt->flags & XML_SCHEMATRON_OUT_ERROR) {
1636 xmlStructuredErrorFunc schannel;
1637 xmlGenericErrorFunc channel;
1638 void *data;
1639 int res;
1640
1641 schannel = ctxt->serror;
1642 channel = ctxt->error;
1643 data = ctxt->userData;
1644
1645 if ((channel == NULL) && (schannel == NULL)) {
1646 channel = xmlGenericError;
1647 data = xmlGenericErrorContext;
1648 }
1649
1650 res = xmlRaiseError(schannel, channel, data, NULL, cur,
1651 XML_FROM_SCHEMATRONV,
1652 (test->type == XML_SCHEMATRON_ASSERT) ?
1653 XML_SCHEMATRONV_ASSERT :
1654 XML_SCHEMATRONV_REPORT,
1655 XML_ERR_ERROR, NULL, line,
1656 (pattern == NULL) ?
1657 NULL :
1658 (const char *) pattern->name,
1659 (const char *) path, (const char *) report, 0, 0,
1660 "%s", msg);
1661 if (res < 0)
1662 xmlSchematronVErrMemory(ctxt);
1663 } else {
1664 xmlSchematronReportOutput(ctxt, cur, &msg[0]);
1665 }
1666
1667 xmlFree((char *) report);
1668
1669 if ((path != NULL) && (path != (xmlChar *) cur->name))
1670 xmlFree(path);
1671 }
1672 }
1673
1674 /**
1675 * xmlSchematronReportPattern:
1676 * @ctxt: the validation context
1677 * @pattern: the current pattern
1678 *
1679 * called from the validation engine when starting to check a pattern
1680 */
1681 static void
xmlSchematronReportPattern(xmlSchematronValidCtxtPtr ctxt,xmlSchematronPatternPtr pattern)1682 xmlSchematronReportPattern(xmlSchematronValidCtxtPtr ctxt,
1683 xmlSchematronPatternPtr pattern) {
1684 if ((ctxt == NULL) || (pattern == NULL))
1685 return;
1686 if ((ctxt->flags & XML_SCHEMATRON_OUT_QUIET) || (ctxt->flags & XML_SCHEMATRON_OUT_ERROR)) /* Error gives pattern name as part of error */
1687 return;
1688 if (ctxt->flags & XML_SCHEMATRON_OUT_XML) {
1689 /* TODO */
1690 } else {
1691 char msg[1000];
1692
1693 if (pattern->name == NULL)
1694 return;
1695 snprintf(msg, 999, "Pattern: %s\n", (const char *) pattern->name);
1696 xmlSchematronReportOutput(ctxt, NULL, &msg[0]);
1697 }
1698 }
1699
1700
1701 /************************************************************************
1702 * *
1703 * Validation against a Schematrontron *
1704 * *
1705 ************************************************************************/
1706
1707 /**
1708 * xmlSchematronSetValidStructuredErrors:
1709 * @ctxt: a Schematron validation context
1710 * @serror: the structured error function
1711 * @ctx: the functions context
1712 *
1713 * Set the structured error callback
1714 */
1715 void
xmlSchematronSetValidStructuredErrors(xmlSchematronValidCtxtPtr ctxt,xmlStructuredErrorFunc serror,void * ctx)1716 xmlSchematronSetValidStructuredErrors(xmlSchematronValidCtxtPtr ctxt,
1717 xmlStructuredErrorFunc serror, void *ctx)
1718 {
1719 if (ctxt == NULL)
1720 return;
1721 ctxt->serror = serror;
1722 ctxt->error = NULL;
1723 ctxt->warning = NULL;
1724 ctxt->userData = ctx;
1725 }
1726
1727 /**
1728 * xmlSchematronNewValidCtxt:
1729 * @schema: a precompiled XML Schematrons
1730 * @options: a set of xmlSchematronValidOptions
1731 *
1732 * Create an XML Schematrons validation context based on the given schema.
1733 *
1734 * Returns the validation context or NULL in case of error
1735 */
1736 xmlSchematronValidCtxtPtr
xmlSchematronNewValidCtxt(xmlSchematronPtr schema,int options)1737 xmlSchematronNewValidCtxt(xmlSchematronPtr schema, int options)
1738 {
1739 int i;
1740 xmlSchematronValidCtxtPtr ret;
1741
1742 ret = (xmlSchematronValidCtxtPtr) xmlMalloc(sizeof(xmlSchematronValidCtxt));
1743 if (ret == NULL) {
1744 xmlSchematronVErrMemory(NULL);
1745 return (NULL);
1746 }
1747 memset(ret, 0, sizeof(xmlSchematronValidCtxt));
1748 ret->type = XML_STRON_CTXT_VALIDATOR;
1749 ret->schema = schema;
1750 ret->xctxt = xmlXPathNewContext(NULL);
1751 ret->flags = options;
1752 if (ret->xctxt == NULL) {
1753 xmlSchematronPErrMemory(NULL);
1754 xmlSchematronFreeValidCtxt(ret);
1755 return (NULL);
1756 }
1757 for (i = 0;i < schema->nbNamespaces;i++) {
1758 if ((schema->namespaces[2 * i] == NULL) ||
1759 (schema->namespaces[2 * i + 1] == NULL))
1760 break;
1761 xmlXPathRegisterNs(ret->xctxt, schema->namespaces[2 * i + 1],
1762 schema->namespaces[2 * i]);
1763 }
1764 return (ret);
1765 }
1766
1767 /**
1768 * xmlSchematronFreeValidCtxt:
1769 * @ctxt: the schema validation context
1770 *
1771 * Free the resources associated to the schema validation context
1772 */
1773 void
xmlSchematronFreeValidCtxt(xmlSchematronValidCtxtPtr ctxt)1774 xmlSchematronFreeValidCtxt(xmlSchematronValidCtxtPtr ctxt)
1775 {
1776 if (ctxt == NULL)
1777 return;
1778 if (ctxt->xctxt != NULL)
1779 xmlXPathFreeContext(ctxt->xctxt);
1780 if (ctxt->dict != NULL)
1781 xmlDictFree(ctxt->dict);
1782 xmlFree(ctxt);
1783 }
1784
1785 static xmlNodePtr
xmlSchematronNextNode(xmlNodePtr cur)1786 xmlSchematronNextNode(xmlNodePtr cur) {
1787 if (cur->children != NULL) {
1788 /*
1789 * Do not descend on entities declarations
1790 */
1791 if (cur->children->type != XML_ENTITY_DECL) {
1792 cur = cur->children;
1793 /*
1794 * Skip DTDs
1795 */
1796 if (cur->type != XML_DTD_NODE)
1797 return(cur);
1798 }
1799 }
1800
1801 while (cur->next != NULL) {
1802 cur = cur->next;
1803 if ((cur->type != XML_ENTITY_DECL) &&
1804 (cur->type != XML_DTD_NODE))
1805 return(cur);
1806 }
1807
1808 do {
1809 cur = cur->parent;
1810 if (cur == NULL) break;
1811 if (cur->type == XML_DOCUMENT_NODE) return(NULL);
1812 if (cur->next != NULL) {
1813 cur = cur->next;
1814 return(cur);
1815 }
1816 } while (cur != NULL);
1817 return(cur);
1818 }
1819
1820 /**
1821 * xmlSchematronRunTest:
1822 * @ctxt: the schema validation context
1823 * @test: the current test
1824 * @instance: the document instance tree
1825 * @cur: the current node in the instance
1826 *
1827 * Validate a rule against a tree instance at a given position
1828 *
1829 * Returns 1 in case of success, 0 if error and -1 in case of internal error
1830 */
1831 static int
xmlSchematronRunTest(xmlSchematronValidCtxtPtr ctxt,xmlSchematronTestPtr test,xmlDocPtr instance,xmlNodePtr cur,xmlSchematronPatternPtr pattern)1832 xmlSchematronRunTest(xmlSchematronValidCtxtPtr ctxt,
1833 xmlSchematronTestPtr test, xmlDocPtr instance, xmlNodePtr cur, xmlSchematronPatternPtr pattern)
1834 {
1835 xmlXPathObjectPtr ret;
1836 int failed;
1837
1838 failed = 0;
1839 ctxt->xctxt->doc = instance;
1840 ctxt->xctxt->node = cur;
1841 ret = xmlXPathCompiledEval(test->comp, ctxt->xctxt);
1842 if (ret == NULL) {
1843 failed = 1;
1844 } else {
1845 switch (ret->type) {
1846 case XPATH_XSLT_TREE:
1847 case XPATH_NODESET:
1848 if ((ret->nodesetval == NULL) ||
1849 (ret->nodesetval->nodeNr == 0))
1850 failed = 1;
1851 break;
1852 case XPATH_BOOLEAN:
1853 failed = !ret->boolval;
1854 break;
1855 case XPATH_NUMBER:
1856 if ((xmlXPathIsNaN(ret->floatval)) ||
1857 (ret->floatval == 0.0))
1858 failed = 1;
1859 break;
1860 case XPATH_STRING:
1861 if ((ret->stringval == NULL) ||
1862 (ret->stringval[0] == 0))
1863 failed = 1;
1864 break;
1865 case XPATH_UNDEFINED:
1866 case XPATH_USERS:
1867 failed = 1;
1868 break;
1869 }
1870 xmlXPathFreeObject(ret);
1871 }
1872 if ((failed) && (test->type == XML_SCHEMATRON_ASSERT))
1873 ctxt->nberrors++;
1874 else if ((!failed) && (test->type == XML_SCHEMATRON_REPORT))
1875 ctxt->nberrors++;
1876
1877 xmlSchematronReportSuccess(ctxt, test, cur, pattern, !failed);
1878
1879 return(!failed);
1880 }
1881
1882 /**
1883 * xmlSchematronRegisterVariables:
1884 * @ctxt: the schema validation context
1885 * @let: the list of let variables
1886 * @instance: the document instance tree
1887 * @cur: the current node
1888 *
1889 * Registers a list of let variables to the current context of @cur
1890 *
1891 * Returns -1 in case of errors, otherwise 0
1892 */
1893 static int
xmlSchematronRegisterVariables(xmlSchematronValidCtxtPtr vctxt,xmlXPathContextPtr ctxt,xmlSchematronLetPtr let,xmlDocPtr instance,xmlNodePtr cur)1894 xmlSchematronRegisterVariables(xmlSchematronValidCtxtPtr vctxt,
1895 xmlXPathContextPtr ctxt,
1896 xmlSchematronLetPtr let,
1897 xmlDocPtr instance, xmlNodePtr cur)
1898 {
1899 xmlXPathObjectPtr let_eval;
1900
1901 ctxt->doc = instance;
1902 ctxt->node = cur;
1903 while (let != NULL) {
1904 let_eval = xmlXPathCompiledEval(let->comp, ctxt);
1905 if (let_eval == NULL) {
1906 xmlSchematronVErr(vctxt, XML_ERR_INTERNAL_ERROR,
1907 "Evaluation of compiled expression failed\n",
1908 NULL);
1909 return -1;
1910 }
1911 if(xmlXPathRegisterVariableNS(ctxt, let->name, NULL, let_eval)) {
1912 xmlSchematronVErr(vctxt, XML_ERR_INTERNAL_ERROR,
1913 "Registering a let variable failed\n", NULL);
1914 return -1;
1915 }
1916 let = let->next;
1917 }
1918 return 0;
1919 }
1920
1921 /**
1922 * xmlSchematronUnregisterVariables:
1923 * @ctxt: the schema validation context
1924 * @let: the list of let variables
1925 *
1926 * Unregisters a list of let variables from the context
1927 *
1928 * Returns -1 in case of errors, otherwise 0
1929 */
1930 static int
xmlSchematronUnregisterVariables(xmlSchematronValidCtxtPtr vctxt,xmlXPathContextPtr ctxt,xmlSchematronLetPtr let)1931 xmlSchematronUnregisterVariables(xmlSchematronValidCtxtPtr vctxt,
1932 xmlXPathContextPtr ctxt,
1933 xmlSchematronLetPtr let)
1934 {
1935 while (let != NULL) {
1936 if (xmlXPathRegisterVariableNS(ctxt, let->name, NULL, NULL)) {
1937 xmlSchematronVErr(vctxt, XML_ERR_INTERNAL_ERROR,
1938 "Unregistering a let variable failed\n", NULL);
1939 return -1;
1940 }
1941 let = let->next;
1942 }
1943 return 0;
1944 }
1945
1946 /**
1947 * xmlSchematronValidateDoc:
1948 * @ctxt: the schema validation context
1949 * @instance: the document instance tree
1950 *
1951 * Validate a tree instance against the schematron
1952 *
1953 * Returns 0 in case of success, -1 in case of internal error
1954 * and an error count otherwise.
1955 */
1956 int
xmlSchematronValidateDoc(xmlSchematronValidCtxtPtr ctxt,xmlDocPtr instance)1957 xmlSchematronValidateDoc(xmlSchematronValidCtxtPtr ctxt, xmlDocPtr instance)
1958 {
1959 xmlNodePtr cur, root;
1960 xmlSchematronPatternPtr pattern;
1961 xmlSchematronRulePtr rule;
1962 xmlSchematronTestPtr test;
1963
1964 if ((ctxt == NULL) || (ctxt->schema == NULL) ||
1965 (ctxt->schema->rules == NULL) || (instance == NULL))
1966 return(-1);
1967 ctxt->nberrors = 0;
1968 root = xmlDocGetRootElement(instance);
1969 if (root == NULL) {
1970 /* TODO */
1971 ctxt->nberrors++;
1972 return(1);
1973 }
1974 if ((ctxt->flags & XML_SCHEMATRON_OUT_QUIET) ||
1975 (ctxt->flags == 0)) {
1976 /*
1977 * we are just trying to assert the validity of the document,
1978 * speed primes over the output, run in a single pass
1979 */
1980 cur = root;
1981 while (cur != NULL) {
1982 rule = ctxt->schema->rules;
1983 while (rule != NULL) {
1984 if (xmlPatternMatch(rule->pattern, cur) == 1) {
1985 test = rule->tests;
1986
1987 if (xmlSchematronRegisterVariables(ctxt, ctxt->xctxt,
1988 rule->lets, instance, cur))
1989 return -1;
1990
1991 while (test != NULL) {
1992 xmlSchematronRunTest(ctxt, test, instance, cur, (xmlSchematronPatternPtr)rule->pattern);
1993 test = test->next;
1994 }
1995
1996 if (xmlSchematronUnregisterVariables(ctxt, ctxt->xctxt,
1997 rule->lets))
1998 return -1;
1999
2000 }
2001 rule = rule->next;
2002 }
2003
2004 cur = xmlSchematronNextNode(cur);
2005 }
2006 } else {
2007 /*
2008 * Process all contexts one at a time
2009 */
2010 pattern = ctxt->schema->patterns;
2011
2012 while (pattern != NULL) {
2013 xmlSchematronReportPattern(ctxt, pattern);
2014
2015 /*
2016 * TODO convert the pattern rule to a direct XPath and
2017 * compute directly instead of using the pattern matching
2018 * over the full document...
2019 * Check the exact semantic
2020 */
2021 cur = root;
2022 while (cur != NULL) {
2023 rule = pattern->rules;
2024 while (rule != NULL) {
2025 if (xmlPatternMatch(rule->pattern, cur) == 1) {
2026 test = rule->tests;
2027 xmlSchematronRegisterVariables(ctxt, ctxt->xctxt,
2028 rule->lets, instance, cur);
2029
2030 while (test != NULL) {
2031 xmlSchematronRunTest(ctxt, test, instance, cur, pattern);
2032 test = test->next;
2033 }
2034
2035 xmlSchematronUnregisterVariables(ctxt, ctxt->xctxt,
2036 rule->lets);
2037 }
2038 rule = rule->patnext;
2039 }
2040
2041 cur = xmlSchematronNextNode(cur);
2042 }
2043 pattern = pattern->next;
2044 }
2045 }
2046 return(ctxt->nberrors);
2047 }
2048
2049 #endif /* LIBXML_SCHEMATRON_ENABLED */
2050