• 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 <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         continue;
955     }
956 }
957 
958 /**
959  * xmlSchematronParseRule:
960  * @ctxt:  a schema validation context
961  * @rule:  the rule node
962  *
963  * parse a rule element
964  */
965 static void
xmlSchematronParseRule(xmlSchematronParserCtxtPtr ctxt,xmlSchematronPatternPtr pattern,xmlNodePtr rule)966 xmlSchematronParseRule(xmlSchematronParserCtxtPtr ctxt,
967                        xmlSchematronPatternPtr pattern,
968                        xmlNodePtr rule)
969 {
970     xmlNodePtr cur;
971     int nbChecks = 0;
972     xmlChar *test;
973     xmlChar *context;
974     xmlChar *report;
975     xmlChar *name;
976     xmlChar *value;
977     xmlSchematronRulePtr ruleptr;
978     xmlSchematronTestPtr testptr;
979 
980     if ((ctxt == NULL) || (rule == NULL)) return;
981 
982     context = xmlGetNoNsProp(rule, BAD_CAST "context");
983     if (context == NULL) {
984         xmlSchematronPErr(ctxt, rule,
985             XML_SCHEMAP_NOROOT,
986             "rule has no context attribute",
987             NULL, NULL);
988         return;
989     } else if (context[0] == 0) {
990         xmlSchematronPErr(ctxt, rule,
991             XML_SCHEMAP_NOROOT,
992             "rule has an empty context attribute",
993             NULL, NULL);
994         xmlFree(context);
995         return;
996     } else {
997         ruleptr = xmlSchematronAddRule(ctxt, ctxt->schema, pattern,
998                                        rule, context, NULL);
999         if (ruleptr == NULL) {
1000             xmlFree(context);
1001             return;
1002         }
1003     }
1004 
1005     cur = rule->children;
1006     NEXT_SCHEMATRON(cur);
1007     while (cur != NULL) {
1008         if (IS_SCHEMATRON(cur, "let")) {
1009             xmlXPathCompExprPtr var_comp;
1010             xmlSchematronLetPtr let;
1011 
1012             name = xmlGetNoNsProp(cur, BAD_CAST "name");
1013             if (name == NULL) {
1014                 xmlSchematronPErr(ctxt, cur,
1015                                   XML_SCHEMAP_NOROOT,
1016                                   "let has no name attribute",
1017                                   NULL, NULL);
1018                 return;
1019             } else if (name[0] == 0) {
1020                 xmlSchematronPErr(ctxt, cur,
1021                                   XML_SCHEMAP_NOROOT,
1022                                   "let has an empty name attribute",
1023                                   NULL, NULL);
1024                 xmlFree(name);
1025                 return;
1026             }
1027             value = xmlGetNoNsProp(cur, BAD_CAST "value");
1028             if (value == NULL) {
1029                 xmlSchematronPErr(ctxt, cur,
1030                                   XML_SCHEMAP_NOROOT,
1031                                   "let has no value attribute",
1032                                   NULL, NULL);
1033                 return;
1034             } else if (value[0] == 0) {
1035                 xmlSchematronPErr(ctxt, cur,
1036                                   XML_SCHEMAP_NOROOT,
1037                                   "let has an empty value attribute",
1038                                   NULL, NULL);
1039                 xmlFree(value);
1040                 return;
1041             }
1042 
1043             var_comp = xmlXPathCtxtCompile(ctxt->xctxt, value);
1044             if (var_comp == NULL) {
1045                 xmlSchematronPErr(ctxt, cur,
1046                                   XML_SCHEMAP_NOROOT,
1047                                   "Failed to compile let expression %s",
1048                                   value, NULL);
1049                 return;
1050             }
1051 
1052             let = (xmlSchematronLetPtr) malloc(sizeof(xmlSchematronLet));
1053             let->name = name;
1054             let->comp = var_comp;
1055             let->next = NULL;
1056 
1057             /* add new let variable to the beginning of the list */
1058             if (ruleptr->lets != NULL) {
1059                 let->next = ruleptr->lets;
1060             }
1061             ruleptr->lets = let;
1062 
1063             xmlFree(value);
1064         } else if (IS_SCHEMATRON(cur, "assert")) {
1065             nbChecks++;
1066             test = xmlGetNoNsProp(cur, BAD_CAST "test");
1067             if (test == NULL) {
1068                 xmlSchematronPErr(ctxt, cur,
1069                     XML_SCHEMAP_NOROOT,
1070                     "assert has no test attribute",
1071                     NULL, NULL);
1072             } else if (test[0] == 0) {
1073                 xmlSchematronPErr(ctxt, cur,
1074                     XML_SCHEMAP_NOROOT,
1075                     "assert has an empty test attribute",
1076                     NULL, NULL);
1077                 xmlFree(test);
1078             } else {
1079                 xmlSchematronParseTestReportMsg(ctxt, cur);
1080                 report = xmlNodeGetContent(cur);
1081 
1082                 testptr = xmlSchematronAddTest(ctxt, XML_SCHEMATRON_ASSERT,
1083                                                ruleptr, cur, test, report);
1084                 if (testptr == NULL)
1085                     xmlFree(test);
1086             }
1087         } else if (IS_SCHEMATRON(cur, "report")) {
1088             nbChecks++;
1089             test = xmlGetNoNsProp(cur, BAD_CAST "test");
1090             if (test == NULL) {
1091                 xmlSchematronPErr(ctxt, cur,
1092                     XML_SCHEMAP_NOROOT,
1093                     "assert has no test attribute",
1094                     NULL, NULL);
1095             } else if (test[0] == 0) {
1096                 xmlSchematronPErr(ctxt, cur,
1097                     XML_SCHEMAP_NOROOT,
1098                     "assert has an empty test attribute",
1099                     NULL, NULL);
1100                 xmlFree(test);
1101             } else {
1102                 xmlSchematronParseTestReportMsg(ctxt, cur);
1103                 report = xmlNodeGetContent(cur);
1104 
1105                 testptr = xmlSchematronAddTest(ctxt, XML_SCHEMATRON_REPORT,
1106                                                ruleptr, cur, test, report);
1107                 if (testptr == NULL)
1108                     xmlFree(test);
1109             }
1110         } else {
1111             xmlSchematronPErr(ctxt, cur,
1112                 XML_SCHEMAP_NOROOT,
1113                 "Expecting an assert or a report element instead of %s",
1114                 cur->name, NULL);
1115         }
1116         cur = cur->next;
1117         NEXT_SCHEMATRON(cur);
1118     }
1119     if (nbChecks == 0) {
1120         xmlSchematronPErr(ctxt, rule,
1121             XML_SCHEMAP_NOROOT,
1122             "rule has no assert nor report element", NULL, NULL);
1123     }
1124 }
1125 
1126 /**
1127  * xmlSchematronParsePattern:
1128  * @ctxt:  a schema validation context
1129  * @pat:  the pattern node
1130  *
1131  * parse a pattern element
1132  */
1133 static void
xmlSchematronParsePattern(xmlSchematronParserCtxtPtr ctxt,xmlNodePtr pat)1134 xmlSchematronParsePattern(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr pat)
1135 {
1136     xmlNodePtr cur;
1137     xmlSchematronPatternPtr pattern;
1138     int nbRules = 0;
1139     xmlChar *id;
1140 
1141     if ((ctxt == NULL) || (pat == NULL)) return;
1142 
1143     id = xmlGetNoNsProp(pat, BAD_CAST "id");
1144     if (id == NULL) {
1145         id = xmlGetNoNsProp(pat, BAD_CAST "name");
1146     }
1147     pattern = xmlSchematronAddPattern(ctxt, ctxt->schema, pat, id);
1148     if (pattern == NULL) {
1149         if (id != NULL)
1150             xmlFree(id);
1151         return;
1152     }
1153     cur = pat->children;
1154     NEXT_SCHEMATRON(cur);
1155     while (cur != NULL) {
1156         if (IS_SCHEMATRON(cur, "rule")) {
1157             xmlSchematronParseRule(ctxt, pattern, cur);
1158             nbRules++;
1159         } else {
1160             xmlSchematronPErr(ctxt, cur,
1161                 XML_SCHEMAP_NOROOT,
1162                 "Expecting a rule element instead of %s", cur->name, NULL);
1163         }
1164         cur = cur->next;
1165         NEXT_SCHEMATRON(cur);
1166     }
1167     if (nbRules == 0) {
1168         xmlSchematronPErr(ctxt, pat,
1169             XML_SCHEMAP_NOROOT,
1170             "Pattern has no rule element", NULL, NULL);
1171     }
1172 }
1173 
1174 #if 0
1175 /**
1176  * xmlSchematronLoadInclude:
1177  * @ctxt:  a schema validation context
1178  * @cur:  the include element
1179  *
1180  * Load the include document, Push the current pointer
1181  *
1182  * Returns the updated node pointer
1183  */
1184 static xmlNodePtr
1185 xmlSchematronLoadInclude(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr cur)
1186 {
1187     xmlNodePtr ret = NULL;
1188     xmlDocPtr doc = NULL;
1189     xmlChar *href = NULL;
1190     xmlChar *base = NULL;
1191     xmlChar *URI = NULL;
1192 
1193     if ((ctxt == NULL) || (cur == NULL))
1194         return(NULL);
1195 
1196     href = xmlGetNoNsProp(cur, BAD_CAST "href");
1197     if (href == NULL) {
1198         xmlSchematronPErr(ctxt, cur,
1199             XML_SCHEMAP_NOROOT,
1200             "Include has no href attribute", NULL, NULL);
1201         return(cur->next);
1202     }
1203 
1204     /* do the URI base composition, load and find the root */
1205     base = xmlNodeGetBase(cur->doc, cur);
1206     URI = xmlBuildURI(href, base);
1207     doc = xmlReadFile((const char *) URI, NULL, SCHEMATRON_PARSE_OPTIONS);
1208     if (doc == NULL) {
1209         xmlSchematronPErr(ctxt, cur,
1210                       XML_SCHEMAP_FAILED_LOAD,
1211                       "could not load include '%s'.\n",
1212                       URI, NULL);
1213         goto done;
1214     }
1215     ret = xmlDocGetRootElement(doc);
1216     if (ret == NULL) {
1217         xmlSchematronPErr(ctxt, cur,
1218                       XML_SCHEMAP_FAILED_LOAD,
1219                       "could not find root from include '%s'.\n",
1220                       URI, NULL);
1221         goto done;
1222     }
1223 
1224     /* Success, push the include for rollback on exit */
1225     xmlSchematronPushInclude(ctxt, doc, cur);
1226 
1227 done:
1228     if (ret == NULL) {
1229         if (doc != NULL)
1230             xmlFreeDoc(doc);
1231     }
1232     xmlFree(href);
1233     if (base != NULL)
1234         xmlFree(base);
1235     if (URI != NULL)
1236         xmlFree(URI);
1237     return(ret);
1238 }
1239 #endif
1240 
1241 /**
1242  * xmlSchematronParse:
1243  * @ctxt:  a schema validation context
1244  *
1245  * parse a schema definition resource and build an internal
1246  * XML Schema structure which can be used to validate instances.
1247  *
1248  * Returns the internal XML Schematron structure built from the resource or
1249  *         NULL in case of error
1250  */
1251 xmlSchematronPtr
xmlSchematronParse(xmlSchematronParserCtxtPtr ctxt)1252 xmlSchematronParse(xmlSchematronParserCtxtPtr ctxt)
1253 {
1254     xmlSchematronPtr ret = NULL;
1255     xmlDocPtr doc;
1256     xmlNodePtr root, cur;
1257     int preserve = 0;
1258 
1259     if (ctxt == NULL)
1260         return (NULL);
1261 
1262     ctxt->nberrors = 0;
1263 
1264     /*
1265      * First step is to parse the input document into an DOM/Infoset
1266      */
1267     if (ctxt->URL != NULL) {
1268         doc = xmlReadFile((const char *) ctxt->URL, NULL,
1269                           SCHEMATRON_PARSE_OPTIONS);
1270         if (doc == NULL) {
1271             xmlSchematronPErr(ctxt, NULL,
1272                           XML_SCHEMAP_FAILED_LOAD,
1273                           "xmlSchematronParse: could not load '%s'.\n",
1274                           ctxt->URL, NULL);
1275             return (NULL);
1276         }
1277         ctxt->preserve = 0;
1278     } else if (ctxt->buffer != NULL) {
1279         doc = xmlReadMemory(ctxt->buffer, ctxt->size, NULL, NULL,
1280                             SCHEMATRON_PARSE_OPTIONS);
1281         if (doc == NULL) {
1282             xmlSchematronPErr(ctxt, NULL,
1283                           XML_SCHEMAP_FAILED_PARSE,
1284                           "xmlSchematronParse: could not parse.\n",
1285                           NULL, NULL);
1286             return (NULL);
1287         }
1288         doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
1289         ctxt->URL = xmlDictLookup(ctxt->dict, BAD_CAST "in_memory_buffer", -1);
1290         ctxt->preserve = 0;
1291     } else if (ctxt->doc != NULL) {
1292         doc = ctxt->doc;
1293         preserve = 1;
1294         ctxt->preserve = 1;
1295     } else {
1296         xmlSchematronPErr(ctxt, NULL,
1297                       XML_SCHEMAP_NOTHING_TO_PARSE,
1298                       "xmlSchematronParse: could not parse.\n",
1299                       NULL, NULL);
1300         return (NULL);
1301     }
1302 
1303     /*
1304      * Then extract the root and Schematron parse it
1305      */
1306     root = xmlDocGetRootElement(doc);
1307     if (root == NULL) {
1308         xmlSchematronPErr(ctxt, (xmlNodePtr) doc,
1309                       XML_SCHEMAP_NOROOT,
1310                       "The schema has no document element.\n", NULL, NULL);
1311         if (!preserve) {
1312             xmlFreeDoc(doc);
1313         }
1314         return (NULL);
1315     }
1316 
1317     if (!IS_SCHEMATRON(root, "schema")) {
1318         xmlSchematronPErr(ctxt, root,
1319             XML_SCHEMAP_NOROOT,
1320             "The XML document '%s' is not a XML schematron document",
1321             ctxt->URL, NULL);
1322         goto exit;
1323     }
1324     ret = xmlSchematronNewSchematron(ctxt);
1325     if (ret == NULL)
1326         goto exit;
1327     ctxt->schema = ret;
1328 
1329     /*
1330      * scan the schema elements
1331      */
1332     cur = root->children;
1333     NEXT_SCHEMATRON(cur);
1334     if (IS_SCHEMATRON(cur, "title")) {
1335         xmlChar *title = xmlNodeGetContent(cur);
1336         if (title != NULL) {
1337             ret->title = xmlDictLookup(ret->dict, title, -1);
1338             xmlFree(title);
1339         }
1340         cur = cur->next;
1341         NEXT_SCHEMATRON(cur);
1342     }
1343     while (IS_SCHEMATRON(cur, "ns")) {
1344         xmlChar *prefix = xmlGetNoNsProp(cur, BAD_CAST "prefix");
1345         xmlChar *uri = xmlGetNoNsProp(cur, BAD_CAST "uri");
1346         if ((uri == NULL) || (uri[0] == 0)) {
1347             xmlSchematronPErr(ctxt, cur,
1348                 XML_SCHEMAP_NOROOT,
1349                 "ns element has no uri", NULL, NULL);
1350         }
1351         if ((prefix == NULL) || (prefix[0] == 0)) {
1352             xmlSchematronPErr(ctxt, cur,
1353                 XML_SCHEMAP_NOROOT,
1354                 "ns element has no prefix", NULL, NULL);
1355         }
1356         if ((prefix) && (uri)) {
1357             xmlXPathRegisterNs(ctxt->xctxt, prefix, uri);
1358             xmlSchematronAddNamespace(ctxt, prefix, uri);
1359             ret->nbNs++;
1360         }
1361         if (uri)
1362             xmlFree(uri);
1363         if (prefix)
1364             xmlFree(prefix);
1365         cur = cur->next;
1366         NEXT_SCHEMATRON(cur);
1367     }
1368     while (cur != NULL) {
1369         if (IS_SCHEMATRON(cur, "pattern")) {
1370             xmlSchematronParsePattern(ctxt, cur);
1371             ret->nbPattern++;
1372         } else {
1373             xmlSchematronPErr(ctxt, cur,
1374                 XML_SCHEMAP_NOROOT,
1375                 "Expecting a pattern element instead of %s", cur->name, NULL);
1376         }
1377         cur = cur->next;
1378         NEXT_SCHEMATRON(cur);
1379     }
1380     if (ret->nbPattern == 0) {
1381         xmlSchematronPErr(ctxt, root,
1382             XML_SCHEMAP_NOROOT,
1383             "The schematron document '%s' has no pattern",
1384             ctxt->URL, NULL);
1385         goto exit;
1386     }
1387     /* the original document must be kept for reporting */
1388     ret->doc = doc;
1389     if (preserve) {
1390             ret->preserve = 1;
1391     }
1392     preserve = 1;
1393 
1394 exit:
1395     if (!preserve) {
1396         xmlFreeDoc(doc);
1397     }
1398     if (ret != NULL) {
1399         if (ctxt->nberrors != 0) {
1400             xmlSchematronFree(ret);
1401             ret = NULL;
1402         } else {
1403             ret->namespaces = ctxt->namespaces;
1404             ret->nbNamespaces = ctxt->nbNamespaces;
1405             ctxt->namespaces = NULL;
1406         }
1407     }
1408     return (ret);
1409 }
1410 
1411 /************************************************************************
1412  *                                                                      *
1413  *              Schematrontron Reports handler                          *
1414  *                                                                      *
1415  ************************************************************************/
1416 
1417 static xmlNodePtr
xmlSchematronGetNode(xmlSchematronValidCtxtPtr ctxt,xmlNodePtr cur,const xmlChar * xpath)1418 xmlSchematronGetNode(xmlSchematronValidCtxtPtr ctxt,
1419                      xmlNodePtr cur, const xmlChar *xpath) {
1420     xmlNodePtr node = NULL;
1421     xmlXPathObjectPtr ret;
1422 
1423     if ((ctxt == NULL) || (cur == NULL) || (xpath == NULL))
1424         return(NULL);
1425 
1426     ctxt->xctxt->doc = cur->doc;
1427     ctxt->xctxt->node = cur;
1428     ret = xmlXPathEval(xpath, ctxt->xctxt);
1429     if (ret == NULL)
1430         return(NULL);
1431 
1432     if ((ret->type == XPATH_NODESET) &&
1433         (ret->nodesetval != NULL) && (ret->nodesetval->nodeNr > 0))
1434         node = ret->nodesetval->nodeTab[0];
1435 
1436     xmlXPathFreeObject(ret);
1437     return(node);
1438 }
1439 
1440 /**
1441  * xmlSchematronReportOutput:
1442  * @ctxt: the validation context
1443  * @cur: the current node tested
1444  * @msg: the message output
1445  *
1446  * Output part of the report to whatever channel the user selected
1447  */
1448 static void
xmlSchematronReportOutput(xmlSchematronValidCtxtPtr ctxt ATTRIBUTE_UNUSED,xmlNodePtr cur ATTRIBUTE_UNUSED,const char * msg)1449 xmlSchematronReportOutput(xmlSchematronValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
1450                           xmlNodePtr cur ATTRIBUTE_UNUSED,
1451                           const char *msg) {
1452     /* TODO */
1453     fprintf(stderr, "%s", msg);
1454 }
1455 
1456 /**
1457  * xmlSchematronFormatReport:
1458  * @ctxt:  the validation context
1459  * @test: the test node
1460  * @cur: the current node tested
1461  *
1462  * Build the string being reported to the user.
1463  *
1464  * Returns a report string or NULL in case of error. The string needs
1465  *         to be deallocated by the caller
1466  */
1467 static xmlChar *
xmlSchematronFormatReport(xmlSchematronValidCtxtPtr ctxt,xmlNodePtr test,xmlNodePtr cur)1468 xmlSchematronFormatReport(xmlSchematronValidCtxtPtr ctxt,
1469                           xmlNodePtr test, xmlNodePtr cur) {
1470     xmlChar *ret = NULL;
1471     xmlNodePtr child, node;
1472     xmlXPathCompExprPtr comp;
1473 
1474     if ((test == NULL) || (cur == NULL))
1475         return(ret);
1476 
1477     child = test->children;
1478     while (child != NULL) {
1479         if ((child->type == XML_TEXT_NODE) ||
1480             (child->type == XML_CDATA_SECTION_NODE))
1481             ret = xmlStrcat(ret, child->content);
1482         else if (IS_SCHEMATRON(child, "name")) {
1483             xmlChar *path;
1484 
1485             path = xmlGetNoNsProp(child, BAD_CAST "path");
1486 
1487             node = cur;
1488             if (path != NULL) {
1489                 node = xmlSchematronGetNode(ctxt, cur, path);
1490                 if (node == NULL)
1491                     node = cur;
1492                 xmlFree(path);
1493             }
1494 
1495             if ((node->ns == NULL) || (node->ns->prefix == NULL))
1496                 ret = xmlStrcat(ret, node->name);
1497             else {
1498                 ret = xmlStrcat(ret, node->ns->prefix);
1499                 ret = xmlStrcat(ret, BAD_CAST ":");
1500                 ret = xmlStrcat(ret, node->name);
1501             }
1502         } else if (IS_SCHEMATRON(child, "value-of")) {
1503             xmlChar *select;
1504             xmlXPathObjectPtr eval;
1505 
1506             select = xmlGetNoNsProp(child, BAD_CAST "select");
1507             comp = xmlXPathCtxtCompile(ctxt->xctxt, select);
1508             eval = xmlXPathCompiledEval(comp, ctxt->xctxt);
1509 
1510             switch (eval->type) {
1511             case XPATH_NODESET: {
1512                 int indx;
1513                 xmlChar *spacer = BAD_CAST " ";
1514 
1515                 if (eval->nodesetval) {
1516                     for (indx = 0; indx < eval->nodesetval->nodeNr; indx++) {
1517                         if (indx > 0)
1518                             ret = xmlStrcat(ret, spacer);
1519                         ret = xmlStrcat(ret, eval->nodesetval->nodeTab[indx]->name);
1520                     }
1521                 }
1522                 break;
1523             }
1524             case XPATH_BOOLEAN: {
1525                 const char *str = eval->boolval ? "True" : "False";
1526                 ret = xmlStrcat(ret, BAD_CAST str);
1527                 break;
1528             }
1529             case XPATH_NUMBER: {
1530                 xmlChar *buf;
1531                 int size;
1532 
1533                 size = snprintf(NULL, 0, "%0g", eval->floatval);
1534                 buf = (xmlChar *) xmlMalloc(size + 1);
1535                 if (buf != NULL) {
1536                     snprintf((char *) buf, size + 1, "%0g", eval->floatval);
1537                     ret = xmlStrcat(ret, buf);
1538                     xmlFree(buf);
1539                 }
1540                 break;
1541             }
1542             case XPATH_STRING:
1543                 ret = xmlStrcat(ret, eval->stringval);
1544                 break;
1545             default:
1546                 xmlSchematronVErr(ctxt, XML_ERR_INTERNAL_ERROR,
1547                                   "Unsupported XPATH Type\n", NULL);
1548             }
1549             xmlXPathFreeObject(eval);
1550             xmlXPathFreeCompExpr(comp);
1551             xmlFree(select);
1552         } else {
1553             child = child->next;
1554             continue;
1555         }
1556 
1557         /*
1558          * remove superfluous \n
1559          */
1560         if (ret != NULL) {
1561             int len = xmlStrlen(ret);
1562             xmlChar c;
1563 
1564             if (len > 0) {
1565                 c = ret[len - 1];
1566                 if ((c == ' ') || (c == '\n') || (c == '\r') || (c == '\t')) {
1567                     while ((c == ' ') || (c == '\n') ||
1568                            (c == '\r') || (c == '\t')) {
1569                         len--;
1570                         if (len == 0)
1571                             break;
1572                         c = ret[len - 1];
1573                     }
1574                     ret[len] = ' ';
1575                     ret[len + 1] = 0;
1576                 }
1577             }
1578         }
1579 
1580         child = child->next;
1581     }
1582     return(ret);
1583 }
1584 
1585 /**
1586  * xmlSchematronReportSuccess:
1587  * @ctxt:  the validation context
1588  * @test: the compiled test
1589  * @cur: the current node tested
1590  * @success: boolean value for the result
1591  *
1592  * called from the validation engine when an assert or report test have
1593  * been done.
1594  */
1595 static void
xmlSchematronReportSuccess(xmlSchematronValidCtxtPtr ctxt,xmlSchematronTestPtr test,xmlNodePtr cur,xmlSchematronPatternPtr pattern,int success)1596 xmlSchematronReportSuccess(xmlSchematronValidCtxtPtr ctxt,
1597                    xmlSchematronTestPtr test, xmlNodePtr cur, xmlSchematronPatternPtr pattern, int success) {
1598     if ((ctxt == NULL) || (cur == NULL) || (test == NULL))
1599         return;
1600     /* if quiet and not SVRL report only failures */
1601     if ((ctxt->flags & XML_SCHEMATRON_OUT_QUIET) &&
1602         ((ctxt->flags & XML_SCHEMATRON_OUT_XML) == 0) &&
1603         (test->type == XML_SCHEMATRON_REPORT))
1604         return;
1605     if (ctxt->flags & XML_SCHEMATRON_OUT_XML) {
1606         /* TODO */
1607     } else {
1608         xmlChar *path;
1609         char msg[1000];
1610         long line;
1611         const xmlChar *report = NULL;
1612 
1613         if (((test->type == XML_SCHEMATRON_REPORT) && (!success)) ||
1614             ((test->type == XML_SCHEMATRON_ASSERT) && (success)))
1615             return;
1616         line = xmlGetLineNo(cur);
1617         path = xmlGetNodePath(cur);
1618         if (path == NULL)
1619             path = (xmlChar *) cur->name;
1620 #if 0
1621         if ((test->report != NULL) && (test->report[0] != 0))
1622             report = test->report;
1623 #endif
1624         if (test->node != NULL)
1625             report = xmlSchematronFormatReport(ctxt, test->node, cur);
1626         if (report == NULL) {
1627             if (test->type == XML_SCHEMATRON_ASSERT) {
1628             report = xmlStrdup((const xmlChar *) "node failed assert");
1629             } else {
1630             report = xmlStrdup((const xmlChar *) "node failed report");
1631             }
1632             }
1633             snprintf(msg, 999, "%s line %ld: %s\n", (const char *) path,
1634                      line, (const char *) report);
1635 
1636     if (ctxt->flags & XML_SCHEMATRON_OUT_ERROR) {
1637         xmlStructuredErrorFunc schannel;
1638         xmlGenericErrorFunc channel;
1639         void *data;
1640         int res;
1641 
1642         schannel = ctxt->serror;
1643         channel = ctxt->error;
1644         data = ctxt->userData;
1645 
1646         if ((channel == NULL) && (schannel == NULL)) {
1647             channel = xmlGenericError;
1648             data = xmlGenericErrorContext;
1649         }
1650 
1651         res = __xmlRaiseError(schannel, channel, data, NULL, cur,
1652                               XML_FROM_SCHEMATRONV,
1653                               (test->type == XML_SCHEMATRON_ASSERT) ?
1654                                   XML_SCHEMATRONV_ASSERT :
1655                                   XML_SCHEMATRONV_REPORT,
1656                               XML_ERR_ERROR, NULL, line,
1657                               (pattern == NULL) ?
1658                                   NULL :
1659                                   (const char *) pattern->name,
1660                               (const char *) path, (const char *) report, 0, 0,
1661                               "%s", msg);
1662         if (res < 0)
1663             xmlSchematronVErrMemory(ctxt);
1664     } else {
1665         xmlSchematronReportOutput(ctxt, cur, &msg[0]);
1666     }
1667 
1668     xmlFree((char *) report);
1669 
1670         if ((path != NULL) && (path != (xmlChar *) cur->name))
1671             xmlFree(path);
1672     }
1673 }
1674 
1675 /**
1676  * xmlSchematronReportPattern:
1677  * @ctxt:  the validation context
1678  * @pattern: the current pattern
1679  *
1680  * called from the validation engine when starting to check a pattern
1681  */
1682 static void
xmlSchematronReportPattern(xmlSchematronValidCtxtPtr ctxt,xmlSchematronPatternPtr pattern)1683 xmlSchematronReportPattern(xmlSchematronValidCtxtPtr ctxt,
1684                            xmlSchematronPatternPtr pattern) {
1685     if ((ctxt == NULL) || (pattern == NULL))
1686         return;
1687     if ((ctxt->flags & XML_SCHEMATRON_OUT_QUIET) || (ctxt->flags & XML_SCHEMATRON_OUT_ERROR)) /* Error gives pattern name as part of error */
1688         return;
1689     if (ctxt->flags & XML_SCHEMATRON_OUT_XML) {
1690         /* TODO */
1691     } else {
1692         char msg[1000];
1693 
1694         if (pattern->name == NULL)
1695             return;
1696         snprintf(msg, 999, "Pattern: %s\n", (const char *) pattern->name);
1697         xmlSchematronReportOutput(ctxt, NULL, &msg[0]);
1698     }
1699 }
1700 
1701 
1702 /************************************************************************
1703  *                                                                      *
1704  *              Validation against a Schematrontron                             *
1705  *                                                                      *
1706  ************************************************************************/
1707 
1708 /**
1709  * xmlSchematronSetValidStructuredErrors:
1710  * @ctxt:  a Schematron validation context
1711  * @serror:  the structured error function
1712  * @ctx: the functions context
1713  *
1714  * Set the structured error callback
1715  */
1716 void
xmlSchematronSetValidStructuredErrors(xmlSchematronValidCtxtPtr ctxt,xmlStructuredErrorFunc serror,void * ctx)1717 xmlSchematronSetValidStructuredErrors(xmlSchematronValidCtxtPtr ctxt,
1718                                       xmlStructuredErrorFunc serror, void *ctx)
1719 {
1720     if (ctxt == NULL)
1721         return;
1722     ctxt->serror = serror;
1723     ctxt->error = NULL;
1724     ctxt->warning = NULL;
1725     ctxt->userData = ctx;
1726 }
1727 
1728 /**
1729  * xmlSchematronNewValidCtxt:
1730  * @schema:  a precompiled XML Schematrons
1731  * @options: a set of xmlSchematronValidOptions
1732  *
1733  * Create an XML Schematrons validation context based on the given schema.
1734  *
1735  * Returns the validation context or NULL in case of error
1736  */
1737 xmlSchematronValidCtxtPtr
xmlSchematronNewValidCtxt(xmlSchematronPtr schema,int options)1738 xmlSchematronNewValidCtxt(xmlSchematronPtr schema, int options)
1739 {
1740     int i;
1741     xmlSchematronValidCtxtPtr ret;
1742 
1743     ret = (xmlSchematronValidCtxtPtr) xmlMalloc(sizeof(xmlSchematronValidCtxt));
1744     if (ret == NULL) {
1745         xmlSchematronVErrMemory(NULL);
1746         return (NULL);
1747     }
1748     memset(ret, 0, sizeof(xmlSchematronValidCtxt));
1749     ret->type = XML_STRON_CTXT_VALIDATOR;
1750     ret->schema = schema;
1751     ret->xctxt = xmlXPathNewContext(NULL);
1752     ret->flags = options;
1753     if (ret->xctxt == NULL) {
1754         xmlSchematronPErrMemory(NULL);
1755         xmlSchematronFreeValidCtxt(ret);
1756         return (NULL);
1757     }
1758     for (i = 0;i < schema->nbNamespaces;i++) {
1759         if ((schema->namespaces[2 * i] == NULL) ||
1760             (schema->namespaces[2 * i + 1] == NULL))
1761             break;
1762         xmlXPathRegisterNs(ret->xctxt, schema->namespaces[2 * i + 1],
1763                            schema->namespaces[2 * i]);
1764     }
1765     return (ret);
1766 }
1767 
1768 /**
1769  * xmlSchematronFreeValidCtxt:
1770  * @ctxt:  the schema validation context
1771  *
1772  * Free the resources associated to the schema validation context
1773  */
1774 void
xmlSchematronFreeValidCtxt(xmlSchematronValidCtxtPtr ctxt)1775 xmlSchematronFreeValidCtxt(xmlSchematronValidCtxtPtr ctxt)
1776 {
1777     if (ctxt == NULL)
1778         return;
1779     if (ctxt->xctxt != NULL)
1780         xmlXPathFreeContext(ctxt->xctxt);
1781     if (ctxt->dict != NULL)
1782         xmlDictFree(ctxt->dict);
1783     xmlFree(ctxt);
1784 }
1785 
1786 static xmlNodePtr
xmlSchematronNextNode(xmlNodePtr cur)1787 xmlSchematronNextNode(xmlNodePtr cur) {
1788     if (cur->children != NULL) {
1789         /*
1790          * Do not descend on entities declarations
1791          */
1792         if (cur->children->type != XML_ENTITY_DECL) {
1793             cur = cur->children;
1794             /*
1795              * Skip DTDs
1796              */
1797             if (cur->type != XML_DTD_NODE)
1798                 return(cur);
1799         }
1800     }
1801 
1802     while (cur->next != NULL) {
1803         cur = cur->next;
1804         if ((cur->type != XML_ENTITY_DECL) &&
1805             (cur->type != XML_DTD_NODE))
1806             return(cur);
1807     }
1808 
1809     do {
1810         cur = cur->parent;
1811         if (cur == NULL) break;
1812         if (cur->type == XML_DOCUMENT_NODE) return(NULL);
1813         if (cur->next != NULL) {
1814             cur = cur->next;
1815             return(cur);
1816         }
1817     } while (cur != NULL);
1818     return(cur);
1819 }
1820 
1821 /**
1822  * xmlSchematronRunTest:
1823  * @ctxt:  the schema validation context
1824  * @test:  the current test
1825  * @instance:  the document instance tree
1826  * @cur:  the current node in the instance
1827  *
1828  * Validate a rule against a tree instance at a given position
1829  *
1830  * Returns 1 in case of success, 0 if error and -1 in case of internal error
1831  */
1832 static int
xmlSchematronRunTest(xmlSchematronValidCtxtPtr ctxt,xmlSchematronTestPtr test,xmlDocPtr instance,xmlNodePtr cur,xmlSchematronPatternPtr pattern)1833 xmlSchematronRunTest(xmlSchematronValidCtxtPtr ctxt,
1834      xmlSchematronTestPtr test, xmlDocPtr instance, xmlNodePtr cur, xmlSchematronPatternPtr pattern)
1835 {
1836     xmlXPathObjectPtr ret;
1837     int failed;
1838 
1839     failed = 0;
1840     ctxt->xctxt->doc = instance;
1841     ctxt->xctxt->node = cur;
1842     ret = xmlXPathCompiledEval(test->comp, ctxt->xctxt);
1843     if (ret == NULL) {
1844         failed = 1;
1845     } else {
1846         switch (ret->type) {
1847             case XPATH_XSLT_TREE:
1848             case XPATH_NODESET:
1849                 if ((ret->nodesetval == NULL) ||
1850                     (ret->nodesetval->nodeNr == 0))
1851                     failed = 1;
1852                 break;
1853             case XPATH_BOOLEAN:
1854                 failed = !ret->boolval;
1855                 break;
1856             case XPATH_NUMBER:
1857                 if ((xmlXPathIsNaN(ret->floatval)) ||
1858                     (ret->floatval == 0.0))
1859                     failed = 1;
1860                 break;
1861             case XPATH_STRING:
1862                 if ((ret->stringval == NULL) ||
1863                     (ret->stringval[0] == 0))
1864                     failed = 1;
1865                 break;
1866             case XPATH_UNDEFINED:
1867             case XPATH_USERS:
1868                 failed = 1;
1869                 break;
1870         }
1871         xmlXPathFreeObject(ret);
1872     }
1873     if ((failed) && (test->type == XML_SCHEMATRON_ASSERT))
1874         ctxt->nberrors++;
1875     else if ((!failed) && (test->type == XML_SCHEMATRON_REPORT))
1876         ctxt->nberrors++;
1877 
1878     xmlSchematronReportSuccess(ctxt, test, cur, pattern, !failed);
1879 
1880     return(!failed);
1881 }
1882 
1883 /**
1884  * xmlSchematronRegisterVariables:
1885  * @ctxt:  the schema validation context
1886  * @let:  the list of let variables
1887  * @instance:  the document instance tree
1888  * @cur:  the current node
1889  *
1890  * Registers a list of let variables to the current context of @cur
1891  *
1892  * Returns -1 in case of errors, otherwise 0
1893  */
1894 static int
xmlSchematronRegisterVariables(xmlSchematronValidCtxtPtr vctxt,xmlXPathContextPtr ctxt,xmlSchematronLetPtr let,xmlDocPtr instance,xmlNodePtr cur)1895 xmlSchematronRegisterVariables(xmlSchematronValidCtxtPtr vctxt,
1896                                xmlXPathContextPtr ctxt,
1897                                xmlSchematronLetPtr let,
1898                                xmlDocPtr instance, xmlNodePtr cur)
1899 {
1900     xmlXPathObjectPtr let_eval;
1901 
1902     ctxt->doc = instance;
1903     ctxt->node = cur;
1904     while (let != NULL) {
1905         let_eval = xmlXPathCompiledEval(let->comp, ctxt);
1906         if (let_eval == NULL) {
1907             xmlSchematronVErr(vctxt, XML_ERR_INTERNAL_ERROR,
1908                               "Evaluation of compiled expression failed\n",
1909                               NULL);
1910             return -1;
1911         }
1912         if(xmlXPathRegisterVariableNS(ctxt, let->name, NULL, let_eval)) {
1913             xmlSchematronVErr(vctxt, XML_ERR_INTERNAL_ERROR,
1914                               "Registering a let variable failed\n", NULL);
1915             return -1;
1916         }
1917         let = let->next;
1918     }
1919     return 0;
1920 }
1921 
1922 /**
1923  * xmlSchematronUnregisterVariables:
1924  * @ctxt:  the schema validation context
1925  * @let:  the list of let variables
1926  *
1927  * Unregisters a list of let variables from the context
1928  *
1929  * Returns -1 in case of errors, otherwise 0
1930  */
1931 static int
xmlSchematronUnregisterVariables(xmlSchematronValidCtxtPtr vctxt,xmlXPathContextPtr ctxt,xmlSchematronLetPtr let)1932 xmlSchematronUnregisterVariables(xmlSchematronValidCtxtPtr vctxt,
1933                                  xmlXPathContextPtr ctxt,
1934                                  xmlSchematronLetPtr let)
1935 {
1936     while (let != NULL) {
1937         if (xmlXPathRegisterVariableNS(ctxt, let->name, NULL, NULL)) {
1938             xmlSchematronVErr(vctxt, XML_ERR_INTERNAL_ERROR,
1939                               "Unregistering a let variable failed\n", NULL);
1940             return -1;
1941         }
1942         let = let->next;
1943     }
1944     return 0;
1945 }
1946 
1947 /**
1948  * xmlSchematronValidateDoc:
1949  * @ctxt:  the schema validation context
1950  * @instance:  the document instance tree
1951  *
1952  * Validate a tree instance against the schematron
1953  *
1954  * Returns 0 in case of success, -1 in case of internal error
1955  *         and an error count otherwise.
1956  */
1957 int
xmlSchematronValidateDoc(xmlSchematronValidCtxtPtr ctxt,xmlDocPtr instance)1958 xmlSchematronValidateDoc(xmlSchematronValidCtxtPtr ctxt, xmlDocPtr instance)
1959 {
1960     xmlNodePtr cur, root;
1961     xmlSchematronPatternPtr pattern;
1962     xmlSchematronRulePtr rule;
1963     xmlSchematronTestPtr test;
1964 
1965     if ((ctxt == NULL) || (ctxt->schema == NULL) ||
1966         (ctxt->schema->rules == NULL) || (instance == NULL))
1967         return(-1);
1968     ctxt->nberrors = 0;
1969     root = xmlDocGetRootElement(instance);
1970     if (root == NULL) {
1971         /* TODO */
1972         ctxt->nberrors++;
1973         return(1);
1974     }
1975     if ((ctxt->flags & XML_SCHEMATRON_OUT_QUIET) ||
1976         (ctxt->flags == 0)) {
1977         /*
1978          * we are just trying to assert the validity of the document,
1979          * speed primes over the output, run in a single pass
1980          */
1981         cur = root;
1982         while (cur != NULL) {
1983             rule = ctxt->schema->rules;
1984             while (rule != NULL) {
1985                 if (xmlPatternMatch(rule->pattern, cur) == 1) {
1986                     test = rule->tests;
1987 
1988                     if (xmlSchematronRegisterVariables(ctxt, ctxt->xctxt,
1989                                 rule->lets, instance, cur))
1990                         return -1;
1991 
1992                     while (test != NULL) {
1993                         xmlSchematronRunTest(ctxt, test, instance, cur, (xmlSchematronPatternPtr)rule->pattern);
1994                         test = test->next;
1995                     }
1996 
1997                     if (xmlSchematronUnregisterVariables(ctxt, ctxt->xctxt,
1998                                 rule->lets))
1999                         return -1;
2000 
2001                 }
2002                 rule = rule->next;
2003             }
2004 
2005             cur = xmlSchematronNextNode(cur);
2006         }
2007     } else {
2008         /*
2009          * Process all contexts one at a time
2010          */
2011         pattern = ctxt->schema->patterns;
2012 
2013         while (pattern != NULL) {
2014             xmlSchematronReportPattern(ctxt, pattern);
2015 
2016             /*
2017              * TODO convert the pattern rule to a direct XPath and
2018              * compute directly instead of using the pattern matching
2019              * over the full document...
2020              * Check the exact semantic
2021              */
2022             cur = root;
2023             while (cur != NULL) {
2024                 rule = pattern->rules;
2025                 while (rule != NULL) {
2026                     if (xmlPatternMatch(rule->pattern, cur) == 1) {
2027                         test = rule->tests;
2028                         xmlSchematronRegisterVariables(ctxt, ctxt->xctxt,
2029                                 rule->lets, instance, cur);
2030 
2031                         while (test != NULL) {
2032                             xmlSchematronRunTest(ctxt, test, instance, cur, pattern);
2033                             test = test->next;
2034                         }
2035 
2036                         xmlSchematronUnregisterVariables(ctxt, ctxt->xctxt,
2037                                 rule->lets);
2038                     }
2039                     rule = rule->patnext;
2040                 }
2041 
2042                 cur = xmlSchematronNextNode(cur);
2043             }
2044             pattern = pattern->next;
2045         }
2046     }
2047     return(ctxt->nberrors);
2048 }
2049 
2050 #ifdef STANDALONE
2051 int
main(void)2052 main(void)
2053 {
2054     int ret;
2055     xmlDocPtr instance;
2056     xmlSchematronParserCtxtPtr pctxt;
2057     xmlSchematronValidCtxtPtr vctxt;
2058     xmlSchematronPtr schema = NULL;
2059 
2060     pctxt = xmlSchematronNewParserCtxt("tst.sct");
2061     if (pctxt == NULL) {
2062         fprintf(stderr, "failed to build schematron parser\n");
2063     } else {
2064         schema = xmlSchematronParse(pctxt);
2065         if (schema == NULL) {
2066             fprintf(stderr, "failed to compile schematron\n");
2067         }
2068         xmlSchematronFreeParserCtxt(pctxt);
2069     }
2070     instance = xmlReadFile("tst.sct", NULL,
2071                            XML_PARSE_NOENT | XML_PARSE_NOCDATA);
2072     if (instance == NULL) {
2073         fprintf(stderr, "failed to parse instance\n");
2074     }
2075     if ((schema != NULL) && (instance != NULL)) {
2076         vctxt = xmlSchematronNewValidCtxt(schema);
2077         if (vctxt == NULL) {
2078             fprintf(stderr, "failed to build schematron validator\n");
2079         } else {
2080             ret = xmlSchematronValidateDoc(vctxt, instance);
2081             xmlSchematronFreeValidCtxt(vctxt);
2082         }
2083     }
2084     xmlSchematronFree(schema);
2085     xmlFreeDoc(instance);
2086 
2087     xmlCleanupParser();
2088 
2089     return (0);
2090 }
2091 #endif
2092 
2093 #endif /* LIBXML_SCHEMATRON_ENABLED */
2094