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