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
44static const xmlChar *xmlSchematronNs = XML_SCHEMATRON_NS;
45static 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
73typedef 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 */
83typedef struct _xmlSchematronTest xmlSchematronTest;
84typedef xmlSchematronTest *xmlSchematronTestPtr;
85struct _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 */
99typedef struct _xmlSchematronRule xmlSchematronRule;
100typedef xmlSchematronRule *xmlSchematronRulePtr;
101struct _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 */
116typedef struct _xmlSchematronPattern xmlSchematronPattern;
117typedef xmlSchematronPattern *xmlSchematronPatternPtr;
118struct _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 */
129struct _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 */
155struct _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
179struct _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 */
225static void
226xmlSchematronPErrMemory(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 */
246static void
247xmlSchematronPErr(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 */
273static void
274xmlSchematronVErrMemory(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 */
304static xmlSchematronTestPtr
305xmlSchematronAddTest(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 */
359static void
360xmlSchematronFreeTests(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 */
388static xmlSchematronRulePtr
389xmlSchematronAddRule(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 */
451static void
452xmlSchematronFreeRules(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 */
481static xmlSchematronPatternPtr
482xmlSchematronAddPattern(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 */
516static void
517xmlSchematronFreePatterns(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 */
537static xmlSchematronPtr
538xmlSchematronNewSchematron(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 */
560void
561xmlSchematronFree(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 */
587xmlSchematronParserCtxtPtr
588xmlSchematronNewParserCtxt(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 */
629xmlSchematronParserCtxtPtr
630xmlSchematronNewMemParserCtxt(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 */
668xmlSchematronParserCtxtPtr
669xmlSchematronNewDocParserCtxt(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 */
706void
707xmlSchematronFreeParserCtxt(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 */
731static void
732xmlSchematronPushInclude(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 */
773static xmlNodePtr
774xmlSchematronPopInclude(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 */
801static void
802xmlSchematronAddNamespace(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 */
846static void
847xmlSchematronParseRule(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 */
956static void
957xmlSchematronParsePattern(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 */
1007static xmlNodePtr
1008xmlSchematronLoadInclude(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
1050done:
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 */
1074xmlSchematronPtr
1075xmlSchematronParse(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
1217exit:
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
1240static xmlNodePtr
1241xmlSchematronGetNode(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 */
1271static void
1272xmlSchematronReportOutput(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 */
1290static xmlChar *
1291xmlSchematronFormatReport(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 */
1367static void
1368xmlSchematronReportSuccess(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 */
1447static void
1448xmlSchematronReportPattern(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 */
1481void
1482xmlSchematronSetValidStructuredErrors(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 */
1502xmlSchematronValidCtxtPtr
1503xmlSchematronNewValidCtxt(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 */
1541void
1542xmlSchematronFreeValidCtxt(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
1553static xmlNodePtr
1554xmlSchematronNextNode(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 */
1599static int
1600xmlSchematronRunTest(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 */
1663int
1664xmlSchematronValidateDoc(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
1741int
1742main(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