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