blob: c3fe55cc059f3909402a3fd2d630b087503bddbf [file] [log] [blame]
Daniel Veillard6eadf632003-01-23 18:29:16 +00001/*
2 * relaxng.c : implementation of the Relax-NG handling and validity checking
3 *
4 * See Copyright for the status of this software.
5 *
6 * Daniel Veillard <veillard@redhat.com>
7 */
8
Daniel Veillardd41f4f42003-01-29 21:07:52 +00009/**
10 * TODO:
Daniel Veillardd41f4f42003-01-29 21:07:52 +000011 * - error reporting
Daniel Veillarde2a5a082003-02-02 14:35:17 +000012 * - simplification of the resulting compiled trees:
13 * - NOT_ALLOWED
14 * - EMPTY
Daniel Veillardd41f4f42003-01-29 21:07:52 +000015 */
16
Daniel Veillard6eadf632003-01-23 18:29:16 +000017#define IN_LIBXML
18#include "libxml.h"
19
20#ifdef LIBXML_SCHEMAS_ENABLED
21
22#include <string.h>
23#include <stdio.h>
24#include <libxml/xmlmemory.h>
25#include <libxml/parser.h>
26#include <libxml/parserInternals.h>
27#include <libxml/hash.h>
28#include <libxml/uri.h>
29
30#include <libxml/relaxng.h>
31
32#include <libxml/xmlschemastypes.h>
33#include <libxml/xmlautomata.h>
34#include <libxml/xmlregexp.h>
Daniel Veillardc6e997c2003-01-27 12:35:42 +000035#include <libxml/xmlschemastypes.h>
Daniel Veillard6eadf632003-01-23 18:29:16 +000036
37/*
38 * The Relax-NG namespace
39 */
40static const xmlChar *xmlRelaxNGNs = (const xmlChar *)
41 "http://relaxng.org/ns/structure/1.0";
42
43#define IS_RELAXNG(node, type) \
44 ((node != NULL) && (node->ns != NULL) && \
45 (xmlStrEqual(node->name, (const xmlChar *) type)) && \
46 (xmlStrEqual(node->ns->href, xmlRelaxNGNs)))
47
48
49#define DEBUG 1 /* very verbose output */
50#define DEBUG_CONTENT 1
51#define DEBUG_TYPE 1
Daniel Veillard276be4a2003-01-24 01:03:34 +000052#define DEBUG_VALID 1
Daniel Veillardb08c9812003-01-28 23:09:49 +000053#define DEBUG_INTERLEAVE 1 */
Daniel Veillard6eadf632003-01-23 18:29:16 +000054
55#define UNBOUNDED (1 << 30)
56#define TODO \
57 xmlGenericError(xmlGenericErrorContext, \
58 "Unimplemented block at %s:%d\n", \
59 __FILE__, __LINE__);
60
61typedef struct _xmlRelaxNGSchema xmlRelaxNGSchema;
62typedef xmlRelaxNGSchema *xmlRelaxNGSchemaPtr;
63
64typedef struct _xmlRelaxNGDefine xmlRelaxNGDefine;
65typedef xmlRelaxNGDefine *xmlRelaxNGDefinePtr;
66
Daniel Veillardd41f4f42003-01-29 21:07:52 +000067typedef struct _xmlRelaxNGDocument xmlRelaxNGDocument;
68typedef xmlRelaxNGDocument *xmlRelaxNGDocumentPtr;
69
Daniel Veillarda9d912d2003-02-01 17:43:10 +000070typedef struct _xmlRelaxNGInclude xmlRelaxNGInclude;
71typedef xmlRelaxNGInclude *xmlRelaxNGIncludePtr;
72
Daniel Veillard6eadf632003-01-23 18:29:16 +000073typedef enum {
74 XML_RELAXNG_COMBINE_UNDEFINED = 0, /* undefined */
75 XML_RELAXNG_COMBINE_CHOICE, /* choice */
76 XML_RELAXNG_COMBINE_INTERLEAVE /* interleave */
77} xmlRelaxNGCombine;
78
79typedef struct _xmlRelaxNGGrammar xmlRelaxNGGrammar;
80typedef xmlRelaxNGGrammar *xmlRelaxNGGrammarPtr;
81
82struct _xmlRelaxNGGrammar {
83 xmlRelaxNGGrammarPtr parent;/* the parent grammar if any */
84 xmlRelaxNGGrammarPtr children;/* the children grammar if any */
85 xmlRelaxNGGrammarPtr next; /* the next grammar if any */
86 xmlRelaxNGDefinePtr start; /* <start> content */
87 xmlRelaxNGCombine combine; /* the default combine value */
Daniel Veillardd41f4f42003-01-29 21:07:52 +000088 xmlRelaxNGDefinePtr startList;/* list of <start> definitions */
Daniel Veillard6eadf632003-01-23 18:29:16 +000089 xmlHashTablePtr defs; /* define* */
90 xmlHashTablePtr refs; /* references */
91};
92
93
Daniel Veillard6eadf632003-01-23 18:29:16 +000094typedef enum {
95 XML_RELAXNG_EMPTY = 0, /* an empty pattern */
96 XML_RELAXNG_NOT_ALLOWED, /* not allowed top */
97 XML_RELAXNG_TEXT, /* textual content */
98 XML_RELAXNG_ELEMENT, /* an element */
99 XML_RELAXNG_DATATYPE, /* extenal data type definition */
100 XML_RELAXNG_VALUE, /* value from an extenal data type definition */
101 XML_RELAXNG_LIST, /* a list of patterns */
102 XML_RELAXNG_ATTRIBUTE, /* an attrbute following a pattern */
103 XML_RELAXNG_DEF, /* a definition */
104 XML_RELAXNG_REF, /* reference to a definition */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000105 XML_RELAXNG_EXTERNALREF, /* reference to an external def */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000106 XML_RELAXNG_OPTIONAL, /* optional patterns */
107 XML_RELAXNG_ZEROORMORE, /* zero or more non empty patterns */
108 XML_RELAXNG_ONEORMORE, /* one or more non empty patterns */
109 XML_RELAXNG_CHOICE, /* a choice between non empty patterns */
110 XML_RELAXNG_GROUP, /* a pair/group of non empty patterns */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000111 XML_RELAXNG_INTERLEAVE, /* interleaving choice of non-empty patterns */
112 XML_RELAXNG_START /* Used to keep track of starts on grammars */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000113} xmlRelaxNGType;
114
115struct _xmlRelaxNGDefine {
116 xmlRelaxNGType type; /* the type of definition */
117 xmlNodePtr node; /* the node in the source */
118 xmlChar *name; /* the element local name if present */
119 xmlChar *ns; /* the namespace local name if present */
Daniel Veillardedc91922003-01-26 00:52:04 +0000120 xmlChar *value; /* value when available */
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000121 void *data; /* data lib or specific pointer */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000122 xmlRelaxNGDefinePtr content;/* the expected content */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000123 xmlRelaxNGDefinePtr parent; /* the parent definition, if any */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000124 xmlRelaxNGDefinePtr next; /* list within grouping sequences */
125 xmlRelaxNGDefinePtr attrs; /* list of attributes for elements */
Daniel Veillard3b2e4e12003-02-03 08:52:58 +0000126 xmlRelaxNGDefinePtr nameClass;/* the nameClass definition if any */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000127 xmlRelaxNGDefinePtr nextHash;/* next define in defs/refs hash tables */
128};
129
130/**
131 * _xmlRelaxNG:
132 *
133 * A RelaxNGs definition
134 */
135struct _xmlRelaxNG {
136 xmlRelaxNGGrammarPtr topgrammar;
137 xmlDocPtr doc;
138
139 xmlHashTablePtr defs; /* define */
140 xmlHashTablePtr refs; /* references */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000141 xmlHashTablePtr documents; /* all the documents loaded */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000142 xmlHashTablePtr includes; /* all the includes loaded */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000143 void *_private; /* unused by the library for users or bindings */
144};
145
146typedef enum {
147 XML_RELAXNG_ERR_OK = 0,
148 XML_RELAXNG_ERR_NOROOT = 1,
149 XML_RELAXNG_ERR_
150} xmlRelaxNGValidError;
151
152#define XML_RELAXNG_IN_ATTRIBUTE 1
153
154struct _xmlRelaxNGParserCtxt {
155 void *userData; /* user specific data block */
156 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
157 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
158 xmlRelaxNGValidError err;
159
160 xmlRelaxNGPtr schema; /* The schema in use */
161 xmlRelaxNGGrammarPtr grammar; /* the current grammar */
162 int flags; /* parser flags */
163 int nbErrors; /* number of errors at parse time */
164 int nbWarnings; /* number of warnings at parse time */
Daniel Veillard276be4a2003-01-24 01:03:34 +0000165 const xmlChar *define; /* the current define scope */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000166 xmlRelaxNGDefinePtr def; /* the current define */
167
168 int nbInterleaves;
169 xmlHashTablePtr interleaves; /* keep track of all the interleaves */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000170
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000171 xmlHashTablePtr documents; /* all the documents loaded */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000172 xmlHashTablePtr includes; /* all the includes loaded */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000173 xmlChar *URL;
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000174 xmlDocPtr document;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000175
176 const char *buffer;
177 int size;
178
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000179 /* the document stack */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000180 xmlRelaxNGDocumentPtr doc; /* Current parsed external ref */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000181 int docNr; /* Depth of the parsing stack */
182 int docMax; /* Max depth of the parsing stack */
183 xmlRelaxNGDocumentPtr *docTab; /* array of docs */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000184
185 /* the include stack */
186 xmlRelaxNGIncludePtr inc; /* Current parsed include */
187 int incNr; /* Depth of the include parsing stack */
188 int incMax; /* Max depth of the parsing stack */
189 xmlRelaxNGIncludePtr *incTab; /* array of incs */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000190};
191
192#define FLAGS_IGNORABLE 1
193#define FLAGS_NEGATIVE 2
194
195/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000196 * xmlRelaxNGInterleaveGroup:
197 *
198 * A RelaxNGs partition set associated to lists of definitions
199 */
200typedef struct _xmlRelaxNGInterleaveGroup xmlRelaxNGInterleaveGroup;
201typedef xmlRelaxNGInterleaveGroup *xmlRelaxNGInterleaveGroupPtr;
202struct _xmlRelaxNGInterleaveGroup {
203 xmlRelaxNGDefinePtr rule; /* the rule to satisfy */
204 xmlRelaxNGDefinePtr *defs; /* the array of element definitions */
205};
206
207/**
208 * xmlRelaxNGPartitions:
209 *
210 * A RelaxNGs partition associated to an interleave group
211 */
212typedef struct _xmlRelaxNGPartition xmlRelaxNGPartition;
213typedef xmlRelaxNGPartition *xmlRelaxNGPartitionPtr;
214struct _xmlRelaxNGPartition {
215 int nbgroups; /* number of groups in the partitions */
216 xmlRelaxNGInterleaveGroupPtr *groups;
217};
218
219/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000220 * xmlRelaxNGValidState:
221 *
222 * A RelaxNGs validation state
223 */
224#define MAX_ATTR 20
225typedef struct _xmlRelaxNGValidState xmlRelaxNGValidState;
226typedef xmlRelaxNGValidState *xmlRelaxNGValidStatePtr;
227struct _xmlRelaxNGValidState {
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000228 xmlNodePtr node; /* the current node */
229 xmlNodePtr seq; /* the sequence of children left to validate */
230 int nbAttrs; /* the number of attributes */
231 xmlChar *value; /* the value when operating on string */
232 xmlChar *endvalue; /* the end value when operating on string */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000233 xmlAttrPtr attrs[1]; /* the array of attributes */
234};
235
236/**
237 * xmlRelaxNGValidCtxt:
238 *
239 * A RelaxNGs validation context
240 */
241
242struct _xmlRelaxNGValidCtxt {
243 void *userData; /* user specific data block */
244 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
245 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
246
247 xmlRelaxNGPtr schema; /* The schema in use */
248 xmlDocPtr doc; /* the document being validated */
249 xmlRelaxNGValidStatePtr state; /* the current validation state */
250 int flags; /* validation flags */
251};
252
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000253/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000254 * xmlRelaxNGInclude:
255 *
256 * Structure associated to a RelaxNGs document element
257 */
258struct _xmlRelaxNGInclude {
259 xmlChar *href; /* the normalized href value */
260 xmlDocPtr doc; /* the associated XML document */
261 xmlRelaxNGDefinePtr content;/* the definitions */
262 xmlRelaxNGPtr schema; /* the schema */
263};
264
265/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000266 * xmlRelaxNGDocument:
267 *
268 * Structure associated to a RelaxNGs document element
269 */
270struct _xmlRelaxNGDocument {
271 xmlChar *href; /* the normalized href value */
272 xmlDocPtr doc; /* the associated XML document */
273 xmlRelaxNGDefinePtr content;/* the definitions */
274 xmlRelaxNGPtr schema; /* the schema */
275};
276
Daniel Veillard6eadf632003-01-23 18:29:16 +0000277/************************************************************************
278 * *
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000279 * Preliminary type checking interfaces *
280 * *
281 ************************************************************************/
282/**
283 * xmlRelaxNGTypeHave:
284 * @data: data needed for the library
285 * @type: the type name
286 * @value: the value to check
287 *
288 * Function provided by a type library to check if a type is exported
289 *
290 * Returns 1 if yes, 0 if no and -1 in case of error.
291 */
292typedef int (*xmlRelaxNGTypeHave) (void *data, const xmlChar *type);
293
294/**
295 * xmlRelaxNGTypeCheck:
296 * @data: data needed for the library
297 * @type: the type name
298 * @value: the value to check
299 *
300 * Function provided by a type library to check if a value match a type
301 *
302 * Returns 1 if yes, 0 if no and -1 in case of error.
303 */
304typedef int (*xmlRelaxNGTypeCheck) (void *data, const xmlChar *type,
305 const xmlChar *value);
306
307/**
308 * xmlRelaxNGTypeCompare:
309 * @data: data needed for the library
310 * @type: the type name
311 * @value1: the first value
312 * @value2: the second value
313 *
314 * Function provided by a type library to compare two values accordingly
315 * to a type.
316 *
317 * Returns 1 if yes, 0 if no and -1 in case of error.
318 */
319typedef int (*xmlRelaxNGTypeCompare) (void *data, const xmlChar *type,
320 const xmlChar *value1,
321 const xmlChar *value2);
322typedef struct _xmlRelaxNGTypeLibrary xmlRelaxNGTypeLibrary;
323typedef xmlRelaxNGTypeLibrary *xmlRelaxNGTypeLibraryPtr;
324struct _xmlRelaxNGTypeLibrary {
325 const xmlChar *namespace; /* the datatypeLibrary value */
326 void *data; /* data needed for the library */
327 xmlRelaxNGTypeHave have; /* the export function */
328 xmlRelaxNGTypeCheck check; /* the checking function */
329 xmlRelaxNGTypeCompare comp; /* the compare function */
330};
331
332/************************************************************************
333 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +0000334 * Allocation functions *
335 * *
336 ************************************************************************/
337static void xmlRelaxNGFreeDefineList(xmlRelaxNGDefinePtr defines);
338static void xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar);
339static void xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define);
340
341/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000342 * xmlRelaxNGFreeDocument:
343 * @docu: a document structure
344 *
345 * Deallocate a RelaxNG document structure.
346 */
347static void
348xmlRelaxNGFreeDocument(xmlRelaxNGDocumentPtr docu)
349{
350 if (docu == NULL)
351 return;
352
353 if (docu->href != NULL)
354 xmlFree(docu->href);
355 if (docu->doc != NULL)
356 xmlFreeDoc(docu->doc);
357 if (docu->schema != NULL)
358 xmlRelaxNGFree(docu->schema);
359 xmlFree(docu);
360}
361
362/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000363 * xmlRelaxNGFreeInclude:
364 * @incl: a include structure
365 *
366 * Deallocate a RelaxNG include structure.
367 */
368static void
369xmlRelaxNGFreeInclude(xmlRelaxNGIncludePtr incl)
370{
371 if (incl == NULL)
372 return;
373
374 if (incl->href != NULL)
375 xmlFree(incl->href);
376 if (incl->doc != NULL)
377 xmlFreeDoc(incl->doc);
378 if (incl->schema != NULL)
379 xmlRelaxNGFree(incl->schema);
380 xmlFree(incl);
381}
382
383/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000384 * xmlRelaxNGNewRelaxNG:
385 * @ctxt: a Relax-NG validation context (optional)
386 *
387 * Allocate a new RelaxNG structure.
388 *
389 * Returns the newly allocated structure or NULL in case or error
390 */
391static xmlRelaxNGPtr
392xmlRelaxNGNewRelaxNG(xmlRelaxNGParserCtxtPtr ctxt)
393{
394 xmlRelaxNGPtr ret;
395
396 ret = (xmlRelaxNGPtr) xmlMalloc(sizeof(xmlRelaxNG));
397 if (ret == NULL) {
398 if ((ctxt != NULL) && (ctxt->error != NULL))
399 ctxt->error(ctxt->userData, "Out of memory\n");
400 ctxt->nbErrors++;
401 return (NULL);
402 }
403 memset(ret, 0, sizeof(xmlRelaxNG));
404
405 return (ret);
406}
407
408/**
409 * xmlRelaxNGFree:
410 * @schema: a schema structure
411 *
412 * Deallocate a RelaxNG structure.
413 */
414void
415xmlRelaxNGFree(xmlRelaxNGPtr schema)
416{
417 if (schema == NULL)
418 return;
419
Daniel Veillard6eadf632003-01-23 18:29:16 +0000420 if (schema->topgrammar != NULL)
421 xmlRelaxNGFreeGrammar(schema->topgrammar);
422 if (schema->doc != NULL)
423 xmlFreeDoc(schema->doc);
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000424 if (schema->documents != NULL)
425 xmlHashFree(schema->documents, (xmlHashDeallocator)
426 xmlRelaxNGFreeDocument);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000427 if (schema->includes != NULL)
428 xmlHashFree(schema->includes, (xmlHashDeallocator)
429 xmlRelaxNGFreeInclude);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000430
431 xmlFree(schema);
432}
433
434/**
435 * xmlRelaxNGNewGrammar:
436 * @ctxt: a Relax-NG validation context (optional)
437 *
438 * Allocate a new RelaxNG grammar.
439 *
440 * Returns the newly allocated structure or NULL in case or error
441 */
442static xmlRelaxNGGrammarPtr
443xmlRelaxNGNewGrammar(xmlRelaxNGParserCtxtPtr ctxt)
444{
445 xmlRelaxNGGrammarPtr ret;
446
447 ret = (xmlRelaxNGGrammarPtr) xmlMalloc(sizeof(xmlRelaxNGGrammar));
448 if (ret == NULL) {
449 if ((ctxt != NULL) && (ctxt->error != NULL))
450 ctxt->error(ctxt->userData, "Out of memory\n");
451 ctxt->nbErrors++;
452 return (NULL);
453 }
454 memset(ret, 0, sizeof(xmlRelaxNGGrammar));
455
456 return (ret);
457}
458
459/**
Daniel Veillard276be4a2003-01-24 01:03:34 +0000460 * xmlRelaxNGFreeDefineHash:
461 * @defines: a list of define structures
462 *
463 * Deallocate a RelaxNG definition in the hash table
464 */
465static void
466xmlRelaxNGFreeDefineHash(xmlRelaxNGDefinePtr defines)
467{
468 xmlRelaxNGDefinePtr next;
469
470 while (defines != NULL) {
471 next = defines->nextHash;
472 xmlRelaxNGFreeDefine(defines);
473 defines = next;
474 }
475}
476
477/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000478 * xmlRelaxNGFreeGrammar:
479 * @grammar: a grammar structure
480 *
481 * Deallocate a RelaxNG grammar structure.
482 */
483static void
484xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar)
485{
486 if (grammar == NULL)
487 return;
488
489 if (grammar->start != NULL)
490 xmlRelaxNGFreeDefine(grammar->start);
491 if (grammar->refs != NULL) {
492 xmlHashFree(grammar->refs, NULL);
493 }
494 if (grammar->defs != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +0000495 xmlHashFree(grammar->defs, (xmlHashDeallocator)
496 xmlRelaxNGFreeDefineHash);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000497 }
498
499 xmlFree(grammar);
500}
501
502/**
503 * xmlRelaxNGNewDefine:
504 * @ctxt: a Relax-NG validation context
505 * @node: the node in the input document.
506 *
507 * Allocate a new RelaxNG define.
508 *
509 * Returns the newly allocated structure or NULL in case or error
510 */
511static xmlRelaxNGDefinePtr
512xmlRelaxNGNewDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
513{
514 xmlRelaxNGDefinePtr ret;
515
516 ret = (xmlRelaxNGDefinePtr) xmlMalloc(sizeof(xmlRelaxNGDefine));
517 if (ret == NULL) {
518 if ((ctxt != NULL) && (ctxt->error != NULL))
519 ctxt->error(ctxt->userData, "Out of memory\n");
520 ctxt->nbErrors++;
521 return (NULL);
522 }
523 memset(ret, 0, sizeof(xmlRelaxNGDefine));
524 ret->node = node;
525
526 return (ret);
527}
528
529/**
530 * xmlRelaxNGFreeDefineList:
531 * @defines: a list of define structures
532 *
533 * Deallocate a RelaxNG define structures.
534 */
535static void
536xmlRelaxNGFreeDefineList(xmlRelaxNGDefinePtr defines)
537{
538 xmlRelaxNGDefinePtr next;
539
540 while (defines != NULL) {
541 next = defines->next;
542 xmlRelaxNGFreeDefine(defines);
543 defines = next;
544 }
545}
546
547/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000548 * xmlRelaxNGFreePartition:
549 * @partitions: a partition set structure
550 *
551 * Deallocate RelaxNG partition set structures.
552 */
553static void
554xmlRelaxNGFreePartition(xmlRelaxNGPartitionPtr partitions) {
555 xmlRelaxNGInterleaveGroupPtr group;
556 int j;
557
558 if (partitions != NULL) {
559 if (partitions->groups != NULL) {
560 for (j = 0;j < partitions->nbgroups;j++) {
561 group = partitions->groups[j];
562 if (group != NULL) {
563 if (group->defs != NULL)
564 xmlFree(group->defs);
565 xmlFree(group);
566 }
567 }
568 xmlFree(partitions->groups);
569 }
570 xmlFree(partitions);
571 }
572}
573/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000574 * xmlRelaxNGFreeDefine:
575 * @define: a define structure
576 *
577 * Deallocate a RelaxNG define structure.
578 */
579static void
580xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define)
581{
582 if (define == NULL)
583 return;
584
585 if (define->name != NULL)
586 xmlFree(define->name);
587 if (define->ns != NULL)
588 xmlFree(define->ns);
Daniel Veillardedc91922003-01-26 00:52:04 +0000589 if (define->value != NULL)
590 xmlFree(define->value);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000591 if (define->attrs != NULL)
592 xmlRelaxNGFreeDefineList(define->attrs);
Daniel Veillard276be4a2003-01-24 01:03:34 +0000593 if ((define->content != NULL) &&
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000594 (define->type != XML_RELAXNG_REF) &&
595 (define->type != XML_RELAXNG_EXTERNALREF))
Daniel Veillard6eadf632003-01-23 18:29:16 +0000596 xmlRelaxNGFreeDefineList(define->content);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000597 if ((define->data != NULL) &&
598 (define->type == XML_RELAXNG_INTERLEAVE))
599 xmlRelaxNGFreePartition((xmlRelaxNGPartitionPtr) define->data);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000600 xmlFree(define);
601}
602
603/**
604 * xmlRelaxNGNewValidState:
605 * @ctxt: a Relax-NG validation context
606 * @node: the current node or NULL for the document
607 *
608 * Allocate a new RelaxNG validation state
609 *
610 * Returns the newly allocated structure or NULL in case or error
611 */
612static xmlRelaxNGValidStatePtr
613xmlRelaxNGNewValidState(xmlRelaxNGValidCtxtPtr ctxt, xmlNodePtr node)
614{
615 xmlRelaxNGValidStatePtr ret;
616 xmlAttrPtr attr;
617 xmlAttrPtr attrs[MAX_ATTR];
618 int nbAttrs = 0;
619 xmlNodePtr root = NULL;
620
621 if (node == NULL) {
622 root = xmlDocGetRootElement(ctxt->doc);
623 if (root == NULL)
624 return(NULL);
625 } else {
626 attr = node->properties;
627 while (attr != NULL) {
628 if (nbAttrs < MAX_ATTR)
629 attrs[nbAttrs++] = attr;
630 else
631 nbAttrs++;
632 attr = attr->next;
633 }
634 }
635
636 if (nbAttrs < MAX_ATTR)
637 attrs[nbAttrs] = NULL;
638 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(sizeof(xmlRelaxNGValidState) +
639 nbAttrs * sizeof(xmlAttrPtr));
640 if (ret == NULL) {
641 if ((ctxt != NULL) && (ctxt->error != NULL))
642 ctxt->error(ctxt->userData, "Out of memory\n");
643 return (NULL);
644 }
645 if (node == NULL) {
646 ret->node = (xmlNodePtr) ctxt->doc;
647 ret->seq = root;
648 ret->nbAttrs = 0;
649 } else {
650 ret->node = node;
651 ret->seq = node->children;
652 ret->nbAttrs = nbAttrs;
653 if (nbAttrs > 0) {
654 if (nbAttrs < MAX_ATTR) {
655 memcpy(&(ret->attrs[0]), attrs,
656 sizeof(xmlAttrPtr) * (nbAttrs + 1));
657 } else {
658 attr = node->properties;
659 nbAttrs = 0;
660 while (attr != NULL) {
661 ret->attrs[nbAttrs++] = attr;
662 attr = attr->next;
663 }
664 ret->attrs[nbAttrs] = NULL;
665 }
666 }
667 }
668 return (ret);
669}
670
671/**
672 * xmlRelaxNGCopyValidState:
673 * @ctxt: a Relax-NG validation context
674 * @state: a validation state
675 *
676 * Copy the validation state
677 *
678 * Returns the newly allocated structure or NULL in case or error
679 */
680static xmlRelaxNGValidStatePtr
681xmlRelaxNGCopyValidState(xmlRelaxNGValidCtxtPtr ctxt,
682 xmlRelaxNGValidStatePtr state)
683{
684 xmlRelaxNGValidStatePtr ret;
685 unsigned int size;
686
687 if (state == NULL)
688 return(NULL);
689
690 size = sizeof(xmlRelaxNGValidState) +
691 state->nbAttrs * sizeof(xmlAttrPtr);
692 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(size);
693 if (ret == NULL) {
694 if ((ctxt != NULL) && (ctxt->error != NULL))
695 ctxt->error(ctxt->userData, "Out of memory\n");
696 return (NULL);
697 }
698 memcpy(ret, state, size);
699 return(ret);
700}
701
702/**
703 * xmlRelaxNGFreeValidState:
704 * @state: a validation state structure
705 *
706 * Deallocate a RelaxNG validation state structure.
707 */
708static void
709xmlRelaxNGFreeValidState(xmlRelaxNGValidStatePtr state)
710{
711 if (state == NULL)
712 return;
713
714 xmlFree(state);
715}
716
717/************************************************************************
718 * *
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000719 * Document functions *
720 * *
721 ************************************************************************/
722static xmlDocPtr xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt,
723 xmlDocPtr doc);
724
725/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000726 * xmlRelaxNGIncludePush:
727 * @ctxt: the parser context
728 * @value: the element doc
729 *
730 * Pushes a new include on top of the include stack
731 *
732 * Returns 0 in case of error, the index in the stack otherwise
733 */
734static int
735xmlRelaxNGIncludePush(xmlRelaxNGParserCtxtPtr ctxt,
736 xmlRelaxNGIncludePtr value)
737{
738 if (ctxt->incTab == NULL) {
739 ctxt->incMax = 4;
740 ctxt->incNr = 0;
741 ctxt->incTab = (xmlRelaxNGIncludePtr *) xmlMalloc(
742 ctxt->incMax * sizeof(ctxt->incTab[0]));
743 if (ctxt->incTab == NULL) {
744 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
745 return (0);
746 }
747 }
748 if (ctxt->incNr >= ctxt->incMax) {
749 ctxt->incMax *= 2;
750 ctxt->incTab =
751 (xmlRelaxNGIncludePtr *) xmlRealloc(ctxt->incTab,
752 ctxt->incMax *
753 sizeof(ctxt->incTab[0]));
754 if (ctxt->incTab == NULL) {
755 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
756 return (0);
757 }
758 }
759 ctxt->incTab[ctxt->incNr] = value;
760 ctxt->inc = value;
761 return (ctxt->incNr++);
762}
763
764/**
765 * xmlRelaxNGIncludePop:
766 * @ctxt: the parser context
767 *
768 * Pops the top include from the include stack
769 *
770 * Returns the include just removed
771 */
772static xmlRelaxNGIncludePtr
773xmlRelaxNGIncludePop(xmlRelaxNGParserCtxtPtr ctxt)
774{
775 xmlRelaxNGIncludePtr ret;
776
777 if (ctxt->incNr <= 0)
778 return (0);
779 ctxt->incNr--;
780 if (ctxt->incNr > 0)
781 ctxt->inc = ctxt->incTab[ctxt->incNr - 1];
782 else
783 ctxt->inc = NULL;
784 ret = ctxt->incTab[ctxt->incNr];
785 ctxt->incTab[ctxt->incNr] = 0;
786 return (ret);
787}
788
789/**
790 * xmlRelaxNGLoadInclude:
791 * @ctxt: the parser context
792 * @URL: the normalized URL
793 * @node: the include node.
794 *
795 * First lookup if the document is already loaded into the parser context,
796 * check against recursion. If not found the resource is loaded and
797 * the content is preprocessed before being returned back to the caller.
798 *
799 * Returns the xmlRelaxNGIncludePtr or NULL in case of error
800 */
801static xmlRelaxNGIncludePtr
802xmlRelaxNGLoadInclude(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
803 xmlNodePtr node) {
804 xmlRelaxNGIncludePtr ret = NULL;
805 xmlDocPtr doc;
806 int i;
807 xmlNodePtr root, tmp, tmp2, cur;
808
809 /*
810 * check against recursion in the stack
811 */
812 for (i = 0;i < ctxt->incNr;i++) {
813 if (xmlStrEqual(ctxt->incTab[i]->href, URL)) {
814 if (ctxt->error != NULL)
815 ctxt->error(ctxt->userData,
816 "Detected an externalRef recursion for %s\n",
817 URL);
818 ctxt->nbErrors++;
819 return(NULL);
820 }
821 }
822
823 /*
824 * Lookup in the hash table
825 */
826 if (ctxt->includes == NULL) {
827 ctxt->includes = xmlHashCreate(10);
828 if (ctxt->includes == NULL) {
829 if (ctxt->error != NULL)
830 ctxt->error(ctxt->userData,
831 "Failed to allocate hash table for document\n");
832 ctxt->nbErrors++;
833 return(NULL);
834 }
835 } else {
836 ret = xmlHashLookup(ctxt->includes, URL);
837 if (ret != NULL)
838 return(ret);
839 }
840
841
842 /*
843 * load the document
844 */
845 doc = xmlParseFile((const char *) URL);
846 if (doc == NULL) {
847 if (ctxt->error != NULL)
848 ctxt->error(ctxt->userData,
849 "xmlRelaxNG: could not load %s\n", URL);
850 ctxt->nbErrors++;
851 return (NULL);
852 }
853
854 /*
855 * Allocate the document structures and register it first.
856 */
857 ret = (xmlRelaxNGIncludePtr) xmlMalloc(sizeof(xmlRelaxNGInclude));
858 if (ret == NULL) {
859 if (ctxt->error != NULL)
860 ctxt->error(ctxt->userData,
861 "xmlRelaxNG: allocate memory for doc %s\n", URL);
862 ctxt->nbErrors++;
863 xmlFreeDoc(doc);
864 return (NULL);
865 }
866 memset(ret, 0, sizeof(xmlRelaxNGInclude));
867 ret->doc = doc;
868 ret->href = xmlStrdup(URL);
869
870 /*
871 * push it on the stack and register it in the hash table
872 */
873 xmlHashAddEntry(ctxt->includes, URL, ret);
874 xmlRelaxNGIncludePush(ctxt, ret);
875
876 /*
877 * Some preprocessing of the document content, this include recursing
878 * in the include stack.
879 */
880 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
881 if (doc == NULL) {
882 /* xmlFreeDoc(ctxt->include); */
883 ctxt->inc = NULL;
884 return(NULL);
885 }
886
887 /*
888 * Pop up the include from the stack
889 */
890 xmlRelaxNGIncludePop(ctxt);
891
892 /*
893 * Check that the top element is a grammar
894 */
895 root = xmlDocGetRootElement(doc);
896 if (root == NULL) {
897 if (ctxt->error != NULL)
898 ctxt->error(ctxt->userData,
899 "xmlRelaxNG: included document is empty %s\n", URL);
900 ctxt->nbErrors++;
901 xmlFreeDoc(doc);
902 return (NULL);
903 }
904 if (!IS_RELAXNG(root, "grammar")) {
905 if (ctxt->error != NULL)
906 ctxt->error(ctxt->userData,
907 "xmlRelaxNG: included document %s root is not a grammar\n",
908 URL);
909 ctxt->nbErrors++;
910 xmlFreeDoc(doc);
911 return (NULL);
912 }
913
914 /*
915 * Elimination of redefined rules in the include.
916 */
917 cur = node->children;
918 while (cur != NULL) {
919 if (IS_RELAXNG(cur, "start")) {
920 int found = 0;
921
922 tmp = root->children;
923 while (tmp != NULL) {
924 tmp2 = tmp->next;
925 if (IS_RELAXNG(tmp, "start")) {
926 found = 1;
927 xmlUnlinkNode(tmp);
928 xmlFreeNode(tmp);
929 }
930 tmp = tmp2;
931 }
932 if (!found) {
933 if (ctxt->error != NULL)
934 ctxt->error(ctxt->userData,
935 "xmlRelaxNG: include %s has a start but not the included grammar\n",
936 URL);
937 ctxt->nbErrors++;
938 }
939 } else if (IS_RELAXNG(cur, "define")) {
940 xmlChar *name, *name2;
941
942 name = xmlGetProp(cur, BAD_CAST "name");
943 if (name == NULL) {
944 if (ctxt->error != NULL)
945 ctxt->error(ctxt->userData,
946 "xmlRelaxNG: include %s has define without name\n",
947 URL);
948 ctxt->nbErrors++;
949 } else {
950 int found = 0;
951
952 tmp = root->children;
953 while (tmp != NULL) {
954 tmp2 = tmp->next;
955 if (IS_RELAXNG(tmp, "define")) {
956 name2 = xmlGetProp(tmp, BAD_CAST "name");
957 if (name2 != NULL) {
958 if (xmlStrEqual(name, name2)) {
959 found = 1;
960 xmlUnlinkNode(tmp);
961 xmlFreeNode(tmp);
962 }
963 xmlFree(name2);
964 }
965 }
966 tmp = tmp2;
967 }
968 if (!found) {
969 if (ctxt->error != NULL)
970 ctxt->error(ctxt->userData,
971 "xmlRelaxNG: include %s has a define %s but not the included grammar\n",
972 URL, name);
973 ctxt->nbErrors++;
974 }
975 xmlFree(name);
976 }
977 }
978 cur = cur->next;
979 }
980
981
982 return(ret);
983}
984
985/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000986 * xmlRelaxNGDocumentPush:
987 * @ctxt: the parser context
988 * @value: the element doc
989 *
990 * Pushes a new doc on top of the doc stack
991 *
992 * Returns 0 in case of error, the index in the stack otherwise
993 */
994static int
995xmlRelaxNGDocumentPush(xmlRelaxNGParserCtxtPtr ctxt,
996 xmlRelaxNGDocumentPtr value)
997{
998 if (ctxt->docTab == NULL) {
999 ctxt->docMax = 4;
1000 ctxt->docNr = 0;
1001 ctxt->docTab = (xmlRelaxNGDocumentPtr *) xmlMalloc(
1002 ctxt->docMax * sizeof(ctxt->docTab[0]));
1003 if (ctxt->docTab == NULL) {
1004 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
1005 return (0);
1006 }
1007 }
1008 if (ctxt->docNr >= ctxt->docMax) {
1009 ctxt->docMax *= 2;
1010 ctxt->docTab =
1011 (xmlRelaxNGDocumentPtr *) xmlRealloc(ctxt->docTab,
1012 ctxt->docMax *
1013 sizeof(ctxt->docTab[0]));
1014 if (ctxt->docTab == NULL) {
1015 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
1016 return (0);
1017 }
1018 }
1019 ctxt->docTab[ctxt->docNr] = value;
1020 ctxt->doc = value;
1021 return (ctxt->docNr++);
1022}
1023
1024/**
1025 * xmlRelaxNGDocumentPop:
1026 * @ctxt: the parser context
1027 *
1028 * Pops the top doc from the doc stack
1029 *
1030 * Returns the doc just removed
1031 */
1032static xmlRelaxNGDocumentPtr
1033xmlRelaxNGDocumentPop(xmlRelaxNGParserCtxtPtr ctxt)
1034{
1035 xmlRelaxNGDocumentPtr ret;
1036
1037 if (ctxt->docNr <= 0)
1038 return (0);
1039 ctxt->docNr--;
1040 if (ctxt->docNr > 0)
1041 ctxt->doc = ctxt->docTab[ctxt->docNr - 1];
1042 else
1043 ctxt->doc = NULL;
1044 ret = ctxt->docTab[ctxt->docNr];
1045 ctxt->docTab[ctxt->docNr] = 0;
1046 return (ret);
1047}
1048
1049/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001050 * xmlRelaxNGLoadExternalRef:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001051 * @ctxt: the parser context
1052 * @URL: the normalized URL
1053 * @ns: the inherited ns if any
1054 *
1055 * First lookup if the document is already loaded into the parser context,
1056 * check against recursion. If not found the resource is loaded and
1057 * the content is preprocessed before being returned back to the caller.
1058 *
1059 * Returns the xmlRelaxNGDocumentPtr or NULL in case of error
1060 */
1061static xmlRelaxNGDocumentPtr
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001062xmlRelaxNGLoadExternalRef(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001063 const xmlChar *ns) {
1064 xmlRelaxNGDocumentPtr ret = NULL;
1065 xmlDocPtr doc;
1066 xmlNodePtr root;
1067 int i;
1068
1069 /*
1070 * check against recursion in the stack
1071 */
1072 for (i = 0;i < ctxt->docNr;i++) {
1073 if (xmlStrEqual(ctxt->docTab[i]->href, URL)) {
1074 if (ctxt->error != NULL)
1075 ctxt->error(ctxt->userData,
1076 "Detected an externalRef recursion for %s\n",
1077 URL);
1078 ctxt->nbErrors++;
1079 return(NULL);
1080 }
1081 }
1082
1083 /*
1084 * Lookup in the hash table
1085 */
1086 if (ctxt->documents == NULL) {
1087 ctxt->documents = xmlHashCreate(10);
1088 if (ctxt->documents == NULL) {
1089 if (ctxt->error != NULL)
1090 ctxt->error(ctxt->userData,
1091 "Failed to allocate hash table for document\n");
1092 ctxt->nbErrors++;
1093 return(NULL);
1094 }
1095 } else {
1096 if (ns == NULL)
1097 ret = xmlHashLookup2(ctxt->documents, BAD_CAST "", URL);
1098 else
1099 ret = xmlHashLookup2(ctxt->documents, ns, URL);
1100 if (ret != NULL)
1101 return(ret);
1102 }
1103
1104
1105 /*
1106 * load the document
1107 */
1108 doc = xmlParseFile((const char *) URL);
1109 if (doc == NULL) {
1110 if (ctxt->error != NULL)
1111 ctxt->error(ctxt->userData,
1112 "xmlRelaxNG: could not load %s\n", URL);
1113 ctxt->nbErrors++;
1114 return (NULL);
1115 }
1116
1117 /*
1118 * Allocate the document structures and register it first.
1119 */
1120 ret = (xmlRelaxNGDocumentPtr) xmlMalloc(sizeof(xmlRelaxNGDocument));
1121 if (ret == NULL) {
1122 if (ctxt->error != NULL)
1123 ctxt->error(ctxt->userData,
1124 "xmlRelaxNG: allocate memory for doc %s\n", URL);
1125 ctxt->nbErrors++;
1126 xmlFreeDoc(doc);
1127 return (NULL);
1128 }
1129 memset(ret, 0, sizeof(xmlRelaxNGDocument));
1130 ret->doc = doc;
1131 ret->href = xmlStrdup(URL);
1132
1133 /*
1134 * transmit the ns if needed
1135 */
1136 if (ns != NULL) {
1137 root = xmlDocGetRootElement(doc);
1138 if (root != NULL) {
1139 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
1140 xmlSetProp(root, BAD_CAST"ns", ns);
1141 }
1142 }
1143 }
1144
1145 /*
1146 * push it on the stack and register it in the hash table
1147 */
1148 if (ns == NULL)
1149 xmlHashAddEntry2(ctxt->documents, BAD_CAST "", URL, ret);
1150 else
1151 xmlHashAddEntry2(ctxt->documents, ns, URL, ret);
1152 xmlRelaxNGDocumentPush(ctxt, ret);
1153
1154 /*
1155 * Some preprocessing of the document content
1156 */
1157 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
1158 if (doc == NULL) {
1159 xmlFreeDoc(ctxt->document);
1160 ctxt->doc = NULL;
1161 return(NULL);
1162 }
1163
1164 xmlRelaxNGDocumentPop(ctxt);
1165
1166 return(ret);
1167}
1168
1169/************************************************************************
1170 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +00001171 * Error functions *
1172 * *
1173 ************************************************************************/
1174
1175#define VALID_CTXT() \
1176 if (ctxt->flags == 0) xmlGenericError(xmlGenericErrorContext, \
1177 "error detected at %s:%d\n", \
1178 __FILE__, __LINE__);
1179#define VALID_ERROR if (ctxt->flags == 0) printf
1180
1181#if 0
1182/**
1183 * xmlRelaxNGErrorContext:
1184 * @ctxt: the parsing context
1185 * @schema: the schema being built
1186 * @node: the node being processed
1187 * @child: the child being processed
1188 *
1189 * Dump a RelaxNGType structure
1190 */
1191static void
1192xmlRelaxNGErrorContext(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGPtr schema,
1193 xmlNodePtr node, xmlNodePtr child)
1194{
1195 int line = 0;
1196 const xmlChar *file = NULL;
1197 const xmlChar *name = NULL;
1198 const char *type = "error";
1199
1200 if ((ctxt == NULL) || (ctxt->error == NULL))
1201 return;
1202
1203 if (child != NULL)
1204 node = child;
1205
1206 if (node != NULL) {
1207 if ((node->type == XML_DOCUMENT_NODE) ||
1208 (node->type == XML_HTML_DOCUMENT_NODE)) {
1209 xmlDocPtr doc = (xmlDocPtr) node;
1210
1211 file = doc->URL;
1212 } else {
1213 /*
1214 * Try to find contextual informations to report
1215 */
1216 if (node->type == XML_ELEMENT_NODE) {
1217 line = (int) node->content;
1218 } else if ((node->prev != NULL) &&
1219 (node->prev->type == XML_ELEMENT_NODE)) {
1220 line = (int) node->prev->content;
1221 } else if ((node->parent != NULL) &&
1222 (node->parent->type == XML_ELEMENT_NODE)) {
1223 line = (int) node->parent->content;
1224 }
1225 if ((node->doc != NULL) && (node->doc->URL != NULL))
1226 file = node->doc->URL;
1227 if (node->name != NULL)
1228 name = node->name;
1229 }
1230 }
1231
1232 if (ctxt != NULL)
1233 type = "compilation error";
1234 else if (schema != NULL)
1235 type = "runtime error";
1236
1237 if ((file != NULL) && (line != 0) && (name != NULL))
1238 ctxt->error(ctxt->userData, "%s: file %s line %d element %s\n",
1239 type, file, line, name);
1240 else if ((file != NULL) && (name != NULL))
1241 ctxt->error(ctxt->userData, "%s: file %s element %s\n",
1242 type, file, name);
1243 else if ((file != NULL) && (line != 0))
1244 ctxt->error(ctxt->userData, "%s: file %s line %d\n", type, file, line);
1245 else if (file != NULL)
1246 ctxt->error(ctxt->userData, "%s: file %s\n", type, file);
1247 else if (name != NULL)
1248 ctxt->error(ctxt->userData, "%s: element %s\n", type, name);
1249 else
1250 ctxt->error(ctxt->userData, "%s\n", type);
1251}
1252#endif
1253
1254/************************************************************************
1255 * *
1256 * Type library hooks *
1257 * *
1258 ************************************************************************/
Daniel Veillardea3f3982003-01-26 19:45:18 +00001259static xmlChar *xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt,
1260 const xmlChar *str);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001261
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001262/**
1263 * xmlRelaxNGSchemaTypeHave:
1264 * @data: data needed for the library
1265 * @type: the type name
1266 *
1267 * Check if the given type is provided by
1268 * the W3C XMLSchema Datatype library.
1269 *
1270 * Returns 1 if yes, 0 if no and -1 in case of error.
1271 */
1272static int
1273xmlRelaxNGSchemaTypeHave(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001274 const xmlChar *type) {
1275 xmlSchemaTypePtr typ;
1276
1277 if (type == NULL)
1278 return(-1);
1279 typ = xmlSchemaGetPredefinedType(type,
1280 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1281 if (typ == NULL)
1282 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001283 return(1);
1284}
1285
1286/**
1287 * xmlRelaxNGSchemaTypeCheck:
1288 * @data: data needed for the library
1289 * @type: the type name
1290 * @value: the value to check
1291 *
1292 * Check if the given type and value are validated by
1293 * the W3C XMLSchema Datatype library.
1294 *
1295 * Returns 1 if yes, 0 if no and -1 in case of error.
1296 */
1297static int
1298xmlRelaxNGSchemaTypeCheck(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001299 const xmlChar *type,
1300 const xmlChar *value) {
1301 xmlSchemaTypePtr typ;
1302 int ret;
1303
1304 /*
1305 * TODO: the type should be cached ab provided back, interface subject
1306 * to changes.
1307 * TODO: handle facets, may require an additional interface and keep
1308 * the value returned from the validation.
1309 */
1310 if ((type == NULL) || (value == NULL))
1311 return(-1);
1312 typ = xmlSchemaGetPredefinedType(type,
1313 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1314 if (typ == NULL)
1315 return(-1);
1316 ret = xmlSchemaValidatePredefinedType(typ, value, NULL);
1317 if (ret == 0)
1318 return(1);
1319 if (ret > 0)
1320 return(0);
1321 return(-1);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001322}
1323
1324/**
1325 * xmlRelaxNGSchemaTypeCompare:
1326 * @data: data needed for the library
1327 * @type: the type name
1328 * @value1: the first value
1329 * @value2: the second value
1330 *
1331 * Compare two values accordingly a type from the W3C XMLSchema
1332 * Datatype library.
1333 *
1334 * Returns 1 if yes, 0 if no and -1 in case of error.
1335 */
1336static int
1337xmlRelaxNGSchemaTypeCompare(void *data ATTRIBUTE_UNUSED,
1338 const xmlChar *type ATTRIBUTE_UNUSED,
1339 const xmlChar *value1 ATTRIBUTE_UNUSED,
1340 const xmlChar *value2 ATTRIBUTE_UNUSED) {
1341 TODO
1342 return(1);
1343}
1344
1345/**
1346 * xmlRelaxNGDefaultTypeHave:
1347 * @data: data needed for the library
1348 * @type: the type name
1349 *
1350 * Check if the given type is provided by
1351 * the default datatype library.
1352 *
1353 * Returns 1 if yes, 0 if no and -1 in case of error.
1354 */
1355static int
1356xmlRelaxNGDefaultTypeHave(void *data ATTRIBUTE_UNUSED, const xmlChar *type) {
1357 if (type == NULL)
1358 return(-1);
1359 if (xmlStrEqual(type, BAD_CAST "string"))
1360 return(1);
1361 if (xmlStrEqual(type, BAD_CAST "token"))
1362 return(1);
1363 return(0);
1364}
1365
1366/**
1367 * xmlRelaxNGDefaultTypeCheck:
1368 * @data: data needed for the library
1369 * @type: the type name
1370 * @value: the value to check
1371 *
1372 * Check if the given type and value are validated by
1373 * the default datatype library.
1374 *
1375 * Returns 1 if yes, 0 if no and -1 in case of error.
1376 */
1377static int
1378xmlRelaxNGDefaultTypeCheck(void *data ATTRIBUTE_UNUSED,
1379 const xmlChar *type ATTRIBUTE_UNUSED,
1380 const xmlChar *value ATTRIBUTE_UNUSED) {
1381 return(1);
1382}
1383
1384/**
1385 * xmlRelaxNGDefaultTypeCompare:
1386 * @data: data needed for the library
1387 * @type: the type name
1388 * @value1: the first value
1389 * @value2: the second value
1390 *
1391 * Compare two values accordingly a type from the default
1392 * datatype library.
1393 *
1394 * Returns 1 if yes, 0 if no and -1 in case of error.
1395 */
1396static int
1397xmlRelaxNGDefaultTypeCompare(void *data ATTRIBUTE_UNUSED,
1398 const xmlChar *type ATTRIBUTE_UNUSED,
1399 const xmlChar *value1 ATTRIBUTE_UNUSED,
1400 const xmlChar *value2 ATTRIBUTE_UNUSED) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00001401 int ret = -1;
1402
1403 if (xmlStrEqual(type, BAD_CAST "string")) {
1404 ret = xmlStrEqual(value1, value2);
1405 } else if (xmlStrEqual(type, BAD_CAST "token")) {
1406 if (!xmlStrEqual(value1, value2)) {
1407 xmlChar *nval, *nvalue;
1408
1409 /*
1410 * TODO: trivial optimizations are possible by
1411 * computing at compile-time
1412 */
1413 nval = xmlRelaxNGNormalize(NULL, value1);
1414 nvalue = xmlRelaxNGNormalize(NULL, value2);
1415
1416 if ((nval == NULL) || (nvalue == NULL) ||
1417 (!xmlStrEqual(nval, nvalue)))
1418 ret = -1;
1419 if (nval != NULL)
1420 xmlFree(nval);
1421 if (nvalue != NULL)
1422 xmlFree(nvalue);
1423 }
1424 }
1425 return(ret);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001426}
1427
1428static int xmlRelaxNGTypeInitialized = 0;
1429static xmlHashTablePtr xmlRelaxNGRegisteredTypes = NULL;
1430
1431/**
1432 * xmlRelaxNGFreeTypeLibrary:
1433 * @lib: the type library structure
1434 * @namespace: the URI bound to the library
1435 *
1436 * Free the structure associated to the type library
1437 */
Daniel Veillard6eadf632003-01-23 18:29:16 +00001438static void
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001439xmlRelaxNGFreeTypeLibrary(xmlRelaxNGTypeLibraryPtr lib,
1440 const xmlChar *namespace ATTRIBUTE_UNUSED) {
1441 if (lib == NULL)
1442 return;
1443 if (lib->namespace != NULL)
1444 xmlFree((xmlChar *)lib->namespace);
1445 xmlFree(lib);
1446}
1447
1448/**
1449 * xmlRelaxNGRegisterTypeLibrary:
1450 * @namespace: the URI bound to the library
1451 * @data: data associated to the library
1452 * @have: the provide function
1453 * @check: the checking function
1454 * @comp: the comparison function
1455 *
1456 * Register a new type library
1457 *
1458 * Returns 0 in case of success and -1 in case of error.
1459 */
1460static int
1461xmlRelaxNGRegisterTypeLibrary(const xmlChar *namespace, void *data,
1462 xmlRelaxNGTypeHave have, xmlRelaxNGTypeCheck check,
1463 xmlRelaxNGTypeCompare comp) {
1464 xmlRelaxNGTypeLibraryPtr lib;
1465 int ret;
1466
1467 if ((xmlRelaxNGRegisteredTypes == NULL) || (namespace == NULL) ||
1468 (check == NULL) || (comp == NULL))
1469 return(-1);
1470 if (xmlHashLookup(xmlRelaxNGRegisteredTypes, namespace) != NULL) {
1471 xmlGenericError(xmlGenericErrorContext,
1472 "Relax-NG types library '%s' already registered\n",
1473 namespace);
1474 return(-1);
1475 }
1476 lib = (xmlRelaxNGTypeLibraryPtr) xmlMalloc(sizeof(xmlRelaxNGTypeLibrary));
1477 if (lib == NULL) {
1478 xmlGenericError(xmlGenericErrorContext,
1479 "Relax-NG types library '%s' malloc() failed\n",
1480 namespace);
1481 return (-1);
1482 }
1483 memset(lib, 0, sizeof(xmlRelaxNGTypeLibrary));
1484 lib->namespace = xmlStrdup(namespace);
1485 lib->data = data;
1486 lib->have = have;
1487 lib->comp = comp;
1488 lib->check = check;
1489 ret = xmlHashAddEntry(xmlRelaxNGRegisteredTypes, namespace, lib);
1490 if (ret < 0) {
1491 xmlGenericError(xmlGenericErrorContext,
1492 "Relax-NG types library failed to register '%s'\n",
1493 namespace);
1494 xmlRelaxNGFreeTypeLibrary(lib, namespace);
1495 return(-1);
1496 }
1497 return(0);
1498}
1499
1500/**
1501 * xmlRelaxNGInitTypes:
1502 *
1503 * Initilize the default type libraries.
1504 *
1505 * Returns 0 in case of success and -1 in case of error.
1506 */
1507static int
Daniel Veillard6eadf632003-01-23 18:29:16 +00001508xmlRelaxNGInitTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001509 if (xmlRelaxNGTypeInitialized != 0)
1510 return(0);
1511 xmlRelaxNGRegisteredTypes = xmlHashCreate(10);
1512 if (xmlRelaxNGRegisteredTypes == NULL) {
1513 xmlGenericError(xmlGenericErrorContext,
1514 "Failed to allocate sh table for Relax-NG types\n");
1515 return(-1);
1516 }
1517 xmlRelaxNGRegisterTypeLibrary(
1518 BAD_CAST "http://www.w3.org/2001/XMLSchema-datatypes",
1519 NULL,
1520 xmlRelaxNGSchemaTypeHave,
1521 xmlRelaxNGSchemaTypeCheck,
1522 xmlRelaxNGSchemaTypeCompare);
1523 xmlRelaxNGRegisterTypeLibrary(
1524 xmlRelaxNGNs,
1525 NULL,
1526 xmlRelaxNGDefaultTypeHave,
1527 xmlRelaxNGDefaultTypeCheck,
1528 xmlRelaxNGDefaultTypeCompare);
1529 xmlRelaxNGTypeInitialized = 1;
1530 return(0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001531}
1532
1533/**
1534 * xmlRelaxNGCleanupTypes:
1535 *
1536 * Cleanup the default Schemas type library associated to RelaxNG
1537 */
1538void
1539xmlRelaxNGCleanupTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001540 if (xmlRelaxNGTypeInitialized == 0)
1541 return;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001542 xmlSchemaCleanupTypes();
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001543 xmlHashFree(xmlRelaxNGRegisteredTypes, (xmlHashDeallocator)
1544 xmlRelaxNGFreeTypeLibrary);
1545 xmlRelaxNGTypeInitialized = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001546}
1547
1548/************************************************************************
1549 * *
1550 * Parsing functions *
1551 * *
1552 ************************************************************************/
1553
1554static xmlRelaxNGDefinePtr xmlRelaxNGParseAttribute(
1555 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1556static xmlRelaxNGDefinePtr xmlRelaxNGParseElement(
1557 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1558static xmlRelaxNGDefinePtr xmlRelaxNGParsePatterns(
Daniel Veillard154877e2003-01-30 12:17:05 +00001559 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes, int group);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001560static xmlRelaxNGDefinePtr xmlRelaxNGParsePattern(
1561 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001562static xmlRelaxNGPtr xmlRelaxNGParseDocument(
1563 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00001564static int xmlRelaxNGParseGrammarContent(
1565 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00001566static xmlRelaxNGDefinePtr xmlRelaxNGParseNameClass(
1567 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
1568 xmlRelaxNGDefinePtr def);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001569
1570
1571#define IS_BLANK_NODE(n) \
1572 (((n)->type == XML_TEXT_NODE) && (xmlRelaxNGIsBlank((n)->content)))
1573
1574/**
1575 * xmlRelaxNGIsBlank:
1576 * @str: a string
1577 *
1578 * Check if a string is ignorable c.f. 4.2. Whitespace
1579 *
1580 * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
1581 */
1582static int
1583xmlRelaxNGIsBlank(xmlChar *str) {
1584 if (str == NULL)
1585 return(1);
1586 while (*str != 0) {
1587 if (!(IS_BLANK(*str))) return(0);
1588 str++;
1589 }
1590 return(1);
1591}
1592
Daniel Veillard6eadf632003-01-23 18:29:16 +00001593/**
1594 * xmlRelaxNGGetDataTypeLibrary:
1595 * @ctxt: a Relax-NG parser context
1596 * @node: the current data or value element
1597 *
1598 * Applies algorithm from 4.3. datatypeLibrary attribute
1599 *
1600 * Returns the datatypeLibary value or NULL if not found
1601 */
1602static xmlChar *
1603xmlRelaxNGGetDataTypeLibrary(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1604 xmlNodePtr node) {
1605 xmlChar *ret, *escape;
1606
Daniel Veillard6eadf632003-01-23 18:29:16 +00001607 if ((IS_RELAXNG(node, "data")) || (IS_RELAXNG(node, "value"))) {
1608 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1609 if (ret != NULL) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001610 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +00001611 if (escape == NULL) {
1612 return(ret);
1613 }
1614 xmlFree(ret);
1615 return(escape);
1616 }
1617 }
1618 node = node->parent;
1619 while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) {
1620 if (IS_RELAXNG(node, "element")) {
1621 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1622 if (ret != NULL) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001623 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +00001624 if (escape == NULL) {
1625 return(ret);
1626 }
1627 xmlFree(ret);
1628 return(escape);
1629 }
1630 }
1631 node = node->parent;
1632 }
1633 return(NULL);
1634}
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001635
1636/**
Daniel Veillardedc91922003-01-26 00:52:04 +00001637 * xmlRelaxNGParseValue:
1638 * @ctxt: a Relax-NG parser context
1639 * @node: the data node.
1640 *
1641 * parse the content of a RelaxNG value node.
1642 *
1643 * Returns the definition pointer or NULL in case of error
1644 */
1645static xmlRelaxNGDefinePtr
1646xmlRelaxNGParseValue(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1647 xmlRelaxNGDefinePtr def = NULL;
1648 xmlRelaxNGTypeLibraryPtr lib;
1649 xmlChar *type;
1650 xmlChar *library;
1651 int tmp;
1652
1653 def = xmlRelaxNGNewDefine(ctxt, node);
1654 if (def == NULL)
1655 return(NULL);
1656 def->type = XML_RELAXNG_VALUE;
1657
1658 type = xmlGetProp(node, BAD_CAST "type");
1659 if (type != NULL) {
1660 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1661 if (library == NULL)
1662 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1663
1664 def->name = type;
1665 def->ns = library;
1666
1667 lib = (xmlRelaxNGTypeLibraryPtr)
1668 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1669 if (lib == NULL) {
1670 if (ctxt->error != NULL)
1671 ctxt->error(ctxt->userData,
1672 "Use of unregistered type library '%s'\n",
1673 library);
1674 ctxt->nbErrors++;
1675 def->data = NULL;
1676 } else {
1677 def->data = lib;
1678 if (lib->have == NULL) {
1679 ctxt->error(ctxt->userData,
1680 "Internal error with type library '%s': no 'have'\n",
1681 library);
1682 ctxt->nbErrors++;
1683 } else {
1684 tmp = lib->have(lib->data, def->name);
1685 if (tmp != 1) {
1686 ctxt->error(ctxt->userData,
1687 "Error type '%s' is not exported by type library '%s'\n",
1688 def->name, library);
1689 ctxt->nbErrors++;
1690 }
1691 }
1692 }
1693 }
1694 if (node->children == NULL) {
1695 if (ctxt->error != NULL)
1696 ctxt->error(ctxt->userData,
1697 "Element <value> has no content\n");
1698 ctxt->nbErrors++;
1699 } else if ((node->children->type != XML_TEXT_NODE) ||
1700 (node->children->next != NULL)) {
1701 if (ctxt->error != NULL)
1702 ctxt->error(ctxt->userData,
1703 "Expecting a single text value for <value>content\n");
1704 ctxt->nbErrors++;
1705 } else {
1706 def->value = xmlNodeGetContent(node);
1707 if (def->value == NULL) {
1708 if (ctxt->error != NULL)
1709 ctxt->error(ctxt->userData,
1710 "Element <value> has no content\n");
1711 ctxt->nbErrors++;
1712 }
1713 }
1714 /* TODO check ahead of time that the value is okay per the type */
1715 return(def);
1716}
1717
1718/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001719 * xmlRelaxNGParseData:
1720 * @ctxt: a Relax-NG parser context
1721 * @node: the data node.
1722 *
1723 * parse the content of a RelaxNG data node.
1724 *
1725 * Returns the definition pointer or NULL in case of error
1726 */
1727static xmlRelaxNGDefinePtr
1728xmlRelaxNGParseData(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1729 xmlRelaxNGDefinePtr def = NULL;
1730 xmlRelaxNGTypeLibraryPtr lib;
1731 xmlChar *type;
1732 xmlChar *library;
1733 xmlNodePtr content;
1734 int tmp;
1735
1736 type = xmlGetProp(node, BAD_CAST "type");
1737 if (type == NULL) {
1738 if (ctxt->error != NULL)
1739 ctxt->error(ctxt->userData,
1740 "data has no type\n");
1741 ctxt->nbErrors++;
1742 return(NULL);
1743 }
1744 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1745 if (library == NULL)
1746 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1747
1748 def = xmlRelaxNGNewDefine(ctxt, node);
1749 if (def == NULL) {
1750 xmlFree(type);
1751 return(NULL);
1752 }
1753 def->type = XML_RELAXNG_DATATYPE;
1754 def->name = type;
1755 def->ns = library;
1756
1757 lib = (xmlRelaxNGTypeLibraryPtr)
1758 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1759 if (lib == NULL) {
1760 if (ctxt->error != NULL)
1761 ctxt->error(ctxt->userData,
1762 "Use of unregistered type library '%s'\n",
1763 library);
1764 ctxt->nbErrors++;
1765 def->data = NULL;
1766 } else {
1767 def->data = lib;
1768 if (lib->have == NULL) {
1769 ctxt->error(ctxt->userData,
1770 "Internal error with type library '%s': no 'have'\n",
1771 library);
1772 ctxt->nbErrors++;
1773 } else {
1774 tmp = lib->have(lib->data, def->name);
1775 if (tmp != 1) {
1776 ctxt->error(ctxt->userData,
1777 "Error type '%s' is not exported by type library '%s'\n",
1778 def->name, library);
1779 ctxt->nbErrors++;
1780 }
1781 }
1782 }
1783 content = node->children;
1784 while (content != NULL) {
1785 TODO
1786 content = content->next;
1787 }
1788
1789 return(def);
1790}
1791
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001792/**
1793 * xmlRelaxNGCompareElemDefLists:
1794 * @ctxt: a Relax-NG parser context
1795 * @defs1: the first list of element defs
1796 * @defs2: the second list of element defs
1797 *
1798 * Compare the 2 lists of element definitions. The comparison is
1799 * that if both lists do not accept the same QNames, it returns 1
1800 * If the 2 lists can accept the same QName the comparison returns 0
1801 *
1802 * Returns 1 disttinct, 0 if equal
1803 */
1804static int
1805xmlRelaxNGCompareElemDefLists(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1806 xmlRelaxNGDefinePtr *def1,
1807 xmlRelaxNGDefinePtr *def2) {
1808 xmlRelaxNGDefinePtr *basedef2 = def2;
1809
Daniel Veillard154877e2003-01-30 12:17:05 +00001810 if ((def1 == NULL) || (def2 == NULL))
1811 return(1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001812 if ((*def1 == NULL) || (*def2 == NULL))
1813 return(1);
1814 while (*def1 != NULL) {
1815 while ((*def2) != NULL) {
1816 if ((*def1)->name == NULL) {
1817 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1818 return(0);
1819 } else if ((*def2)->name == NULL) {
1820 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1821 return(0);
1822 } else if (xmlStrEqual((*def1)->name, (*def2)->name)) {
1823 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1824 return(0);
1825 }
1826 def2++;
1827 }
1828 def2 = basedef2;
1829 def1++;
1830 }
1831 return(1);
1832}
1833
1834/**
1835 * xmlRelaxNGGetElements:
1836 * @ctxt: a Relax-NG parser context
1837 * @def: the interleave definition
1838 *
1839 * Compute the list of top elements a definition can generate
1840 *
1841 * Returns a list of elements or NULL if none was found.
1842 */
1843static xmlRelaxNGDefinePtr *
1844xmlRelaxNGGetElements(xmlRelaxNGParserCtxtPtr ctxt,
1845 xmlRelaxNGDefinePtr def) {
1846 xmlRelaxNGDefinePtr *ret = NULL, parent, cur, tmp;
1847 int len = 0;
1848 int max = 0;
1849
1850 parent = NULL;
1851 cur = def;
1852 while (cur != NULL) {
Daniel Veillardb08c9812003-01-28 23:09:49 +00001853 if ((cur->type == XML_RELAXNG_ELEMENT) ||
1854 (cur->type == XML_RELAXNG_TEXT)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001855 if (ret == NULL) {
1856 max = 10;
1857 ret = (xmlRelaxNGDefinePtr *)
1858 xmlMalloc((max + 1) * sizeof(xmlRelaxNGDefinePtr));
1859 if (ret == NULL) {
1860 if (ctxt->error != NULL)
1861 ctxt->error(ctxt->userData,
1862 "Out of memory in element search\n");
1863 ctxt->nbErrors++;
1864 return(NULL);
1865 }
1866 } else if (max <= len) {
1867 max *= 2;
1868 ret = xmlRealloc(ret, (max + 1) * sizeof(xmlRelaxNGDefinePtr));
1869 if (ret == NULL) {
1870 if (ctxt->error != NULL)
1871 ctxt->error(ctxt->userData,
1872 "Out of memory in element search\n");
1873 ctxt->nbErrors++;
1874 return(NULL);
1875 }
1876 }
Daniel Veillardb08c9812003-01-28 23:09:49 +00001877 ret[len++] = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001878 ret[len] = NULL;
1879 } else if ((cur->type == XML_RELAXNG_CHOICE) ||
1880 (cur->type == XML_RELAXNG_INTERLEAVE) ||
1881 (cur->type == XML_RELAXNG_GROUP) ||
1882 (cur->type == XML_RELAXNG_ONEORMORE) ||
Daniel Veillardb08c9812003-01-28 23:09:49 +00001883 (cur->type == XML_RELAXNG_ZEROORMORE) ||
1884 (cur->type == XML_RELAXNG_OPTIONAL) ||
1885 (cur->type == XML_RELAXNG_REF) ||
1886 (cur->type == XML_RELAXNG_DEF)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001887 /*
1888 * Don't go within elements or attributes or string values.
1889 * Just gather the element top list
1890 */
1891 if (cur->content != NULL) {
1892 parent = cur;
1893 cur = cur->content;
1894 tmp = cur;
1895 while (tmp != NULL) {
1896 tmp->parent = parent;
1897 tmp = tmp->next;
1898 }
1899 continue;
1900 }
1901 }
Daniel Veillard154877e2003-01-30 12:17:05 +00001902 if (cur == def)
1903 return(ret);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001904 if (cur->next != NULL) {
1905 cur = cur->next;
1906 continue;
1907 }
1908 do {
1909 cur = cur->parent;
1910 if (cur == NULL) break;
1911 if (cur == def) return(ret);
1912 if (cur->next != NULL) {
1913 cur = cur->next;
1914 break;
1915 }
1916 } while (cur != NULL);
1917 }
1918 return(ret);
1919}
1920
1921/**
1922 * xmlRelaxNGComputeInterleaves:
1923 * @def: the interleave definition
1924 * @ctxt: a Relax-NG parser context
1925 * @node: the data node.
1926 *
1927 * A lot of work for preprocessing interleave definitions
1928 * is potentially needed to get a decent execution speed at runtime
1929 * - trying to get a total order on the element nodes generated
1930 * by the interleaves, order the list of interleave definitions
1931 * following that order.
1932 * - if <text/> is used to handle mixed content, it is better to
1933 * flag this in the define and simplify the runtime checking
1934 * algorithm
1935 */
1936static void
1937xmlRelaxNGComputeInterleaves(xmlRelaxNGDefinePtr def,
1938 xmlRelaxNGParserCtxtPtr ctxt,
1939 xmlChar *name ATTRIBUTE_UNUSED) {
1940 xmlRelaxNGDefinePtr cur;
1941
1942 xmlRelaxNGDefinePtr *list = NULL;
1943 xmlRelaxNGPartitionPtr partitions = NULL;
1944 xmlRelaxNGInterleaveGroupPtr *groups = NULL;
1945 xmlRelaxNGInterleaveGroupPtr group;
1946 int i,j,ret;
1947 int nbgroups = 0;
1948 int nbchild = 0;
1949
1950#ifdef DEBUG_INTERLEAVE
1951 xmlGenericError(xmlGenericErrorContext,
1952 "xmlRelaxNGComputeInterleaves(%s)\n",
1953 name);
1954#endif
1955 cur = def->content;
1956 while (cur != NULL) {
1957 nbchild++;
1958 cur = cur->next;
1959 }
1960
1961#ifdef DEBUG_INTERLEAVE
1962 xmlGenericError(xmlGenericErrorContext, " %d child\n", nbchild);
1963#endif
1964 groups = (xmlRelaxNGInterleaveGroupPtr *)
1965 xmlMalloc(nbchild * sizeof(xmlRelaxNGInterleaveGroupPtr));
1966 if (groups == NULL)
1967 goto error;
1968 cur = def->content;
1969 while (cur != NULL) {
Daniel Veillard154877e2003-01-30 12:17:05 +00001970 groups[nbgroups] = (xmlRelaxNGInterleaveGroupPtr)
1971 xmlMalloc(sizeof(xmlRelaxNGInterleaveGroup));
1972 if (groups[nbgroups] == NULL)
1973 goto error;
1974 groups[nbgroups]->rule = cur;
1975 groups[nbgroups]->defs = xmlRelaxNGGetElements(ctxt, cur);
1976 nbgroups++;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001977 cur = cur->next;
1978 }
1979 list = NULL;
1980#ifdef DEBUG_INTERLEAVE
1981 xmlGenericError(xmlGenericErrorContext, " %d groups\n", nbgroups);
1982#endif
1983
1984 /*
1985 * Let's check that all rules makes a partitions according to 7.4
1986 */
1987 partitions = (xmlRelaxNGPartitionPtr)
1988 xmlMalloc(sizeof(xmlRelaxNGPartition));
1989 if (partitions == NULL)
1990 goto error;
1991 partitions->nbgroups = nbgroups;
1992 for (i = 0;i < nbgroups;i++) {
1993 group = groups[i];
1994 for (j = i+1;j < nbgroups;j++) {
1995 if (groups[j] == NULL)
1996 continue;
1997 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->defs,
1998 groups[j]->defs);
1999 if (ret == 0) {
2000 if (ctxt->error != NULL)
2001 ctxt->error(ctxt->userData,
2002 "Element or text conflicts in interleave\n");
2003 ctxt->nbErrors++;
2004 }
2005 }
2006 }
2007 partitions->groups = groups;
2008
2009 /*
2010 * Free Up the child list, and save the partition list back in the def
2011 */
2012 def->data = partitions;
2013 return;
2014
2015error:
2016 if (ctxt->error != NULL)
2017 ctxt->error(ctxt->userData,
2018 "Out of memory in interleave computation\n");
2019 ctxt->nbErrors++;
2020 if (list == NULL)
2021 xmlFree(list);
2022 if (groups != NULL) {
2023 for (i = 0;i < nbgroups;i++)
2024 if (groups[i] != NULL) {
2025 if (groups[i]->defs != NULL)
2026 xmlFree(groups[i]->defs);
2027 xmlFree(groups[i]);
2028 }
2029 xmlFree(groups);
2030 }
2031 xmlRelaxNGFreePartition(partitions);
2032}
2033
2034/**
2035 * xmlRelaxNGParseInterleave:
2036 * @ctxt: a Relax-NG parser context
2037 * @node: the data node.
2038 *
2039 * parse the content of a RelaxNG interleave node.
2040 *
2041 * Returns the definition pointer or NULL in case of error
2042 */
2043static xmlRelaxNGDefinePtr
2044xmlRelaxNGParseInterleave(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2045 xmlRelaxNGDefinePtr def = NULL;
2046 xmlRelaxNGDefinePtr last = NULL, cur;
2047 xmlNodePtr child;
2048
2049 def = xmlRelaxNGNewDefine(ctxt, node);
2050 if (def == NULL) {
2051 return(NULL);
2052 }
2053 def->type = XML_RELAXNG_INTERLEAVE;
2054
2055 if (ctxt->interleaves == NULL)
2056 ctxt->interleaves = xmlHashCreate(10);
2057 if (ctxt->interleaves == NULL) {
2058 if (ctxt->error != NULL)
2059 ctxt->error(ctxt->userData,
2060 "Failed to create interleaves hash table\n");
2061 ctxt->nbErrors++;
2062 } else {
2063 char name[32];
2064
2065 snprintf(name, 32, "interleave%d", ctxt->nbInterleaves++);
2066 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST name, def) < 0) {
2067 if (ctxt->error != NULL)
2068 ctxt->error(ctxt->userData,
2069 "Failed to add %s to hash table\n", name);
2070 ctxt->nbErrors++;
2071 }
2072 }
2073 child = node->children;
2074 while (child != NULL) {
2075 if (IS_RELAXNG(child, "element")) {
2076 cur = xmlRelaxNGParseElement(ctxt, child);
2077 } else {
2078 cur = xmlRelaxNGParsePattern(ctxt, child);
2079 }
2080 if (cur != NULL) {
2081 cur->parent = def;
2082 if (last == NULL) {
2083 def->content = last = cur;
2084 } else {
2085 last->next = cur;
2086 last = cur;
2087 }
2088 }
2089 child = child->next;
2090 }
2091
2092 return(def);
2093}
Daniel Veillard6eadf632003-01-23 18:29:16 +00002094
2095/**
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002096 * xmlRelaxNGParseInclude:
2097 * @ctxt: a Relax-NG parser context
2098 * @node: the include node
2099 *
2100 * Integrate the content of an include node in the current grammar
2101 *
2102 * Returns 0 in case of success or -1 in case of error
2103 */
2104static int
2105xmlRelaxNGParseInclude(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2106 xmlRelaxNGIncludePtr incl;
2107 xmlNodePtr root;
2108 int ret = 0, tmp;
2109
2110 incl = node->_private;
2111 if (incl == NULL) {
2112 if (ctxt->error != NULL)
2113 ctxt->error(ctxt->userData,
2114 "Include node has no data\n");
2115 ctxt->nbErrors++;
2116 return(-1);
2117 }
2118 root = xmlDocGetRootElement(incl->doc);
2119 if (root == NULL) {
2120 if (ctxt->error != NULL)
2121 ctxt->error(ctxt->userData,
2122 "Include document is empty\n");
2123 ctxt->nbErrors++;
2124 return(-1);
2125 }
2126 if (!xmlStrEqual(root->name, BAD_CAST "grammar")) {
2127 if (ctxt->error != NULL)
2128 ctxt->error(ctxt->userData,
2129 "Include document root is not a grammar\n");
2130 ctxt->nbErrors++;
2131 return(-1);
2132 }
2133
2134 /*
2135 * Merge the definition from both the include and the internal list
2136 */
2137 if (root->children != NULL) {
2138 tmp = xmlRelaxNGParseGrammarContent(ctxt, root->children);
2139 if (tmp != 0)
2140 ret = -1;
2141 }
2142 if (node->children != NULL) {
2143 tmp = xmlRelaxNGParseGrammarContent(ctxt, node->children);
2144 if (tmp != 0)
2145 ret = -1;
2146 }
2147 return(ret);
2148}
2149
2150/**
Daniel Veillard276be4a2003-01-24 01:03:34 +00002151 * xmlRelaxNGParseDefine:
2152 * @ctxt: a Relax-NG parser context
2153 * @node: the define node
2154 *
2155 * parse the content of a RelaxNG define element node.
2156 *
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002157 * Returns 0 in case of success or -1 in case of error
Daniel Veillard276be4a2003-01-24 01:03:34 +00002158 */
2159static int
2160xmlRelaxNGParseDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2161 xmlChar *name;
2162 int ret = 0, tmp;
2163 xmlRelaxNGDefinePtr def;
2164 const xmlChar *olddefine;
2165
2166 name = xmlGetProp(node, BAD_CAST "name");
2167 if (name == NULL) {
2168 if (ctxt->error != NULL)
2169 ctxt->error(ctxt->userData,
2170 "define has no name\n");
2171 ctxt->nbErrors++;
2172 } else {
2173 def = xmlRelaxNGNewDefine(ctxt, node);
2174 if (def == NULL) {
2175 xmlFree(name);
2176 return(-1);
2177 }
2178 def->type = XML_RELAXNG_DEF;
2179 def->name = name;
2180 if (node->children == NULL) {
2181 if (ctxt->error != NULL)
2182 ctxt->error(ctxt->userData,
2183 "define has no children\n");
2184 ctxt->nbErrors++;
2185 } else {
2186 olddefine = ctxt->define;
2187 ctxt->define = name;
Daniel Veillard154877e2003-01-30 12:17:05 +00002188 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard276be4a2003-01-24 01:03:34 +00002189 ctxt->define = olddefine;
2190 }
2191 if (ctxt->grammar->defs == NULL)
2192 ctxt->grammar->defs = xmlHashCreate(10);
2193 if (ctxt->grammar->defs == NULL) {
2194 if (ctxt->error != NULL)
2195 ctxt->error(ctxt->userData,
2196 "Could not create definition hash\n");
2197 ctxt->nbErrors++;
2198 ret = -1;
2199 xmlRelaxNGFreeDefine(def);
2200 } else {
2201 tmp = xmlHashAddEntry(ctxt->grammar->defs, name, def);
2202 if (tmp < 0) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002203 xmlRelaxNGDefinePtr prev;
2204
2205 prev = xmlHashLookup(ctxt->grammar->defs, name);
2206 if (prev == NULL) {
2207 if (ctxt->error != NULL)
2208 ctxt->error(ctxt->userData,
2209 "Internal error on define aggregation of %s\n",
2210 name);
2211 ctxt->nbErrors++;
2212 ret = -1;
2213 xmlRelaxNGFreeDefine(def);
2214 } else {
2215 while (prev->nextHash != NULL)
2216 prev = prev->nextHash;
2217 prev->nextHash = def;
2218 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002219 }
2220 }
2221 }
2222 return(ret);
2223}
2224
2225/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00002226 * xmlRelaxNGParsePattern:
2227 * @ctxt: a Relax-NG parser context
2228 * @node: the pattern node.
2229 *
2230 * parse the content of a RelaxNG pattern node.
2231 *
Daniel Veillard276be4a2003-01-24 01:03:34 +00002232 * Returns the definition pointer or NULL in case of error or if no
2233 * pattern is generated.
Daniel Veillard6eadf632003-01-23 18:29:16 +00002234 */
2235static xmlRelaxNGDefinePtr
2236xmlRelaxNGParsePattern(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2237 xmlRelaxNGDefinePtr def = NULL;
2238
2239 if (IS_RELAXNG(node, "element")) {
2240 def = xmlRelaxNGParseElement(ctxt, node);
2241 } else if (IS_RELAXNG(node, "attribute")) {
2242 def = xmlRelaxNGParseAttribute(ctxt, node);
2243 } else if (IS_RELAXNG(node, "empty")) {
2244 def = xmlRelaxNGNewDefine(ctxt, node);
2245 if (def == NULL)
2246 return(NULL);
2247 def->type = XML_RELAXNG_EMPTY;
2248 } else if (IS_RELAXNG(node, "text")) {
2249 def = xmlRelaxNGNewDefine(ctxt, node);
2250 if (def == NULL)
2251 return(NULL);
2252 def->type = XML_RELAXNG_TEXT;
2253 if (node->children != NULL) {
2254 if (ctxt->error != NULL)
2255 ctxt->error(ctxt->userData, "text: had a child node\n");
2256 ctxt->nbErrors++;
2257 }
2258 } else if (IS_RELAXNG(node, "zeroOrMore")) {
2259 def = xmlRelaxNGNewDefine(ctxt, node);
2260 if (def == NULL)
2261 return(NULL);
2262 def->type = XML_RELAXNG_ZEROORMORE;
Daniel Veillard154877e2003-01-30 12:17:05 +00002263 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002264 } else if (IS_RELAXNG(node, "oneOrMore")) {
2265 def = xmlRelaxNGNewDefine(ctxt, node);
2266 if (def == NULL)
2267 return(NULL);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002268 def->type = XML_RELAXNG_ONEORMORE;
Daniel Veillard154877e2003-01-30 12:17:05 +00002269 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002270 } else if (IS_RELAXNG(node, "optional")) {
2271 def = xmlRelaxNGNewDefine(ctxt, node);
2272 if (def == NULL)
2273 return(NULL);
2274 def->type = XML_RELAXNG_OPTIONAL;
Daniel Veillard154877e2003-01-30 12:17:05 +00002275 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002276 } else if (IS_RELAXNG(node, "choice")) {
2277 def = xmlRelaxNGNewDefine(ctxt, node);
2278 if (def == NULL)
2279 return(NULL);
2280 def->type = XML_RELAXNG_CHOICE;
Daniel Veillard154877e2003-01-30 12:17:05 +00002281 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002282 } else if (IS_RELAXNG(node, "group")) {
2283 def = xmlRelaxNGNewDefine(ctxt, node);
2284 if (def == NULL)
2285 return(NULL);
2286 def->type = XML_RELAXNG_GROUP;
Daniel Veillard154877e2003-01-30 12:17:05 +00002287 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002288 } else if (IS_RELAXNG(node, "ref")) {
2289 def = xmlRelaxNGNewDefine(ctxt, node);
2290 if (def == NULL)
2291 return(NULL);
2292 def->type = XML_RELAXNG_REF;
2293 def->name = xmlGetProp(node, BAD_CAST "name");
2294 if (def->name == NULL) {
2295 if (ctxt->error != NULL)
2296 ctxt->error(ctxt->userData,
2297 "ref has no name\n");
2298 ctxt->nbErrors++;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002299 } else {
2300 if ((ctxt->define != NULL) &&
2301 (xmlStrEqual(ctxt->define, def->name))) {
2302 if (ctxt->error != NULL)
2303 ctxt->error(ctxt->userData,
2304 "Recursive reference to %s not in an element\n",
2305 def->name);
2306 ctxt->nbErrors++;
2307 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002308 }
2309 if (node->children != NULL) {
2310 if (ctxt->error != NULL)
2311 ctxt->error(ctxt->userData,
2312 "ref is not empty\n");
2313 ctxt->nbErrors++;
2314 }
2315 if (ctxt->grammar->refs == NULL)
2316 ctxt->grammar->refs = xmlHashCreate(10);
2317 if (ctxt->grammar->refs == NULL) {
2318 if (ctxt->error != NULL)
2319 ctxt->error(ctxt->userData,
2320 "Could not create references hash\n");
2321 ctxt->nbErrors++;
2322 xmlRelaxNGFreeDefine(def);
2323 def = NULL;
2324 } else {
2325 int tmp;
2326
2327 tmp = xmlHashAddEntry(ctxt->grammar->refs, def->name, def);
2328 if (tmp < 0) {
2329 xmlRelaxNGDefinePtr prev;
2330
2331 prev = (xmlRelaxNGDefinePtr)
2332 xmlHashLookup(ctxt->grammar->refs, def->name);
2333 if (prev == NULL) {
2334 if (ctxt->error != NULL)
2335 ctxt->error(ctxt->userData,
2336 "Internal error refs definitions '%s'\n",
2337 def->name);
2338 ctxt->nbErrors++;
2339 xmlRelaxNGFreeDefine(def);
2340 def = NULL;
2341 } else {
2342 def->nextHash = prev->nextHash;
2343 prev->nextHash = def;
2344 }
2345 }
2346 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002347 } else if (IS_RELAXNG(node, "data")) {
2348 def = xmlRelaxNGParseData(ctxt, node);
Daniel Veillard276be4a2003-01-24 01:03:34 +00002349 } else if (IS_RELAXNG(node, "define")) {
2350 xmlRelaxNGParseDefine(ctxt, node);
2351 def = NULL;
Daniel Veillardedc91922003-01-26 00:52:04 +00002352 } else if (IS_RELAXNG(node, "value")) {
2353 def = xmlRelaxNGParseValue(ctxt, node);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002354 } else if (IS_RELAXNG(node, "list")) {
2355 def = xmlRelaxNGNewDefine(ctxt, node);
2356 if (def == NULL)
2357 return(NULL);
2358 def->type = XML_RELAXNG_LIST;
Daniel Veillard154877e2003-01-30 12:17:05 +00002359 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002360 } else if (IS_RELAXNG(node, "interleave")) {
2361 def = xmlRelaxNGParseInterleave(ctxt, node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002362 } else if (IS_RELAXNG(node, "externalRef")) {
2363 xmlRelaxNGDocumentPtr docu;
2364 xmlNodePtr root;
2365
2366 docu = node->_private;
2367 if (docu != NULL) {
2368 def = xmlRelaxNGNewDefine(ctxt, node);
2369 if (def == NULL)
2370 return(NULL);
2371 def->type = XML_RELAXNG_EXTERNALREF;
2372
2373 if (docu->content == NULL) {
2374 /*
2375 * Then do the parsing for good
2376 */
2377 root = xmlDocGetRootElement(docu->doc);
2378 if (root == NULL) {
2379 if (ctxt->error != NULL)
2380 ctxt->error(ctxt->userData,
2381 "xmlRelaxNGParse: %s is empty\n",
2382 ctxt->URL);
2383 ctxt->nbErrors++;
2384 return (NULL);
2385 }
2386 docu->schema = xmlRelaxNGParseDocument(ctxt, root);
2387 if ((docu->schema != NULL) &&
2388 (docu->schema->topgrammar != NULL)) {
2389 docu->content = docu->schema->topgrammar->start;
2390 }
2391 }
2392 def->content = docu->content;
2393 } else {
2394 def = NULL;
2395 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002396 } else if (IS_RELAXNG(node, "notAllowed")) {
2397 def = xmlRelaxNGNewDefine(ctxt, node);
2398 if (def == NULL)
2399 return(NULL);
2400 def->type = XML_RELAXNG_NOT_ALLOWED;
2401 if (node->children != NULL) {
2402 if (ctxt->error != NULL)
2403 ctxt->error(ctxt->userData,
2404 "xmlRelaxNGParse: notAllowed element is not empty\n");
2405 ctxt->nbErrors++;
2406 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002407 } else {
2408 TODO
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002409 def = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002410 }
2411 return(def);
2412}
2413
2414/**
2415 * xmlRelaxNGParseAttribute:
2416 * @ctxt: a Relax-NG parser context
2417 * @node: the element node
2418 *
2419 * parse the content of a RelaxNG attribute node.
2420 *
2421 * Returns the definition pointer or NULL in case of error.
2422 */
2423static xmlRelaxNGDefinePtr
2424xmlRelaxNGParseAttribute(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2425 xmlRelaxNGDefinePtr ret, cur, last;
2426 xmlNodePtr child;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002427 int old_flags;
2428
2429 ret = xmlRelaxNGNewDefine(ctxt, node);
2430 if (ret == NULL)
2431 return(NULL);
2432 ret->type = XML_RELAXNG_ATTRIBUTE;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002433 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002434 child = node->children;
2435 if (child == NULL) {
2436 if (ctxt->error != NULL)
2437 ctxt->error(ctxt->userData,
2438 "xmlRelaxNGParseattribute: attribute has no children\n");
2439 ctxt->nbErrors++;
2440 return(ret);
2441 }
2442 old_flags = ctxt->flags;
2443 ctxt->flags |= XML_RELAXNG_IN_ATTRIBUTE;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002444 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
2445 if (cur != NULL)
2446 child = child->next;
2447
Daniel Veillard6eadf632003-01-23 18:29:16 +00002448 last = NULL;
2449 while (child != NULL) {
2450 cur = xmlRelaxNGParsePattern(ctxt, child);
2451 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002452 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002453 switch (cur->type) {
2454 case XML_RELAXNG_EMPTY:
2455 case XML_RELAXNG_NOT_ALLOWED:
2456 case XML_RELAXNG_TEXT:
2457 case XML_RELAXNG_ELEMENT:
2458 case XML_RELAXNG_DATATYPE:
2459 case XML_RELAXNG_VALUE:
2460 case XML_RELAXNG_LIST:
2461 case XML_RELAXNG_REF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002462 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00002463 case XML_RELAXNG_DEF:
2464 case XML_RELAXNG_ONEORMORE:
2465 case XML_RELAXNG_ZEROORMORE:
2466 case XML_RELAXNG_OPTIONAL:
2467 case XML_RELAXNG_CHOICE:
2468 case XML_RELAXNG_GROUP:
2469 case XML_RELAXNG_INTERLEAVE:
2470 if (last == NULL) {
2471 ret->content = last = cur;
2472 } else {
2473 if ((last->type == XML_RELAXNG_ELEMENT) &&
2474 (ret->content == last)) {
2475 ret->content = xmlRelaxNGNewDefine(ctxt, node);
2476 if (ret->content != NULL) {
2477 ret->content->type = XML_RELAXNG_GROUP;
2478 ret->content->content = last;
2479 } else {
2480 ret->content = last;
2481 }
2482 }
2483 last->next = cur;
2484 last = cur;
2485 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002486 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002487 break;
2488 case XML_RELAXNG_ATTRIBUTE:
2489 cur->next = ret->attrs;
2490 ret->attrs = cur;
2491 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002492 case XML_RELAXNG_START:
2493 TODO
2494 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002495 }
2496 }
2497 child = child->next;
2498 }
2499 ctxt->flags = old_flags;
2500 return(ret);
2501}
2502
2503/**
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002504 * xmlRelaxNGParseExceptNameClass:
2505 * @ctxt: a Relax-NG parser context
2506 * @node: the except node
2507 *
2508 * parse the content of a RelaxNG nameClass node.
2509 *
2510 * Returns the definition pointer or NULL in case of error.
2511 */
2512static xmlRelaxNGDefinePtr
2513xmlRelaxNGParseExceptNameClass(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2514 TODO
2515 return(NULL);
2516}
2517
2518/**
2519 * xmlRelaxNGParseNameClass:
2520 * @ctxt: a Relax-NG parser context
2521 * @node: the nameClass node
2522 * @def: the current definition
2523 *
2524 * parse the content of a RelaxNG nameClass node.
2525 *
2526 * Returns the definition pointer or NULL in case of error.
2527 */
2528static xmlRelaxNGDefinePtr
2529xmlRelaxNGParseNameClass(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
2530 xmlRelaxNGDefinePtr def) {
2531 xmlRelaxNGDefinePtr ret = def;
2532 xmlChar *val;
2533
2534 if (IS_RELAXNG(node, "name")) {
2535 val = xmlNodeGetContent(node);
2536 ret->name = val;
2537 val = xmlGetProp(node, BAD_CAST "ns");
2538 ret->ns = val;
2539 } else if (IS_RELAXNG(node, "anyName")) {
2540 ret->name = NULL;
2541 ret->ns = NULL;
2542 if (node->children != NULL) {
2543 ret->nameClass =
2544 xmlRelaxNGParseExceptNameClass(ctxt, node->children);
2545 }
2546 } else if (IS_RELAXNG(node, "nsName")) {
2547 ret->name = NULL;
2548 ret->ns = xmlGetProp(node, BAD_CAST "ns");
2549 if (ret->ns == NULL) {
2550 if (ctxt->error != NULL)
2551 ctxt->error(ctxt->userData,
2552 "nsName has no ns attribute\n");
2553 ctxt->nbErrors++;
2554 }
2555 if (node->children != NULL) {
2556 ret->nameClass =
2557 xmlRelaxNGParseExceptNameClass(ctxt, node->children);
2558 }
2559 } else if (IS_RELAXNG(node, "choice")) {
2560 TODO
2561 } else {
2562 if (ctxt->error != NULL)
2563 ctxt->error(ctxt->userData,
2564 "expecting name, anyName, nsName or choice : got %s\n",
2565 node->name);
2566 ctxt->nbErrors++;
2567 return(NULL);
2568 }
2569 return(ret);
2570}
2571
2572/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00002573 * xmlRelaxNGParseElement:
2574 * @ctxt: a Relax-NG parser context
2575 * @node: the element node
2576 *
2577 * parse the content of a RelaxNG element node.
2578 *
2579 * Returns the definition pointer or NULL in case of error.
2580 */
2581static xmlRelaxNGDefinePtr
2582xmlRelaxNGParseElement(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2583 xmlRelaxNGDefinePtr ret, cur, last;
2584 xmlNodePtr child;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002585 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002586
2587 ret = xmlRelaxNGNewDefine(ctxt, node);
2588 if (ret == NULL)
2589 return(NULL);
2590 ret->type = XML_RELAXNG_ELEMENT;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002591 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002592 child = node->children;
2593 if (child == NULL) {
2594 if (ctxt->error != NULL)
2595 ctxt->error(ctxt->userData,
2596 "xmlRelaxNGParseElement: element has no children\n");
2597 ctxt->nbErrors++;
2598 return(ret);
2599 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002600 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
2601 if (cur != NULL)
2602 child = child->next;
2603
Daniel Veillard6eadf632003-01-23 18:29:16 +00002604 if (child == NULL) {
2605 if (ctxt->error != NULL)
2606 ctxt->error(ctxt->userData,
2607 "xmlRelaxNGParseElement: element has no content\n");
2608 ctxt->nbErrors++;
2609 return(ret);
2610 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002611 olddefine = ctxt->define;
2612 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002613 last = NULL;
2614 while (child != NULL) {
2615 cur = xmlRelaxNGParsePattern(ctxt, child);
2616 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002617 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002618 switch (cur->type) {
2619 case XML_RELAXNG_EMPTY:
2620 case XML_RELAXNG_NOT_ALLOWED:
2621 case XML_RELAXNG_TEXT:
2622 case XML_RELAXNG_ELEMENT:
2623 case XML_RELAXNG_DATATYPE:
2624 case XML_RELAXNG_VALUE:
2625 case XML_RELAXNG_LIST:
2626 case XML_RELAXNG_REF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002627 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00002628 case XML_RELAXNG_DEF:
2629 case XML_RELAXNG_ZEROORMORE:
2630 case XML_RELAXNG_ONEORMORE:
2631 case XML_RELAXNG_OPTIONAL:
2632 case XML_RELAXNG_CHOICE:
2633 case XML_RELAXNG_GROUP:
2634 case XML_RELAXNG_INTERLEAVE:
2635 if (last == NULL) {
2636 ret->content = last = cur;
2637 } else {
2638 if ((last->type == XML_RELAXNG_ELEMENT) &&
2639 (ret->content == last)) {
2640 ret->content = xmlRelaxNGNewDefine(ctxt, node);
2641 if (ret->content != NULL) {
2642 ret->content->type = XML_RELAXNG_GROUP;
2643 ret->content->content = last;
2644 } else {
2645 ret->content = last;
2646 }
2647 }
2648 last->next = cur;
2649 last = cur;
2650 }
2651 break;
2652 case XML_RELAXNG_ATTRIBUTE:
2653 cur->next = ret->attrs;
2654 ret->attrs = cur;
2655 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002656 case XML_RELAXNG_START:
2657 TODO
2658 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002659 }
2660 }
2661 child = child->next;
2662 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002663 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002664 return(ret);
2665}
2666
2667/**
2668 * xmlRelaxNGParsePatterns:
2669 * @ctxt: a Relax-NG parser context
2670 * @nodes: list of nodes
Daniel Veillard154877e2003-01-30 12:17:05 +00002671 * @group: use an implicit <group> for elements
Daniel Veillard6eadf632003-01-23 18:29:16 +00002672 *
2673 * parse the content of a RelaxNG start node.
2674 *
2675 * Returns the definition pointer or NULL in case of error.
2676 */
2677static xmlRelaxNGDefinePtr
Daniel Veillard154877e2003-01-30 12:17:05 +00002678xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes,
2679 int group) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002680 xmlRelaxNGDefinePtr def = NULL, last = NULL, cur, parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002681
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002682 parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002683 while (nodes != NULL) {
2684 if (IS_RELAXNG(nodes, "element")) {
2685 cur = xmlRelaxNGParseElement(ctxt, nodes);
2686 if (def == NULL) {
2687 def = last = cur;
2688 } else {
Daniel Veillard154877e2003-01-30 12:17:05 +00002689 if ((group == 1) && (def->type == XML_RELAXNG_ELEMENT) &&
2690 (def == last)) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00002691 def = xmlRelaxNGNewDefine(ctxt, nodes);
2692 def->type = XML_RELAXNG_GROUP;
2693 def->content = last;
2694 }
2695 last->next = cur;
2696 last = cur;
2697 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002698 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002699 } else {
2700 cur = xmlRelaxNGParsePattern(ctxt, nodes);
2701 if (def == NULL) {
2702 def = last = cur;
2703 } else {
2704 last->next = cur;
2705 last = cur;
2706 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002707 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002708 }
2709 nodes = nodes->next;
2710 }
2711 return(def);
2712}
2713
2714/**
2715 * xmlRelaxNGParseStart:
2716 * @ctxt: a Relax-NG parser context
2717 * @nodes: start children nodes
2718 *
2719 * parse the content of a RelaxNG start node.
2720 *
2721 * Returns 0 in case of success, -1 in case of error
2722 */
2723static int
2724xmlRelaxNGParseStart(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
2725 int ret = 0;
2726 xmlRelaxNGDefinePtr def = NULL;
2727
2728 while (nodes != NULL) {
2729 if (IS_RELAXNG(nodes, "empty")) {
2730 TODO
2731 xmlElemDump(stdout, nodes->doc, nodes);
2732 } else if (IS_RELAXNG(nodes, "notAllowed")) {
2733 TODO
2734 xmlElemDump(stdout, nodes->doc, nodes);
2735 } else {
Daniel Veillard154877e2003-01-30 12:17:05 +00002736 def = xmlRelaxNGParsePatterns(ctxt, nodes, 1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002737 ctxt->grammar->start = def;
2738 }
2739 nodes = nodes->next;
2740 }
2741 return(ret);
2742}
2743
2744/**
2745 * xmlRelaxNGParseGrammarContent:
2746 * @ctxt: a Relax-NG parser context
2747 * @nodes: grammar children nodes
2748 *
2749 * parse the content of a RelaxNG grammar node.
2750 *
2751 * Returns 0 in case of success, -1 in case of error
2752 */
2753static int
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002754xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes)
Daniel Veillard6eadf632003-01-23 18:29:16 +00002755{
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002756 int ret = 0, tmp;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002757
2758 if (nodes == NULL) {
2759 if (ctxt->error != NULL)
2760 ctxt->error(ctxt->userData,
2761 "grammar has no children\n");
2762 ctxt->nbErrors++;
2763 return(-1);
2764 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002765 while (nodes != NULL) {
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002766 if (IS_RELAXNG(nodes, "start")) {
2767 if (nodes->children == NULL) {
2768 if (ctxt->error != NULL)
2769 ctxt->error(ctxt->userData,
2770 "grammar has no children\n");
2771 ctxt->nbErrors++;
2772 } else {
2773 tmp = xmlRelaxNGParseStart(ctxt, nodes->children);
2774 if (tmp != 0)
2775 ret = -1;
2776 }
2777 } else if (IS_RELAXNG(nodes, "define")) {
2778 tmp = xmlRelaxNGParseDefine(ctxt, nodes);
2779 if (tmp != 0)
2780 ret = -1;
2781 } else if (IS_RELAXNG(nodes, "include")) {
2782 tmp = xmlRelaxNGParseInclude(ctxt, nodes);
2783 if (tmp != 0)
2784 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002785 } else {
2786 if (ctxt->error != NULL)
2787 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002788 "grammar has unexpected child %s\n", nodes->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002789 ctxt->nbErrors++;
2790 ret = -1;
2791 }
2792 nodes = nodes->next;
2793 }
2794 return (ret);
2795}
2796
2797/**
2798 * xmlRelaxNGCheckReference:
2799 * @ref: the ref
2800 * @ctxt: a Relax-NG parser context
2801 * @name: the name associated to the defines
2802 *
2803 * Applies the 4.17. combine attribute rule for all the define
2804 * element of a given grammar using the same name.
2805 */
2806static void
2807xmlRelaxNGCheckReference(xmlRelaxNGDefinePtr ref,
2808 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
2809 xmlRelaxNGGrammarPtr grammar;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002810 xmlRelaxNGDefinePtr def, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002811
2812 grammar = ctxt->grammar;
2813 if (grammar == NULL) {
2814 if (ctxt->error != NULL)
2815 ctxt->error(ctxt->userData,
2816 "Internal error: no grammar in CheckReference %s\n",
2817 name);
2818 ctxt->nbErrors++;
2819 return;
2820 }
2821 if (ref->content != NULL) {
2822 if (ctxt->error != NULL)
2823 ctxt->error(ctxt->userData,
2824 "Internal error: reference has content in CheckReference %s\n",
2825 name);
2826 ctxt->nbErrors++;
2827 return;
2828 }
2829 if (grammar->defs != NULL) {
2830 def = xmlHashLookup(grammar->defs, name);
2831 if (def != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00002832 cur = ref;
2833 while (cur != NULL) {
2834 cur->content = def;
2835 cur = cur->nextHash;
2836 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002837 } else {
2838 TODO
2839 }
2840 }
2841 /*
2842 * TODO: make a closure and verify there is no loop !
2843 */
2844}
2845
2846/**
2847 * xmlRelaxNGCheckCombine:
2848 * @define: the define(s) list
2849 * @ctxt: a Relax-NG parser context
2850 * @name: the name associated to the defines
2851 *
2852 * Applies the 4.17. combine attribute rule for all the define
2853 * element of a given grammar using the same name.
2854 */
2855static void
2856xmlRelaxNGCheckCombine(xmlRelaxNGDefinePtr define,
2857 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
2858 xmlChar *combine;
2859 int choiceOrInterleave = -1;
2860 int missing = 0;
2861 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
2862
2863 if (define->nextHash == NULL)
2864 return;
2865 cur = define;
2866 while (cur != NULL) {
2867 combine = xmlGetProp(cur->node, BAD_CAST "combine");
2868 if (combine != NULL) {
2869 if (xmlStrEqual(combine, BAD_CAST "choice")) {
2870 if (choiceOrInterleave == -1)
2871 choiceOrInterleave = 1;
2872 else if (choiceOrInterleave == 0) {
2873 if (ctxt->error != NULL)
2874 ctxt->error(ctxt->userData,
2875 "Defines for %s use both 'choice' and 'interleave'\n",
2876 name);
2877 ctxt->nbErrors++;
2878 }
Daniel Veillard154877e2003-01-30 12:17:05 +00002879 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00002880 if (choiceOrInterleave == -1)
2881 choiceOrInterleave = 0;
2882 else if (choiceOrInterleave == 1) {
2883 if (ctxt->error != NULL)
2884 ctxt->error(ctxt->userData,
2885 "Defines for %s use both 'choice' and 'interleave'\n",
2886 name);
2887 ctxt->nbErrors++;
2888 }
2889 } else {
2890 if (ctxt->error != NULL)
2891 ctxt->error(ctxt->userData,
2892 "Defines for %s use unknown combine value '%s''\n",
2893 name, combine);
2894 ctxt->nbErrors++;
2895 }
2896 xmlFree(combine);
2897 } else {
2898 if (missing == 0)
2899 missing = 1;
2900 else {
2901 if (ctxt->error != NULL)
2902 ctxt->error(ctxt->userData,
2903 "Some defines for %s lacks the combine attribute\n",
2904 name);
2905 ctxt->nbErrors++;
2906 }
2907 }
2908
2909 cur = cur->nextHash;
2910 }
2911#ifdef DEBUG
2912 xmlGenericError(xmlGenericErrorContext,
2913 "xmlRelaxNGCheckCombine(): merging %s defines: %d\n",
2914 name, choiceOrInterleave);
2915#endif
2916 if (choiceOrInterleave == -1)
2917 choiceOrInterleave = 0;
2918 cur = xmlRelaxNGNewDefine(ctxt, define->node);
2919 if (cur == NULL)
2920 return;
2921 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00002922 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard154877e2003-01-30 12:17:05 +00002923 else
2924 cur->type = XML_RELAXNG_CHOICE;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002925 tmp = define;
2926 last = NULL;
2927 while (tmp != NULL) {
2928 if (tmp->content != NULL) {
2929 if (tmp->content->next != NULL) {
2930 /*
2931 * we need first to create a wrapper.
2932 */
2933 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
2934 if (tmp2 == NULL)
2935 break;
2936 tmp2->type = XML_RELAXNG_GROUP;
2937 tmp2->content = tmp->content;
2938 } else {
2939 tmp2 = tmp->content;
2940 }
2941 if (last == NULL) {
2942 cur->content = tmp2;
2943 } else {
2944 last->next = tmp2;
2945 }
2946 last = tmp2;
2947 tmp->content = NULL;
2948 }
2949 tmp = tmp->nextHash;
2950 }
2951 define->content = cur;
Daniel Veillard154877e2003-01-30 12:17:05 +00002952 if (choiceOrInterleave == 0) {
2953 if (ctxt->interleaves == NULL)
2954 ctxt->interleaves = xmlHashCreate(10);
2955 if (ctxt->interleaves == NULL) {
2956 if (ctxt->error != NULL)
2957 ctxt->error(ctxt->userData,
2958 "Failed to create interleaves hash table\n");
2959 ctxt->nbErrors++;
2960 } else {
2961 char tmpname[32];
2962
2963 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
2964 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
2965 if (ctxt->error != NULL)
2966 ctxt->error(ctxt->userData,
2967 "Failed to add %s to hash table\n", tmpname);
2968 ctxt->nbErrors++;
2969 }
2970 }
2971 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002972}
2973
2974/**
2975 * xmlRelaxNGCombineStart:
2976 * @ctxt: a Relax-NG parser context
2977 * @grammar: the grammar
2978 *
2979 * Applies the 4.17. combine rule for all the start
2980 * element of a given grammar.
2981 */
2982static void
2983xmlRelaxNGCombineStart(xmlRelaxNGParserCtxtPtr ctxt,
2984 xmlRelaxNGGrammarPtr grammar) {
2985 xmlRelaxNGDefinePtr starts;
2986 xmlChar *combine;
2987 int choiceOrInterleave = -1;
2988 int missing = 0;
2989 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
2990
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002991 starts = grammar->startList;
2992 if ((starts == NULL) || (starts->nextHash == NULL))
Daniel Veillard6eadf632003-01-23 18:29:16 +00002993 return;
2994 cur = starts;
2995 while (cur != NULL) {
2996 combine = xmlGetProp(cur->node, BAD_CAST "combine");
2997 if (combine != NULL) {
2998 if (xmlStrEqual(combine, BAD_CAST "choice")) {
2999 if (choiceOrInterleave == -1)
3000 choiceOrInterleave = 1;
3001 else if (choiceOrInterleave == 0) {
3002 if (ctxt->error != NULL)
3003 ctxt->error(ctxt->userData,
3004 "<start> use both 'choice' and 'interleave'\n");
3005 ctxt->nbErrors++;
3006 }
3007 } else if (xmlStrEqual(combine, BAD_CAST "choice")) {
3008 if (choiceOrInterleave == -1)
3009 choiceOrInterleave = 0;
3010 else if (choiceOrInterleave == 1) {
3011 if (ctxt->error != NULL)
3012 ctxt->error(ctxt->userData,
3013 "<start> use both 'choice' and 'interleave'\n");
3014 ctxt->nbErrors++;
3015 }
3016 } else {
3017 if (ctxt->error != NULL)
3018 ctxt->error(ctxt->userData,
3019 "<start> uses unknown combine value '%s''\n", combine);
3020 ctxt->nbErrors++;
3021 }
3022 xmlFree(combine);
3023 } else {
3024 if (missing == 0)
3025 missing = 1;
3026 else {
3027 if (ctxt->error != NULL)
3028 ctxt->error(ctxt->userData,
3029 "Some <start> elements lacks the combine attribute\n");
3030 ctxt->nbErrors++;
3031 }
3032 }
3033
3034 cur = cur->nextHash;
3035 }
3036#ifdef DEBUG
3037 xmlGenericError(xmlGenericErrorContext,
3038 "xmlRelaxNGCombineStart(): merging <start>: %d\n",
3039 choiceOrInterleave);
3040#endif
3041 if (choiceOrInterleave == -1)
3042 choiceOrInterleave = 0;
3043 cur = xmlRelaxNGNewDefine(ctxt, starts->node);
3044 if (cur == NULL)
3045 return;
3046 if (choiceOrInterleave == 0)
3047 cur->type = XML_RELAXNG_CHOICE;
3048 else
3049 cur->type = XML_RELAXNG_INTERLEAVE;
3050 tmp = starts;
3051 last = NULL;
3052 while (tmp != NULL) {
3053 if (tmp->content != NULL) {
3054 if (tmp->content->next != NULL) {
3055 /*
3056 * we need first to create a wrapper.
3057 */
3058 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
3059 if (tmp2 == NULL)
3060 break;
3061 tmp2->type = XML_RELAXNG_GROUP;
3062 tmp2->content = tmp->content;
3063 } else {
3064 tmp2 = tmp->content;
3065 }
3066 if (last == NULL) {
3067 cur->content = tmp2;
3068 } else {
3069 last->next = tmp2;
3070 }
3071 last = tmp2;
3072 tmp->content = NULL;
3073 }
3074 tmp = tmp->nextHash;
3075 }
3076 starts->content = cur;
3077}
3078
3079/**
3080 * xmlRelaxNGParseGrammar:
3081 * @ctxt: a Relax-NG parser context
3082 * @nodes: grammar children nodes
3083 *
3084 * parse a Relax-NG <grammar> node
3085 *
3086 * Returns the internal xmlRelaxNGGrammarPtr built or
3087 * NULL in case of error
3088 */
3089static xmlRelaxNGGrammarPtr
3090xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
3091 xmlRelaxNGGrammarPtr ret, tmp, old;
3092
Daniel Veillard6eadf632003-01-23 18:29:16 +00003093 ret = xmlRelaxNGNewGrammar(ctxt);
3094 if (ret == NULL)
3095 return(NULL);
3096
3097 /*
3098 * Link the new grammar in the tree
3099 */
3100 ret->parent = ctxt->grammar;
3101 if (ctxt->grammar != NULL) {
3102 tmp = ctxt->grammar->children;
3103 if (tmp == NULL) {
3104 ctxt->grammar->children = ret;
3105 } else {
3106 while (tmp->next != NULL)
3107 tmp = tmp->next;
3108 tmp->next = ret;
3109 }
3110 }
3111
3112 old = ctxt->grammar;
3113 ctxt->grammar = ret;
3114 xmlRelaxNGParseGrammarContent(ctxt, nodes);
3115 ctxt->grammar = ret;
3116
3117 /*
3118 * Apply 4.17 mergingd rules to defines and starts
3119 */
3120 xmlRelaxNGCombineStart(ctxt, ret);
3121 if (ret->defs != NULL) {
3122 xmlHashScan(ret->defs, (xmlHashScanner) xmlRelaxNGCheckCombine,
3123 ctxt);
3124 }
3125
3126 /*
3127 * link together defines and refs in this grammar
3128 */
3129 if (ret->refs != NULL) {
3130 xmlHashScan(ret->refs, (xmlHashScanner) xmlRelaxNGCheckReference,
3131 ctxt);
3132 }
3133 ctxt->grammar = old;
3134 return(ret);
3135}
3136
3137/**
3138 * xmlRelaxNGParseDocument:
3139 * @ctxt: a Relax-NG parser context
3140 * @node: the root node of the RelaxNG schema
3141 *
3142 * parse a Relax-NG definition resource and build an internal
3143 * xmlRelaxNG struture which can be used to validate instances.
3144 *
3145 * Returns the internal XML RelaxNG structure built or
3146 * NULL in case of error
3147 */
3148static xmlRelaxNGPtr
3149xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3150 xmlRelaxNGPtr schema = NULL;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003151 const xmlChar *olddefine;
Daniel Veillarde431a272003-01-29 23:02:33 +00003152 xmlRelaxNGGrammarPtr old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003153
3154 if ((ctxt == NULL) || (node == NULL))
3155 return (NULL);
3156
3157 schema = xmlRelaxNGNewRelaxNG(ctxt);
3158 if (schema == NULL)
3159 return(NULL);
3160
Daniel Veillard276be4a2003-01-24 01:03:34 +00003161 olddefine = ctxt->define;
3162 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003163 if (IS_RELAXNG(node, "grammar")) {
3164 schema->topgrammar = xmlRelaxNGParseGrammar(ctxt, node->children);
3165 } else {
3166 schema->topgrammar = xmlRelaxNGNewGrammar(ctxt);
3167 if (schema->topgrammar == NULL) {
3168 return(schema);
3169 }
3170 schema->topgrammar->parent = NULL;
Daniel Veillarde431a272003-01-29 23:02:33 +00003171 old = ctxt->grammar;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003172 ctxt->grammar = schema->topgrammar;
3173 xmlRelaxNGParseStart(ctxt, node);
Daniel Veillarde431a272003-01-29 23:02:33 +00003174 if (old != NULL)
3175 ctxt->grammar = old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003176 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003177 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003178
3179#ifdef DEBUG
3180 if (schema == NULL)
3181 xmlGenericError(xmlGenericErrorContext,
3182 "xmlRelaxNGParseDocument() failed\n");
3183#endif
3184
3185 return (schema);
3186}
3187
3188/************************************************************************
3189 * *
3190 * Reading RelaxNGs *
3191 * *
3192 ************************************************************************/
3193
3194/**
3195 * xmlRelaxNGNewParserCtxt:
3196 * @URL: the location of the schema
3197 *
3198 * Create an XML RelaxNGs parse context for that file/resource expected
3199 * to contain an XML RelaxNGs file.
3200 *
3201 * Returns the parser context or NULL in case of error
3202 */
3203xmlRelaxNGParserCtxtPtr
3204xmlRelaxNGNewParserCtxt(const char *URL) {
3205 xmlRelaxNGParserCtxtPtr ret;
3206
3207 if (URL == NULL)
3208 return(NULL);
3209
3210 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
3211 if (ret == NULL) {
3212 xmlGenericError(xmlGenericErrorContext,
3213 "Failed to allocate new schama parser context for %s\n", URL);
3214 return (NULL);
3215 }
3216 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
3217 ret->URL = xmlStrdup((const xmlChar *)URL);
3218 return (ret);
3219}
3220
3221/**
3222 * xmlRelaxNGNewMemParserCtxt:
3223 * @buffer: a pointer to a char array containing the schemas
3224 * @size: the size of the array
3225 *
3226 * Create an XML RelaxNGs parse context for that memory buffer expected
3227 * to contain an XML RelaxNGs file.
3228 *
3229 * Returns the parser context or NULL in case of error
3230 */
3231xmlRelaxNGParserCtxtPtr
3232xmlRelaxNGNewMemParserCtxt(const char *buffer, int size) {
3233 xmlRelaxNGParserCtxtPtr ret;
3234
3235 if ((buffer == NULL) || (size <= 0))
3236 return(NULL);
3237
3238 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
3239 if (ret == NULL) {
3240 xmlGenericError(xmlGenericErrorContext,
3241 "Failed to allocate new schama parser context\n");
3242 return (NULL);
3243 }
3244 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
3245 ret->buffer = buffer;
3246 ret->size = size;
3247 return (ret);
3248}
3249
3250/**
3251 * xmlRelaxNGFreeParserCtxt:
3252 * @ctxt: the schema parser context
3253 *
3254 * Free the resources associated to the schema parser context
3255 */
3256void
3257xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxtPtr ctxt) {
3258 if (ctxt == NULL)
3259 return;
3260 if (ctxt->URL != NULL)
3261 xmlFree(ctxt->URL);
3262 if (ctxt->doc != NULL)
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003263 xmlFreeDoc(ctxt->document);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003264 if (ctxt->interleaves != NULL)
3265 xmlHashFree(ctxt->interleaves, NULL);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003266 if (ctxt->documents != NULL)
3267 xmlHashFree(ctxt->documents, (xmlHashDeallocator)
3268 xmlRelaxNGFreeDocument);
3269 if (ctxt->docTab != NULL)
3270 xmlFree(ctxt->docTab);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003271 if (ctxt->incTab != NULL)
3272 xmlFree(ctxt->incTab);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003273 xmlFree(ctxt);
3274}
3275
Daniel Veillard6eadf632003-01-23 18:29:16 +00003276/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003277 * xmlRelaxNGCleanupDoc:
3278 * @ctxt: a Relax-NG parser context
3279 * @doc: an xmldocPtr document pointer
Daniel Veillard6eadf632003-01-23 18:29:16 +00003280 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003281 * Cleanup the document from unwanted nodes for parsing, resolve
3282 * Include and externalRef lookups.
Daniel Veillard6eadf632003-01-23 18:29:16 +00003283 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003284 * Returns the cleaned up document or NULL in case of error
Daniel Veillard6eadf632003-01-23 18:29:16 +00003285 */
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003286static xmlDocPtr
3287xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt, xmlDocPtr doc) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003288 xmlNodePtr root, cur, delete;
3289
Daniel Veillard6eadf632003-01-23 18:29:16 +00003290 /*
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003291 * Extract the root
Daniel Veillard6eadf632003-01-23 18:29:16 +00003292 */
3293 root = xmlDocGetRootElement(doc);
3294 if (root == NULL) {
3295 if (ctxt->error != NULL)
3296 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
3297 ctxt->URL);
3298 ctxt->nbErrors++;
3299 return (NULL);
3300 }
3301
3302 /*
3303 * Remove all the blank text nodes
3304 */
3305 delete = NULL;
3306 cur = root;
3307 while (cur != NULL) {
3308 if (delete != NULL) {
3309 xmlUnlinkNode(delete);
3310 xmlFreeNode(delete);
3311 delete = NULL;
3312 }
3313 if (cur->type == XML_ELEMENT_NODE) {
3314 /*
3315 * Simplification 4.1. Annotations
3316 */
3317 if ((cur->ns == NULL) ||
3318 (!xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
3319 delete = cur;
3320 goto skip_children;
3321 } else {
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003322 if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003323 xmlChar *href, *ns, *base, *URL;
3324 xmlRelaxNGDocumentPtr docu;
3325
3326 ns = xmlGetProp(cur, BAD_CAST "ns");
3327 href = xmlGetProp(cur, BAD_CAST "href");
3328 if (href == NULL) {
3329 if (ctxt->error != NULL)
3330 ctxt->error(ctxt->userData,
3331 "xmlRelaxNGParse: externalRef has no href attribute\n");
3332 ctxt->nbErrors++;
3333 delete = cur;
3334 goto skip_children;
3335 }
3336 base = xmlNodeGetBase(cur->doc, cur);
3337 URL = xmlBuildURI(href, base);
3338 if (URL == NULL) {
3339 if (ctxt->error != NULL)
3340 ctxt->error(ctxt->userData,
3341 "Failed to compute URL for externalRef %s\n", href);
3342 ctxt->nbErrors++;
3343 if (href != NULL)
3344 xmlFree(href);
3345 if (base != NULL)
3346 xmlFree(base);
3347 delete = cur;
3348 goto skip_children;
3349 }
3350 if (href != NULL)
3351 xmlFree(href);
3352 if (base != NULL)
3353 xmlFree(base);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003354 docu = xmlRelaxNGLoadExternalRef(ctxt, URL, ns);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003355 if (docu == NULL) {
3356 if (ctxt->error != NULL)
3357 ctxt->error(ctxt->userData,
3358 "Failed to load externalRef %s\n", URL);
3359 ctxt->nbErrors++;
3360 xmlFree(URL);
3361 delete = cur;
3362 goto skip_children;
3363 }
3364 xmlFree(URL);
3365 cur->_private = docu;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003366 } else if (xmlStrEqual(cur->name, BAD_CAST "include")) {
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003367 xmlChar *href, *base, *URL;
3368 xmlRelaxNGIncludePtr incl;
3369
3370 href = xmlGetProp(cur, BAD_CAST "href");
3371 if (href == NULL) {
3372 if (ctxt->error != NULL)
3373 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003374 "xmlRelaxNGParse: include has no href attribute\n");
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003375 ctxt->nbErrors++;
3376 delete = cur;
3377 goto skip_children;
3378 }
3379 base = xmlNodeGetBase(cur->doc, cur);
3380 URL = xmlBuildURI(href, base);
3381 if (URL == NULL) {
3382 if (ctxt->error != NULL)
3383 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003384 "Failed to compute URL for include %s\n", href);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003385 ctxt->nbErrors++;
3386 if (href != NULL)
3387 xmlFree(href);
3388 if (base != NULL)
3389 xmlFree(base);
3390 delete = cur;
3391 goto skip_children;
3392 }
3393 if (href != NULL)
3394 xmlFree(href);
3395 if (base != NULL)
3396 xmlFree(base);
3397 incl = xmlRelaxNGLoadInclude(ctxt, URL, cur);
3398 if (incl == NULL) {
3399 if (ctxt->error != NULL)
3400 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003401 "Failed to load include %s\n", URL);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003402 ctxt->nbErrors++;
3403 xmlFree(URL);
3404 delete = cur;
3405 goto skip_children;
3406 }
3407 xmlFree(URL);
3408 cur->_private = incl;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003409 } else if ((xmlStrEqual(cur->name, BAD_CAST "element")) ||
3410 (xmlStrEqual(cur->name, BAD_CAST "attribute"))) {
3411 xmlChar *name;
3412 xmlNodePtr text = NULL;
3413
3414 /*
3415 * Simplification 4.8. name attribute of element
3416 * and attribute elements
3417 */
3418 name = xmlGetProp(cur, BAD_CAST "name");
3419 if (name != NULL) {
3420 if (cur->children == NULL) {
3421 text = xmlNewChild(cur, cur->ns, BAD_CAST "name",
3422 name);
3423 } else {
3424 xmlNodePtr node;
3425 node = xmlNewNode(cur->ns, BAD_CAST "name");
3426 if (node != NULL) {
3427 xmlAddPrevSibling(cur->children, node);
3428 text = xmlNewText(name);
3429 xmlAddChild(node, text);
3430 text = node;
3431 }
3432 }
3433 xmlUnsetProp(cur, BAD_CAST "name");
3434 xmlFree(name);
3435 }
3436 if (xmlStrEqual(cur->name, BAD_CAST "attribute")) {
3437 if (text == NULL) {
3438 text = cur->children;
3439 while (text != NULL) {
3440 if ((text->type == XML_ELEMENT_NODE) &&
3441 (xmlStrEqual(text->name, BAD_CAST "name")))
3442 break;
3443 text = text->next;
3444 }
3445 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003446 if (text != NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003447 xmlSetProp(text, BAD_CAST "ns", BAD_CAST "");
3448 }
3449 }
3450 } else if ((xmlStrEqual(cur->name, BAD_CAST "name")) ||
3451 (xmlStrEqual(cur->name, BAD_CAST "nsName")) ||
3452 (xmlStrEqual(cur->name, BAD_CAST "value"))) {
3453 /*
3454 * Simplification 4.8. name attribute of element
3455 * and attribute elements
3456 */
3457 if (xmlHasProp(cur, BAD_CAST "ns") == NULL) {
3458 xmlNodePtr node;
3459 xmlChar *ns = NULL;
3460
3461 node = cur->parent;
3462 while ((node != NULL) &&
3463 (node->type == XML_ELEMENT_NODE)) {
3464 ns = xmlGetProp(node, BAD_CAST "ns");
3465 if (ns != NULL) {
3466 break;
3467 }
3468 node = node->parent;
3469 }
3470 if (ns == NULL) {
3471 xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
3472 } else {
3473 xmlSetProp(cur, BAD_CAST "ns", ns);
3474 xmlFree(ns);
3475 }
3476 }
3477 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
3478 xmlChar *name, *local, *prefix;
3479
3480 /*
3481 * Simplification: 4.10. QNames
3482 */
3483 name = xmlNodeGetContent(cur);
3484 if (name != NULL) {
3485 local = xmlSplitQName2(name, &prefix);
3486 if (local != NULL) {
3487 xmlNsPtr ns;
3488
3489 ns = xmlSearchNs(cur->doc, cur, prefix);
3490 if (ns == NULL) {
3491 if (ctxt->error != NULL)
3492 ctxt->error(ctxt->userData,
3493 "xmlRelaxNGParse: no namespace for prefix %s\n", prefix);
3494 ctxt->nbErrors++;
3495 } else {
3496 xmlSetProp(cur, BAD_CAST "ns", ns->href);
3497 xmlNodeSetContent(cur, local);
3498 }
3499 xmlFree(local);
3500 xmlFree(prefix);
3501 }
3502 xmlFree(name);
3503 }
3504 }
3505 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003506 /*
3507 * Thisd is not an else since "include" is transformed
3508 * into a div
3509 */
3510 if (xmlStrEqual(cur->name, BAD_CAST "div")) {
3511 /*
3512 * implements rule 4.11
3513 */
3514 xmlNodePtr child, ins, tmp;
3515
3516 child = cur->children;
3517 ins = child;
3518 while (child != NULL) {
3519 tmp = child->next;
3520 xmlUnlinkNode(child);
3521 ins = xmlAddNextSibling(ins, child);
3522 child = tmp;
3523 }
3524 delete = cur;
3525 goto skip_children;
3526 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003527 }
3528 }
3529 /*
3530 * Simplification 4.2 whitespaces
3531 */
3532 else if (cur->type == XML_TEXT_NODE) {
3533 if (IS_BLANK_NODE(cur)) {
3534 if (cur->parent->type == XML_ELEMENT_NODE) {
3535 if ((!xmlStrEqual(cur->parent->name, BAD_CAST "value")) &&
3536 (!xmlStrEqual(cur->parent->name, BAD_CAST "param")))
3537 delete = cur;
3538 } else {
3539 delete = cur;
3540 goto skip_children;
3541 }
3542 }
3543 } else if (cur->type != XML_CDATA_SECTION_NODE) {
3544 delete = cur;
3545 goto skip_children;
3546 }
3547
3548 /*
3549 * Skip to next node
3550 */
3551 if (cur->children != NULL) {
3552 if ((cur->children->type != XML_ENTITY_DECL) &&
3553 (cur->children->type != XML_ENTITY_REF_NODE) &&
3554 (cur->children->type != XML_ENTITY_NODE)) {
3555 cur = cur->children;
3556 continue;
3557 }
3558 }
3559skip_children:
3560 if (cur->next != NULL) {
3561 cur = cur->next;
3562 continue;
3563 }
3564
3565 do {
3566 cur = cur->parent;
3567 if (cur == NULL)
3568 break;
3569 if (cur == root) {
3570 cur = NULL;
3571 break;
3572 }
3573 if (cur->next != NULL) {
3574 cur = cur->next;
3575 break;
3576 }
3577 } while (cur != NULL);
3578 }
3579 if (delete != NULL) {
3580 xmlUnlinkNode(delete);
3581 xmlFreeNode(delete);
3582 delete = NULL;
3583 }
3584
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003585 return(doc);
3586}
3587
3588/**
3589 * xmlRelaxNGParse:
3590 * @ctxt: a Relax-NG parser context
3591 *
3592 * parse a schema definition resource and build an internal
3593 * XML Shema struture which can be used to validate instances.
3594 * *WARNING* this interface is highly subject to change
3595 *
3596 * Returns the internal XML RelaxNG structure built from the resource or
3597 * NULL in case of error
3598 */
3599xmlRelaxNGPtr
3600xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt)
3601{
3602 xmlRelaxNGPtr ret = NULL;
3603 xmlDocPtr doc;
3604 xmlNodePtr root;
3605
3606 xmlRelaxNGInitTypes();
3607
3608 if (ctxt == NULL)
3609 return (NULL);
3610
3611 /*
3612 * First step is to parse the input document into an DOM/Infoset
3613 */
3614 if (ctxt->URL != NULL) {
3615 doc = xmlParseFile((const char *) ctxt->URL);
3616 if (doc == NULL) {
3617 if (ctxt->error != NULL)
3618 ctxt->error(ctxt->userData,
3619 "xmlRelaxNGParse: could not load %s\n", ctxt->URL);
3620 ctxt->nbErrors++;
3621 return (NULL);
3622 }
3623 } else if (ctxt->buffer != NULL) {
3624 doc = xmlParseMemory(ctxt->buffer, ctxt->size);
3625 if (doc == NULL) {
3626 if (ctxt->error != NULL)
3627 ctxt->error(ctxt->userData,
3628 "xmlRelaxNGParse: could not parse schemas\n");
3629 ctxt->nbErrors++;
3630 return (NULL);
3631 }
3632 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
3633 ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
3634 } else {
3635 if (ctxt->error != NULL)
3636 ctxt->error(ctxt->userData,
3637 "xmlRelaxNGParse: nothing to parse\n");
3638 ctxt->nbErrors++;
3639 return (NULL);
3640 }
3641 ctxt->document = doc;
3642
3643 /*
3644 * Some preprocessing of the document content
3645 */
3646 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
3647 if (doc == NULL) {
3648 xmlFreeDoc(ctxt->document);
3649 ctxt->document = NULL;
3650 return(NULL);
3651 }
3652
Daniel Veillard6eadf632003-01-23 18:29:16 +00003653 /*
3654 * Then do the parsing for good
3655 */
3656 root = xmlDocGetRootElement(doc);
3657 if (root == NULL) {
3658 if (ctxt->error != NULL)
3659 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
3660 ctxt->URL);
3661 ctxt->nbErrors++;
3662 return (NULL);
3663 }
3664 ret = xmlRelaxNGParseDocument(ctxt, root);
3665 if (ret == NULL)
3666 return(NULL);
3667
3668 /*
3669 * Check the ref/defines links
3670 */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003671 /*
3672 * try to preprocess interleaves
3673 */
3674 if (ctxt->interleaves != NULL) {
3675 xmlHashScan(ctxt->interleaves,
3676 (xmlHashScanner)xmlRelaxNGComputeInterleaves, ctxt);
3677 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003678
3679 /*
3680 * if there was a parsing error return NULL
3681 */
3682 if (ctxt->nbErrors > 0) {
3683 xmlRelaxNGFree(ret);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003684 ctxt->document = NULL;
3685 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003686 return(NULL);
3687 }
3688
3689 /*
3690 * Transfer the pointer for cleanup at the schema level.
3691 */
3692 ret->doc = doc;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003693 ctxt->document = NULL;
3694 ret->documents = ctxt->documents;
3695 ctxt->documents = NULL;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003696 ret->includes = ctxt->includes;
3697 ctxt->includes = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003698
3699 return (ret);
3700}
3701
3702/**
3703 * xmlRelaxNGSetParserErrors:
3704 * @ctxt: a Relax-NG validation context
3705 * @err: the error callback
3706 * @warn: the warning callback
3707 * @ctx: contextual data for the callbacks
3708 *
3709 * Set the callback functions used to handle errors for a validation context
3710 */
3711void
3712xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
3713 xmlRelaxNGValidityErrorFunc err,
3714 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
3715 if (ctxt == NULL)
3716 return;
3717 ctxt->error = err;
3718 ctxt->warning = warn;
3719 ctxt->userData = ctx;
3720}
3721/************************************************************************
3722 * *
3723 * Dump back a compiled form *
3724 * *
3725 ************************************************************************/
3726static void xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define);
3727
3728/**
3729 * xmlRelaxNGDumpDefines:
3730 * @output: the file output
3731 * @defines: a list of define structures
3732 *
3733 * Dump a RelaxNG structure back
3734 */
3735static void
3736xmlRelaxNGDumpDefines(FILE * output, xmlRelaxNGDefinePtr defines) {
3737 while (defines != NULL) {
3738 xmlRelaxNGDumpDefine(output, defines);
3739 defines = defines->next;
3740 }
3741}
3742
3743/**
3744 * xmlRelaxNGDumpDefine:
3745 * @output: the file output
3746 * @define: a define structure
3747 *
3748 * Dump a RelaxNG structure back
3749 */
3750static void
3751xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define) {
3752 if (define == NULL)
3753 return;
3754 switch(define->type) {
3755 case XML_RELAXNG_EMPTY:
3756 fprintf(output, "<empty/>\n");
3757 break;
3758 case XML_RELAXNG_NOT_ALLOWED:
3759 fprintf(output, "<notAllowed/>\n");
3760 break;
3761 case XML_RELAXNG_TEXT:
3762 fprintf(output, "<text/>\n");
3763 break;
3764 case XML_RELAXNG_ELEMENT:
3765 fprintf(output, "<element>\n");
3766 if (define->name != NULL) {
3767 fprintf(output, "<name");
3768 if (define->ns != NULL)
3769 fprintf(output, " ns=\"%s\"", define->ns);
3770 fprintf(output, ">%s</name>\n", define->name);
3771 }
3772 xmlRelaxNGDumpDefines(output, define->attrs);
3773 xmlRelaxNGDumpDefines(output, define->content);
3774 fprintf(output, "</element>\n");
3775 break;
3776 case XML_RELAXNG_LIST:
3777 fprintf(output, "<list>\n");
3778 xmlRelaxNGDumpDefines(output, define->content);
3779 fprintf(output, "</list>\n");
3780 break;
3781 case XML_RELAXNG_ONEORMORE:
3782 fprintf(output, "<oneOrMore>\n");
3783 xmlRelaxNGDumpDefines(output, define->content);
3784 fprintf(output, "</oneOrMore>\n");
3785 break;
3786 case XML_RELAXNG_ZEROORMORE:
3787 fprintf(output, "<zeroOrMore>\n");
3788 xmlRelaxNGDumpDefines(output, define->content);
3789 fprintf(output, "</zeroOrMore>\n");
3790 break;
3791 case XML_RELAXNG_CHOICE:
3792 fprintf(output, "<choice>\n");
3793 xmlRelaxNGDumpDefines(output, define->content);
3794 fprintf(output, "</choice>\n");
3795 break;
3796 case XML_RELAXNG_GROUP:
3797 fprintf(output, "<group>\n");
3798 xmlRelaxNGDumpDefines(output, define->content);
3799 fprintf(output, "</group>\n");
3800 break;
3801 case XML_RELAXNG_INTERLEAVE:
3802 fprintf(output, "<interleave>\n");
3803 xmlRelaxNGDumpDefines(output, define->content);
3804 fprintf(output, "</interleave>\n");
3805 break;
3806 case XML_RELAXNG_OPTIONAL:
3807 fprintf(output, "<optional>\n");
3808 xmlRelaxNGDumpDefines(output, define->content);
3809 fprintf(output, "</optional>\n");
3810 break;
3811 case XML_RELAXNG_ATTRIBUTE:
3812 fprintf(output, "<attribute>\n");
3813 xmlRelaxNGDumpDefines(output, define->content);
3814 fprintf(output, "</attribute>\n");
3815 break;
3816 case XML_RELAXNG_DEF:
3817 fprintf(output, "<define");
3818 if (define->name != NULL)
3819 fprintf(output, " name=\"%s\"", define->name);
3820 fprintf(output, ">\n");
3821 xmlRelaxNGDumpDefines(output, define->content);
3822 fprintf(output, "</define>\n");
3823 break;
3824 case XML_RELAXNG_REF:
3825 fprintf(output, "<ref");
3826 if (define->name != NULL)
3827 fprintf(output, " name=\"%s\"", define->name);
3828 fprintf(output, ">\n");
3829 xmlRelaxNGDumpDefines(output, define->content);
3830 fprintf(output, "</ref>\n");
3831 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003832 case XML_RELAXNG_EXTERNALREF:
Daniel Veillarde431a272003-01-29 23:02:33 +00003833 fprintf(output, "<externalRef");
3834 xmlRelaxNGDumpDefines(output, define->content);
3835 fprintf(output, "</externalRef>\n");
3836 break;
3837 case XML_RELAXNG_DATATYPE:
Daniel Veillard6eadf632003-01-23 18:29:16 +00003838 case XML_RELAXNG_VALUE:
3839 TODO
3840 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003841 case XML_RELAXNG_START:
3842 TODO
3843 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003844 }
3845}
3846
3847/**
3848 * xmlRelaxNGDumpGrammar:
3849 * @output: the file output
3850 * @grammar: a grammar structure
3851 * @top: is this a top grammar
3852 *
3853 * Dump a RelaxNG structure back
3854 */
3855static void
3856xmlRelaxNGDumpGrammar(FILE * output, xmlRelaxNGGrammarPtr grammar, int top)
3857{
3858 if (grammar == NULL)
3859 return;
3860
3861 fprintf(output, "<grammar");
3862 if (top)
3863 fprintf(output,
3864 " xmlns=\"http://relaxng.org/ns/structure/1.0\"");
3865 switch(grammar->combine) {
3866 case XML_RELAXNG_COMBINE_UNDEFINED:
3867 break;
3868 case XML_RELAXNG_COMBINE_CHOICE:
3869 fprintf(output, " combine=\"choice\"");
3870 break;
3871 case XML_RELAXNG_COMBINE_INTERLEAVE:
3872 fprintf(output, " combine=\"interleave\"");
3873 break;
3874 default:
3875 fprintf(output, " <!-- invalid combine value -->");
3876 }
3877 fprintf(output, ">\n");
3878 if (grammar->start == NULL) {
3879 fprintf(output, " <!-- grammar had no start -->");
3880 } else {
3881 fprintf(output, "<start>\n");
3882 xmlRelaxNGDumpDefine(output, grammar->start);
3883 fprintf(output, "</start>\n");
3884 }
3885 /* TODO ? Dump the defines ? */
3886 fprintf(output, "</grammar>\n");
3887}
3888
3889/**
3890 * xmlRelaxNGDump:
3891 * @output: the file output
3892 * @schema: a schema structure
3893 *
3894 * Dump a RelaxNG structure back
3895 */
3896void
3897xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema)
3898{
3899 if (schema == NULL) {
3900 fprintf(output, "RelaxNG empty or failed to compile\n");
3901 return;
3902 }
3903 fprintf(output, "RelaxNG: ");
3904 if (schema->doc == NULL) {
3905 fprintf(output, "no document\n");
3906 } else if (schema->doc->URL != NULL) {
3907 fprintf(output, "%s\n", schema->doc->URL);
3908 } else {
3909 fprintf(output, "\n");
3910 }
3911 if (schema->topgrammar == NULL) {
3912 fprintf(output, "RelaxNG has no top grammar\n");
3913 return;
3914 }
3915 xmlRelaxNGDumpGrammar(output, schema->topgrammar, 1);
3916}
3917
3918/************************************************************************
3919 * *
3920 * Validation implementation *
3921 * *
3922 ************************************************************************/
3923static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
3924 xmlRelaxNGDefinePtr define);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00003925static int xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
3926 xmlRelaxNGDefinePtr define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003927
3928/**
3929 * xmlRelaxNGSkipIgnored:
3930 * @ctxt: a schema validation context
3931 * @node: the top node.
3932 *
3933 * Skip ignorable nodes in that context
3934 *
3935 * Returns the new sibling or NULL in case of error.
3936 */
3937static xmlNodePtr
3938xmlRelaxNGSkipIgnored(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
3939 xmlNodePtr node) {
3940 /*
3941 * TODO complete and handle entities
3942 */
3943 while ((node != NULL) &&
3944 ((node->type == XML_COMMENT_NODE) ||
3945 ((node->type == XML_TEXT_NODE) &&
3946 (IS_BLANK_NODE(node))))) {
3947 node = node->next;
3948 }
3949 return(node);
3950}
3951
3952/**
Daniel Veillardedc91922003-01-26 00:52:04 +00003953 * xmlRelaxNGNormalize:
3954 * @ctxt: a schema validation context
3955 * @str: the string to normalize
3956 *
3957 * Implements the normalizeWhiteSpace( s ) function from
3958 * section 6.2.9 of the spec
3959 *
3960 * Returns the new string or NULL in case of error.
3961 */
3962static xmlChar *
3963xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *str) {
3964 xmlChar *ret, *p;
3965 const xmlChar *tmp;
3966 int len;
3967
3968 if (str == NULL)
3969 return(NULL);
3970 tmp = str;
3971 while (*tmp != 0) tmp++;
3972 len = tmp - str;
3973
3974 ret = (xmlChar *) xmlMalloc((len + 1) * sizeof(xmlChar));
3975 if (ret == NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00003976 if (ctxt != NULL) {
3977 VALID_CTXT();
3978 VALID_ERROR("xmlRelaxNGNormalize: out of memory\n");
3979 } else {
3980 xmlGenericError(xmlGenericErrorContext,
3981 "xmlRelaxNGNormalize: out of memory\n");
3982 }
Daniel Veillardedc91922003-01-26 00:52:04 +00003983 return(NULL);
3984 }
3985 p = ret;
3986 while (IS_BLANK(*str)) str++;
3987 while (*str != 0) {
3988 if (IS_BLANK(*str)) {
3989 while (IS_BLANK(*str)) str++;
3990 if (*str == 0)
3991 break;
3992 *p++ = ' ';
3993 } else
3994 *p++ = *str++;
3995 }
3996 *p = 0;
3997 return(ret);
3998}
3999
4000/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004001 * xmlRelaxNGValidateDatatype:
4002 * @ctxt: a Relax-NG validation context
4003 * @value: the string value
4004 * @type: the datatype definition
4005 *
4006 * Validate the given value against the dataype
4007 *
4008 * Returns 0 if the validation succeeded or an error code.
4009 */
4010static int
4011xmlRelaxNGValidateDatatype(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *value,
4012 xmlRelaxNGDefinePtr define) {
4013 int ret;
4014 xmlRelaxNGTypeLibraryPtr lib;
4015
4016 if ((define == NULL) || (define->data == NULL)) {
4017 return(-1);
4018 }
4019 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
4020 if (lib->check != NULL)
4021 ret = lib->check(lib->data, define->name, value);
4022 else
4023 ret = -1;
4024 if (ret < 0) {
4025 VALID_CTXT();
4026 VALID_ERROR("Internal: failed to validate type %s\n", define->name);
4027 return(-1);
4028 } else if (ret == 1) {
4029 ret = 0;
4030 } else {
4031 VALID_CTXT();
4032 VALID_ERROR("Type %s doesn't allow value %s\n", define->name, value);
4033 return(-1);
4034 ret = -1;
4035 }
4036 return(ret);
4037}
4038
4039/**
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004040 * xmlRelaxNGNextValue:
4041 * @ctxt: a Relax-NG validation context
4042 *
4043 * Skip to the next value when validating within a list
4044 *
4045 * Returns 0 if the operation succeeded or an error code.
4046 */
4047static int
4048xmlRelaxNGNextValue(xmlRelaxNGValidCtxtPtr ctxt) {
4049 xmlChar *cur;
4050
4051 cur = ctxt->state->value;
4052 if ((cur == NULL) || (ctxt->state->endvalue == NULL)) {
4053 ctxt->state->value = NULL;
4054 return(0);
4055 }
4056 while (*cur != 0) cur++;
4057 while ((cur != ctxt->state->endvalue) && (*cur == 0)) cur++;
4058 if (cur == ctxt->state->endvalue)
4059 ctxt->state->value = NULL;
4060 else
4061 ctxt->state->value = cur;
4062 return(0);
4063}
4064
4065/**
4066 * xmlRelaxNGValidateValueList:
4067 * @ctxt: a Relax-NG validation context
4068 * @defines: the list of definitions to verify
4069 *
4070 * Validate the given set of definitions for the current value
4071 *
4072 * Returns 0 if the validation succeeded or an error code.
4073 */
4074static int
4075xmlRelaxNGValidateValueList(xmlRelaxNGValidCtxtPtr ctxt,
4076 xmlRelaxNGDefinePtr defines) {
4077 int ret = 0;
4078
4079 while (defines != NULL) {
4080 ret = xmlRelaxNGValidateValue(ctxt, defines);
4081 if (ret != 0)
4082 break;
4083 defines = defines->next;
4084 }
4085 return(ret);
4086}
4087
4088/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004089 * xmlRelaxNGValidateValue:
4090 * @ctxt: a Relax-NG validation context
4091 * @define: the definition to verify
4092 *
4093 * Validate the given definition for the current value
4094 *
4095 * Returns 0 if the validation succeeded or an error code.
4096 */
4097static int
4098xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
4099 xmlRelaxNGDefinePtr define) {
Daniel Veillardedc91922003-01-26 00:52:04 +00004100 int ret = 0, oldflags;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004101 xmlChar *value;
4102
4103 value = ctxt->state->value;
4104 switch (define->type) {
4105 case XML_RELAXNG_EMPTY:
4106 if ((value != NULL) && (value[0] != '0'))
4107 ret = -1;
4108 break;
4109 case XML_RELAXNG_TEXT:
4110 break;
Daniel Veillardedc91922003-01-26 00:52:04 +00004111 case XML_RELAXNG_VALUE: {
4112 if (!xmlStrEqual(value, define->value)) {
4113 if (define->name != NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00004114 xmlRelaxNGTypeLibraryPtr lib;
4115
4116 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
4117 if ((lib != NULL) && (lib->comp != NULL))
4118 ret = lib->comp(lib->data, define->name, value,
4119 define->value);
4120 else
4121 ret = -1;
4122 if (ret < 0) {
4123 VALID_CTXT();
4124 VALID_ERROR("Internal: failed to compare type %s\n",
4125 define->name);
4126 return(-1);
4127 } else if (ret == 1) {
4128 ret = 0;
4129 } else {
4130 ret = -1;
4131 }
Daniel Veillardedc91922003-01-26 00:52:04 +00004132 } else {
4133 xmlChar *nval, *nvalue;
4134
4135 /*
4136 * TODO: trivial optimizations are possible by
4137 * computing at compile-time
4138 */
4139 nval = xmlRelaxNGNormalize(ctxt, define->value);
4140 nvalue = xmlRelaxNGNormalize(ctxt, value);
4141
Daniel Veillardea3f3982003-01-26 19:45:18 +00004142 if ((nval == NULL) || (nvalue == NULL) ||
4143 (!xmlStrEqual(nval, nvalue)))
Daniel Veillardedc91922003-01-26 00:52:04 +00004144 ret = -1;
4145 if (nval != NULL)
4146 xmlFree(nval);
4147 if (nvalue != NULL)
4148 xmlFree(nvalue);
4149 }
4150 }
4151 break;
4152 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004153 case XML_RELAXNG_DATATYPE: {
4154 ret = xmlRelaxNGValidateDatatype(ctxt, value, define);
4155 if (ret == 0)
4156 xmlRelaxNGNextValue(ctxt);
4157
4158 break;
4159 }
4160 case XML_RELAXNG_CHOICE: {
4161 xmlRelaxNGDefinePtr list = define->content;
4162 xmlChar *oldvalue;
4163
4164 oldflags = ctxt->flags;
4165 ctxt->flags |= FLAGS_IGNORABLE;
4166
4167 oldvalue = ctxt->state->value;
4168 while (list != NULL) {
4169 ret = xmlRelaxNGValidateValue(ctxt, list);
4170 if (ret == 0) {
4171 break;
4172 }
4173 ctxt->state->value = oldvalue;
4174 list = list->next;
4175 }
4176 ctxt->flags = oldflags;
4177 break;
4178 }
4179 case XML_RELAXNG_LIST: {
4180 xmlRelaxNGDefinePtr list = define->content;
4181 xmlChar *oldvalue, *oldend, *val, *cur;
4182
4183 oldvalue = ctxt->state->value;
4184 oldend = ctxt->state->endvalue;
4185
4186 val = xmlStrdup(oldvalue);
4187 if (val == NULL) {
4188 VALID_CTXT();
4189 VALID_ERROR("Internal: no state\n");
4190 return(-1);
4191 }
4192 cur = val;
4193 while (*cur != 0) {
4194 if (IS_BLANK(*cur))
4195 *cur = 0;
4196 cur++;
4197 }
4198 ctxt->state->endvalue = cur;
4199 cur = val;
4200 while ((*cur == 0) && (cur != ctxt->state->endvalue)) cur++;
4201
4202 ctxt->state->value = cur;
4203
4204 while (list != NULL) {
4205 ret = xmlRelaxNGValidateValue(ctxt, list);
4206 if (ret != 0) {
4207 break;
4208 }
4209 list = list->next;
4210 }
4211 if ((ret == 0) && (ctxt->state->value != NULL) &&
4212 (ctxt->state->value != ctxt->state->endvalue)) {
4213 VALID_CTXT();
4214 VALID_ERROR("Extra data in list: %s\n", ctxt->state->value);
4215 ret = -1;
4216 }
4217 xmlFree(val);
4218 ctxt->state->value = oldvalue;
4219 ctxt->state->endvalue = oldend;
4220 break;
4221 }
4222 case XML_RELAXNG_ONEORMORE:
4223 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
4224 if (ret != 0) {
4225 break;
4226 }
4227 /* no break on purpose */
4228 case XML_RELAXNG_ZEROORMORE: {
4229 xmlChar *cur, *temp;
4230
4231 oldflags = ctxt->flags;
4232 ctxt->flags |= FLAGS_IGNORABLE;
4233 cur = ctxt->state->value;
4234 temp = NULL;
4235 while ((cur != NULL) && (cur != ctxt->state->endvalue) &&
4236 (temp != cur)) {
4237 temp = cur;
4238 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
4239 if (ret != 0) {
4240 ctxt->state->value = temp;
4241 ret = 0;
4242 break;
4243 }
4244 cur = ctxt->state->value;
4245 }
4246 ctxt->flags = oldflags;
4247 break;
4248 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004249 default:
4250 TODO
4251 ret = -1;
4252 }
4253 return(ret);
4254}
4255
4256/**
4257 * xmlRelaxNGValidateValueContent:
4258 * @ctxt: a Relax-NG validation context
4259 * @defines: the list of definitions to verify
4260 *
4261 * Validate the given definitions for the current value
4262 *
4263 * Returns 0 if the validation succeeded or an error code.
4264 */
4265static int
4266xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt,
4267 xmlRelaxNGDefinePtr defines) {
4268 int ret = 0;
4269
4270 while (defines != NULL) {
4271 ret = xmlRelaxNGValidateValue(ctxt, defines);
4272 if (ret != 0)
4273 break;
4274 defines = defines->next;
4275 }
4276 return(ret);
4277}
4278
4279/**
4280 * xmlRelaxNGValidateAttribute:
4281 * @ctxt: a Relax-NG validation context
4282 * @define: the definition to verify
4283 *
4284 * Validate the given attribute definition for that node
4285 *
4286 * Returns 0 if the validation succeeded or an error code.
4287 */
4288static int
4289xmlRelaxNGValidateAttribute(xmlRelaxNGValidCtxtPtr ctxt,
4290 xmlRelaxNGDefinePtr define) {
4291 int ret = 0, i;
4292 xmlChar *value, *oldvalue;
4293 xmlAttrPtr prop = NULL, tmp;
4294
4295 if (define->name != NULL) {
4296 for (i = 0;i < ctxt->state->nbAttrs;i++) {
4297 tmp = ctxt->state->attrs[i];
4298 if ((tmp != NULL) && (xmlStrEqual(define->name, tmp->name))) {
4299 if ((((define->ns == NULL) || (define->ns[0] == 0)) &&
4300 (tmp->ns == NULL)) ||
4301 ((tmp->ns != NULL) &&
4302 (xmlStrEqual(define->ns, tmp->ns->href)))) {
4303 prop = tmp;
4304 break;
4305 }
4306 }
4307 }
4308 if (prop != NULL) {
4309 value = xmlNodeListGetString(prop->doc, prop->children, 1);
4310 oldvalue = ctxt->state->value;
4311 ctxt->state->value = value;
4312 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
4313 value = ctxt->state->value;
4314 ctxt->state->value = oldvalue;
4315 if (value != NULL)
4316 xmlFree(value);
4317 if (ret == 0) {
4318 /*
4319 * flag the attribute as processed
4320 */
4321 ctxt->state->attrs[i] = NULL;
4322 }
4323 } else {
4324 ret = -1;
4325 }
4326#ifdef DEBUG
4327 xmlGenericError(xmlGenericErrorContext,
4328 "xmlRelaxNGValidateAttribute(%s): %d\n", define->name, ret);
4329#endif
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004330 } else if (define->ns != NULL) {
4331 for (i = 0;i < ctxt->state->nbAttrs;i++) {
4332 tmp = ctxt->state->attrs[i];
4333 if ((tmp != NULL) && (tmp->ns != NULL) &&
4334 (xmlStrEqual(define->ns, tmp->ns->href))) {
4335 prop = tmp;
4336 break;
4337 }
4338 }
4339 if (prop != NULL) {
4340 value = xmlNodeListGetString(prop->doc, prop->children, 1);
4341 oldvalue = ctxt->state->value;
4342 ctxt->state->value = value;
4343 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
4344 value = ctxt->state->value;
4345 ctxt->state->value = oldvalue;
4346 if (value != NULL)
4347 xmlFree(value);
4348 if (ret == 0) {
4349 /*
4350 * flag the attribute as processed
4351 */
4352 ctxt->state->attrs[i] = NULL;
4353 }
4354 } else {
4355 ret = -1;
4356 }
4357#ifdef DEBUG
4358 xmlGenericError(xmlGenericErrorContext,
4359 "xmlRelaxNGValidateAttribute(nsName ns = %s): %d\n",
4360 define->ns, ret);
4361#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00004362 } else {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004363 for (i = 0;i < ctxt->state->nbAttrs;i++) {
4364 tmp = ctxt->state->attrs[i];
4365 if (tmp != NULL) {
4366 prop = tmp;
4367 break;
4368 }
4369 }
4370 if (prop != NULL) {
4371 value = xmlNodeListGetString(prop->doc, prop->children, 1);
4372 oldvalue = ctxt->state->value;
4373 ctxt->state->value = value;
4374 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
4375 value = ctxt->state->value;
4376 ctxt->state->value = oldvalue;
4377 if (value != NULL)
4378 xmlFree(value);
4379 if (ret == 0) {
4380 /*
4381 * flag the attribute as processed
4382 */
4383 ctxt->state->attrs[i] = NULL;
4384 }
4385 } else {
4386 ret = -1;
4387 }
4388#ifdef DEBUG
4389 xmlGenericError(xmlGenericErrorContext,
4390 "xmlRelaxNGValidateAttribute(anyName): %d\n",
4391 ret);
4392#endif
4393
Daniel Veillard6eadf632003-01-23 18:29:16 +00004394 }
4395
4396 return(ret);
4397}
4398
4399/**
4400 * xmlRelaxNGValidateAttributeList:
4401 * @ctxt: a Relax-NG validation context
4402 * @define: the list of definition to verify
4403 *
4404 * Validate the given node against the list of attribute definitions
4405 *
4406 * Returns 0 if the validation succeeded or an error code.
4407 */
4408static int
4409xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt,
4410 xmlRelaxNGDefinePtr defines) {
4411 int ret = 0;
4412 while (defines != NULL) {
4413 if (xmlRelaxNGValidateAttribute(ctxt, defines) != 0)
4414 ret = -1;
4415 defines = defines->next;
4416 }
4417 return(ret);
4418}
4419
4420/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004421 * xmlRelaxNGValidateTryPermutation:
4422 * @ctxt: a Relax-NG validation context
4423 * @groups: the array of groups
4424 * @nbgroups: the number of groups in the array
4425 * @array: the permutation to try
4426 * @len: the size of the set
4427 *
4428 * Try to validate a permutation for the group of definitions.
4429 *
4430 * Returns 0 if the validation succeeded or an error code.
4431 */
4432static int
4433xmlRelaxNGValidateTryPermutation(xmlRelaxNGValidCtxtPtr ctxt,
4434 xmlRelaxNGDefinePtr rule,
4435 xmlNodePtr *array, int len) {
4436 int i, ret;
4437
4438 if (len > 0) {
4439 /*
4440 * One only need the next pointer set-up to do the validation
4441 */
4442 for (i = 0;i < (len - 1);i++)
4443 array[i]->next = array[i + 1];
4444 array[i]->next = NULL;
4445
4446 /*
4447 * Now try to validate the sequence
4448 */
4449 ctxt->state->seq = array[0];
4450 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
4451 } else {
4452 ctxt->state->seq = NULL;
4453 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
4454 }
4455
4456 /*
4457 * the sequence must be fully consumed
4458 */
4459 if (ctxt->state->seq != NULL)
4460 return(-1);
4461
4462 return(ret);
4463}
4464
4465/**
4466 * xmlRelaxNGValidateWalkPermutations:
4467 * @ctxt: a Relax-NG validation context
4468 * @groups: the array of groups
4469 * @nbgroups: the number of groups in the array
4470 * @nodes: the set of nodes
4471 * @array: the current state of the parmutation
4472 * @len: the size of the set
4473 * @level: a pointer to the level variable
4474 * @k: the index in the array to fill
4475 *
4476 * Validate a set of nodes for a groups of definitions, will try the
4477 * full set of permutations
4478 *
4479 * Returns 0 if the validation succeeded or an error code.
4480 */
4481static int
4482xmlRelaxNGValidateWalkPermutations(xmlRelaxNGValidCtxtPtr ctxt,
4483 xmlRelaxNGDefinePtr rule, xmlNodePtr *nodes,
4484 xmlNodePtr *array, int len,
4485 int *level, int k) {
4486 int i, ret;
4487
4488 if ((k >= 0) && (k < len))
4489 array[k] = nodes[*level];
4490 *level = *level + 1;
4491 if (*level == len) {
4492 ret = xmlRelaxNGValidateTryPermutation(ctxt, rule, array, len);
4493 if (ret == 0)
4494 return(0);
4495 } else {
4496 for (i = 0;i < len;i++) {
4497 if (array[i] == NULL) {
4498 ret = xmlRelaxNGValidateWalkPermutations(ctxt, rule,
4499 nodes, array, len, level, i);
4500 if (ret == 0)
4501 return(0);
4502 }
4503 }
4504 }
4505 *level = *level - 1;
4506 array[k] = NULL;
4507 return(-1);
4508}
4509
4510/**
4511 * xmlRelaxNGNodeMatchesList:
4512 * @node: the node
4513 * @list: a NULL terminated array of definitions
4514 *
4515 * Check if a node can be matched by one of the definitions
4516 *
4517 * Returns 1 if matches 0 otherwise
4518 */
4519static int
4520xmlRelaxNGNodeMatchesList(xmlNodePtr node, xmlRelaxNGDefinePtr *list) {
4521 xmlRelaxNGDefinePtr cur;
4522 int i = 0;
4523
4524 if ((node == NULL) || (list == NULL))
4525 return(0);
4526
4527 cur = list[i++];
4528 while (cur != NULL) {
4529 if ((node->type == XML_ELEMENT_NODE) &&
4530 (cur->type == XML_RELAXNG_ELEMENT)) {
4531 if (cur->name == NULL) {
4532 if ((node->ns != NULL) &&
4533 (xmlStrEqual(node->ns->href, cur->ns)))
4534 return(1);
4535 } else if (xmlStrEqual(cur->name, node->name)) {
4536 if ((cur->ns == NULL) || (cur->ns[0] == 0)) {
4537 if (node->ns == NULL)
4538 return(1);
4539 } else {
4540 if ((node->ns != NULL) &&
4541 (xmlStrEqual(node->ns->href, cur->ns)))
4542 return(1);
4543 }
4544 }
4545 } else if ((node->type == XML_TEXT_NODE) &&
4546 (cur->type == XML_RELAXNG_TEXT)) {
4547 return(1);
4548 }
4549 cur = list[i++];
4550 }
4551 return(0);
4552}
4553
4554/**
4555 * xmlRelaxNGValidatePartGroup:
4556 * @ctxt: a Relax-NG validation context
4557 * @groups: the array of groups
4558 * @nbgroups: the number of groups in the array
4559 * @nodes: the set of nodes
4560 * @len: the size of the set of nodes
4561 *
4562 * Validate a set of nodes for a groups of definitions
4563 *
4564 * Returns 0 if the validation succeeded or an error code.
4565 */
4566static int
4567xmlRelaxNGValidatePartGroup(xmlRelaxNGValidCtxtPtr ctxt,
4568 xmlRelaxNGInterleaveGroupPtr *groups,
4569 int nbgroups, xmlNodePtr *nodes, int len) {
Daniel Veillardb08c9812003-01-28 23:09:49 +00004570 int level, ret = -1, i, j, k;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004571 xmlNodePtr *array = NULL, *list, oldseq;
4572 xmlRelaxNGInterleaveGroupPtr group;
4573
4574 list = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
4575 if (list == NULL) {
4576 return(-1);
4577 }
4578 array = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
4579 if (array == NULL) {
4580 xmlFree(list);
4581 return(-1);
4582 }
4583 memset(array, 0, len * sizeof(xmlNodePtr));
4584
4585 /*
4586 * Partition the elements and validate the subsets.
4587 */
4588 oldseq = ctxt->state->seq;
4589 for (i = 0;i < nbgroups;i++) {
4590 group = groups[i];
4591 if (group == NULL)
4592 continue;
4593 k = 0;
4594 for (j = 0;j < len;j++) {
4595 if (nodes[j] == NULL)
4596 continue;
4597 if (xmlRelaxNGNodeMatchesList(nodes[j], group->defs)) {
4598 list[k++] = nodes[j];
4599 nodes[j] = NULL;
4600 }
4601 }
4602 ctxt->state->seq = oldseq;
4603 if (k > 1) {
4604 memset(array, 0, k * sizeof(xmlNodePtr));
Daniel Veillardb08c9812003-01-28 23:09:49 +00004605 level = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004606 ret = xmlRelaxNGValidateWalkPermutations(ctxt, group->rule,
4607 list, array, k, &level, -1);
4608 } else {
4609 ret = xmlRelaxNGValidateTryPermutation(ctxt, group->rule, list, k);
4610 }
4611 if (ret != 0) {
4612 ctxt->state->seq = oldseq;
4613 break;
4614 }
4615 }
4616
4617 xmlFree(list);
4618 xmlFree(array);
4619 return(ret);
4620}
4621
4622/**
4623 * xmlRelaxNGValidateInterleave:
4624 * @ctxt: a Relax-NG validation context
4625 * @define: the definition to verify
4626 *
4627 * Validate an interleave definition for a node.
4628 *
4629 * Returns 0 if the validation succeeded or an error code.
4630 */
4631static int
4632xmlRelaxNGValidateInterleave(xmlRelaxNGValidCtxtPtr ctxt,
4633 xmlRelaxNGDefinePtr define) {
4634 int ret = 0, nbchildren, nbtot, i, j;
4635 xmlRelaxNGPartitionPtr partitions;
4636 xmlNodePtr *children = NULL;
4637 xmlNodePtr *order = NULL;
4638 xmlNodePtr cur;
4639
4640 if (define->data != NULL) {
4641 partitions = (xmlRelaxNGPartitionPtr) define->data;
4642 } else {
4643 VALID_CTXT();
4644 VALID_ERROR("Internal: interleave block has no data\n");
4645 return(-1);
4646 }
4647
4648 /*
4649 * Build the sequence of child and an array preserving the children
4650 * initial order.
4651 */
4652 cur = ctxt->state->seq;
4653 nbchildren = 0;
4654 nbtot = 0;
4655 while (cur != NULL) {
4656 if ((cur->type == XML_COMMENT_NODE) ||
4657 (cur->type == XML_PI_NODE) ||
4658 ((cur->type == XML_TEXT_NODE) &&
4659 (IS_BLANK_NODE(cur)))) {
4660 nbtot++;
4661 } else {
4662 nbchildren++;
4663 nbtot++;
4664 }
4665 cur = cur->next;
4666 }
4667 children = (xmlNodePtr *) xmlMalloc(nbchildren * sizeof(xmlNodePtr));
4668 if (children == NULL)
4669 goto error;
4670 order = (xmlNodePtr *) xmlMalloc(nbtot * sizeof(xmlNodePtr));
4671 if (order == NULL)
4672 goto error;
4673 cur = ctxt->state->seq;
4674 i = 0;
4675 j = 0;
4676 while (cur != NULL) {
4677 if ((cur->type == XML_COMMENT_NODE) ||
4678 (cur->type == XML_PI_NODE) ||
4679 ((cur->type == XML_TEXT_NODE) &&
4680 (IS_BLANK_NODE(cur)))) {
4681 order[j++] = cur;
4682 } else {
4683 order[j++] = cur;
4684 children[i++] = cur;
4685 }
4686 cur = cur->next;
4687 }
4688
4689 /* TODO: retry with a maller set of child if there is a next... */
4690 ret = xmlRelaxNGValidatePartGroup(ctxt, partitions->groups,
4691 partitions->nbgroups, children, nbchildren);
4692 if (ret == 0) {
4693 ctxt->state->seq = NULL;
4694 }
4695
4696 /*
4697 * Cleanup: rebuid the child sequence and free the structure
4698 */
4699 if (order != NULL) {
4700 for (i = 0;i < nbtot;i++) {
4701 if (i == 0)
4702 order[i]->prev = NULL;
4703 else
4704 order[i]->prev = order[i - 1];
4705 if (i == nbtot - 1)
4706 order[i]->next = NULL;
4707 else
4708 order[i]->next = order[i + 1];
4709 }
4710 xmlFree(order);
4711 }
4712 if (children != NULL)
4713 xmlFree(children);
4714
4715 return(ret);
4716
4717error:
4718 if (order != NULL) {
4719 for (i = 0;i < nbtot;i++) {
4720 if (i == 0)
4721 order[i]->prev = NULL;
4722 else
4723 order[i]->prev = order[i - 1];
4724 if (i == nbtot - 1)
4725 order[i]->next = NULL;
4726 else
4727 order[i]->next = order[i + 1];
4728 }
4729 xmlFree(order);
4730 }
4731 if (children != NULL)
4732 xmlFree(children);
4733 return(-1);
4734}
4735
4736/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004737 * xmlRelaxNGValidateElementContent:
4738 * @ctxt: a Relax-NG validation context
4739 * @define: the list of definition to verify
4740 *
4741 * Validate the given node content against the (list) of definitions
4742 *
4743 * Returns 0 if the validation succeeded or an error code.
4744 */
4745static int
4746xmlRelaxNGValidateElementContent(xmlRelaxNGValidCtxtPtr ctxt,
4747 xmlRelaxNGDefinePtr defines) {
4748 int ret = 0, res;
4749
4750 if (ctxt->state == NULL) {
4751 VALID_CTXT();
4752 VALID_ERROR("Internal: no state\n");
4753 return(-1);
4754 }
4755 while (defines != NULL) {
4756 res = xmlRelaxNGValidateDefinition(ctxt, defines);
4757 if (res < 0)
4758 ret = -1;
4759 defines = defines->next;
4760 }
4761
4762 return(ret);
4763}
4764
4765/**
4766 * xmlRelaxNGValidateDefinition:
4767 * @ctxt: a Relax-NG validation context
4768 * @define: the definition to verify
4769 *
4770 * Validate the current node against the definition
4771 *
4772 * Returns 0 if the validation succeeded or an error code.
4773 */
4774static int
4775xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
4776 xmlRelaxNGDefinePtr define) {
4777 xmlNodePtr node;
4778 int ret = 0, i, tmp, oldflags;
4779 xmlRelaxNGValidStatePtr oldstate, state;
4780
4781 if (define == NULL) {
4782 VALID_CTXT();
4783 VALID_ERROR("internal error: define == NULL\n");
4784 return(-1);
4785 }
4786 if (ctxt->state != NULL) {
4787 node = ctxt->state->seq;
4788 } else {
4789 node = NULL;
4790 }
4791 switch (define->type) {
4792 case XML_RELAXNG_EMPTY:
4793 if (node != NULL) {
4794 VALID_CTXT();
4795 VALID_ERROR("Expecting an empty element\n");
4796 return(-1);
4797 }
4798#ifdef DEBUG
4799 xmlGenericError(xmlGenericErrorContext,
4800 "xmlRelaxNGValidateDefinition(): validated empty\n");
4801#endif
4802 return(0);
4803 case XML_RELAXNG_NOT_ALLOWED:
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004804 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004805 case XML_RELAXNG_TEXT:
4806 if (node == NULL)
4807 return(0);
4808 while ((node != NULL) &&
4809 ((node->type == XML_TEXT_NODE) ||
4810 (node->type == XML_CDATA_SECTION_NODE)))
4811 node = node->next;
Daniel Veillard276be4a2003-01-24 01:03:34 +00004812 if (node == ctxt->state->seq) {
4813 VALID_CTXT();
4814 VALID_ERROR("Expecting text content\n");
4815 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004816 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00004817 ctxt->state->seq = node;
4818 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004819 case XML_RELAXNG_ELEMENT:
4820 node = xmlRelaxNGSkipIgnored(ctxt, node);
4821 if ((node == NULL) || (node->type != XML_ELEMENT_NODE)) {
4822 VALID_CTXT();
4823 VALID_ERROR("Expecting an element\n");
4824 return(-1);
4825 }
4826 if (define->name != NULL) {
4827 if (!xmlStrEqual(node->name, define->name)) {
4828 VALID_CTXT();
4829 VALID_ERROR("Expecting element %s, got %s\n",
4830 define->name, node->name);
4831 return(-1);
4832 }
4833 }
4834 if ((define->ns != NULL) && (define->ns[0] != 0)) {
4835 if (node->ns == NULL) {
4836 VALID_CTXT();
4837 VALID_ERROR("Expecting a namespace for element %s\n",
4838 node->name);
4839 return(-1);
4840 } else if (!xmlStrEqual(node->ns->href, define->ns)) {
4841 VALID_CTXT();
4842 VALID_ERROR("Expecting element %s has wrong namespace: expecting %s\n",
4843 node->name, define->ns);
4844 return(-1);
4845 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004846 } else if (define->name != NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00004847 if (node->ns != NULL) {
4848 VALID_CTXT();
4849 VALID_ERROR("Expecting no namespace for element %s\n",
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004850 define->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004851 return(-1);
4852 }
4853 }
4854
4855 state = xmlRelaxNGNewValidState(ctxt, node);
4856 if (state == NULL) {
4857 return(-1);
4858 }
4859
4860 oldstate = ctxt->state;
4861 ctxt->state = state;
4862 if (define->attrs != NULL) {
4863 tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
4864 if (tmp != 0)
4865 ret = -1;
4866 }
4867 if (define->content != NULL) {
4868 tmp = xmlRelaxNGValidateElementContent(ctxt, define->content);
4869 if (tmp != 0)
4870 ret = -1;
4871 }
4872 state = ctxt->state;
4873 if (state->seq != NULL) {
4874 state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq);
4875 if (state->seq != NULL) {
4876 VALID_CTXT();
4877 VALID_ERROR("Extra content for element %s\n",
4878 node->name);
4879 ret = -1;
4880 }
4881 }
4882 for (i = 0;i < state->nbAttrs;i++) {
4883 if (state->attrs[i] != NULL) {
4884 VALID_CTXT();
Daniel Veillardea3f3982003-01-26 19:45:18 +00004885 VALID_ERROR("Invalid attribute %s for element %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00004886 state->attrs[i]->name, node->name);
4887 ret = -1;
4888 }
4889 }
4890 ctxt->state = oldstate;
4891 xmlRelaxNGFreeValidState(state);
4892 if (oldstate != NULL)
4893 oldstate->seq = node->next;
4894
4895
4896#ifdef DEBUG
4897 xmlGenericError(xmlGenericErrorContext,
4898 "xmlRelaxNGValidateDefinition(): validated %s : %d\n",
4899 node->name, ret);
4900#endif
4901 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004902 case XML_RELAXNG_OPTIONAL:
4903 oldflags = ctxt->flags;
4904 ctxt->flags |= FLAGS_IGNORABLE;
4905 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
4906 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
4907 if (ret != 0) {
4908 xmlRelaxNGFreeValidState(ctxt->state);
4909 ctxt->state = oldstate;
4910 ret = 0;
4911 break;
4912 }
4913 xmlRelaxNGFreeValidState(oldstate);
4914 ctxt->flags = oldflags;
4915 ret = 0;
4916 break;
4917 case XML_RELAXNG_ONEORMORE:
4918 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
4919 if (ret != 0) {
4920 break;
4921 }
4922 /* no break on purpose */
Daniel Veillard276be4a2003-01-24 01:03:34 +00004923 case XML_RELAXNG_ZEROORMORE: {
4924 xmlNodePtr cur, temp;
4925
Daniel Veillard6eadf632003-01-23 18:29:16 +00004926 oldflags = ctxt->flags;
4927 ctxt->flags |= FLAGS_IGNORABLE;
Daniel Veillard276be4a2003-01-24 01:03:34 +00004928 cur = ctxt->state->seq;
4929 temp = NULL;
4930 while ((cur != NULL) && (temp != cur)) {
4931 temp = cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004932 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
4933 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
4934 if (ret != 0) {
4935 xmlRelaxNGFreeValidState(ctxt->state);
4936 ctxt->state = oldstate;
4937 ret = 0;
4938 break;
4939 }
4940 xmlRelaxNGFreeValidState(oldstate);
Daniel Veillard276be4a2003-01-24 01:03:34 +00004941 cur = ctxt->state->seq;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004942 }
4943 ctxt->flags = oldflags;
4944 break;
Daniel Veillard276be4a2003-01-24 01:03:34 +00004945 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004946 case XML_RELAXNG_CHOICE: {
4947 xmlRelaxNGDefinePtr list = define->content;
4948
4949 oldflags = ctxt->flags;
4950 ctxt->flags |= FLAGS_IGNORABLE;
4951
4952 while (list != NULL) {
4953 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
4954 ret = xmlRelaxNGValidateDefinition(ctxt, list);
4955 if (ret == 0) {
4956 xmlRelaxNGFreeValidState(oldstate);
4957 break;
4958 }
4959 xmlRelaxNGFreeValidState(ctxt->state);
4960 ctxt->state = oldstate;
4961 list = list->next;
4962 }
4963 ctxt->flags = oldflags;
4964 break;
4965 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004966 case XML_RELAXNG_DEF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00004967 case XML_RELAXNG_GROUP: {
4968 xmlRelaxNGDefinePtr list = define->content;
4969
4970 while (list != NULL) {
4971 ret = xmlRelaxNGValidateDefinition(ctxt, list);
4972 if (ret != 0)
4973 break;
4974 list = list->next;
4975 }
4976 break;
4977 }
4978 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004979 ret = xmlRelaxNGValidateInterleave(ctxt, define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004980 break;
4981 case XML_RELAXNG_ATTRIBUTE:
4982 ret = xmlRelaxNGValidateAttribute(ctxt, define);
4983 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004984 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00004985 case XML_RELAXNG_REF:
4986 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
4987 break;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004988 case XML_RELAXNG_DATATYPE: {
4989 xmlChar *content;
4990
4991 content = xmlNodeGetContent(node);
4992 ret = xmlRelaxNGValidateDatatype(ctxt, content, define);
4993 if (ret == -1) {
4994 VALID_CTXT();
4995 VALID_ERROR("internal error validating %s\n", define->name);
4996 } else if (ret == 0) {
4997 ctxt->state->seq = node->next;
4998 }
4999 /*
5000 * TODO cover the problems with
5001 * <p>12<!-- comment -->34</p>
5002 * TODO detect full element coverage at compilation time.
5003 */
5004 if ((node != NULL) && (node->next != NULL)) {
5005 VALID_CTXT();
5006 VALID_ERROR("The data does not cover the full element %s\n",
5007 node->parent->name);
5008 ret = -1;
5009 }
5010 if (content != NULL)
5011 xmlFree(content);
5012 break;
5013 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00005014 case XML_RELAXNG_VALUE: {
5015 xmlChar *content;
5016 xmlChar *oldvalue;
5017
5018 content = xmlNodeGetContent(node);
5019 oldvalue = ctxt->state->value;
5020 ctxt->state->value = content;
5021 ret = xmlRelaxNGValidateValue(ctxt, define);
5022 ctxt->state->value = oldvalue;
5023 if (ret == -1) {
5024 VALID_CTXT();
5025 VALID_ERROR("internal error validating %s\n", define->name);
5026 } else if (ret == 0) {
5027 ctxt->state->seq = node->next;
5028 }
5029 /*
5030 * TODO cover the problems with
5031 * <p>12<!-- comment -->34</p>
5032 * TODO detect full element coverage at compilation time.
5033 */
5034 if ((node != NULL) && (node->next != NULL)) {
5035 VALID_CTXT();
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005036 VALID_ERROR("The value does not cover the full element %s\n",
Daniel Veillardea3f3982003-01-26 19:45:18 +00005037 node->parent->name);
5038 ret = -1;
5039 }
5040 if (content != NULL)
5041 xmlFree(content);
5042 break;
5043 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005044 case XML_RELAXNG_LIST: {
5045 xmlChar *content;
5046 xmlChar *oldvalue, *oldendvalue;
5047 int len;
Daniel Veillardea3f3982003-01-26 19:45:18 +00005048
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005049 content = xmlNodeGetContent(node);
5050 len = xmlStrlen(content);
5051 oldvalue = ctxt->state->value;
5052 oldendvalue = ctxt->state->endvalue;
5053 ctxt->state->value = content;
5054 ctxt->state->endvalue = content + len;
5055 ret = xmlRelaxNGValidateValue(ctxt, define);
5056 ctxt->state->value = oldvalue;
5057 ctxt->state->endvalue = oldendvalue;
5058 if (ret == -1) {
5059 VALID_CTXT();
5060 VALID_ERROR("internal error validating list\n");
5061 } else if (ret == 0) {
5062 ctxt->state->seq = node->next;
5063 }
5064 /*
5065 * TODO cover the problems with
5066 * <p>12<!-- comment -->34</p>
5067 * TODO detect full element coverage at compilation time.
5068 */
5069 if ((node != NULL) && (node->next != NULL)) {
5070 VALID_CTXT();
5071 VALID_ERROR("The list does not cover the full element %s\n",
5072 node->parent->name);
5073 ret = -1;
5074 }
5075 if (content != NULL)
5076 xmlFree(content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005077 break;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005078 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005079 case XML_RELAXNG_START:
5080 TODO
5081 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005082 }
5083 return(ret);
5084}
5085
5086/**
5087 * xmlRelaxNGValidateDocument:
5088 * @ctxt: a Relax-NG validation context
5089 * @doc: the document
5090 *
5091 * Validate the given document
5092 *
5093 * Returns 0 if the validation succeeded or an error code.
5094 */
5095static int
5096xmlRelaxNGValidateDocument(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
5097 int ret;
5098 xmlRelaxNGPtr schema;
5099 xmlRelaxNGGrammarPtr grammar;
5100 xmlRelaxNGValidStatePtr state;
5101
5102 if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL))
5103 return(-1);
5104
5105 schema = ctxt->schema;
5106 grammar = schema->topgrammar;
5107 if (grammar == NULL) {
5108 VALID_CTXT();
5109 VALID_ERROR("No top grammar defined\n");
5110 return(-1);
5111 }
5112 state = xmlRelaxNGNewValidState(ctxt, NULL);
5113 ctxt->state = state;
5114 ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start);
5115 state = ctxt->state;
5116 if ((state != NULL) && (state->seq != NULL)) {
5117 xmlNodePtr node;
5118
5119 node = state->seq;
5120 node = xmlRelaxNGSkipIgnored(ctxt, node);
5121 if (node != NULL) {
5122 VALID_CTXT();
5123 VALID_ERROR("extra data on the document\n");
5124 ret = -1;
5125 }
5126 }
5127 xmlRelaxNGFreeValidState(state);
5128
5129 return(ret);
5130}
5131
5132/************************************************************************
5133 * *
5134 * Validation interfaces *
5135 * *
5136 ************************************************************************/
5137/**
5138 * xmlRelaxNGNewValidCtxt:
5139 * @schema: a precompiled XML RelaxNGs
5140 *
5141 * Create an XML RelaxNGs validation context based on the given schema
5142 *
5143 * Returns the validation context or NULL in case of error
5144 */
5145xmlRelaxNGValidCtxtPtr
5146xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema) {
5147 xmlRelaxNGValidCtxtPtr ret;
5148
5149 ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt));
5150 if (ret == NULL) {
5151 xmlGenericError(xmlGenericErrorContext,
5152 "Failed to allocate new schama validation context\n");
5153 return (NULL);
5154 }
5155 memset(ret, 0, sizeof(xmlRelaxNGValidCtxt));
5156 ret->schema = schema;
5157 return (ret);
5158}
5159
5160/**
5161 * xmlRelaxNGFreeValidCtxt:
5162 * @ctxt: the schema validation context
5163 *
5164 * Free the resources associated to the schema validation context
5165 */
5166void
5167xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt) {
5168 if (ctxt == NULL)
5169 return;
5170 xmlFree(ctxt);
5171}
5172
5173/**
5174 * xmlRelaxNGSetValidErrors:
5175 * @ctxt: a Relax-NG validation context
5176 * @err: the error function
5177 * @warn: the warning function
5178 * @ctx: the functions context
5179 *
5180 * Set the error and warning callback informations
5181 */
5182void
5183xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
5184 xmlRelaxNGValidityErrorFunc err,
5185 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
5186 if (ctxt == NULL)
5187 return;
5188 ctxt->error = err;
5189 ctxt->warning = warn;
5190 ctxt->userData = ctx;
5191}
5192
5193/**
5194 * xmlRelaxNGValidateDoc:
5195 * @ctxt: a Relax-NG validation context
5196 * @doc: a parsed document tree
5197 *
5198 * Validate a document tree in memory.
5199 *
5200 * Returns 0 if the document is valid, a positive error code
5201 * number otherwise and -1 in case of internal or API error.
5202 */
5203int
5204xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
5205 int ret;
5206
5207 if ((ctxt == NULL) || (doc == NULL))
5208 return(-1);
5209
5210 ctxt->doc = doc;
5211
5212 ret = xmlRelaxNGValidateDocument(ctxt, doc);
5213 return(ret);
5214}
5215
5216#endif /* LIBXML_SCHEMAS_ENABLED */
5217