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