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