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