• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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