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