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