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