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