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