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