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