blob: 220edba11565eed83c0ed03fbec4fa599cfd6243 [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 */
126 xmlRelaxNGDefinePtr nextHash;/* next define in defs/refs hash tables */
127};
128
129/**
130 * _xmlRelaxNG:
131 *
132 * A RelaxNGs definition
133 */
134struct _xmlRelaxNG {
135 xmlRelaxNGGrammarPtr topgrammar;
136 xmlDocPtr doc;
137
138 xmlHashTablePtr defs; /* define */
139 xmlHashTablePtr refs; /* references */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000140 xmlHashTablePtr documents; /* all the documents loaded */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000141 xmlHashTablePtr includes; /* all the includes loaded */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000142 void *_private; /* unused by the library for users or bindings */
143};
144
145typedef enum {
146 XML_RELAXNG_ERR_OK = 0,
147 XML_RELAXNG_ERR_NOROOT = 1,
148 XML_RELAXNG_ERR_
149} xmlRelaxNGValidError;
150
151#define XML_RELAXNG_IN_ATTRIBUTE 1
152
153struct _xmlRelaxNGParserCtxt {
154 void *userData; /* user specific data block */
155 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
156 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
157 xmlRelaxNGValidError err;
158
159 xmlRelaxNGPtr schema; /* The schema in use */
160 xmlRelaxNGGrammarPtr grammar; /* the current grammar */
161 int flags; /* parser flags */
162 int nbErrors; /* number of errors at parse time */
163 int nbWarnings; /* number of warnings at parse time */
Daniel Veillard276be4a2003-01-24 01:03:34 +0000164 const xmlChar *define; /* the current define scope */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000165 xmlRelaxNGDefinePtr def; /* the current define */
166
167 int nbInterleaves;
168 xmlHashTablePtr interleaves; /* keep track of all the interleaves */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000169
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000170 xmlHashTablePtr documents; /* all the documents loaded */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000171 xmlHashTablePtr includes; /* all the includes loaded */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000172 xmlChar *URL;
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000173 xmlDocPtr document;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000174
175 const char *buffer;
176 int size;
177
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000178 /* the document stack */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000179 xmlRelaxNGDocumentPtr doc; /* Current parsed external ref */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000180 int docNr; /* Depth of the parsing stack */
181 int docMax; /* Max depth of the parsing stack */
182 xmlRelaxNGDocumentPtr *docTab; /* array of docs */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000183
184 /* the include stack */
185 xmlRelaxNGIncludePtr inc; /* Current parsed include */
186 int incNr; /* Depth of the include parsing stack */
187 int incMax; /* Max depth of the parsing stack */
188 xmlRelaxNGIncludePtr *incTab; /* array of incs */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000189};
190
191#define FLAGS_IGNORABLE 1
192#define FLAGS_NEGATIVE 2
193
194/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000195 * xmlRelaxNGInterleaveGroup:
196 *
197 * A RelaxNGs partition set associated to lists of definitions
198 */
199typedef struct _xmlRelaxNGInterleaveGroup xmlRelaxNGInterleaveGroup;
200typedef xmlRelaxNGInterleaveGroup *xmlRelaxNGInterleaveGroupPtr;
201struct _xmlRelaxNGInterleaveGroup {
202 xmlRelaxNGDefinePtr rule; /* the rule to satisfy */
203 xmlRelaxNGDefinePtr *defs; /* the array of element definitions */
204};
205
206/**
207 * xmlRelaxNGPartitions:
208 *
209 * A RelaxNGs partition associated to an interleave group
210 */
211typedef struct _xmlRelaxNGPartition xmlRelaxNGPartition;
212typedef xmlRelaxNGPartition *xmlRelaxNGPartitionPtr;
213struct _xmlRelaxNGPartition {
214 int nbgroups; /* number of groups in the partitions */
215 xmlRelaxNGInterleaveGroupPtr *groups;
216};
217
218/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000219 * xmlRelaxNGValidState:
220 *
221 * A RelaxNGs validation state
222 */
223#define MAX_ATTR 20
224typedef struct _xmlRelaxNGValidState xmlRelaxNGValidState;
225typedef xmlRelaxNGValidState *xmlRelaxNGValidStatePtr;
226struct _xmlRelaxNGValidState {
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000227 xmlNodePtr node; /* the current node */
228 xmlNodePtr seq; /* the sequence of children left to validate */
229 int nbAttrs; /* the number of attributes */
230 xmlChar *value; /* the value when operating on string */
231 xmlChar *endvalue; /* the end value when operating on string */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000232 xmlAttrPtr attrs[1]; /* the array of attributes */
233};
234
235/**
236 * xmlRelaxNGValidCtxt:
237 *
238 * A RelaxNGs validation context
239 */
240
241struct _xmlRelaxNGValidCtxt {
242 void *userData; /* user specific data block */
243 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
244 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
245
246 xmlRelaxNGPtr schema; /* The schema in use */
247 xmlDocPtr doc; /* the document being validated */
248 xmlRelaxNGValidStatePtr state; /* the current validation state */
249 int flags; /* validation flags */
250};
251
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000252/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000253 * xmlRelaxNGInclude:
254 *
255 * Structure associated to a RelaxNGs document element
256 */
257struct _xmlRelaxNGInclude {
258 xmlChar *href; /* the normalized href value */
259 xmlDocPtr doc; /* the associated XML document */
260 xmlRelaxNGDefinePtr content;/* the definitions */
261 xmlRelaxNGPtr schema; /* the schema */
262};
263
264/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000265 * xmlRelaxNGDocument:
266 *
267 * Structure associated to a RelaxNGs document element
268 */
269struct _xmlRelaxNGDocument {
270 xmlChar *href; /* the normalized href value */
271 xmlDocPtr doc; /* the associated XML document */
272 xmlRelaxNGDefinePtr content;/* the definitions */
273 xmlRelaxNGPtr schema; /* the schema */
274};
275
Daniel Veillard6eadf632003-01-23 18:29:16 +0000276/************************************************************************
277 * *
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000278 * Preliminary type checking interfaces *
279 * *
280 ************************************************************************/
281/**
282 * xmlRelaxNGTypeHave:
283 * @data: data needed for the library
284 * @type: the type name
285 * @value: the value to check
286 *
287 * Function provided by a type library to check if a type is exported
288 *
289 * Returns 1 if yes, 0 if no and -1 in case of error.
290 */
291typedef int (*xmlRelaxNGTypeHave) (void *data, const xmlChar *type);
292
293/**
294 * xmlRelaxNGTypeCheck:
295 * @data: data needed for the library
296 * @type: the type name
297 * @value: the value to check
298 *
299 * Function provided by a type library to check if a value match a type
300 *
301 * Returns 1 if yes, 0 if no and -1 in case of error.
302 */
303typedef int (*xmlRelaxNGTypeCheck) (void *data, const xmlChar *type,
304 const xmlChar *value);
305
306/**
307 * xmlRelaxNGTypeCompare:
308 * @data: data needed for the library
309 * @type: the type name
310 * @value1: the first value
311 * @value2: the second value
312 *
313 * Function provided by a type library to compare two values accordingly
314 * to a type.
315 *
316 * Returns 1 if yes, 0 if no and -1 in case of error.
317 */
318typedef int (*xmlRelaxNGTypeCompare) (void *data, const xmlChar *type,
319 const xmlChar *value1,
320 const xmlChar *value2);
321typedef struct _xmlRelaxNGTypeLibrary xmlRelaxNGTypeLibrary;
322typedef xmlRelaxNGTypeLibrary *xmlRelaxNGTypeLibraryPtr;
323struct _xmlRelaxNGTypeLibrary {
324 const xmlChar *namespace; /* the datatypeLibrary value */
325 void *data; /* data needed for the library */
326 xmlRelaxNGTypeHave have; /* the export function */
327 xmlRelaxNGTypeCheck check; /* the checking function */
328 xmlRelaxNGTypeCompare comp; /* the compare function */
329};
330
331/************************************************************************
332 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +0000333 * Allocation functions *
334 * *
335 ************************************************************************/
336static void xmlRelaxNGFreeDefineList(xmlRelaxNGDefinePtr defines);
337static void xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar);
338static void xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define);
339
340/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000341 * xmlRelaxNGFreeDocument:
342 * @docu: a document structure
343 *
344 * Deallocate a RelaxNG document structure.
345 */
346static void
347xmlRelaxNGFreeDocument(xmlRelaxNGDocumentPtr docu)
348{
349 if (docu == NULL)
350 return;
351
352 if (docu->href != NULL)
353 xmlFree(docu->href);
354 if (docu->doc != NULL)
355 xmlFreeDoc(docu->doc);
356 if (docu->schema != NULL)
357 xmlRelaxNGFree(docu->schema);
358 xmlFree(docu);
359}
360
361/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000362 * xmlRelaxNGFreeInclude:
363 * @incl: a include structure
364 *
365 * Deallocate a RelaxNG include structure.
366 */
367static void
368xmlRelaxNGFreeInclude(xmlRelaxNGIncludePtr incl)
369{
370 if (incl == NULL)
371 return;
372
373 if (incl->href != NULL)
374 xmlFree(incl->href);
375 if (incl->doc != NULL)
376 xmlFreeDoc(incl->doc);
377 if (incl->schema != NULL)
378 xmlRelaxNGFree(incl->schema);
379 xmlFree(incl);
380}
381
382/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000383 * xmlRelaxNGNewRelaxNG:
384 * @ctxt: a Relax-NG validation context (optional)
385 *
386 * Allocate a new RelaxNG structure.
387 *
388 * Returns the newly allocated structure or NULL in case or error
389 */
390static xmlRelaxNGPtr
391xmlRelaxNGNewRelaxNG(xmlRelaxNGParserCtxtPtr ctxt)
392{
393 xmlRelaxNGPtr ret;
394
395 ret = (xmlRelaxNGPtr) xmlMalloc(sizeof(xmlRelaxNG));
396 if (ret == NULL) {
397 if ((ctxt != NULL) && (ctxt->error != NULL))
398 ctxt->error(ctxt->userData, "Out of memory\n");
399 ctxt->nbErrors++;
400 return (NULL);
401 }
402 memset(ret, 0, sizeof(xmlRelaxNG));
403
404 return (ret);
405}
406
407/**
408 * xmlRelaxNGFree:
409 * @schema: a schema structure
410 *
411 * Deallocate a RelaxNG structure.
412 */
413void
414xmlRelaxNGFree(xmlRelaxNGPtr schema)
415{
416 if (schema == NULL)
417 return;
418
Daniel Veillard6eadf632003-01-23 18:29:16 +0000419 if (schema->topgrammar != NULL)
420 xmlRelaxNGFreeGrammar(schema->topgrammar);
421 if (schema->doc != NULL)
422 xmlFreeDoc(schema->doc);
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000423 if (schema->documents != NULL)
424 xmlHashFree(schema->documents, (xmlHashDeallocator)
425 xmlRelaxNGFreeDocument);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000426 if (schema->includes != NULL)
427 xmlHashFree(schema->includes, (xmlHashDeallocator)
428 xmlRelaxNGFreeInclude);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000429
430 xmlFree(schema);
431}
432
433/**
434 * xmlRelaxNGNewGrammar:
435 * @ctxt: a Relax-NG validation context (optional)
436 *
437 * Allocate a new RelaxNG grammar.
438 *
439 * Returns the newly allocated structure or NULL in case or error
440 */
441static xmlRelaxNGGrammarPtr
442xmlRelaxNGNewGrammar(xmlRelaxNGParserCtxtPtr ctxt)
443{
444 xmlRelaxNGGrammarPtr ret;
445
446 ret = (xmlRelaxNGGrammarPtr) xmlMalloc(sizeof(xmlRelaxNGGrammar));
447 if (ret == NULL) {
448 if ((ctxt != NULL) && (ctxt->error != NULL))
449 ctxt->error(ctxt->userData, "Out of memory\n");
450 ctxt->nbErrors++;
451 return (NULL);
452 }
453 memset(ret, 0, sizeof(xmlRelaxNGGrammar));
454
455 return (ret);
456}
457
458/**
Daniel Veillard276be4a2003-01-24 01:03:34 +0000459 * xmlRelaxNGFreeDefineHash:
460 * @defines: a list of define structures
461 *
462 * Deallocate a RelaxNG definition in the hash table
463 */
464static void
465xmlRelaxNGFreeDefineHash(xmlRelaxNGDefinePtr defines)
466{
467 xmlRelaxNGDefinePtr next;
468
469 while (defines != NULL) {
470 next = defines->nextHash;
471 xmlRelaxNGFreeDefine(defines);
472 defines = next;
473 }
474}
475
476/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000477 * xmlRelaxNGFreeGrammar:
478 * @grammar: a grammar structure
479 *
480 * Deallocate a RelaxNG grammar structure.
481 */
482static void
483xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar)
484{
485 if (grammar == NULL)
486 return;
487
488 if (grammar->start != NULL)
489 xmlRelaxNGFreeDefine(grammar->start);
490 if (grammar->refs != NULL) {
491 xmlHashFree(grammar->refs, NULL);
492 }
493 if (grammar->defs != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +0000494 xmlHashFree(grammar->defs, (xmlHashDeallocator)
495 xmlRelaxNGFreeDefineHash);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000496 }
497
498 xmlFree(grammar);
499}
500
501/**
502 * xmlRelaxNGNewDefine:
503 * @ctxt: a Relax-NG validation context
504 * @node: the node in the input document.
505 *
506 * Allocate a new RelaxNG define.
507 *
508 * Returns the newly allocated structure or NULL in case or error
509 */
510static xmlRelaxNGDefinePtr
511xmlRelaxNGNewDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
512{
513 xmlRelaxNGDefinePtr ret;
514
515 ret = (xmlRelaxNGDefinePtr) xmlMalloc(sizeof(xmlRelaxNGDefine));
516 if (ret == NULL) {
517 if ((ctxt != NULL) && (ctxt->error != NULL))
518 ctxt->error(ctxt->userData, "Out of memory\n");
519 ctxt->nbErrors++;
520 return (NULL);
521 }
522 memset(ret, 0, sizeof(xmlRelaxNGDefine));
523 ret->node = node;
524
525 return (ret);
526}
527
528/**
529 * xmlRelaxNGFreeDefineList:
530 * @defines: a list of define structures
531 *
532 * Deallocate a RelaxNG define structures.
533 */
534static void
535xmlRelaxNGFreeDefineList(xmlRelaxNGDefinePtr defines)
536{
537 xmlRelaxNGDefinePtr next;
538
539 while (defines != NULL) {
540 next = defines->next;
541 xmlRelaxNGFreeDefine(defines);
542 defines = next;
543 }
544}
545
546/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000547 * xmlRelaxNGFreePartition:
548 * @partitions: a partition set structure
549 *
550 * Deallocate RelaxNG partition set structures.
551 */
552static void
553xmlRelaxNGFreePartition(xmlRelaxNGPartitionPtr partitions) {
554 xmlRelaxNGInterleaveGroupPtr group;
555 int j;
556
557 if (partitions != NULL) {
558 if (partitions->groups != NULL) {
559 for (j = 0;j < partitions->nbgroups;j++) {
560 group = partitions->groups[j];
561 if (group != NULL) {
562 if (group->defs != NULL)
563 xmlFree(group->defs);
564 xmlFree(group);
565 }
566 }
567 xmlFree(partitions->groups);
568 }
569 xmlFree(partitions);
570 }
571}
572/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000573 * xmlRelaxNGFreeDefine:
574 * @define: a define structure
575 *
576 * Deallocate a RelaxNG define structure.
577 */
578static void
579xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define)
580{
581 if (define == NULL)
582 return;
583
584 if (define->name != NULL)
585 xmlFree(define->name);
586 if (define->ns != NULL)
587 xmlFree(define->ns);
Daniel Veillardedc91922003-01-26 00:52:04 +0000588 if (define->value != NULL)
589 xmlFree(define->value);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000590 if (define->attrs != NULL)
591 xmlRelaxNGFreeDefineList(define->attrs);
Daniel Veillard276be4a2003-01-24 01:03:34 +0000592 if ((define->content != NULL) &&
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000593 (define->type != XML_RELAXNG_REF) &&
594 (define->type != XML_RELAXNG_EXTERNALREF))
Daniel Veillard6eadf632003-01-23 18:29:16 +0000595 xmlRelaxNGFreeDefineList(define->content);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000596 if ((define->data != NULL) &&
597 (define->type == XML_RELAXNG_INTERLEAVE))
598 xmlRelaxNGFreePartition((xmlRelaxNGPartitionPtr) define->data);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000599 xmlFree(define);
600}
601
602/**
603 * xmlRelaxNGNewValidState:
604 * @ctxt: a Relax-NG validation context
605 * @node: the current node or NULL for the document
606 *
607 * Allocate a new RelaxNG validation state
608 *
609 * Returns the newly allocated structure or NULL in case or error
610 */
611static xmlRelaxNGValidStatePtr
612xmlRelaxNGNewValidState(xmlRelaxNGValidCtxtPtr ctxt, xmlNodePtr node)
613{
614 xmlRelaxNGValidStatePtr ret;
615 xmlAttrPtr attr;
616 xmlAttrPtr attrs[MAX_ATTR];
617 int nbAttrs = 0;
618 xmlNodePtr root = NULL;
619
620 if (node == NULL) {
621 root = xmlDocGetRootElement(ctxt->doc);
622 if (root == NULL)
623 return(NULL);
624 } else {
625 attr = node->properties;
626 while (attr != NULL) {
627 if (nbAttrs < MAX_ATTR)
628 attrs[nbAttrs++] = attr;
629 else
630 nbAttrs++;
631 attr = attr->next;
632 }
633 }
634
635 if (nbAttrs < MAX_ATTR)
636 attrs[nbAttrs] = NULL;
637 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(sizeof(xmlRelaxNGValidState) +
638 nbAttrs * sizeof(xmlAttrPtr));
639 if (ret == NULL) {
640 if ((ctxt != NULL) && (ctxt->error != NULL))
641 ctxt->error(ctxt->userData, "Out of memory\n");
642 return (NULL);
643 }
644 if (node == NULL) {
645 ret->node = (xmlNodePtr) ctxt->doc;
646 ret->seq = root;
647 ret->nbAttrs = 0;
648 } else {
649 ret->node = node;
650 ret->seq = node->children;
651 ret->nbAttrs = nbAttrs;
652 if (nbAttrs > 0) {
653 if (nbAttrs < MAX_ATTR) {
654 memcpy(&(ret->attrs[0]), attrs,
655 sizeof(xmlAttrPtr) * (nbAttrs + 1));
656 } else {
657 attr = node->properties;
658 nbAttrs = 0;
659 while (attr != NULL) {
660 ret->attrs[nbAttrs++] = attr;
661 attr = attr->next;
662 }
663 ret->attrs[nbAttrs] = NULL;
664 }
665 }
666 }
667 return (ret);
668}
669
670/**
671 * xmlRelaxNGCopyValidState:
672 * @ctxt: a Relax-NG validation context
673 * @state: a validation state
674 *
675 * Copy the validation state
676 *
677 * Returns the newly allocated structure or NULL in case or error
678 */
679static xmlRelaxNGValidStatePtr
680xmlRelaxNGCopyValidState(xmlRelaxNGValidCtxtPtr ctxt,
681 xmlRelaxNGValidStatePtr state)
682{
683 xmlRelaxNGValidStatePtr ret;
684 unsigned int size;
685
686 if (state == NULL)
687 return(NULL);
688
689 size = sizeof(xmlRelaxNGValidState) +
690 state->nbAttrs * sizeof(xmlAttrPtr);
691 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(size);
692 if (ret == NULL) {
693 if ((ctxt != NULL) && (ctxt->error != NULL))
694 ctxt->error(ctxt->userData, "Out of memory\n");
695 return (NULL);
696 }
697 memcpy(ret, state, size);
698 return(ret);
699}
700
701/**
702 * xmlRelaxNGFreeValidState:
703 * @state: a validation state structure
704 *
705 * Deallocate a RelaxNG validation state structure.
706 */
707static void
708xmlRelaxNGFreeValidState(xmlRelaxNGValidStatePtr state)
709{
710 if (state == NULL)
711 return;
712
713 xmlFree(state);
714}
715
716/************************************************************************
717 * *
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000718 * Document functions *
719 * *
720 ************************************************************************/
721static xmlDocPtr xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt,
722 xmlDocPtr doc);
723
724/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000725 * xmlRelaxNGIncludePush:
726 * @ctxt: the parser context
727 * @value: the element doc
728 *
729 * Pushes a new include on top of the include stack
730 *
731 * Returns 0 in case of error, the index in the stack otherwise
732 */
733static int
734xmlRelaxNGIncludePush(xmlRelaxNGParserCtxtPtr ctxt,
735 xmlRelaxNGIncludePtr value)
736{
737 if (ctxt->incTab == NULL) {
738 ctxt->incMax = 4;
739 ctxt->incNr = 0;
740 ctxt->incTab = (xmlRelaxNGIncludePtr *) xmlMalloc(
741 ctxt->incMax * sizeof(ctxt->incTab[0]));
742 if (ctxt->incTab == NULL) {
743 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
744 return (0);
745 }
746 }
747 if (ctxt->incNr >= ctxt->incMax) {
748 ctxt->incMax *= 2;
749 ctxt->incTab =
750 (xmlRelaxNGIncludePtr *) xmlRealloc(ctxt->incTab,
751 ctxt->incMax *
752 sizeof(ctxt->incTab[0]));
753 if (ctxt->incTab == NULL) {
754 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
755 return (0);
756 }
757 }
758 ctxt->incTab[ctxt->incNr] = value;
759 ctxt->inc = value;
760 return (ctxt->incNr++);
761}
762
763/**
764 * xmlRelaxNGIncludePop:
765 * @ctxt: the parser context
766 *
767 * Pops the top include from the include stack
768 *
769 * Returns the include just removed
770 */
771static xmlRelaxNGIncludePtr
772xmlRelaxNGIncludePop(xmlRelaxNGParserCtxtPtr ctxt)
773{
774 xmlRelaxNGIncludePtr ret;
775
776 if (ctxt->incNr <= 0)
777 return (0);
778 ctxt->incNr--;
779 if (ctxt->incNr > 0)
780 ctxt->inc = ctxt->incTab[ctxt->incNr - 1];
781 else
782 ctxt->inc = NULL;
783 ret = ctxt->incTab[ctxt->incNr];
784 ctxt->incTab[ctxt->incNr] = 0;
785 return (ret);
786}
787
788/**
789 * xmlRelaxNGLoadInclude:
790 * @ctxt: the parser context
791 * @URL: the normalized URL
792 * @node: the include node.
793 *
794 * First lookup if the document is already loaded into the parser context,
795 * check against recursion. If not found the resource is loaded and
796 * the content is preprocessed before being returned back to the caller.
797 *
798 * Returns the xmlRelaxNGIncludePtr or NULL in case of error
799 */
800static xmlRelaxNGIncludePtr
801xmlRelaxNGLoadInclude(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
802 xmlNodePtr node) {
803 xmlRelaxNGIncludePtr ret = NULL;
804 xmlDocPtr doc;
805 int i;
806 xmlNodePtr root, tmp, tmp2, cur;
807
808 /*
809 * check against recursion in the stack
810 */
811 for (i = 0;i < ctxt->incNr;i++) {
812 if (xmlStrEqual(ctxt->incTab[i]->href, URL)) {
813 if (ctxt->error != NULL)
814 ctxt->error(ctxt->userData,
815 "Detected an externalRef recursion for %s\n",
816 URL);
817 ctxt->nbErrors++;
818 return(NULL);
819 }
820 }
821
822 /*
823 * Lookup in the hash table
824 */
825 if (ctxt->includes == NULL) {
826 ctxt->includes = xmlHashCreate(10);
827 if (ctxt->includes == NULL) {
828 if (ctxt->error != NULL)
829 ctxt->error(ctxt->userData,
830 "Failed to allocate hash table for document\n");
831 ctxt->nbErrors++;
832 return(NULL);
833 }
834 } else {
835 ret = xmlHashLookup(ctxt->includes, URL);
836 if (ret != NULL)
837 return(ret);
838 }
839
840
841 /*
842 * load the document
843 */
844 doc = xmlParseFile((const char *) URL);
845 if (doc == NULL) {
846 if (ctxt->error != NULL)
847 ctxt->error(ctxt->userData,
848 "xmlRelaxNG: could not load %s\n", URL);
849 ctxt->nbErrors++;
850 return (NULL);
851 }
852
853 /*
854 * Allocate the document structures and register it first.
855 */
856 ret = (xmlRelaxNGIncludePtr) xmlMalloc(sizeof(xmlRelaxNGInclude));
857 if (ret == NULL) {
858 if (ctxt->error != NULL)
859 ctxt->error(ctxt->userData,
860 "xmlRelaxNG: allocate memory for doc %s\n", URL);
861 ctxt->nbErrors++;
862 xmlFreeDoc(doc);
863 return (NULL);
864 }
865 memset(ret, 0, sizeof(xmlRelaxNGInclude));
866 ret->doc = doc;
867 ret->href = xmlStrdup(URL);
868
869 /*
870 * push it on the stack and register it in the hash table
871 */
872 xmlHashAddEntry(ctxt->includes, URL, ret);
873 xmlRelaxNGIncludePush(ctxt, ret);
874
875 /*
876 * Some preprocessing of the document content, this include recursing
877 * in the include stack.
878 */
879 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
880 if (doc == NULL) {
881 /* xmlFreeDoc(ctxt->include); */
882 ctxt->inc = NULL;
883 return(NULL);
884 }
885
886 /*
887 * Pop up the include from the stack
888 */
889 xmlRelaxNGIncludePop(ctxt);
890
891 /*
892 * Check that the top element is a grammar
893 */
894 root = xmlDocGetRootElement(doc);
895 if (root == NULL) {
896 if (ctxt->error != NULL)
897 ctxt->error(ctxt->userData,
898 "xmlRelaxNG: included document is empty %s\n", URL);
899 ctxt->nbErrors++;
900 xmlFreeDoc(doc);
901 return (NULL);
902 }
903 if (!IS_RELAXNG(root, "grammar")) {
904 if (ctxt->error != NULL)
905 ctxt->error(ctxt->userData,
906 "xmlRelaxNG: included document %s root is not a grammar\n",
907 URL);
908 ctxt->nbErrors++;
909 xmlFreeDoc(doc);
910 return (NULL);
911 }
912
913 /*
914 * Elimination of redefined rules in the include.
915 */
916 cur = node->children;
917 while (cur != NULL) {
918 if (IS_RELAXNG(cur, "start")) {
919 int found = 0;
920
921 tmp = root->children;
922 while (tmp != NULL) {
923 tmp2 = tmp->next;
924 if (IS_RELAXNG(tmp, "start")) {
925 found = 1;
926 xmlUnlinkNode(tmp);
927 xmlFreeNode(tmp);
928 }
929 tmp = tmp2;
930 }
931 if (!found) {
932 if (ctxt->error != NULL)
933 ctxt->error(ctxt->userData,
934 "xmlRelaxNG: include %s has a start but not the included grammar\n",
935 URL);
936 ctxt->nbErrors++;
937 }
938 } else if (IS_RELAXNG(cur, "define")) {
939 xmlChar *name, *name2;
940
941 name = xmlGetProp(cur, BAD_CAST "name");
942 if (name == NULL) {
943 if (ctxt->error != NULL)
944 ctxt->error(ctxt->userData,
945 "xmlRelaxNG: include %s has define without name\n",
946 URL);
947 ctxt->nbErrors++;
948 } else {
949 int found = 0;
950
951 tmp = root->children;
952 while (tmp != NULL) {
953 tmp2 = tmp->next;
954 if (IS_RELAXNG(tmp, "define")) {
955 name2 = xmlGetProp(tmp, BAD_CAST "name");
956 if (name2 != NULL) {
957 if (xmlStrEqual(name, name2)) {
958 found = 1;
959 xmlUnlinkNode(tmp);
960 xmlFreeNode(tmp);
961 }
962 xmlFree(name2);
963 }
964 }
965 tmp = tmp2;
966 }
967 if (!found) {
968 if (ctxt->error != NULL)
969 ctxt->error(ctxt->userData,
970 "xmlRelaxNG: include %s has a define %s but not the included grammar\n",
971 URL, name);
972 ctxt->nbErrors++;
973 }
974 xmlFree(name);
975 }
976 }
977 cur = cur->next;
978 }
979
980
981 return(ret);
982}
983
984/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000985 * xmlRelaxNGDocumentPush:
986 * @ctxt: the parser context
987 * @value: the element doc
988 *
989 * Pushes a new doc on top of the doc stack
990 *
991 * Returns 0 in case of error, the index in the stack otherwise
992 */
993static int
994xmlRelaxNGDocumentPush(xmlRelaxNGParserCtxtPtr ctxt,
995 xmlRelaxNGDocumentPtr value)
996{
997 if (ctxt->docTab == NULL) {
998 ctxt->docMax = 4;
999 ctxt->docNr = 0;
1000 ctxt->docTab = (xmlRelaxNGDocumentPtr *) xmlMalloc(
1001 ctxt->docMax * sizeof(ctxt->docTab[0]));
1002 if (ctxt->docTab == NULL) {
1003 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
1004 return (0);
1005 }
1006 }
1007 if (ctxt->docNr >= ctxt->docMax) {
1008 ctxt->docMax *= 2;
1009 ctxt->docTab =
1010 (xmlRelaxNGDocumentPtr *) xmlRealloc(ctxt->docTab,
1011 ctxt->docMax *
1012 sizeof(ctxt->docTab[0]));
1013 if (ctxt->docTab == NULL) {
1014 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
1015 return (0);
1016 }
1017 }
1018 ctxt->docTab[ctxt->docNr] = value;
1019 ctxt->doc = value;
1020 return (ctxt->docNr++);
1021}
1022
1023/**
1024 * xmlRelaxNGDocumentPop:
1025 * @ctxt: the parser context
1026 *
1027 * Pops the top doc from the doc stack
1028 *
1029 * Returns the doc just removed
1030 */
1031static xmlRelaxNGDocumentPtr
1032xmlRelaxNGDocumentPop(xmlRelaxNGParserCtxtPtr ctxt)
1033{
1034 xmlRelaxNGDocumentPtr ret;
1035
1036 if (ctxt->docNr <= 0)
1037 return (0);
1038 ctxt->docNr--;
1039 if (ctxt->docNr > 0)
1040 ctxt->doc = ctxt->docTab[ctxt->docNr - 1];
1041 else
1042 ctxt->doc = NULL;
1043 ret = ctxt->docTab[ctxt->docNr];
1044 ctxt->docTab[ctxt->docNr] = 0;
1045 return (ret);
1046}
1047
1048/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001049 * xmlRelaxNGLoadExternalRef:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001050 * @ctxt: the parser context
1051 * @URL: the normalized URL
1052 * @ns: the inherited ns if any
1053 *
1054 * First lookup if the document is already loaded into the parser context,
1055 * check against recursion. If not found the resource is loaded and
1056 * the content is preprocessed before being returned back to the caller.
1057 *
1058 * Returns the xmlRelaxNGDocumentPtr or NULL in case of error
1059 */
1060static xmlRelaxNGDocumentPtr
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001061xmlRelaxNGLoadExternalRef(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001062 const xmlChar *ns) {
1063 xmlRelaxNGDocumentPtr ret = NULL;
1064 xmlDocPtr doc;
1065 xmlNodePtr root;
1066 int i;
1067
1068 /*
1069 * check against recursion in the stack
1070 */
1071 for (i = 0;i < ctxt->docNr;i++) {
1072 if (xmlStrEqual(ctxt->docTab[i]->href, URL)) {
1073 if (ctxt->error != NULL)
1074 ctxt->error(ctxt->userData,
1075 "Detected an externalRef recursion for %s\n",
1076 URL);
1077 ctxt->nbErrors++;
1078 return(NULL);
1079 }
1080 }
1081
1082 /*
1083 * Lookup in the hash table
1084 */
1085 if (ctxt->documents == NULL) {
1086 ctxt->documents = xmlHashCreate(10);
1087 if (ctxt->documents == NULL) {
1088 if (ctxt->error != NULL)
1089 ctxt->error(ctxt->userData,
1090 "Failed to allocate hash table for document\n");
1091 ctxt->nbErrors++;
1092 return(NULL);
1093 }
1094 } else {
1095 if (ns == NULL)
1096 ret = xmlHashLookup2(ctxt->documents, BAD_CAST "", URL);
1097 else
1098 ret = xmlHashLookup2(ctxt->documents, ns, URL);
1099 if (ret != NULL)
1100 return(ret);
1101 }
1102
1103
1104 /*
1105 * load the document
1106 */
1107 doc = xmlParseFile((const char *) URL);
1108 if (doc == NULL) {
1109 if (ctxt->error != NULL)
1110 ctxt->error(ctxt->userData,
1111 "xmlRelaxNG: could not load %s\n", URL);
1112 ctxt->nbErrors++;
1113 return (NULL);
1114 }
1115
1116 /*
1117 * Allocate the document structures and register it first.
1118 */
1119 ret = (xmlRelaxNGDocumentPtr) xmlMalloc(sizeof(xmlRelaxNGDocument));
1120 if (ret == NULL) {
1121 if (ctxt->error != NULL)
1122 ctxt->error(ctxt->userData,
1123 "xmlRelaxNG: allocate memory for doc %s\n", URL);
1124 ctxt->nbErrors++;
1125 xmlFreeDoc(doc);
1126 return (NULL);
1127 }
1128 memset(ret, 0, sizeof(xmlRelaxNGDocument));
1129 ret->doc = doc;
1130 ret->href = xmlStrdup(URL);
1131
1132 /*
1133 * transmit the ns if needed
1134 */
1135 if (ns != NULL) {
1136 root = xmlDocGetRootElement(doc);
1137 if (root != NULL) {
1138 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
1139 xmlSetProp(root, BAD_CAST"ns", ns);
1140 }
1141 }
1142 }
1143
1144 /*
1145 * push it on the stack and register it in the hash table
1146 */
1147 if (ns == NULL)
1148 xmlHashAddEntry2(ctxt->documents, BAD_CAST "", URL, ret);
1149 else
1150 xmlHashAddEntry2(ctxt->documents, ns, URL, ret);
1151 xmlRelaxNGDocumentPush(ctxt, ret);
1152
1153 /*
1154 * Some preprocessing of the document content
1155 */
1156 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
1157 if (doc == NULL) {
1158 xmlFreeDoc(ctxt->document);
1159 ctxt->doc = NULL;
1160 return(NULL);
1161 }
1162
1163 xmlRelaxNGDocumentPop(ctxt);
1164
1165 return(ret);
1166}
1167
1168/************************************************************************
1169 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +00001170 * Error functions *
1171 * *
1172 ************************************************************************/
1173
1174#define VALID_CTXT() \
1175 if (ctxt->flags == 0) xmlGenericError(xmlGenericErrorContext, \
1176 "error detected at %s:%d\n", \
1177 __FILE__, __LINE__);
1178#define VALID_ERROR if (ctxt->flags == 0) printf
1179
1180#if 0
1181/**
1182 * xmlRelaxNGErrorContext:
1183 * @ctxt: the parsing context
1184 * @schema: the schema being built
1185 * @node: the node being processed
1186 * @child: the child being processed
1187 *
1188 * Dump a RelaxNGType structure
1189 */
1190static void
1191xmlRelaxNGErrorContext(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGPtr schema,
1192 xmlNodePtr node, xmlNodePtr child)
1193{
1194 int line = 0;
1195 const xmlChar *file = NULL;
1196 const xmlChar *name = NULL;
1197 const char *type = "error";
1198
1199 if ((ctxt == NULL) || (ctxt->error == NULL))
1200 return;
1201
1202 if (child != NULL)
1203 node = child;
1204
1205 if (node != NULL) {
1206 if ((node->type == XML_DOCUMENT_NODE) ||
1207 (node->type == XML_HTML_DOCUMENT_NODE)) {
1208 xmlDocPtr doc = (xmlDocPtr) node;
1209
1210 file = doc->URL;
1211 } else {
1212 /*
1213 * Try to find contextual informations to report
1214 */
1215 if (node->type == XML_ELEMENT_NODE) {
1216 line = (int) node->content;
1217 } else if ((node->prev != NULL) &&
1218 (node->prev->type == XML_ELEMENT_NODE)) {
1219 line = (int) node->prev->content;
1220 } else if ((node->parent != NULL) &&
1221 (node->parent->type == XML_ELEMENT_NODE)) {
1222 line = (int) node->parent->content;
1223 }
1224 if ((node->doc != NULL) && (node->doc->URL != NULL))
1225 file = node->doc->URL;
1226 if (node->name != NULL)
1227 name = node->name;
1228 }
1229 }
1230
1231 if (ctxt != NULL)
1232 type = "compilation error";
1233 else if (schema != NULL)
1234 type = "runtime error";
1235
1236 if ((file != NULL) && (line != 0) && (name != NULL))
1237 ctxt->error(ctxt->userData, "%s: file %s line %d element %s\n",
1238 type, file, line, name);
1239 else if ((file != NULL) && (name != NULL))
1240 ctxt->error(ctxt->userData, "%s: file %s element %s\n",
1241 type, file, name);
1242 else if ((file != NULL) && (line != 0))
1243 ctxt->error(ctxt->userData, "%s: file %s line %d\n", type, file, line);
1244 else if (file != NULL)
1245 ctxt->error(ctxt->userData, "%s: file %s\n", type, file);
1246 else if (name != NULL)
1247 ctxt->error(ctxt->userData, "%s: element %s\n", type, name);
1248 else
1249 ctxt->error(ctxt->userData, "%s\n", type);
1250}
1251#endif
1252
1253/************************************************************************
1254 * *
1255 * Type library hooks *
1256 * *
1257 ************************************************************************/
Daniel Veillardea3f3982003-01-26 19:45:18 +00001258static xmlChar *xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt,
1259 const xmlChar *str);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001260
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001261/**
1262 * xmlRelaxNGSchemaTypeHave:
1263 * @data: data needed for the library
1264 * @type: the type name
1265 *
1266 * Check if the given type is provided by
1267 * the W3C XMLSchema Datatype library.
1268 *
1269 * Returns 1 if yes, 0 if no and -1 in case of error.
1270 */
1271static int
1272xmlRelaxNGSchemaTypeHave(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001273 const xmlChar *type) {
1274 xmlSchemaTypePtr typ;
1275
1276 if (type == NULL)
1277 return(-1);
1278 typ = xmlSchemaGetPredefinedType(type,
1279 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1280 if (typ == NULL)
1281 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001282 return(1);
1283}
1284
1285/**
1286 * xmlRelaxNGSchemaTypeCheck:
1287 * @data: data needed for the library
1288 * @type: the type name
1289 * @value: the value to check
1290 *
1291 * Check if the given type and value are validated by
1292 * the W3C XMLSchema Datatype library.
1293 *
1294 * Returns 1 if yes, 0 if no and -1 in case of error.
1295 */
1296static int
1297xmlRelaxNGSchemaTypeCheck(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001298 const xmlChar *type,
1299 const xmlChar *value) {
1300 xmlSchemaTypePtr typ;
1301 int ret;
1302
1303 /*
1304 * TODO: the type should be cached ab provided back, interface subject
1305 * to changes.
1306 * TODO: handle facets, may require an additional interface and keep
1307 * the value returned from the validation.
1308 */
1309 if ((type == NULL) || (value == NULL))
1310 return(-1);
1311 typ = xmlSchemaGetPredefinedType(type,
1312 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1313 if (typ == NULL)
1314 return(-1);
1315 ret = xmlSchemaValidatePredefinedType(typ, value, NULL);
1316 if (ret == 0)
1317 return(1);
1318 if (ret > 0)
1319 return(0);
1320 return(-1);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001321}
1322
1323/**
1324 * xmlRelaxNGSchemaTypeCompare:
1325 * @data: data needed for the library
1326 * @type: the type name
1327 * @value1: the first value
1328 * @value2: the second value
1329 *
1330 * Compare two values accordingly a type from the W3C XMLSchema
1331 * Datatype library.
1332 *
1333 * Returns 1 if yes, 0 if no and -1 in case of error.
1334 */
1335static int
1336xmlRelaxNGSchemaTypeCompare(void *data ATTRIBUTE_UNUSED,
1337 const xmlChar *type ATTRIBUTE_UNUSED,
1338 const xmlChar *value1 ATTRIBUTE_UNUSED,
1339 const xmlChar *value2 ATTRIBUTE_UNUSED) {
1340 TODO
1341 return(1);
1342}
1343
1344/**
1345 * xmlRelaxNGDefaultTypeHave:
1346 * @data: data needed for the library
1347 * @type: the type name
1348 *
1349 * Check if the given type is provided by
1350 * the default datatype library.
1351 *
1352 * Returns 1 if yes, 0 if no and -1 in case of error.
1353 */
1354static int
1355xmlRelaxNGDefaultTypeHave(void *data ATTRIBUTE_UNUSED, const xmlChar *type) {
1356 if (type == NULL)
1357 return(-1);
1358 if (xmlStrEqual(type, BAD_CAST "string"))
1359 return(1);
1360 if (xmlStrEqual(type, BAD_CAST "token"))
1361 return(1);
1362 return(0);
1363}
1364
1365/**
1366 * xmlRelaxNGDefaultTypeCheck:
1367 * @data: data needed for the library
1368 * @type: the type name
1369 * @value: the value to check
1370 *
1371 * Check if the given type and value are validated by
1372 * the default datatype library.
1373 *
1374 * Returns 1 if yes, 0 if no and -1 in case of error.
1375 */
1376static int
1377xmlRelaxNGDefaultTypeCheck(void *data ATTRIBUTE_UNUSED,
1378 const xmlChar *type ATTRIBUTE_UNUSED,
1379 const xmlChar *value ATTRIBUTE_UNUSED) {
1380 return(1);
1381}
1382
1383/**
1384 * xmlRelaxNGDefaultTypeCompare:
1385 * @data: data needed for the library
1386 * @type: the type name
1387 * @value1: the first value
1388 * @value2: the second value
1389 *
1390 * Compare two values accordingly a type from the default
1391 * datatype library.
1392 *
1393 * Returns 1 if yes, 0 if no and -1 in case of error.
1394 */
1395static int
1396xmlRelaxNGDefaultTypeCompare(void *data ATTRIBUTE_UNUSED,
1397 const xmlChar *type ATTRIBUTE_UNUSED,
1398 const xmlChar *value1 ATTRIBUTE_UNUSED,
1399 const xmlChar *value2 ATTRIBUTE_UNUSED) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00001400 int ret = -1;
1401
1402 if (xmlStrEqual(type, BAD_CAST "string")) {
1403 ret = xmlStrEqual(value1, value2);
1404 } else if (xmlStrEqual(type, BAD_CAST "token")) {
1405 if (!xmlStrEqual(value1, value2)) {
1406 xmlChar *nval, *nvalue;
1407
1408 /*
1409 * TODO: trivial optimizations are possible by
1410 * computing at compile-time
1411 */
1412 nval = xmlRelaxNGNormalize(NULL, value1);
1413 nvalue = xmlRelaxNGNormalize(NULL, value2);
1414
1415 if ((nval == NULL) || (nvalue == NULL) ||
1416 (!xmlStrEqual(nval, nvalue)))
1417 ret = -1;
1418 if (nval != NULL)
1419 xmlFree(nval);
1420 if (nvalue != NULL)
1421 xmlFree(nvalue);
1422 }
1423 }
1424 return(ret);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001425}
1426
1427static int xmlRelaxNGTypeInitialized = 0;
1428static xmlHashTablePtr xmlRelaxNGRegisteredTypes = NULL;
1429
1430/**
1431 * xmlRelaxNGFreeTypeLibrary:
1432 * @lib: the type library structure
1433 * @namespace: the URI bound to the library
1434 *
1435 * Free the structure associated to the type library
1436 */
Daniel Veillard6eadf632003-01-23 18:29:16 +00001437static void
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001438xmlRelaxNGFreeTypeLibrary(xmlRelaxNGTypeLibraryPtr lib,
1439 const xmlChar *namespace ATTRIBUTE_UNUSED) {
1440 if (lib == NULL)
1441 return;
1442 if (lib->namespace != NULL)
1443 xmlFree((xmlChar *)lib->namespace);
1444 xmlFree(lib);
1445}
1446
1447/**
1448 * xmlRelaxNGRegisterTypeLibrary:
1449 * @namespace: the URI bound to the library
1450 * @data: data associated to the library
1451 * @have: the provide function
1452 * @check: the checking function
1453 * @comp: the comparison function
1454 *
1455 * Register a new type library
1456 *
1457 * Returns 0 in case of success and -1 in case of error.
1458 */
1459static int
1460xmlRelaxNGRegisterTypeLibrary(const xmlChar *namespace, void *data,
1461 xmlRelaxNGTypeHave have, xmlRelaxNGTypeCheck check,
1462 xmlRelaxNGTypeCompare comp) {
1463 xmlRelaxNGTypeLibraryPtr lib;
1464 int ret;
1465
1466 if ((xmlRelaxNGRegisteredTypes == NULL) || (namespace == NULL) ||
1467 (check == NULL) || (comp == NULL))
1468 return(-1);
1469 if (xmlHashLookup(xmlRelaxNGRegisteredTypes, namespace) != NULL) {
1470 xmlGenericError(xmlGenericErrorContext,
1471 "Relax-NG types library '%s' already registered\n",
1472 namespace);
1473 return(-1);
1474 }
1475 lib = (xmlRelaxNGTypeLibraryPtr) xmlMalloc(sizeof(xmlRelaxNGTypeLibrary));
1476 if (lib == NULL) {
1477 xmlGenericError(xmlGenericErrorContext,
1478 "Relax-NG types library '%s' malloc() failed\n",
1479 namespace);
1480 return (-1);
1481 }
1482 memset(lib, 0, sizeof(xmlRelaxNGTypeLibrary));
1483 lib->namespace = xmlStrdup(namespace);
1484 lib->data = data;
1485 lib->have = have;
1486 lib->comp = comp;
1487 lib->check = check;
1488 ret = xmlHashAddEntry(xmlRelaxNGRegisteredTypes, namespace, lib);
1489 if (ret < 0) {
1490 xmlGenericError(xmlGenericErrorContext,
1491 "Relax-NG types library failed to register '%s'\n",
1492 namespace);
1493 xmlRelaxNGFreeTypeLibrary(lib, namespace);
1494 return(-1);
1495 }
1496 return(0);
1497}
1498
1499/**
1500 * xmlRelaxNGInitTypes:
1501 *
1502 * Initilize the default type libraries.
1503 *
1504 * Returns 0 in case of success and -1 in case of error.
1505 */
1506static int
Daniel Veillard6eadf632003-01-23 18:29:16 +00001507xmlRelaxNGInitTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001508 if (xmlRelaxNGTypeInitialized != 0)
1509 return(0);
1510 xmlRelaxNGRegisteredTypes = xmlHashCreate(10);
1511 if (xmlRelaxNGRegisteredTypes == NULL) {
1512 xmlGenericError(xmlGenericErrorContext,
1513 "Failed to allocate sh table for Relax-NG types\n");
1514 return(-1);
1515 }
1516 xmlRelaxNGRegisterTypeLibrary(
1517 BAD_CAST "http://www.w3.org/2001/XMLSchema-datatypes",
1518 NULL,
1519 xmlRelaxNGSchemaTypeHave,
1520 xmlRelaxNGSchemaTypeCheck,
1521 xmlRelaxNGSchemaTypeCompare);
1522 xmlRelaxNGRegisterTypeLibrary(
1523 xmlRelaxNGNs,
1524 NULL,
1525 xmlRelaxNGDefaultTypeHave,
1526 xmlRelaxNGDefaultTypeCheck,
1527 xmlRelaxNGDefaultTypeCompare);
1528 xmlRelaxNGTypeInitialized = 1;
1529 return(0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001530}
1531
1532/**
1533 * xmlRelaxNGCleanupTypes:
1534 *
1535 * Cleanup the default Schemas type library associated to RelaxNG
1536 */
1537void
1538xmlRelaxNGCleanupTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001539 if (xmlRelaxNGTypeInitialized == 0)
1540 return;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001541 xmlSchemaCleanupTypes();
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001542 xmlHashFree(xmlRelaxNGRegisteredTypes, (xmlHashDeallocator)
1543 xmlRelaxNGFreeTypeLibrary);
1544 xmlRelaxNGTypeInitialized = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001545}
1546
1547/************************************************************************
1548 * *
1549 * Parsing functions *
1550 * *
1551 ************************************************************************/
1552
1553static xmlRelaxNGDefinePtr xmlRelaxNGParseAttribute(
1554 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1555static xmlRelaxNGDefinePtr xmlRelaxNGParseElement(
1556 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1557static xmlRelaxNGDefinePtr xmlRelaxNGParsePatterns(
Daniel Veillard154877e2003-01-30 12:17:05 +00001558 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes, int group);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001559static xmlRelaxNGDefinePtr xmlRelaxNGParsePattern(
1560 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001561static xmlRelaxNGPtr xmlRelaxNGParseDocument(
1562 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00001563static int xmlRelaxNGParseGrammarContent(
1564 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001565
1566
1567#define IS_BLANK_NODE(n) \
1568 (((n)->type == XML_TEXT_NODE) && (xmlRelaxNGIsBlank((n)->content)))
1569
1570/**
1571 * xmlRelaxNGIsBlank:
1572 * @str: a string
1573 *
1574 * Check if a string is ignorable c.f. 4.2. Whitespace
1575 *
1576 * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
1577 */
1578static int
1579xmlRelaxNGIsBlank(xmlChar *str) {
1580 if (str == NULL)
1581 return(1);
1582 while (*str != 0) {
1583 if (!(IS_BLANK(*str))) return(0);
1584 str++;
1585 }
1586 return(1);
1587}
1588
Daniel Veillard6eadf632003-01-23 18:29:16 +00001589/**
1590 * xmlRelaxNGGetDataTypeLibrary:
1591 * @ctxt: a Relax-NG parser context
1592 * @node: the current data or value element
1593 *
1594 * Applies algorithm from 4.3. datatypeLibrary attribute
1595 *
1596 * Returns the datatypeLibary value or NULL if not found
1597 */
1598static xmlChar *
1599xmlRelaxNGGetDataTypeLibrary(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1600 xmlNodePtr node) {
1601 xmlChar *ret, *escape;
1602
Daniel Veillard6eadf632003-01-23 18:29:16 +00001603 if ((IS_RELAXNG(node, "data")) || (IS_RELAXNG(node, "value"))) {
1604 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1605 if (ret != NULL) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001606 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +00001607 if (escape == NULL) {
1608 return(ret);
1609 }
1610 xmlFree(ret);
1611 return(escape);
1612 }
1613 }
1614 node = node->parent;
1615 while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) {
1616 if (IS_RELAXNG(node, "element")) {
1617 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1618 if (ret != NULL) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001619 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +00001620 if (escape == NULL) {
1621 return(ret);
1622 }
1623 xmlFree(ret);
1624 return(escape);
1625 }
1626 }
1627 node = node->parent;
1628 }
1629 return(NULL);
1630}
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001631
1632/**
Daniel Veillardedc91922003-01-26 00:52:04 +00001633 * xmlRelaxNGParseValue:
1634 * @ctxt: a Relax-NG parser context
1635 * @node: the data node.
1636 *
1637 * parse the content of a RelaxNG value node.
1638 *
1639 * Returns the definition pointer or NULL in case of error
1640 */
1641static xmlRelaxNGDefinePtr
1642xmlRelaxNGParseValue(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1643 xmlRelaxNGDefinePtr def = NULL;
1644 xmlRelaxNGTypeLibraryPtr lib;
1645 xmlChar *type;
1646 xmlChar *library;
1647 int tmp;
1648
1649 def = xmlRelaxNGNewDefine(ctxt, node);
1650 if (def == NULL)
1651 return(NULL);
1652 def->type = XML_RELAXNG_VALUE;
1653
1654 type = xmlGetProp(node, BAD_CAST "type");
1655 if (type != NULL) {
1656 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1657 if (library == NULL)
1658 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1659
1660 def->name = type;
1661 def->ns = library;
1662
1663 lib = (xmlRelaxNGTypeLibraryPtr)
1664 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1665 if (lib == NULL) {
1666 if (ctxt->error != NULL)
1667 ctxt->error(ctxt->userData,
1668 "Use of unregistered type library '%s'\n",
1669 library);
1670 ctxt->nbErrors++;
1671 def->data = NULL;
1672 } else {
1673 def->data = lib;
1674 if (lib->have == NULL) {
1675 ctxt->error(ctxt->userData,
1676 "Internal error with type library '%s': no 'have'\n",
1677 library);
1678 ctxt->nbErrors++;
1679 } else {
1680 tmp = lib->have(lib->data, def->name);
1681 if (tmp != 1) {
1682 ctxt->error(ctxt->userData,
1683 "Error type '%s' is not exported by type library '%s'\n",
1684 def->name, library);
1685 ctxt->nbErrors++;
1686 }
1687 }
1688 }
1689 }
1690 if (node->children == NULL) {
1691 if (ctxt->error != NULL)
1692 ctxt->error(ctxt->userData,
1693 "Element <value> has no content\n");
1694 ctxt->nbErrors++;
1695 } else if ((node->children->type != XML_TEXT_NODE) ||
1696 (node->children->next != NULL)) {
1697 if (ctxt->error != NULL)
1698 ctxt->error(ctxt->userData,
1699 "Expecting a single text value for <value>content\n");
1700 ctxt->nbErrors++;
1701 } else {
1702 def->value = xmlNodeGetContent(node);
1703 if (def->value == NULL) {
1704 if (ctxt->error != NULL)
1705 ctxt->error(ctxt->userData,
1706 "Element <value> has no content\n");
1707 ctxt->nbErrors++;
1708 }
1709 }
1710 /* TODO check ahead of time that the value is okay per the type */
1711 return(def);
1712}
1713
1714/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001715 * xmlRelaxNGParseData:
1716 * @ctxt: a Relax-NG parser context
1717 * @node: the data node.
1718 *
1719 * parse the content of a RelaxNG data node.
1720 *
1721 * Returns the definition pointer or NULL in case of error
1722 */
1723static xmlRelaxNGDefinePtr
1724xmlRelaxNGParseData(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1725 xmlRelaxNGDefinePtr def = NULL;
1726 xmlRelaxNGTypeLibraryPtr lib;
1727 xmlChar *type;
1728 xmlChar *library;
1729 xmlNodePtr content;
1730 int tmp;
1731
1732 type = xmlGetProp(node, BAD_CAST "type");
1733 if (type == NULL) {
1734 if (ctxt->error != NULL)
1735 ctxt->error(ctxt->userData,
1736 "data has no type\n");
1737 ctxt->nbErrors++;
1738 return(NULL);
1739 }
1740 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1741 if (library == NULL)
1742 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1743
1744 def = xmlRelaxNGNewDefine(ctxt, node);
1745 if (def == NULL) {
1746 xmlFree(type);
1747 return(NULL);
1748 }
1749 def->type = XML_RELAXNG_DATATYPE;
1750 def->name = type;
1751 def->ns = library;
1752
1753 lib = (xmlRelaxNGTypeLibraryPtr)
1754 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1755 if (lib == NULL) {
1756 if (ctxt->error != NULL)
1757 ctxt->error(ctxt->userData,
1758 "Use of unregistered type library '%s'\n",
1759 library);
1760 ctxt->nbErrors++;
1761 def->data = NULL;
1762 } else {
1763 def->data = lib;
1764 if (lib->have == NULL) {
1765 ctxt->error(ctxt->userData,
1766 "Internal error with type library '%s': no 'have'\n",
1767 library);
1768 ctxt->nbErrors++;
1769 } else {
1770 tmp = lib->have(lib->data, def->name);
1771 if (tmp != 1) {
1772 ctxt->error(ctxt->userData,
1773 "Error type '%s' is not exported by type library '%s'\n",
1774 def->name, library);
1775 ctxt->nbErrors++;
1776 }
1777 }
1778 }
1779 content = node->children;
1780 while (content != NULL) {
1781 TODO
1782 content = content->next;
1783 }
1784
1785 return(def);
1786}
1787
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001788/**
1789 * xmlRelaxNGCompareElemDefLists:
1790 * @ctxt: a Relax-NG parser context
1791 * @defs1: the first list of element defs
1792 * @defs2: the second list of element defs
1793 *
1794 * Compare the 2 lists of element definitions. The comparison is
1795 * that if both lists do not accept the same QNames, it returns 1
1796 * If the 2 lists can accept the same QName the comparison returns 0
1797 *
1798 * Returns 1 disttinct, 0 if equal
1799 */
1800static int
1801xmlRelaxNGCompareElemDefLists(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1802 xmlRelaxNGDefinePtr *def1,
1803 xmlRelaxNGDefinePtr *def2) {
1804 xmlRelaxNGDefinePtr *basedef2 = def2;
1805
Daniel Veillard154877e2003-01-30 12:17:05 +00001806 if ((def1 == NULL) || (def2 == NULL))
1807 return(1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001808 if ((*def1 == NULL) || (*def2 == NULL))
1809 return(1);
1810 while (*def1 != NULL) {
1811 while ((*def2) != NULL) {
1812 if ((*def1)->name == NULL) {
1813 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1814 return(0);
1815 } else if ((*def2)->name == NULL) {
1816 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1817 return(0);
1818 } else if (xmlStrEqual((*def1)->name, (*def2)->name)) {
1819 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1820 return(0);
1821 }
1822 def2++;
1823 }
1824 def2 = basedef2;
1825 def1++;
1826 }
1827 return(1);
1828}
1829
1830/**
1831 * xmlRelaxNGGetElements:
1832 * @ctxt: a Relax-NG parser context
1833 * @def: the interleave definition
1834 *
1835 * Compute the list of top elements a definition can generate
1836 *
1837 * Returns a list of elements or NULL if none was found.
1838 */
1839static xmlRelaxNGDefinePtr *
1840xmlRelaxNGGetElements(xmlRelaxNGParserCtxtPtr ctxt,
1841 xmlRelaxNGDefinePtr def) {
1842 xmlRelaxNGDefinePtr *ret = NULL, parent, cur, tmp;
1843 int len = 0;
1844 int max = 0;
1845
1846 parent = NULL;
1847 cur = def;
1848 while (cur != NULL) {
Daniel Veillardb08c9812003-01-28 23:09:49 +00001849 if ((cur->type == XML_RELAXNG_ELEMENT) ||
1850 (cur->type == XML_RELAXNG_TEXT)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001851 if (ret == NULL) {
1852 max = 10;
1853 ret = (xmlRelaxNGDefinePtr *)
1854 xmlMalloc((max + 1) * sizeof(xmlRelaxNGDefinePtr));
1855 if (ret == NULL) {
1856 if (ctxt->error != NULL)
1857 ctxt->error(ctxt->userData,
1858 "Out of memory in element search\n");
1859 ctxt->nbErrors++;
1860 return(NULL);
1861 }
1862 } else if (max <= len) {
1863 max *= 2;
1864 ret = xmlRealloc(ret, (max + 1) * sizeof(xmlRelaxNGDefinePtr));
1865 if (ret == NULL) {
1866 if (ctxt->error != NULL)
1867 ctxt->error(ctxt->userData,
1868 "Out of memory in element search\n");
1869 ctxt->nbErrors++;
1870 return(NULL);
1871 }
1872 }
Daniel Veillardb08c9812003-01-28 23:09:49 +00001873 ret[len++] = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001874 ret[len] = NULL;
1875 } else if ((cur->type == XML_RELAXNG_CHOICE) ||
1876 (cur->type == XML_RELAXNG_INTERLEAVE) ||
1877 (cur->type == XML_RELAXNG_GROUP) ||
1878 (cur->type == XML_RELAXNG_ONEORMORE) ||
Daniel Veillardb08c9812003-01-28 23:09:49 +00001879 (cur->type == XML_RELAXNG_ZEROORMORE) ||
1880 (cur->type == XML_RELAXNG_OPTIONAL) ||
1881 (cur->type == XML_RELAXNG_REF) ||
1882 (cur->type == XML_RELAXNG_DEF)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001883 /*
1884 * Don't go within elements or attributes or string values.
1885 * Just gather the element top list
1886 */
1887 if (cur->content != NULL) {
1888 parent = cur;
1889 cur = cur->content;
1890 tmp = cur;
1891 while (tmp != NULL) {
1892 tmp->parent = parent;
1893 tmp = tmp->next;
1894 }
1895 continue;
1896 }
1897 }
Daniel Veillard154877e2003-01-30 12:17:05 +00001898 if (cur == def)
1899 return(ret);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001900 if (cur->next != NULL) {
1901 cur = cur->next;
1902 continue;
1903 }
1904 do {
1905 cur = cur->parent;
1906 if (cur == NULL) break;
1907 if (cur == def) return(ret);
1908 if (cur->next != NULL) {
1909 cur = cur->next;
1910 break;
1911 }
1912 } while (cur != NULL);
1913 }
1914 return(ret);
1915}
1916
1917/**
1918 * xmlRelaxNGComputeInterleaves:
1919 * @def: the interleave definition
1920 * @ctxt: a Relax-NG parser context
1921 * @node: the data node.
1922 *
1923 * A lot of work for preprocessing interleave definitions
1924 * is potentially needed to get a decent execution speed at runtime
1925 * - trying to get a total order on the element nodes generated
1926 * by the interleaves, order the list of interleave definitions
1927 * following that order.
1928 * - if <text/> is used to handle mixed content, it is better to
1929 * flag this in the define and simplify the runtime checking
1930 * algorithm
1931 */
1932static void
1933xmlRelaxNGComputeInterleaves(xmlRelaxNGDefinePtr def,
1934 xmlRelaxNGParserCtxtPtr ctxt,
1935 xmlChar *name ATTRIBUTE_UNUSED) {
1936 xmlRelaxNGDefinePtr cur;
1937
1938 xmlRelaxNGDefinePtr *list = NULL;
1939 xmlRelaxNGPartitionPtr partitions = NULL;
1940 xmlRelaxNGInterleaveGroupPtr *groups = NULL;
1941 xmlRelaxNGInterleaveGroupPtr group;
1942 int i,j,ret;
1943 int nbgroups = 0;
1944 int nbchild = 0;
1945
1946#ifdef DEBUG_INTERLEAVE
1947 xmlGenericError(xmlGenericErrorContext,
1948 "xmlRelaxNGComputeInterleaves(%s)\n",
1949 name);
1950#endif
1951 cur = def->content;
1952 while (cur != NULL) {
1953 nbchild++;
1954 cur = cur->next;
1955 }
1956
1957#ifdef DEBUG_INTERLEAVE
1958 xmlGenericError(xmlGenericErrorContext, " %d child\n", nbchild);
1959#endif
1960 groups = (xmlRelaxNGInterleaveGroupPtr *)
1961 xmlMalloc(nbchild * sizeof(xmlRelaxNGInterleaveGroupPtr));
1962 if (groups == NULL)
1963 goto error;
1964 cur = def->content;
1965 while (cur != NULL) {
Daniel Veillard154877e2003-01-30 12:17:05 +00001966 groups[nbgroups] = (xmlRelaxNGInterleaveGroupPtr)
1967 xmlMalloc(sizeof(xmlRelaxNGInterleaveGroup));
1968 if (groups[nbgroups] == NULL)
1969 goto error;
1970 groups[nbgroups]->rule = cur;
1971 groups[nbgroups]->defs = xmlRelaxNGGetElements(ctxt, cur);
1972 nbgroups++;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001973 cur = cur->next;
1974 }
1975 list = NULL;
1976#ifdef DEBUG_INTERLEAVE
1977 xmlGenericError(xmlGenericErrorContext, " %d groups\n", nbgroups);
1978#endif
1979
1980 /*
1981 * Let's check that all rules makes a partitions according to 7.4
1982 */
1983 partitions = (xmlRelaxNGPartitionPtr)
1984 xmlMalloc(sizeof(xmlRelaxNGPartition));
1985 if (partitions == NULL)
1986 goto error;
1987 partitions->nbgroups = nbgroups;
1988 for (i = 0;i < nbgroups;i++) {
1989 group = groups[i];
1990 for (j = i+1;j < nbgroups;j++) {
1991 if (groups[j] == NULL)
1992 continue;
1993 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->defs,
1994 groups[j]->defs);
1995 if (ret == 0) {
1996 if (ctxt->error != NULL)
1997 ctxt->error(ctxt->userData,
1998 "Element or text conflicts in interleave\n");
1999 ctxt->nbErrors++;
2000 }
2001 }
2002 }
2003 partitions->groups = groups;
2004
2005 /*
2006 * Free Up the child list, and save the partition list back in the def
2007 */
2008 def->data = partitions;
2009 return;
2010
2011error:
2012 if (ctxt->error != NULL)
2013 ctxt->error(ctxt->userData,
2014 "Out of memory in interleave computation\n");
2015 ctxt->nbErrors++;
2016 if (list == NULL)
2017 xmlFree(list);
2018 if (groups != NULL) {
2019 for (i = 0;i < nbgroups;i++)
2020 if (groups[i] != NULL) {
2021 if (groups[i]->defs != NULL)
2022 xmlFree(groups[i]->defs);
2023 xmlFree(groups[i]);
2024 }
2025 xmlFree(groups);
2026 }
2027 xmlRelaxNGFreePartition(partitions);
2028}
2029
2030/**
2031 * xmlRelaxNGParseInterleave:
2032 * @ctxt: a Relax-NG parser context
2033 * @node: the data node.
2034 *
2035 * parse the content of a RelaxNG interleave node.
2036 *
2037 * Returns the definition pointer or NULL in case of error
2038 */
2039static xmlRelaxNGDefinePtr
2040xmlRelaxNGParseInterleave(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2041 xmlRelaxNGDefinePtr def = NULL;
2042 xmlRelaxNGDefinePtr last = NULL, cur;
2043 xmlNodePtr child;
2044
2045 def = xmlRelaxNGNewDefine(ctxt, node);
2046 if (def == NULL) {
2047 return(NULL);
2048 }
2049 def->type = XML_RELAXNG_INTERLEAVE;
2050
2051 if (ctxt->interleaves == NULL)
2052 ctxt->interleaves = xmlHashCreate(10);
2053 if (ctxt->interleaves == NULL) {
2054 if (ctxt->error != NULL)
2055 ctxt->error(ctxt->userData,
2056 "Failed to create interleaves hash table\n");
2057 ctxt->nbErrors++;
2058 } else {
2059 char name[32];
2060
2061 snprintf(name, 32, "interleave%d", ctxt->nbInterleaves++);
2062 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST name, def) < 0) {
2063 if (ctxt->error != NULL)
2064 ctxt->error(ctxt->userData,
2065 "Failed to add %s to hash table\n", name);
2066 ctxt->nbErrors++;
2067 }
2068 }
2069 child = node->children;
2070 while (child != NULL) {
2071 if (IS_RELAXNG(child, "element")) {
2072 cur = xmlRelaxNGParseElement(ctxt, child);
2073 } else {
2074 cur = xmlRelaxNGParsePattern(ctxt, child);
2075 }
2076 if (cur != NULL) {
2077 cur->parent = def;
2078 if (last == NULL) {
2079 def->content = last = cur;
2080 } else {
2081 last->next = cur;
2082 last = cur;
2083 }
2084 }
2085 child = child->next;
2086 }
2087
2088 return(def);
2089}
Daniel Veillard6eadf632003-01-23 18:29:16 +00002090
2091/**
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002092 * xmlRelaxNGParseInclude:
2093 * @ctxt: a Relax-NG parser context
2094 * @node: the include node
2095 *
2096 * Integrate the content of an include node in the current grammar
2097 *
2098 * Returns 0 in case of success or -1 in case of error
2099 */
2100static int
2101xmlRelaxNGParseInclude(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2102 xmlRelaxNGIncludePtr incl;
2103 xmlNodePtr root;
2104 int ret = 0, tmp;
2105
2106 incl = node->_private;
2107 if (incl == NULL) {
2108 if (ctxt->error != NULL)
2109 ctxt->error(ctxt->userData,
2110 "Include node has no data\n");
2111 ctxt->nbErrors++;
2112 return(-1);
2113 }
2114 root = xmlDocGetRootElement(incl->doc);
2115 if (root == NULL) {
2116 if (ctxt->error != NULL)
2117 ctxt->error(ctxt->userData,
2118 "Include document is empty\n");
2119 ctxt->nbErrors++;
2120 return(-1);
2121 }
2122 if (!xmlStrEqual(root->name, BAD_CAST "grammar")) {
2123 if (ctxt->error != NULL)
2124 ctxt->error(ctxt->userData,
2125 "Include document root is not a grammar\n");
2126 ctxt->nbErrors++;
2127 return(-1);
2128 }
2129
2130 /*
2131 * Merge the definition from both the include and the internal list
2132 */
2133 if (root->children != NULL) {
2134 tmp = xmlRelaxNGParseGrammarContent(ctxt, root->children);
2135 if (tmp != 0)
2136 ret = -1;
2137 }
2138 if (node->children != NULL) {
2139 tmp = xmlRelaxNGParseGrammarContent(ctxt, node->children);
2140 if (tmp != 0)
2141 ret = -1;
2142 }
2143 return(ret);
2144}
2145
2146/**
Daniel Veillard276be4a2003-01-24 01:03:34 +00002147 * xmlRelaxNGParseDefine:
2148 * @ctxt: a Relax-NG parser context
2149 * @node: the define node
2150 *
2151 * parse the content of a RelaxNG define element node.
2152 *
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002153 * Returns 0 in case of success or -1 in case of error
Daniel Veillard276be4a2003-01-24 01:03:34 +00002154 */
2155static int
2156xmlRelaxNGParseDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2157 xmlChar *name;
2158 int ret = 0, tmp;
2159 xmlRelaxNGDefinePtr def;
2160 const xmlChar *olddefine;
2161
2162 name = xmlGetProp(node, BAD_CAST "name");
2163 if (name == NULL) {
2164 if (ctxt->error != NULL)
2165 ctxt->error(ctxt->userData,
2166 "define has no name\n");
2167 ctxt->nbErrors++;
2168 } else {
2169 def = xmlRelaxNGNewDefine(ctxt, node);
2170 if (def == NULL) {
2171 xmlFree(name);
2172 return(-1);
2173 }
2174 def->type = XML_RELAXNG_DEF;
2175 def->name = name;
2176 if (node->children == NULL) {
2177 if (ctxt->error != NULL)
2178 ctxt->error(ctxt->userData,
2179 "define has no children\n");
2180 ctxt->nbErrors++;
2181 } else {
2182 olddefine = ctxt->define;
2183 ctxt->define = name;
Daniel Veillard154877e2003-01-30 12:17:05 +00002184 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard276be4a2003-01-24 01:03:34 +00002185 ctxt->define = olddefine;
2186 }
2187 if (ctxt->grammar->defs == NULL)
2188 ctxt->grammar->defs = xmlHashCreate(10);
2189 if (ctxt->grammar->defs == NULL) {
2190 if (ctxt->error != NULL)
2191 ctxt->error(ctxt->userData,
2192 "Could not create definition hash\n");
2193 ctxt->nbErrors++;
2194 ret = -1;
2195 xmlRelaxNGFreeDefine(def);
2196 } else {
2197 tmp = xmlHashAddEntry(ctxt->grammar->defs, name, def);
2198 if (tmp < 0) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002199 xmlRelaxNGDefinePtr prev;
2200
2201 prev = xmlHashLookup(ctxt->grammar->defs, name);
2202 if (prev == NULL) {
2203 if (ctxt->error != NULL)
2204 ctxt->error(ctxt->userData,
2205 "Internal error on define aggregation of %s\n",
2206 name);
2207 ctxt->nbErrors++;
2208 ret = -1;
2209 xmlRelaxNGFreeDefine(def);
2210 } else {
2211 while (prev->nextHash != NULL)
2212 prev = prev->nextHash;
2213 prev->nextHash = def;
2214 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002215 }
2216 }
2217 }
2218 return(ret);
2219}
2220
2221/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00002222 * xmlRelaxNGParsePattern:
2223 * @ctxt: a Relax-NG parser context
2224 * @node: the pattern node.
2225 *
2226 * parse the content of a RelaxNG pattern node.
2227 *
Daniel Veillard276be4a2003-01-24 01:03:34 +00002228 * Returns the definition pointer or NULL in case of error or if no
2229 * pattern is generated.
Daniel Veillard6eadf632003-01-23 18:29:16 +00002230 */
2231static xmlRelaxNGDefinePtr
2232xmlRelaxNGParsePattern(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2233 xmlRelaxNGDefinePtr def = NULL;
2234
2235 if (IS_RELAXNG(node, "element")) {
2236 def = xmlRelaxNGParseElement(ctxt, node);
2237 } else if (IS_RELAXNG(node, "attribute")) {
2238 def = xmlRelaxNGParseAttribute(ctxt, node);
2239 } else if (IS_RELAXNG(node, "empty")) {
2240 def = xmlRelaxNGNewDefine(ctxt, node);
2241 if (def == NULL)
2242 return(NULL);
2243 def->type = XML_RELAXNG_EMPTY;
2244 } else if (IS_RELAXNG(node, "text")) {
2245 def = xmlRelaxNGNewDefine(ctxt, node);
2246 if (def == NULL)
2247 return(NULL);
2248 def->type = XML_RELAXNG_TEXT;
2249 if (node->children != NULL) {
2250 if (ctxt->error != NULL)
2251 ctxt->error(ctxt->userData, "text: had a child node\n");
2252 ctxt->nbErrors++;
2253 }
2254 } else if (IS_RELAXNG(node, "zeroOrMore")) {
2255 def = xmlRelaxNGNewDefine(ctxt, node);
2256 if (def == NULL)
2257 return(NULL);
2258 def->type = XML_RELAXNG_ZEROORMORE;
Daniel Veillard154877e2003-01-30 12:17:05 +00002259 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002260 } else if (IS_RELAXNG(node, "oneOrMore")) {
2261 def = xmlRelaxNGNewDefine(ctxt, node);
2262 if (def == NULL)
2263 return(NULL);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002264 def->type = XML_RELAXNG_ONEORMORE;
Daniel Veillard154877e2003-01-30 12:17:05 +00002265 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002266 } else if (IS_RELAXNG(node, "optional")) {
2267 def = xmlRelaxNGNewDefine(ctxt, node);
2268 if (def == NULL)
2269 return(NULL);
2270 def->type = XML_RELAXNG_OPTIONAL;
Daniel Veillard154877e2003-01-30 12:17:05 +00002271 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002272 } else if (IS_RELAXNG(node, "choice")) {
2273 def = xmlRelaxNGNewDefine(ctxt, node);
2274 if (def == NULL)
2275 return(NULL);
2276 def->type = XML_RELAXNG_CHOICE;
Daniel Veillard154877e2003-01-30 12:17:05 +00002277 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002278 } else if (IS_RELAXNG(node, "group")) {
2279 def = xmlRelaxNGNewDefine(ctxt, node);
2280 if (def == NULL)
2281 return(NULL);
2282 def->type = XML_RELAXNG_GROUP;
Daniel Veillard154877e2003-01-30 12:17:05 +00002283 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002284 } else if (IS_RELAXNG(node, "ref")) {
2285 def = xmlRelaxNGNewDefine(ctxt, node);
2286 if (def == NULL)
2287 return(NULL);
2288 def->type = XML_RELAXNG_REF;
2289 def->name = xmlGetProp(node, BAD_CAST "name");
2290 if (def->name == NULL) {
2291 if (ctxt->error != NULL)
2292 ctxt->error(ctxt->userData,
2293 "ref has no name\n");
2294 ctxt->nbErrors++;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002295 } else {
2296 if ((ctxt->define != NULL) &&
2297 (xmlStrEqual(ctxt->define, def->name))) {
2298 if (ctxt->error != NULL)
2299 ctxt->error(ctxt->userData,
2300 "Recursive reference to %s not in an element\n",
2301 def->name);
2302 ctxt->nbErrors++;
2303 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002304 }
2305 if (node->children != NULL) {
2306 if (ctxt->error != NULL)
2307 ctxt->error(ctxt->userData,
2308 "ref is not empty\n");
2309 ctxt->nbErrors++;
2310 }
2311 if (ctxt->grammar->refs == NULL)
2312 ctxt->grammar->refs = xmlHashCreate(10);
2313 if (ctxt->grammar->refs == NULL) {
2314 if (ctxt->error != NULL)
2315 ctxt->error(ctxt->userData,
2316 "Could not create references hash\n");
2317 ctxt->nbErrors++;
2318 xmlRelaxNGFreeDefine(def);
2319 def = NULL;
2320 } else {
2321 int tmp;
2322
2323 tmp = xmlHashAddEntry(ctxt->grammar->refs, def->name, def);
2324 if (tmp < 0) {
2325 xmlRelaxNGDefinePtr prev;
2326
2327 prev = (xmlRelaxNGDefinePtr)
2328 xmlHashLookup(ctxt->grammar->refs, def->name);
2329 if (prev == NULL) {
2330 if (ctxt->error != NULL)
2331 ctxt->error(ctxt->userData,
2332 "Internal error refs definitions '%s'\n",
2333 def->name);
2334 ctxt->nbErrors++;
2335 xmlRelaxNGFreeDefine(def);
2336 def = NULL;
2337 } else {
2338 def->nextHash = prev->nextHash;
2339 prev->nextHash = def;
2340 }
2341 }
2342 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002343 } else if (IS_RELAXNG(node, "data")) {
2344 def = xmlRelaxNGParseData(ctxt, node);
Daniel Veillard276be4a2003-01-24 01:03:34 +00002345 } else if (IS_RELAXNG(node, "define")) {
2346 xmlRelaxNGParseDefine(ctxt, node);
2347 def = NULL;
Daniel Veillardedc91922003-01-26 00:52:04 +00002348 } else if (IS_RELAXNG(node, "value")) {
2349 def = xmlRelaxNGParseValue(ctxt, node);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002350 } else if (IS_RELAXNG(node, "list")) {
2351 def = xmlRelaxNGNewDefine(ctxt, node);
2352 if (def == NULL)
2353 return(NULL);
2354 def->type = XML_RELAXNG_LIST;
Daniel Veillard154877e2003-01-30 12:17:05 +00002355 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002356 } else if (IS_RELAXNG(node, "interleave")) {
2357 def = xmlRelaxNGParseInterleave(ctxt, node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002358 } else if (IS_RELAXNG(node, "externalRef")) {
2359 xmlRelaxNGDocumentPtr docu;
2360 xmlNodePtr root;
2361
2362 docu = node->_private;
2363 if (docu != NULL) {
2364 def = xmlRelaxNGNewDefine(ctxt, node);
2365 if (def == NULL)
2366 return(NULL);
2367 def->type = XML_RELAXNG_EXTERNALREF;
2368
2369 if (docu->content == NULL) {
2370 /*
2371 * Then do the parsing for good
2372 */
2373 root = xmlDocGetRootElement(docu->doc);
2374 if (root == NULL) {
2375 if (ctxt->error != NULL)
2376 ctxt->error(ctxt->userData,
2377 "xmlRelaxNGParse: %s is empty\n",
2378 ctxt->URL);
2379 ctxt->nbErrors++;
2380 return (NULL);
2381 }
2382 docu->schema = xmlRelaxNGParseDocument(ctxt, root);
2383 if ((docu->schema != NULL) &&
2384 (docu->schema->topgrammar != NULL)) {
2385 docu->content = docu->schema->topgrammar->start;
2386 }
2387 }
2388 def->content = docu->content;
2389 } else {
2390 def = NULL;
2391 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002392 } else if (IS_RELAXNG(node, "notAllowed")) {
2393 def = xmlRelaxNGNewDefine(ctxt, node);
2394 if (def == NULL)
2395 return(NULL);
2396 def->type = XML_RELAXNG_NOT_ALLOWED;
2397 if (node->children != NULL) {
2398 if (ctxt->error != NULL)
2399 ctxt->error(ctxt->userData,
2400 "xmlRelaxNGParse: notAllowed element is not empty\n");
2401 ctxt->nbErrors++;
2402 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002403 } else {
2404 TODO
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002405 def = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002406 }
2407 return(def);
2408}
2409
2410/**
2411 * xmlRelaxNGParseAttribute:
2412 * @ctxt: a Relax-NG parser context
2413 * @node: the element node
2414 *
2415 * parse the content of a RelaxNG attribute node.
2416 *
2417 * Returns the definition pointer or NULL in case of error.
2418 */
2419static xmlRelaxNGDefinePtr
2420xmlRelaxNGParseAttribute(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2421 xmlRelaxNGDefinePtr ret, cur, last;
2422 xmlNodePtr child;
2423 xmlChar *val;
2424 int old_flags;
2425
2426 ret = xmlRelaxNGNewDefine(ctxt, node);
2427 if (ret == NULL)
2428 return(NULL);
2429 ret->type = XML_RELAXNG_ATTRIBUTE;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002430 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002431 child = node->children;
2432 if (child == NULL) {
2433 if (ctxt->error != NULL)
2434 ctxt->error(ctxt->userData,
2435 "xmlRelaxNGParseattribute: attribute has no children\n");
2436 ctxt->nbErrors++;
2437 return(ret);
2438 }
2439 old_flags = ctxt->flags;
2440 ctxt->flags |= XML_RELAXNG_IN_ATTRIBUTE;
2441 if (IS_RELAXNG(child, "name")) {
2442 val = xmlNodeGetContent(child);
2443 ret->name = val;
2444 val = xmlGetProp(child, BAD_CAST "ns");
2445 ret->ns = val;
2446 } else if (IS_RELAXNG(child, "anyName")) {
2447 TODO
2448 } else if (IS_RELAXNG(child, "nsName")) {
2449 TODO
2450 } else if (IS_RELAXNG(child, "choice")) {
2451 TODO
2452 } else {
2453 if (ctxt->error != NULL)
2454 ctxt->error(ctxt->userData,
2455 "element: expecting name, anyName, nsName or choice : got %s\n",
2456 child->name);
2457 ctxt->nbErrors++;
2458 ctxt->flags = old_flags;
2459 return(ret);
2460 }
2461 child = child->next;
2462 last = NULL;
2463 while (child != NULL) {
2464 cur = xmlRelaxNGParsePattern(ctxt, child);
2465 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002466 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002467 switch (cur->type) {
2468 case XML_RELAXNG_EMPTY:
2469 case XML_RELAXNG_NOT_ALLOWED:
2470 case XML_RELAXNG_TEXT:
2471 case XML_RELAXNG_ELEMENT:
2472 case XML_RELAXNG_DATATYPE:
2473 case XML_RELAXNG_VALUE:
2474 case XML_RELAXNG_LIST:
2475 case XML_RELAXNG_REF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002476 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00002477 case XML_RELAXNG_DEF:
2478 case XML_RELAXNG_ONEORMORE:
2479 case XML_RELAXNG_ZEROORMORE:
2480 case XML_RELAXNG_OPTIONAL:
2481 case XML_RELAXNG_CHOICE:
2482 case XML_RELAXNG_GROUP:
2483 case XML_RELAXNG_INTERLEAVE:
2484 if (last == NULL) {
2485 ret->content = last = cur;
2486 } else {
2487 if ((last->type == XML_RELAXNG_ELEMENT) &&
2488 (ret->content == last)) {
2489 ret->content = xmlRelaxNGNewDefine(ctxt, node);
2490 if (ret->content != NULL) {
2491 ret->content->type = XML_RELAXNG_GROUP;
2492 ret->content->content = last;
2493 } else {
2494 ret->content = last;
2495 }
2496 }
2497 last->next = cur;
2498 last = cur;
2499 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002500 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002501 break;
2502 case XML_RELAXNG_ATTRIBUTE:
2503 cur->next = ret->attrs;
2504 ret->attrs = cur;
2505 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002506 case XML_RELAXNG_START:
2507 TODO
2508 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002509 }
2510 }
2511 child = child->next;
2512 }
2513 ctxt->flags = old_flags;
2514 return(ret);
2515}
2516
2517/**
2518 * xmlRelaxNGParseElement:
2519 * @ctxt: a Relax-NG parser context
2520 * @node: the element node
2521 *
2522 * parse the content of a RelaxNG element node.
2523 *
2524 * Returns the definition pointer or NULL in case of error.
2525 */
2526static xmlRelaxNGDefinePtr
2527xmlRelaxNGParseElement(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2528 xmlRelaxNGDefinePtr ret, cur, last;
2529 xmlNodePtr child;
2530 xmlChar *val;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002531 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002532
2533 ret = xmlRelaxNGNewDefine(ctxt, node);
2534 if (ret == NULL)
2535 return(NULL);
2536 ret->type = XML_RELAXNG_ELEMENT;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002537 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002538 child = node->children;
2539 if (child == NULL) {
2540 if (ctxt->error != NULL)
2541 ctxt->error(ctxt->userData,
2542 "xmlRelaxNGParseElement: element has no children\n");
2543 ctxt->nbErrors++;
2544 return(ret);
2545 }
2546 if (IS_RELAXNG(child, "name")) {
2547 val = xmlNodeGetContent(child);
2548 ret->name = val;
2549 val = xmlGetProp(child, BAD_CAST "ns");
2550 ret->ns = val;
2551 } else if (IS_RELAXNG(child, "anyName")) {
2552 TODO
2553 } else if (IS_RELAXNG(child, "nsName")) {
2554 TODO
2555 } else if (IS_RELAXNG(child, "choice")) {
2556 TODO
2557 } else {
2558 if (ctxt->error != NULL)
2559 ctxt->error(ctxt->userData,
2560 "element: expecting name, anyName, nsName or choice : got %s\n",
2561 child->name);
2562 ctxt->nbErrors++;
2563 return(ret);
2564 }
2565 child = child->next;
2566 if (child == NULL) {
2567 if (ctxt->error != NULL)
2568 ctxt->error(ctxt->userData,
2569 "xmlRelaxNGParseElement: element has no content\n");
2570 ctxt->nbErrors++;
2571 return(ret);
2572 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002573 olddefine = ctxt->define;
2574 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002575 last = NULL;
2576 while (child != NULL) {
2577 cur = xmlRelaxNGParsePattern(ctxt, child);
2578 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002579 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002580 switch (cur->type) {
2581 case XML_RELAXNG_EMPTY:
2582 case XML_RELAXNG_NOT_ALLOWED:
2583 case XML_RELAXNG_TEXT:
2584 case XML_RELAXNG_ELEMENT:
2585 case XML_RELAXNG_DATATYPE:
2586 case XML_RELAXNG_VALUE:
2587 case XML_RELAXNG_LIST:
2588 case XML_RELAXNG_REF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002589 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00002590 case XML_RELAXNG_DEF:
2591 case XML_RELAXNG_ZEROORMORE:
2592 case XML_RELAXNG_ONEORMORE:
2593 case XML_RELAXNG_OPTIONAL:
2594 case XML_RELAXNG_CHOICE:
2595 case XML_RELAXNG_GROUP:
2596 case XML_RELAXNG_INTERLEAVE:
2597 if (last == NULL) {
2598 ret->content = last = cur;
2599 } else {
2600 if ((last->type == XML_RELAXNG_ELEMENT) &&
2601 (ret->content == last)) {
2602 ret->content = xmlRelaxNGNewDefine(ctxt, node);
2603 if (ret->content != NULL) {
2604 ret->content->type = XML_RELAXNG_GROUP;
2605 ret->content->content = last;
2606 } else {
2607 ret->content = last;
2608 }
2609 }
2610 last->next = cur;
2611 last = cur;
2612 }
2613 break;
2614 case XML_RELAXNG_ATTRIBUTE:
2615 cur->next = ret->attrs;
2616 ret->attrs = cur;
2617 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002618 case XML_RELAXNG_START:
2619 TODO
2620 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002621 }
2622 }
2623 child = child->next;
2624 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002625 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002626 return(ret);
2627}
2628
2629/**
2630 * xmlRelaxNGParsePatterns:
2631 * @ctxt: a Relax-NG parser context
2632 * @nodes: list of nodes
Daniel Veillard154877e2003-01-30 12:17:05 +00002633 * @group: use an implicit <group> for elements
Daniel Veillard6eadf632003-01-23 18:29:16 +00002634 *
2635 * parse the content of a RelaxNG start node.
2636 *
2637 * Returns the definition pointer or NULL in case of error.
2638 */
2639static xmlRelaxNGDefinePtr
Daniel Veillard154877e2003-01-30 12:17:05 +00002640xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes,
2641 int group) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002642 xmlRelaxNGDefinePtr def = NULL, last = NULL, cur, parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002643
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002644 parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002645 while (nodes != NULL) {
2646 if (IS_RELAXNG(nodes, "element")) {
2647 cur = xmlRelaxNGParseElement(ctxt, nodes);
2648 if (def == NULL) {
2649 def = last = cur;
2650 } else {
Daniel Veillard154877e2003-01-30 12:17:05 +00002651 if ((group == 1) && (def->type == XML_RELAXNG_ELEMENT) &&
2652 (def == last)) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00002653 def = xmlRelaxNGNewDefine(ctxt, nodes);
2654 def->type = XML_RELAXNG_GROUP;
2655 def->content = last;
2656 }
2657 last->next = cur;
2658 last = cur;
2659 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002660 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002661 } else {
2662 cur = xmlRelaxNGParsePattern(ctxt, nodes);
2663 if (def == NULL) {
2664 def = last = cur;
2665 } else {
2666 last->next = cur;
2667 last = cur;
2668 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002669 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002670 }
2671 nodes = nodes->next;
2672 }
2673 return(def);
2674}
2675
2676/**
2677 * xmlRelaxNGParseStart:
2678 * @ctxt: a Relax-NG parser context
2679 * @nodes: start children nodes
2680 *
2681 * parse the content of a RelaxNG start node.
2682 *
2683 * Returns 0 in case of success, -1 in case of error
2684 */
2685static int
2686xmlRelaxNGParseStart(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
2687 int ret = 0;
2688 xmlRelaxNGDefinePtr def = NULL;
2689
2690 while (nodes != NULL) {
2691 if (IS_RELAXNG(nodes, "empty")) {
2692 TODO
2693 xmlElemDump(stdout, nodes->doc, nodes);
2694 } else if (IS_RELAXNG(nodes, "notAllowed")) {
2695 TODO
2696 xmlElemDump(stdout, nodes->doc, nodes);
2697 } else {
Daniel Veillard154877e2003-01-30 12:17:05 +00002698 def = xmlRelaxNGParsePatterns(ctxt, nodes, 1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002699 ctxt->grammar->start = def;
2700 }
2701 nodes = nodes->next;
2702 }
2703 return(ret);
2704}
2705
2706/**
2707 * xmlRelaxNGParseGrammarContent:
2708 * @ctxt: a Relax-NG parser context
2709 * @nodes: grammar children nodes
2710 *
2711 * parse the content of a RelaxNG grammar node.
2712 *
2713 * Returns 0 in case of success, -1 in case of error
2714 */
2715static int
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002716xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes)
Daniel Veillard6eadf632003-01-23 18:29:16 +00002717{
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002718 int ret = 0, tmp;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002719
2720 if (nodes == NULL) {
2721 if (ctxt->error != NULL)
2722 ctxt->error(ctxt->userData,
2723 "grammar has no children\n");
2724 ctxt->nbErrors++;
2725 return(-1);
2726 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002727 while (nodes != NULL) {
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002728 if (IS_RELAXNG(nodes, "start")) {
2729 if (nodes->children == NULL) {
2730 if (ctxt->error != NULL)
2731 ctxt->error(ctxt->userData,
2732 "grammar has no children\n");
2733 ctxt->nbErrors++;
2734 } else {
2735 tmp = xmlRelaxNGParseStart(ctxt, nodes->children);
2736 if (tmp != 0)
2737 ret = -1;
2738 }
2739 } else if (IS_RELAXNG(nodes, "define")) {
2740 tmp = xmlRelaxNGParseDefine(ctxt, nodes);
2741 if (tmp != 0)
2742 ret = -1;
2743 } else if (IS_RELAXNG(nodes, "include")) {
2744 tmp = xmlRelaxNGParseInclude(ctxt, nodes);
2745 if (tmp != 0)
2746 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002747 } else {
2748 if (ctxt->error != NULL)
2749 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002750 "grammar has unexpected child %s\n", nodes->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002751 ctxt->nbErrors++;
2752 ret = -1;
2753 }
2754 nodes = nodes->next;
2755 }
2756 return (ret);
2757}
2758
2759/**
2760 * xmlRelaxNGCheckReference:
2761 * @ref: the ref
2762 * @ctxt: a Relax-NG parser context
2763 * @name: the name associated to the defines
2764 *
2765 * Applies the 4.17. combine attribute rule for all the define
2766 * element of a given grammar using the same name.
2767 */
2768static void
2769xmlRelaxNGCheckReference(xmlRelaxNGDefinePtr ref,
2770 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
2771 xmlRelaxNGGrammarPtr grammar;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002772 xmlRelaxNGDefinePtr def, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002773
2774 grammar = ctxt->grammar;
2775 if (grammar == NULL) {
2776 if (ctxt->error != NULL)
2777 ctxt->error(ctxt->userData,
2778 "Internal error: no grammar in CheckReference %s\n",
2779 name);
2780 ctxt->nbErrors++;
2781 return;
2782 }
2783 if (ref->content != NULL) {
2784 if (ctxt->error != NULL)
2785 ctxt->error(ctxt->userData,
2786 "Internal error: reference has content in CheckReference %s\n",
2787 name);
2788 ctxt->nbErrors++;
2789 return;
2790 }
2791 if (grammar->defs != NULL) {
2792 def = xmlHashLookup(grammar->defs, name);
2793 if (def != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00002794 cur = ref;
2795 while (cur != NULL) {
2796 cur->content = def;
2797 cur = cur->nextHash;
2798 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002799 } else {
2800 TODO
2801 }
2802 }
2803 /*
2804 * TODO: make a closure and verify there is no loop !
2805 */
2806}
2807
2808/**
2809 * xmlRelaxNGCheckCombine:
2810 * @define: the define(s) list
2811 * @ctxt: a Relax-NG parser context
2812 * @name: the name associated to the defines
2813 *
2814 * Applies the 4.17. combine attribute rule for all the define
2815 * element of a given grammar using the same name.
2816 */
2817static void
2818xmlRelaxNGCheckCombine(xmlRelaxNGDefinePtr define,
2819 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
2820 xmlChar *combine;
2821 int choiceOrInterleave = -1;
2822 int missing = 0;
2823 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
2824
2825 if (define->nextHash == NULL)
2826 return;
2827 cur = define;
2828 while (cur != NULL) {
2829 combine = xmlGetProp(cur->node, BAD_CAST "combine");
2830 if (combine != NULL) {
2831 if (xmlStrEqual(combine, BAD_CAST "choice")) {
2832 if (choiceOrInterleave == -1)
2833 choiceOrInterleave = 1;
2834 else if (choiceOrInterleave == 0) {
2835 if (ctxt->error != NULL)
2836 ctxt->error(ctxt->userData,
2837 "Defines for %s use both 'choice' and 'interleave'\n",
2838 name);
2839 ctxt->nbErrors++;
2840 }
Daniel Veillard154877e2003-01-30 12:17:05 +00002841 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00002842 if (choiceOrInterleave == -1)
2843 choiceOrInterleave = 0;
2844 else if (choiceOrInterleave == 1) {
2845 if (ctxt->error != NULL)
2846 ctxt->error(ctxt->userData,
2847 "Defines for %s use both 'choice' and 'interleave'\n",
2848 name);
2849 ctxt->nbErrors++;
2850 }
2851 } else {
2852 if (ctxt->error != NULL)
2853 ctxt->error(ctxt->userData,
2854 "Defines for %s use unknown combine value '%s''\n",
2855 name, combine);
2856 ctxt->nbErrors++;
2857 }
2858 xmlFree(combine);
2859 } else {
2860 if (missing == 0)
2861 missing = 1;
2862 else {
2863 if (ctxt->error != NULL)
2864 ctxt->error(ctxt->userData,
2865 "Some defines for %s lacks the combine attribute\n",
2866 name);
2867 ctxt->nbErrors++;
2868 }
2869 }
2870
2871 cur = cur->nextHash;
2872 }
2873#ifdef DEBUG
2874 xmlGenericError(xmlGenericErrorContext,
2875 "xmlRelaxNGCheckCombine(): merging %s defines: %d\n",
2876 name, choiceOrInterleave);
2877#endif
2878 if (choiceOrInterleave == -1)
2879 choiceOrInterleave = 0;
2880 cur = xmlRelaxNGNewDefine(ctxt, define->node);
2881 if (cur == NULL)
2882 return;
2883 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00002884 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard154877e2003-01-30 12:17:05 +00002885 else
2886 cur->type = XML_RELAXNG_CHOICE;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002887 tmp = define;
2888 last = NULL;
2889 while (tmp != NULL) {
2890 if (tmp->content != NULL) {
2891 if (tmp->content->next != NULL) {
2892 /*
2893 * we need first to create a wrapper.
2894 */
2895 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
2896 if (tmp2 == NULL)
2897 break;
2898 tmp2->type = XML_RELAXNG_GROUP;
2899 tmp2->content = tmp->content;
2900 } else {
2901 tmp2 = tmp->content;
2902 }
2903 if (last == NULL) {
2904 cur->content = tmp2;
2905 } else {
2906 last->next = tmp2;
2907 }
2908 last = tmp2;
2909 tmp->content = NULL;
2910 }
2911 tmp = tmp->nextHash;
2912 }
2913 define->content = cur;
Daniel Veillard154877e2003-01-30 12:17:05 +00002914 if (choiceOrInterleave == 0) {
2915 if (ctxt->interleaves == NULL)
2916 ctxt->interleaves = xmlHashCreate(10);
2917 if (ctxt->interleaves == NULL) {
2918 if (ctxt->error != NULL)
2919 ctxt->error(ctxt->userData,
2920 "Failed to create interleaves hash table\n");
2921 ctxt->nbErrors++;
2922 } else {
2923 char tmpname[32];
2924
2925 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
2926 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
2927 if (ctxt->error != NULL)
2928 ctxt->error(ctxt->userData,
2929 "Failed to add %s to hash table\n", tmpname);
2930 ctxt->nbErrors++;
2931 }
2932 }
2933 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002934}
2935
2936/**
2937 * xmlRelaxNGCombineStart:
2938 * @ctxt: a Relax-NG parser context
2939 * @grammar: the grammar
2940 *
2941 * Applies the 4.17. combine rule for all the start
2942 * element of a given grammar.
2943 */
2944static void
2945xmlRelaxNGCombineStart(xmlRelaxNGParserCtxtPtr ctxt,
2946 xmlRelaxNGGrammarPtr grammar) {
2947 xmlRelaxNGDefinePtr starts;
2948 xmlChar *combine;
2949 int choiceOrInterleave = -1;
2950 int missing = 0;
2951 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
2952
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002953 starts = grammar->startList;
2954 if ((starts == NULL) || (starts->nextHash == NULL))
Daniel Veillard6eadf632003-01-23 18:29:16 +00002955 return;
2956 cur = starts;
2957 while (cur != NULL) {
2958 combine = xmlGetProp(cur->node, BAD_CAST "combine");
2959 if (combine != NULL) {
2960 if (xmlStrEqual(combine, BAD_CAST "choice")) {
2961 if (choiceOrInterleave == -1)
2962 choiceOrInterleave = 1;
2963 else if (choiceOrInterleave == 0) {
2964 if (ctxt->error != NULL)
2965 ctxt->error(ctxt->userData,
2966 "<start> use both 'choice' and 'interleave'\n");
2967 ctxt->nbErrors++;
2968 }
2969 } else if (xmlStrEqual(combine, BAD_CAST "choice")) {
2970 if (choiceOrInterleave == -1)
2971 choiceOrInterleave = 0;
2972 else if (choiceOrInterleave == 1) {
2973 if (ctxt->error != NULL)
2974 ctxt->error(ctxt->userData,
2975 "<start> use both 'choice' and 'interleave'\n");
2976 ctxt->nbErrors++;
2977 }
2978 } else {
2979 if (ctxt->error != NULL)
2980 ctxt->error(ctxt->userData,
2981 "<start> uses unknown combine value '%s''\n", combine);
2982 ctxt->nbErrors++;
2983 }
2984 xmlFree(combine);
2985 } else {
2986 if (missing == 0)
2987 missing = 1;
2988 else {
2989 if (ctxt->error != NULL)
2990 ctxt->error(ctxt->userData,
2991 "Some <start> elements lacks the combine attribute\n");
2992 ctxt->nbErrors++;
2993 }
2994 }
2995
2996 cur = cur->nextHash;
2997 }
2998#ifdef DEBUG
2999 xmlGenericError(xmlGenericErrorContext,
3000 "xmlRelaxNGCombineStart(): merging <start>: %d\n",
3001 choiceOrInterleave);
3002#endif
3003 if (choiceOrInterleave == -1)
3004 choiceOrInterleave = 0;
3005 cur = xmlRelaxNGNewDefine(ctxt, starts->node);
3006 if (cur == NULL)
3007 return;
3008 if (choiceOrInterleave == 0)
3009 cur->type = XML_RELAXNG_CHOICE;
3010 else
3011 cur->type = XML_RELAXNG_INTERLEAVE;
3012 tmp = starts;
3013 last = NULL;
3014 while (tmp != NULL) {
3015 if (tmp->content != NULL) {
3016 if (tmp->content->next != NULL) {
3017 /*
3018 * we need first to create a wrapper.
3019 */
3020 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
3021 if (tmp2 == NULL)
3022 break;
3023 tmp2->type = XML_RELAXNG_GROUP;
3024 tmp2->content = tmp->content;
3025 } else {
3026 tmp2 = tmp->content;
3027 }
3028 if (last == NULL) {
3029 cur->content = tmp2;
3030 } else {
3031 last->next = tmp2;
3032 }
3033 last = tmp2;
3034 tmp->content = NULL;
3035 }
3036 tmp = tmp->nextHash;
3037 }
3038 starts->content = cur;
3039}
3040
3041/**
3042 * xmlRelaxNGParseGrammar:
3043 * @ctxt: a Relax-NG parser context
3044 * @nodes: grammar children nodes
3045 *
3046 * parse a Relax-NG <grammar> node
3047 *
3048 * Returns the internal xmlRelaxNGGrammarPtr built or
3049 * NULL in case of error
3050 */
3051static xmlRelaxNGGrammarPtr
3052xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
3053 xmlRelaxNGGrammarPtr ret, tmp, old;
3054
Daniel Veillard6eadf632003-01-23 18:29:16 +00003055 ret = xmlRelaxNGNewGrammar(ctxt);
3056 if (ret == NULL)
3057 return(NULL);
3058
3059 /*
3060 * Link the new grammar in the tree
3061 */
3062 ret->parent = ctxt->grammar;
3063 if (ctxt->grammar != NULL) {
3064 tmp = ctxt->grammar->children;
3065 if (tmp == NULL) {
3066 ctxt->grammar->children = ret;
3067 } else {
3068 while (tmp->next != NULL)
3069 tmp = tmp->next;
3070 tmp->next = ret;
3071 }
3072 }
3073
3074 old = ctxt->grammar;
3075 ctxt->grammar = ret;
3076 xmlRelaxNGParseGrammarContent(ctxt, nodes);
3077 ctxt->grammar = ret;
3078
3079 /*
3080 * Apply 4.17 mergingd rules to defines and starts
3081 */
3082 xmlRelaxNGCombineStart(ctxt, ret);
3083 if (ret->defs != NULL) {
3084 xmlHashScan(ret->defs, (xmlHashScanner) xmlRelaxNGCheckCombine,
3085 ctxt);
3086 }
3087
3088 /*
3089 * link together defines and refs in this grammar
3090 */
3091 if (ret->refs != NULL) {
3092 xmlHashScan(ret->refs, (xmlHashScanner) xmlRelaxNGCheckReference,
3093 ctxt);
3094 }
3095 ctxt->grammar = old;
3096 return(ret);
3097}
3098
3099/**
3100 * xmlRelaxNGParseDocument:
3101 * @ctxt: a Relax-NG parser context
3102 * @node: the root node of the RelaxNG schema
3103 *
3104 * parse a Relax-NG definition resource and build an internal
3105 * xmlRelaxNG struture which can be used to validate instances.
3106 *
3107 * Returns the internal XML RelaxNG structure built or
3108 * NULL in case of error
3109 */
3110static xmlRelaxNGPtr
3111xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3112 xmlRelaxNGPtr schema = NULL;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003113 const xmlChar *olddefine;
Daniel Veillarde431a272003-01-29 23:02:33 +00003114 xmlRelaxNGGrammarPtr old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003115
3116 if ((ctxt == NULL) || (node == NULL))
3117 return (NULL);
3118
3119 schema = xmlRelaxNGNewRelaxNG(ctxt);
3120 if (schema == NULL)
3121 return(NULL);
3122
Daniel Veillard276be4a2003-01-24 01:03:34 +00003123 olddefine = ctxt->define;
3124 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003125 if (IS_RELAXNG(node, "grammar")) {
3126 schema->topgrammar = xmlRelaxNGParseGrammar(ctxt, node->children);
3127 } else {
3128 schema->topgrammar = xmlRelaxNGNewGrammar(ctxt);
3129 if (schema->topgrammar == NULL) {
3130 return(schema);
3131 }
3132 schema->topgrammar->parent = NULL;
Daniel Veillarde431a272003-01-29 23:02:33 +00003133 old = ctxt->grammar;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003134 ctxt->grammar = schema->topgrammar;
3135 xmlRelaxNGParseStart(ctxt, node);
Daniel Veillarde431a272003-01-29 23:02:33 +00003136 if (old != NULL)
3137 ctxt->grammar = old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003138 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003139 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003140
3141#ifdef DEBUG
3142 if (schema == NULL)
3143 xmlGenericError(xmlGenericErrorContext,
3144 "xmlRelaxNGParseDocument() failed\n");
3145#endif
3146
3147 return (schema);
3148}
3149
3150/************************************************************************
3151 * *
3152 * Reading RelaxNGs *
3153 * *
3154 ************************************************************************/
3155
3156/**
3157 * xmlRelaxNGNewParserCtxt:
3158 * @URL: the location of the schema
3159 *
3160 * Create an XML RelaxNGs parse context for that file/resource expected
3161 * to contain an XML RelaxNGs file.
3162 *
3163 * Returns the parser context or NULL in case of error
3164 */
3165xmlRelaxNGParserCtxtPtr
3166xmlRelaxNGNewParserCtxt(const char *URL) {
3167 xmlRelaxNGParserCtxtPtr ret;
3168
3169 if (URL == NULL)
3170 return(NULL);
3171
3172 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
3173 if (ret == NULL) {
3174 xmlGenericError(xmlGenericErrorContext,
3175 "Failed to allocate new schama parser context for %s\n", URL);
3176 return (NULL);
3177 }
3178 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
3179 ret->URL = xmlStrdup((const xmlChar *)URL);
3180 return (ret);
3181}
3182
3183/**
3184 * xmlRelaxNGNewMemParserCtxt:
3185 * @buffer: a pointer to a char array containing the schemas
3186 * @size: the size of the array
3187 *
3188 * Create an XML RelaxNGs parse context for that memory buffer expected
3189 * to contain an XML RelaxNGs file.
3190 *
3191 * Returns the parser context or NULL in case of error
3192 */
3193xmlRelaxNGParserCtxtPtr
3194xmlRelaxNGNewMemParserCtxt(const char *buffer, int size) {
3195 xmlRelaxNGParserCtxtPtr ret;
3196
3197 if ((buffer == NULL) || (size <= 0))
3198 return(NULL);
3199
3200 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
3201 if (ret == NULL) {
3202 xmlGenericError(xmlGenericErrorContext,
3203 "Failed to allocate new schama parser context\n");
3204 return (NULL);
3205 }
3206 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
3207 ret->buffer = buffer;
3208 ret->size = size;
3209 return (ret);
3210}
3211
3212/**
3213 * xmlRelaxNGFreeParserCtxt:
3214 * @ctxt: the schema parser context
3215 *
3216 * Free the resources associated to the schema parser context
3217 */
3218void
3219xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxtPtr ctxt) {
3220 if (ctxt == NULL)
3221 return;
3222 if (ctxt->URL != NULL)
3223 xmlFree(ctxt->URL);
3224 if (ctxt->doc != NULL)
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003225 xmlFreeDoc(ctxt->document);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003226 if (ctxt->interleaves != NULL)
3227 xmlHashFree(ctxt->interleaves, NULL);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003228 if (ctxt->documents != NULL)
3229 xmlHashFree(ctxt->documents, (xmlHashDeallocator)
3230 xmlRelaxNGFreeDocument);
3231 if (ctxt->docTab != NULL)
3232 xmlFree(ctxt->docTab);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003233 if (ctxt->incTab != NULL)
3234 xmlFree(ctxt->incTab);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003235 xmlFree(ctxt);
3236}
3237
Daniel Veillard6eadf632003-01-23 18:29:16 +00003238/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003239 * xmlRelaxNGCleanupDoc:
3240 * @ctxt: a Relax-NG parser context
3241 * @doc: an xmldocPtr document pointer
Daniel Veillard6eadf632003-01-23 18:29:16 +00003242 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003243 * Cleanup the document from unwanted nodes for parsing, resolve
3244 * Include and externalRef lookups.
Daniel Veillard6eadf632003-01-23 18:29:16 +00003245 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003246 * Returns the cleaned up document or NULL in case of error
Daniel Veillard6eadf632003-01-23 18:29:16 +00003247 */
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003248static xmlDocPtr
3249xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt, xmlDocPtr doc) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003250 xmlNodePtr root, cur, delete;
3251
Daniel Veillard6eadf632003-01-23 18:29:16 +00003252 /*
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003253 * Extract the root
Daniel Veillard6eadf632003-01-23 18:29:16 +00003254 */
3255 root = xmlDocGetRootElement(doc);
3256 if (root == NULL) {
3257 if (ctxt->error != NULL)
3258 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
3259 ctxt->URL);
3260 ctxt->nbErrors++;
3261 return (NULL);
3262 }
3263
3264 /*
3265 * Remove all the blank text nodes
3266 */
3267 delete = NULL;
3268 cur = root;
3269 while (cur != NULL) {
3270 if (delete != NULL) {
3271 xmlUnlinkNode(delete);
3272 xmlFreeNode(delete);
3273 delete = NULL;
3274 }
3275 if (cur->type == XML_ELEMENT_NODE) {
3276 /*
3277 * Simplification 4.1. Annotations
3278 */
3279 if ((cur->ns == NULL) ||
3280 (!xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
3281 delete = cur;
3282 goto skip_children;
3283 } else {
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003284 if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003285 xmlChar *href, *ns, *base, *URL;
3286 xmlRelaxNGDocumentPtr docu;
3287
3288 ns = xmlGetProp(cur, BAD_CAST "ns");
3289 href = xmlGetProp(cur, BAD_CAST "href");
3290 if (href == NULL) {
3291 if (ctxt->error != NULL)
3292 ctxt->error(ctxt->userData,
3293 "xmlRelaxNGParse: externalRef has no href attribute\n");
3294 ctxt->nbErrors++;
3295 delete = cur;
3296 goto skip_children;
3297 }
3298 base = xmlNodeGetBase(cur->doc, cur);
3299 URL = xmlBuildURI(href, base);
3300 if (URL == NULL) {
3301 if (ctxt->error != NULL)
3302 ctxt->error(ctxt->userData,
3303 "Failed to compute URL for externalRef %s\n", href);
3304 ctxt->nbErrors++;
3305 if (href != NULL)
3306 xmlFree(href);
3307 if (base != NULL)
3308 xmlFree(base);
3309 delete = cur;
3310 goto skip_children;
3311 }
3312 if (href != NULL)
3313 xmlFree(href);
3314 if (base != NULL)
3315 xmlFree(base);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003316 docu = xmlRelaxNGLoadExternalRef(ctxt, URL, ns);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003317 if (docu == NULL) {
3318 if (ctxt->error != NULL)
3319 ctxt->error(ctxt->userData,
3320 "Failed to load externalRef %s\n", URL);
3321 ctxt->nbErrors++;
3322 xmlFree(URL);
3323 delete = cur;
3324 goto skip_children;
3325 }
3326 xmlFree(URL);
3327 cur->_private = docu;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003328 } else if (xmlStrEqual(cur->name, BAD_CAST "include")) {
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003329 xmlChar *href, *base, *URL;
3330 xmlRelaxNGIncludePtr incl;
3331
3332 href = xmlGetProp(cur, BAD_CAST "href");
3333 if (href == NULL) {
3334 if (ctxt->error != NULL)
3335 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003336 "xmlRelaxNGParse: include has no href attribute\n");
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003337 ctxt->nbErrors++;
3338 delete = cur;
3339 goto skip_children;
3340 }
3341 base = xmlNodeGetBase(cur->doc, cur);
3342 URL = xmlBuildURI(href, base);
3343 if (URL == NULL) {
3344 if (ctxt->error != NULL)
3345 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003346 "Failed to compute URL for include %s\n", href);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003347 ctxt->nbErrors++;
3348 if (href != NULL)
3349 xmlFree(href);
3350 if (base != NULL)
3351 xmlFree(base);
3352 delete = cur;
3353 goto skip_children;
3354 }
3355 if (href != NULL)
3356 xmlFree(href);
3357 if (base != NULL)
3358 xmlFree(base);
3359 incl = xmlRelaxNGLoadInclude(ctxt, URL, cur);
3360 if (incl == NULL) {
3361 if (ctxt->error != NULL)
3362 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003363 "Failed to load include %s\n", URL);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003364 ctxt->nbErrors++;
3365 xmlFree(URL);
3366 delete = cur;
3367 goto skip_children;
3368 }
3369 xmlFree(URL);
3370 cur->_private = incl;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003371 } else if ((xmlStrEqual(cur->name, BAD_CAST "element")) ||
3372 (xmlStrEqual(cur->name, BAD_CAST "attribute"))) {
3373 xmlChar *name;
3374 xmlNodePtr text = NULL;
3375
3376 /*
3377 * Simplification 4.8. name attribute of element
3378 * and attribute elements
3379 */
3380 name = xmlGetProp(cur, BAD_CAST "name");
3381 if (name != NULL) {
3382 if (cur->children == NULL) {
3383 text = xmlNewChild(cur, cur->ns, BAD_CAST "name",
3384 name);
3385 } else {
3386 xmlNodePtr node;
3387 node = xmlNewNode(cur->ns, BAD_CAST "name");
3388 if (node != NULL) {
3389 xmlAddPrevSibling(cur->children, node);
3390 text = xmlNewText(name);
3391 xmlAddChild(node, text);
3392 text = node;
3393 }
3394 }
3395 xmlUnsetProp(cur, BAD_CAST "name");
3396 xmlFree(name);
3397 }
3398 if (xmlStrEqual(cur->name, BAD_CAST "attribute")) {
3399 if (text == NULL) {
3400 text = cur->children;
3401 while (text != NULL) {
3402 if ((text->type == XML_ELEMENT_NODE) &&
3403 (xmlStrEqual(text->name, BAD_CAST "name")))
3404 break;
3405 text = text->next;
3406 }
3407 }
3408 if (text == NULL) {
3409 if (ctxt->error != NULL)
3410 ctxt->error(ctxt->userData,
3411 "xmlRelaxNGParse: attribute without name\n");
3412 ctxt->nbErrors++;
3413 } else {
3414 xmlSetProp(text, BAD_CAST "ns", BAD_CAST "");
3415 }
3416 }
3417 } else if ((xmlStrEqual(cur->name, BAD_CAST "name")) ||
3418 (xmlStrEqual(cur->name, BAD_CAST "nsName")) ||
3419 (xmlStrEqual(cur->name, BAD_CAST "value"))) {
3420 /*
3421 * Simplification 4.8. name attribute of element
3422 * and attribute elements
3423 */
3424 if (xmlHasProp(cur, BAD_CAST "ns") == NULL) {
3425 xmlNodePtr node;
3426 xmlChar *ns = NULL;
3427
3428 node = cur->parent;
3429 while ((node != NULL) &&
3430 (node->type == XML_ELEMENT_NODE)) {
3431 ns = xmlGetProp(node, BAD_CAST "ns");
3432 if (ns != NULL) {
3433 break;
3434 }
3435 node = node->parent;
3436 }
3437 if (ns == NULL) {
3438 xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
3439 } else {
3440 xmlSetProp(cur, BAD_CAST "ns", ns);
3441 xmlFree(ns);
3442 }
3443 }
3444 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
3445 xmlChar *name, *local, *prefix;
3446
3447 /*
3448 * Simplification: 4.10. QNames
3449 */
3450 name = xmlNodeGetContent(cur);
3451 if (name != NULL) {
3452 local = xmlSplitQName2(name, &prefix);
3453 if (local != NULL) {
3454 xmlNsPtr ns;
3455
3456 ns = xmlSearchNs(cur->doc, cur, prefix);
3457 if (ns == NULL) {
3458 if (ctxt->error != NULL)
3459 ctxt->error(ctxt->userData,
3460 "xmlRelaxNGParse: no namespace for prefix %s\n", prefix);
3461 ctxt->nbErrors++;
3462 } else {
3463 xmlSetProp(cur, BAD_CAST "ns", ns->href);
3464 xmlNodeSetContent(cur, local);
3465 }
3466 xmlFree(local);
3467 xmlFree(prefix);
3468 }
3469 xmlFree(name);
3470 }
3471 }
3472 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003473 /*
3474 * Thisd is not an else since "include" is transformed
3475 * into a div
3476 */
3477 if (xmlStrEqual(cur->name, BAD_CAST "div")) {
3478 /*
3479 * implements rule 4.11
3480 */
3481 xmlNodePtr child, ins, tmp;
3482
3483 child = cur->children;
3484 ins = child;
3485 while (child != NULL) {
3486 tmp = child->next;
3487 xmlUnlinkNode(child);
3488 ins = xmlAddNextSibling(ins, child);
3489 child = tmp;
3490 }
3491 delete = cur;
3492 goto skip_children;
3493 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003494 }
3495 }
3496 /*
3497 * Simplification 4.2 whitespaces
3498 */
3499 else if (cur->type == XML_TEXT_NODE) {
3500 if (IS_BLANK_NODE(cur)) {
3501 if (cur->parent->type == XML_ELEMENT_NODE) {
3502 if ((!xmlStrEqual(cur->parent->name, BAD_CAST "value")) &&
3503 (!xmlStrEqual(cur->parent->name, BAD_CAST "param")))
3504 delete = cur;
3505 } else {
3506 delete = cur;
3507 goto skip_children;
3508 }
3509 }
3510 } else if (cur->type != XML_CDATA_SECTION_NODE) {
3511 delete = cur;
3512 goto skip_children;
3513 }
3514
3515 /*
3516 * Skip to next node
3517 */
3518 if (cur->children != NULL) {
3519 if ((cur->children->type != XML_ENTITY_DECL) &&
3520 (cur->children->type != XML_ENTITY_REF_NODE) &&
3521 (cur->children->type != XML_ENTITY_NODE)) {
3522 cur = cur->children;
3523 continue;
3524 }
3525 }
3526skip_children:
3527 if (cur->next != NULL) {
3528 cur = cur->next;
3529 continue;
3530 }
3531
3532 do {
3533 cur = cur->parent;
3534 if (cur == NULL)
3535 break;
3536 if (cur == root) {
3537 cur = NULL;
3538 break;
3539 }
3540 if (cur->next != NULL) {
3541 cur = cur->next;
3542 break;
3543 }
3544 } while (cur != NULL);
3545 }
3546 if (delete != NULL) {
3547 xmlUnlinkNode(delete);
3548 xmlFreeNode(delete);
3549 delete = NULL;
3550 }
3551
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003552 return(doc);
3553}
3554
3555/**
3556 * xmlRelaxNGParse:
3557 * @ctxt: a Relax-NG parser context
3558 *
3559 * parse a schema definition resource and build an internal
3560 * XML Shema struture which can be used to validate instances.
3561 * *WARNING* this interface is highly subject to change
3562 *
3563 * Returns the internal XML RelaxNG structure built from the resource or
3564 * NULL in case of error
3565 */
3566xmlRelaxNGPtr
3567xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt)
3568{
3569 xmlRelaxNGPtr ret = NULL;
3570 xmlDocPtr doc;
3571 xmlNodePtr root;
3572
3573 xmlRelaxNGInitTypes();
3574
3575 if (ctxt == NULL)
3576 return (NULL);
3577
3578 /*
3579 * First step is to parse the input document into an DOM/Infoset
3580 */
3581 if (ctxt->URL != NULL) {
3582 doc = xmlParseFile((const char *) ctxt->URL);
3583 if (doc == NULL) {
3584 if (ctxt->error != NULL)
3585 ctxt->error(ctxt->userData,
3586 "xmlRelaxNGParse: could not load %s\n", ctxt->URL);
3587 ctxt->nbErrors++;
3588 return (NULL);
3589 }
3590 } else if (ctxt->buffer != NULL) {
3591 doc = xmlParseMemory(ctxt->buffer, ctxt->size);
3592 if (doc == NULL) {
3593 if (ctxt->error != NULL)
3594 ctxt->error(ctxt->userData,
3595 "xmlRelaxNGParse: could not parse schemas\n");
3596 ctxt->nbErrors++;
3597 return (NULL);
3598 }
3599 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
3600 ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
3601 } else {
3602 if (ctxt->error != NULL)
3603 ctxt->error(ctxt->userData,
3604 "xmlRelaxNGParse: nothing to parse\n");
3605 ctxt->nbErrors++;
3606 return (NULL);
3607 }
3608 ctxt->document = doc;
3609
3610 /*
3611 * Some preprocessing of the document content
3612 */
3613 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
3614 if (doc == NULL) {
3615 xmlFreeDoc(ctxt->document);
3616 ctxt->document = NULL;
3617 return(NULL);
3618 }
3619
Daniel Veillard6eadf632003-01-23 18:29:16 +00003620 /*
3621 * Then do the parsing for good
3622 */
3623 root = xmlDocGetRootElement(doc);
3624 if (root == NULL) {
3625 if (ctxt->error != NULL)
3626 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
3627 ctxt->URL);
3628 ctxt->nbErrors++;
3629 return (NULL);
3630 }
3631 ret = xmlRelaxNGParseDocument(ctxt, root);
3632 if (ret == NULL)
3633 return(NULL);
3634
3635 /*
3636 * Check the ref/defines links
3637 */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003638 /*
3639 * try to preprocess interleaves
3640 */
3641 if (ctxt->interleaves != NULL) {
3642 xmlHashScan(ctxt->interleaves,
3643 (xmlHashScanner)xmlRelaxNGComputeInterleaves, ctxt);
3644 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003645
3646 /*
3647 * if there was a parsing error return NULL
3648 */
3649 if (ctxt->nbErrors > 0) {
3650 xmlRelaxNGFree(ret);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003651 ctxt->document = NULL;
3652 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003653 return(NULL);
3654 }
3655
3656 /*
3657 * Transfer the pointer for cleanup at the schema level.
3658 */
3659 ret->doc = doc;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003660 ctxt->document = NULL;
3661 ret->documents = ctxt->documents;
3662 ctxt->documents = NULL;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003663 ret->includes = ctxt->includes;
3664 ctxt->includes = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003665
3666 return (ret);
3667}
3668
3669/**
3670 * xmlRelaxNGSetParserErrors:
3671 * @ctxt: a Relax-NG validation context
3672 * @err: the error callback
3673 * @warn: the warning callback
3674 * @ctx: contextual data for the callbacks
3675 *
3676 * Set the callback functions used to handle errors for a validation context
3677 */
3678void
3679xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
3680 xmlRelaxNGValidityErrorFunc err,
3681 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
3682 if (ctxt == NULL)
3683 return;
3684 ctxt->error = err;
3685 ctxt->warning = warn;
3686 ctxt->userData = ctx;
3687}
3688/************************************************************************
3689 * *
3690 * Dump back a compiled form *
3691 * *
3692 ************************************************************************/
3693static void xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define);
3694
3695/**
3696 * xmlRelaxNGDumpDefines:
3697 * @output: the file output
3698 * @defines: a list of define structures
3699 *
3700 * Dump a RelaxNG structure back
3701 */
3702static void
3703xmlRelaxNGDumpDefines(FILE * output, xmlRelaxNGDefinePtr defines) {
3704 while (defines != NULL) {
3705 xmlRelaxNGDumpDefine(output, defines);
3706 defines = defines->next;
3707 }
3708}
3709
3710/**
3711 * xmlRelaxNGDumpDefine:
3712 * @output: the file output
3713 * @define: a define structure
3714 *
3715 * Dump a RelaxNG structure back
3716 */
3717static void
3718xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define) {
3719 if (define == NULL)
3720 return;
3721 switch(define->type) {
3722 case XML_RELAXNG_EMPTY:
3723 fprintf(output, "<empty/>\n");
3724 break;
3725 case XML_RELAXNG_NOT_ALLOWED:
3726 fprintf(output, "<notAllowed/>\n");
3727 break;
3728 case XML_RELAXNG_TEXT:
3729 fprintf(output, "<text/>\n");
3730 break;
3731 case XML_RELAXNG_ELEMENT:
3732 fprintf(output, "<element>\n");
3733 if (define->name != NULL) {
3734 fprintf(output, "<name");
3735 if (define->ns != NULL)
3736 fprintf(output, " ns=\"%s\"", define->ns);
3737 fprintf(output, ">%s</name>\n", define->name);
3738 }
3739 xmlRelaxNGDumpDefines(output, define->attrs);
3740 xmlRelaxNGDumpDefines(output, define->content);
3741 fprintf(output, "</element>\n");
3742 break;
3743 case XML_RELAXNG_LIST:
3744 fprintf(output, "<list>\n");
3745 xmlRelaxNGDumpDefines(output, define->content);
3746 fprintf(output, "</list>\n");
3747 break;
3748 case XML_RELAXNG_ONEORMORE:
3749 fprintf(output, "<oneOrMore>\n");
3750 xmlRelaxNGDumpDefines(output, define->content);
3751 fprintf(output, "</oneOrMore>\n");
3752 break;
3753 case XML_RELAXNG_ZEROORMORE:
3754 fprintf(output, "<zeroOrMore>\n");
3755 xmlRelaxNGDumpDefines(output, define->content);
3756 fprintf(output, "</zeroOrMore>\n");
3757 break;
3758 case XML_RELAXNG_CHOICE:
3759 fprintf(output, "<choice>\n");
3760 xmlRelaxNGDumpDefines(output, define->content);
3761 fprintf(output, "</choice>\n");
3762 break;
3763 case XML_RELAXNG_GROUP:
3764 fprintf(output, "<group>\n");
3765 xmlRelaxNGDumpDefines(output, define->content);
3766 fprintf(output, "</group>\n");
3767 break;
3768 case XML_RELAXNG_INTERLEAVE:
3769 fprintf(output, "<interleave>\n");
3770 xmlRelaxNGDumpDefines(output, define->content);
3771 fprintf(output, "</interleave>\n");
3772 break;
3773 case XML_RELAXNG_OPTIONAL:
3774 fprintf(output, "<optional>\n");
3775 xmlRelaxNGDumpDefines(output, define->content);
3776 fprintf(output, "</optional>\n");
3777 break;
3778 case XML_RELAXNG_ATTRIBUTE:
3779 fprintf(output, "<attribute>\n");
3780 xmlRelaxNGDumpDefines(output, define->content);
3781 fprintf(output, "</attribute>\n");
3782 break;
3783 case XML_RELAXNG_DEF:
3784 fprintf(output, "<define");
3785 if (define->name != NULL)
3786 fprintf(output, " name=\"%s\"", define->name);
3787 fprintf(output, ">\n");
3788 xmlRelaxNGDumpDefines(output, define->content);
3789 fprintf(output, "</define>\n");
3790 break;
3791 case XML_RELAXNG_REF:
3792 fprintf(output, "<ref");
3793 if (define->name != NULL)
3794 fprintf(output, " name=\"%s\"", define->name);
3795 fprintf(output, ">\n");
3796 xmlRelaxNGDumpDefines(output, define->content);
3797 fprintf(output, "</ref>\n");
3798 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003799 case XML_RELAXNG_EXTERNALREF:
Daniel Veillarde431a272003-01-29 23:02:33 +00003800 fprintf(output, "<externalRef");
3801 xmlRelaxNGDumpDefines(output, define->content);
3802 fprintf(output, "</externalRef>\n");
3803 break;
3804 case XML_RELAXNG_DATATYPE:
Daniel Veillard6eadf632003-01-23 18:29:16 +00003805 case XML_RELAXNG_VALUE:
3806 TODO
3807 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003808 case XML_RELAXNG_START:
3809 TODO
3810 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003811 }
3812}
3813
3814/**
3815 * xmlRelaxNGDumpGrammar:
3816 * @output: the file output
3817 * @grammar: a grammar structure
3818 * @top: is this a top grammar
3819 *
3820 * Dump a RelaxNG structure back
3821 */
3822static void
3823xmlRelaxNGDumpGrammar(FILE * output, xmlRelaxNGGrammarPtr grammar, int top)
3824{
3825 if (grammar == NULL)
3826 return;
3827
3828 fprintf(output, "<grammar");
3829 if (top)
3830 fprintf(output,
3831 " xmlns=\"http://relaxng.org/ns/structure/1.0\"");
3832 switch(grammar->combine) {
3833 case XML_RELAXNG_COMBINE_UNDEFINED:
3834 break;
3835 case XML_RELAXNG_COMBINE_CHOICE:
3836 fprintf(output, " combine=\"choice\"");
3837 break;
3838 case XML_RELAXNG_COMBINE_INTERLEAVE:
3839 fprintf(output, " combine=\"interleave\"");
3840 break;
3841 default:
3842 fprintf(output, " <!-- invalid combine value -->");
3843 }
3844 fprintf(output, ">\n");
3845 if (grammar->start == NULL) {
3846 fprintf(output, " <!-- grammar had no start -->");
3847 } else {
3848 fprintf(output, "<start>\n");
3849 xmlRelaxNGDumpDefine(output, grammar->start);
3850 fprintf(output, "</start>\n");
3851 }
3852 /* TODO ? Dump the defines ? */
3853 fprintf(output, "</grammar>\n");
3854}
3855
3856/**
3857 * xmlRelaxNGDump:
3858 * @output: the file output
3859 * @schema: a schema structure
3860 *
3861 * Dump a RelaxNG structure back
3862 */
3863void
3864xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema)
3865{
3866 if (schema == NULL) {
3867 fprintf(output, "RelaxNG empty or failed to compile\n");
3868 return;
3869 }
3870 fprintf(output, "RelaxNG: ");
3871 if (schema->doc == NULL) {
3872 fprintf(output, "no document\n");
3873 } else if (schema->doc->URL != NULL) {
3874 fprintf(output, "%s\n", schema->doc->URL);
3875 } else {
3876 fprintf(output, "\n");
3877 }
3878 if (schema->topgrammar == NULL) {
3879 fprintf(output, "RelaxNG has no top grammar\n");
3880 return;
3881 }
3882 xmlRelaxNGDumpGrammar(output, schema->topgrammar, 1);
3883}
3884
3885/************************************************************************
3886 * *
3887 * Validation implementation *
3888 * *
3889 ************************************************************************/
3890static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
3891 xmlRelaxNGDefinePtr define);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00003892static int xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
3893 xmlRelaxNGDefinePtr define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003894
3895/**
3896 * xmlRelaxNGSkipIgnored:
3897 * @ctxt: a schema validation context
3898 * @node: the top node.
3899 *
3900 * Skip ignorable nodes in that context
3901 *
3902 * Returns the new sibling or NULL in case of error.
3903 */
3904static xmlNodePtr
3905xmlRelaxNGSkipIgnored(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
3906 xmlNodePtr node) {
3907 /*
3908 * TODO complete and handle entities
3909 */
3910 while ((node != NULL) &&
3911 ((node->type == XML_COMMENT_NODE) ||
3912 ((node->type == XML_TEXT_NODE) &&
3913 (IS_BLANK_NODE(node))))) {
3914 node = node->next;
3915 }
3916 return(node);
3917}
3918
3919/**
Daniel Veillardedc91922003-01-26 00:52:04 +00003920 * xmlRelaxNGNormalize:
3921 * @ctxt: a schema validation context
3922 * @str: the string to normalize
3923 *
3924 * Implements the normalizeWhiteSpace( s ) function from
3925 * section 6.2.9 of the spec
3926 *
3927 * Returns the new string or NULL in case of error.
3928 */
3929static xmlChar *
3930xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *str) {
3931 xmlChar *ret, *p;
3932 const xmlChar *tmp;
3933 int len;
3934
3935 if (str == NULL)
3936 return(NULL);
3937 tmp = str;
3938 while (*tmp != 0) tmp++;
3939 len = tmp - str;
3940
3941 ret = (xmlChar *) xmlMalloc((len + 1) * sizeof(xmlChar));
3942 if (ret == NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00003943 if (ctxt != NULL) {
3944 VALID_CTXT();
3945 VALID_ERROR("xmlRelaxNGNormalize: out of memory\n");
3946 } else {
3947 xmlGenericError(xmlGenericErrorContext,
3948 "xmlRelaxNGNormalize: out of memory\n");
3949 }
Daniel Veillardedc91922003-01-26 00:52:04 +00003950 return(NULL);
3951 }
3952 p = ret;
3953 while (IS_BLANK(*str)) str++;
3954 while (*str != 0) {
3955 if (IS_BLANK(*str)) {
3956 while (IS_BLANK(*str)) str++;
3957 if (*str == 0)
3958 break;
3959 *p++ = ' ';
3960 } else
3961 *p++ = *str++;
3962 }
3963 *p = 0;
3964 return(ret);
3965}
3966
3967/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00003968 * xmlRelaxNGValidateDatatype:
3969 * @ctxt: a Relax-NG validation context
3970 * @value: the string value
3971 * @type: the datatype definition
3972 *
3973 * Validate the given value against the dataype
3974 *
3975 * Returns 0 if the validation succeeded or an error code.
3976 */
3977static int
3978xmlRelaxNGValidateDatatype(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *value,
3979 xmlRelaxNGDefinePtr define) {
3980 int ret;
3981 xmlRelaxNGTypeLibraryPtr lib;
3982
3983 if ((define == NULL) || (define->data == NULL)) {
3984 return(-1);
3985 }
3986 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
3987 if (lib->check != NULL)
3988 ret = lib->check(lib->data, define->name, value);
3989 else
3990 ret = -1;
3991 if (ret < 0) {
3992 VALID_CTXT();
3993 VALID_ERROR("Internal: failed to validate type %s\n", define->name);
3994 return(-1);
3995 } else if (ret == 1) {
3996 ret = 0;
3997 } else {
3998 VALID_CTXT();
3999 VALID_ERROR("Type %s doesn't allow value %s\n", define->name, value);
4000 return(-1);
4001 ret = -1;
4002 }
4003 return(ret);
4004}
4005
4006/**
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004007 * xmlRelaxNGNextValue:
4008 * @ctxt: a Relax-NG validation context
4009 *
4010 * Skip to the next value when validating within a list
4011 *
4012 * Returns 0 if the operation succeeded or an error code.
4013 */
4014static int
4015xmlRelaxNGNextValue(xmlRelaxNGValidCtxtPtr ctxt) {
4016 xmlChar *cur;
4017
4018 cur = ctxt->state->value;
4019 if ((cur == NULL) || (ctxt->state->endvalue == NULL)) {
4020 ctxt->state->value = NULL;
4021 return(0);
4022 }
4023 while (*cur != 0) cur++;
4024 while ((cur != ctxt->state->endvalue) && (*cur == 0)) cur++;
4025 if (cur == ctxt->state->endvalue)
4026 ctxt->state->value = NULL;
4027 else
4028 ctxt->state->value = cur;
4029 return(0);
4030}
4031
4032/**
4033 * xmlRelaxNGValidateValueList:
4034 * @ctxt: a Relax-NG validation context
4035 * @defines: the list of definitions to verify
4036 *
4037 * Validate the given set of definitions for the current value
4038 *
4039 * Returns 0 if the validation succeeded or an error code.
4040 */
4041static int
4042xmlRelaxNGValidateValueList(xmlRelaxNGValidCtxtPtr ctxt,
4043 xmlRelaxNGDefinePtr defines) {
4044 int ret = 0;
4045
4046 while (defines != NULL) {
4047 ret = xmlRelaxNGValidateValue(ctxt, defines);
4048 if (ret != 0)
4049 break;
4050 defines = defines->next;
4051 }
4052 return(ret);
4053}
4054
4055/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004056 * xmlRelaxNGValidateValue:
4057 * @ctxt: a Relax-NG validation context
4058 * @define: the definition to verify
4059 *
4060 * Validate the given definition for the current value
4061 *
4062 * Returns 0 if the validation succeeded or an error code.
4063 */
4064static int
4065xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
4066 xmlRelaxNGDefinePtr define) {
Daniel Veillardedc91922003-01-26 00:52:04 +00004067 int ret = 0, oldflags;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004068 xmlChar *value;
4069
4070 value = ctxt->state->value;
4071 switch (define->type) {
4072 case XML_RELAXNG_EMPTY:
4073 if ((value != NULL) && (value[0] != '0'))
4074 ret = -1;
4075 break;
4076 case XML_RELAXNG_TEXT:
4077 break;
Daniel Veillardedc91922003-01-26 00:52:04 +00004078 case XML_RELAXNG_VALUE: {
4079 if (!xmlStrEqual(value, define->value)) {
4080 if (define->name != NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00004081 xmlRelaxNGTypeLibraryPtr lib;
4082
4083 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
4084 if ((lib != NULL) && (lib->comp != NULL))
4085 ret = lib->comp(lib->data, define->name, value,
4086 define->value);
4087 else
4088 ret = -1;
4089 if (ret < 0) {
4090 VALID_CTXT();
4091 VALID_ERROR("Internal: failed to compare type %s\n",
4092 define->name);
4093 return(-1);
4094 } else if (ret == 1) {
4095 ret = 0;
4096 } else {
4097 ret = -1;
4098 }
Daniel Veillardedc91922003-01-26 00:52:04 +00004099 } else {
4100 xmlChar *nval, *nvalue;
4101
4102 /*
4103 * TODO: trivial optimizations are possible by
4104 * computing at compile-time
4105 */
4106 nval = xmlRelaxNGNormalize(ctxt, define->value);
4107 nvalue = xmlRelaxNGNormalize(ctxt, value);
4108
Daniel Veillardea3f3982003-01-26 19:45:18 +00004109 if ((nval == NULL) || (nvalue == NULL) ||
4110 (!xmlStrEqual(nval, nvalue)))
Daniel Veillardedc91922003-01-26 00:52:04 +00004111 ret = -1;
4112 if (nval != NULL)
4113 xmlFree(nval);
4114 if (nvalue != NULL)
4115 xmlFree(nvalue);
4116 }
4117 }
4118 break;
4119 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004120 case XML_RELAXNG_DATATYPE: {
4121 ret = xmlRelaxNGValidateDatatype(ctxt, value, define);
4122 if (ret == 0)
4123 xmlRelaxNGNextValue(ctxt);
4124
4125 break;
4126 }
4127 case XML_RELAXNG_CHOICE: {
4128 xmlRelaxNGDefinePtr list = define->content;
4129 xmlChar *oldvalue;
4130
4131 oldflags = ctxt->flags;
4132 ctxt->flags |= FLAGS_IGNORABLE;
4133
4134 oldvalue = ctxt->state->value;
4135 while (list != NULL) {
4136 ret = xmlRelaxNGValidateValue(ctxt, list);
4137 if (ret == 0) {
4138 break;
4139 }
4140 ctxt->state->value = oldvalue;
4141 list = list->next;
4142 }
4143 ctxt->flags = oldflags;
4144 break;
4145 }
4146 case XML_RELAXNG_LIST: {
4147 xmlRelaxNGDefinePtr list = define->content;
4148 xmlChar *oldvalue, *oldend, *val, *cur;
4149
4150 oldvalue = ctxt->state->value;
4151 oldend = ctxt->state->endvalue;
4152
4153 val = xmlStrdup(oldvalue);
4154 if (val == NULL) {
4155 VALID_CTXT();
4156 VALID_ERROR("Internal: no state\n");
4157 return(-1);
4158 }
4159 cur = val;
4160 while (*cur != 0) {
4161 if (IS_BLANK(*cur))
4162 *cur = 0;
4163 cur++;
4164 }
4165 ctxt->state->endvalue = cur;
4166 cur = val;
4167 while ((*cur == 0) && (cur != ctxt->state->endvalue)) cur++;
4168
4169 ctxt->state->value = cur;
4170
4171 while (list != NULL) {
4172 ret = xmlRelaxNGValidateValue(ctxt, list);
4173 if (ret != 0) {
4174 break;
4175 }
4176 list = list->next;
4177 }
4178 if ((ret == 0) && (ctxt->state->value != NULL) &&
4179 (ctxt->state->value != ctxt->state->endvalue)) {
4180 VALID_CTXT();
4181 VALID_ERROR("Extra data in list: %s\n", ctxt->state->value);
4182 ret = -1;
4183 }
4184 xmlFree(val);
4185 ctxt->state->value = oldvalue;
4186 ctxt->state->endvalue = oldend;
4187 break;
4188 }
4189 case XML_RELAXNG_ONEORMORE:
4190 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
4191 if (ret != 0) {
4192 break;
4193 }
4194 /* no break on purpose */
4195 case XML_RELAXNG_ZEROORMORE: {
4196 xmlChar *cur, *temp;
4197
4198 oldflags = ctxt->flags;
4199 ctxt->flags |= FLAGS_IGNORABLE;
4200 cur = ctxt->state->value;
4201 temp = NULL;
4202 while ((cur != NULL) && (cur != ctxt->state->endvalue) &&
4203 (temp != cur)) {
4204 temp = cur;
4205 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
4206 if (ret != 0) {
4207 ctxt->state->value = temp;
4208 ret = 0;
4209 break;
4210 }
4211 cur = ctxt->state->value;
4212 }
4213 ctxt->flags = oldflags;
4214 break;
4215 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004216 default:
4217 TODO
4218 ret = -1;
4219 }
4220 return(ret);
4221}
4222
4223/**
4224 * xmlRelaxNGValidateValueContent:
4225 * @ctxt: a Relax-NG validation context
4226 * @defines: the list of definitions to verify
4227 *
4228 * Validate the given definitions for the current value
4229 *
4230 * Returns 0 if the validation succeeded or an error code.
4231 */
4232static int
4233xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt,
4234 xmlRelaxNGDefinePtr defines) {
4235 int ret = 0;
4236
4237 while (defines != NULL) {
4238 ret = xmlRelaxNGValidateValue(ctxt, defines);
4239 if (ret != 0)
4240 break;
4241 defines = defines->next;
4242 }
4243 return(ret);
4244}
4245
4246/**
4247 * xmlRelaxNGValidateAttribute:
4248 * @ctxt: a Relax-NG validation context
4249 * @define: the definition to verify
4250 *
4251 * Validate the given attribute definition for that node
4252 *
4253 * Returns 0 if the validation succeeded or an error code.
4254 */
4255static int
4256xmlRelaxNGValidateAttribute(xmlRelaxNGValidCtxtPtr ctxt,
4257 xmlRelaxNGDefinePtr define) {
4258 int ret = 0, i;
4259 xmlChar *value, *oldvalue;
4260 xmlAttrPtr prop = NULL, tmp;
4261
4262 if (define->name != NULL) {
4263 for (i = 0;i < ctxt->state->nbAttrs;i++) {
4264 tmp = ctxt->state->attrs[i];
4265 if ((tmp != NULL) && (xmlStrEqual(define->name, tmp->name))) {
4266 if ((((define->ns == NULL) || (define->ns[0] == 0)) &&
4267 (tmp->ns == NULL)) ||
4268 ((tmp->ns != NULL) &&
4269 (xmlStrEqual(define->ns, tmp->ns->href)))) {
4270 prop = tmp;
4271 break;
4272 }
4273 }
4274 }
4275 if (prop != NULL) {
4276 value = xmlNodeListGetString(prop->doc, prop->children, 1);
4277 oldvalue = ctxt->state->value;
4278 ctxt->state->value = value;
4279 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
4280 value = ctxt->state->value;
4281 ctxt->state->value = oldvalue;
4282 if (value != NULL)
4283 xmlFree(value);
4284 if (ret == 0) {
4285 /*
4286 * flag the attribute as processed
4287 */
4288 ctxt->state->attrs[i] = NULL;
4289 }
4290 } else {
4291 ret = -1;
4292 }
4293#ifdef DEBUG
4294 xmlGenericError(xmlGenericErrorContext,
4295 "xmlRelaxNGValidateAttribute(%s): %d\n", define->name, ret);
4296#endif
4297 } else {
4298 TODO
4299 }
4300
4301 return(ret);
4302}
4303
4304/**
4305 * xmlRelaxNGValidateAttributeList:
4306 * @ctxt: a Relax-NG validation context
4307 * @define: the list of definition to verify
4308 *
4309 * Validate the given node against the list of attribute definitions
4310 *
4311 * Returns 0 if the validation succeeded or an error code.
4312 */
4313static int
4314xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt,
4315 xmlRelaxNGDefinePtr defines) {
4316 int ret = 0;
4317 while (defines != NULL) {
4318 if (xmlRelaxNGValidateAttribute(ctxt, defines) != 0)
4319 ret = -1;
4320 defines = defines->next;
4321 }
4322 return(ret);
4323}
4324
4325/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004326 * xmlRelaxNGValidateTryPermutation:
4327 * @ctxt: a Relax-NG validation context
4328 * @groups: the array of groups
4329 * @nbgroups: the number of groups in the array
4330 * @array: the permutation to try
4331 * @len: the size of the set
4332 *
4333 * Try to validate a permutation for the group of definitions.
4334 *
4335 * Returns 0 if the validation succeeded or an error code.
4336 */
4337static int
4338xmlRelaxNGValidateTryPermutation(xmlRelaxNGValidCtxtPtr ctxt,
4339 xmlRelaxNGDefinePtr rule,
4340 xmlNodePtr *array, int len) {
4341 int i, ret;
4342
4343 if (len > 0) {
4344 /*
4345 * One only need the next pointer set-up to do the validation
4346 */
4347 for (i = 0;i < (len - 1);i++)
4348 array[i]->next = array[i + 1];
4349 array[i]->next = NULL;
4350
4351 /*
4352 * Now try to validate the sequence
4353 */
4354 ctxt->state->seq = array[0];
4355 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
4356 } else {
4357 ctxt->state->seq = NULL;
4358 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
4359 }
4360
4361 /*
4362 * the sequence must be fully consumed
4363 */
4364 if (ctxt->state->seq != NULL)
4365 return(-1);
4366
4367 return(ret);
4368}
4369
4370/**
4371 * xmlRelaxNGValidateWalkPermutations:
4372 * @ctxt: a Relax-NG validation context
4373 * @groups: the array of groups
4374 * @nbgroups: the number of groups in the array
4375 * @nodes: the set of nodes
4376 * @array: the current state of the parmutation
4377 * @len: the size of the set
4378 * @level: a pointer to the level variable
4379 * @k: the index in the array to fill
4380 *
4381 * Validate a set of nodes for a groups of definitions, will try the
4382 * full set of permutations
4383 *
4384 * Returns 0 if the validation succeeded or an error code.
4385 */
4386static int
4387xmlRelaxNGValidateWalkPermutations(xmlRelaxNGValidCtxtPtr ctxt,
4388 xmlRelaxNGDefinePtr rule, xmlNodePtr *nodes,
4389 xmlNodePtr *array, int len,
4390 int *level, int k) {
4391 int i, ret;
4392
4393 if ((k >= 0) && (k < len))
4394 array[k] = nodes[*level];
4395 *level = *level + 1;
4396 if (*level == len) {
4397 ret = xmlRelaxNGValidateTryPermutation(ctxt, rule, array, len);
4398 if (ret == 0)
4399 return(0);
4400 } else {
4401 for (i = 0;i < len;i++) {
4402 if (array[i] == NULL) {
4403 ret = xmlRelaxNGValidateWalkPermutations(ctxt, rule,
4404 nodes, array, len, level, i);
4405 if (ret == 0)
4406 return(0);
4407 }
4408 }
4409 }
4410 *level = *level - 1;
4411 array[k] = NULL;
4412 return(-1);
4413}
4414
4415/**
4416 * xmlRelaxNGNodeMatchesList:
4417 * @node: the node
4418 * @list: a NULL terminated array of definitions
4419 *
4420 * Check if a node can be matched by one of the definitions
4421 *
4422 * Returns 1 if matches 0 otherwise
4423 */
4424static int
4425xmlRelaxNGNodeMatchesList(xmlNodePtr node, xmlRelaxNGDefinePtr *list) {
4426 xmlRelaxNGDefinePtr cur;
4427 int i = 0;
4428
4429 if ((node == NULL) || (list == NULL))
4430 return(0);
4431
4432 cur = list[i++];
4433 while (cur != NULL) {
4434 if ((node->type == XML_ELEMENT_NODE) &&
4435 (cur->type == XML_RELAXNG_ELEMENT)) {
4436 if (cur->name == NULL) {
4437 if ((node->ns != NULL) &&
4438 (xmlStrEqual(node->ns->href, cur->ns)))
4439 return(1);
4440 } else if (xmlStrEqual(cur->name, node->name)) {
4441 if ((cur->ns == NULL) || (cur->ns[0] == 0)) {
4442 if (node->ns == NULL)
4443 return(1);
4444 } else {
4445 if ((node->ns != NULL) &&
4446 (xmlStrEqual(node->ns->href, cur->ns)))
4447 return(1);
4448 }
4449 }
4450 } else if ((node->type == XML_TEXT_NODE) &&
4451 (cur->type == XML_RELAXNG_TEXT)) {
4452 return(1);
4453 }
4454 cur = list[i++];
4455 }
4456 return(0);
4457}
4458
4459/**
4460 * xmlRelaxNGValidatePartGroup:
4461 * @ctxt: a Relax-NG validation context
4462 * @groups: the array of groups
4463 * @nbgroups: the number of groups in the array
4464 * @nodes: the set of nodes
4465 * @len: the size of the set of nodes
4466 *
4467 * Validate a set of nodes for a groups of definitions
4468 *
4469 * Returns 0 if the validation succeeded or an error code.
4470 */
4471static int
4472xmlRelaxNGValidatePartGroup(xmlRelaxNGValidCtxtPtr ctxt,
4473 xmlRelaxNGInterleaveGroupPtr *groups,
4474 int nbgroups, xmlNodePtr *nodes, int len) {
Daniel Veillardb08c9812003-01-28 23:09:49 +00004475 int level, ret = -1, i, j, k;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004476 xmlNodePtr *array = NULL, *list, oldseq;
4477 xmlRelaxNGInterleaveGroupPtr group;
4478
4479 list = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
4480 if (list == NULL) {
4481 return(-1);
4482 }
4483 array = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
4484 if (array == NULL) {
4485 xmlFree(list);
4486 return(-1);
4487 }
4488 memset(array, 0, len * sizeof(xmlNodePtr));
4489
4490 /*
4491 * Partition the elements and validate the subsets.
4492 */
4493 oldseq = ctxt->state->seq;
4494 for (i = 0;i < nbgroups;i++) {
4495 group = groups[i];
4496 if (group == NULL)
4497 continue;
4498 k = 0;
4499 for (j = 0;j < len;j++) {
4500 if (nodes[j] == NULL)
4501 continue;
4502 if (xmlRelaxNGNodeMatchesList(nodes[j], group->defs)) {
4503 list[k++] = nodes[j];
4504 nodes[j] = NULL;
4505 }
4506 }
4507 ctxt->state->seq = oldseq;
4508 if (k > 1) {
4509 memset(array, 0, k * sizeof(xmlNodePtr));
Daniel Veillardb08c9812003-01-28 23:09:49 +00004510 level = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004511 ret = xmlRelaxNGValidateWalkPermutations(ctxt, group->rule,
4512 list, array, k, &level, -1);
4513 } else {
4514 ret = xmlRelaxNGValidateTryPermutation(ctxt, group->rule, list, k);
4515 }
4516 if (ret != 0) {
4517 ctxt->state->seq = oldseq;
4518 break;
4519 }
4520 }
4521
4522 xmlFree(list);
4523 xmlFree(array);
4524 return(ret);
4525}
4526
4527/**
4528 * xmlRelaxNGValidateInterleave:
4529 * @ctxt: a Relax-NG validation context
4530 * @define: the definition to verify
4531 *
4532 * Validate an interleave definition for a node.
4533 *
4534 * Returns 0 if the validation succeeded or an error code.
4535 */
4536static int
4537xmlRelaxNGValidateInterleave(xmlRelaxNGValidCtxtPtr ctxt,
4538 xmlRelaxNGDefinePtr define) {
4539 int ret = 0, nbchildren, nbtot, i, j;
4540 xmlRelaxNGPartitionPtr partitions;
4541 xmlNodePtr *children = NULL;
4542 xmlNodePtr *order = NULL;
4543 xmlNodePtr cur;
4544
4545 if (define->data != NULL) {
4546 partitions = (xmlRelaxNGPartitionPtr) define->data;
4547 } else {
4548 VALID_CTXT();
4549 VALID_ERROR("Internal: interleave block has no data\n");
4550 return(-1);
4551 }
4552
4553 /*
4554 * Build the sequence of child and an array preserving the children
4555 * initial order.
4556 */
4557 cur = ctxt->state->seq;
4558 nbchildren = 0;
4559 nbtot = 0;
4560 while (cur != NULL) {
4561 if ((cur->type == XML_COMMENT_NODE) ||
4562 (cur->type == XML_PI_NODE) ||
4563 ((cur->type == XML_TEXT_NODE) &&
4564 (IS_BLANK_NODE(cur)))) {
4565 nbtot++;
4566 } else {
4567 nbchildren++;
4568 nbtot++;
4569 }
4570 cur = cur->next;
4571 }
4572 children = (xmlNodePtr *) xmlMalloc(nbchildren * sizeof(xmlNodePtr));
4573 if (children == NULL)
4574 goto error;
4575 order = (xmlNodePtr *) xmlMalloc(nbtot * sizeof(xmlNodePtr));
4576 if (order == NULL)
4577 goto error;
4578 cur = ctxt->state->seq;
4579 i = 0;
4580 j = 0;
4581 while (cur != NULL) {
4582 if ((cur->type == XML_COMMENT_NODE) ||
4583 (cur->type == XML_PI_NODE) ||
4584 ((cur->type == XML_TEXT_NODE) &&
4585 (IS_BLANK_NODE(cur)))) {
4586 order[j++] = cur;
4587 } else {
4588 order[j++] = cur;
4589 children[i++] = cur;
4590 }
4591 cur = cur->next;
4592 }
4593
4594 /* TODO: retry with a maller set of child if there is a next... */
4595 ret = xmlRelaxNGValidatePartGroup(ctxt, partitions->groups,
4596 partitions->nbgroups, children, nbchildren);
4597 if (ret == 0) {
4598 ctxt->state->seq = NULL;
4599 }
4600
4601 /*
4602 * Cleanup: rebuid the child sequence and free the structure
4603 */
4604 if (order != NULL) {
4605 for (i = 0;i < nbtot;i++) {
4606 if (i == 0)
4607 order[i]->prev = NULL;
4608 else
4609 order[i]->prev = order[i - 1];
4610 if (i == nbtot - 1)
4611 order[i]->next = NULL;
4612 else
4613 order[i]->next = order[i + 1];
4614 }
4615 xmlFree(order);
4616 }
4617 if (children != NULL)
4618 xmlFree(children);
4619
4620 return(ret);
4621
4622error:
4623 if (order != NULL) {
4624 for (i = 0;i < nbtot;i++) {
4625 if (i == 0)
4626 order[i]->prev = NULL;
4627 else
4628 order[i]->prev = order[i - 1];
4629 if (i == nbtot - 1)
4630 order[i]->next = NULL;
4631 else
4632 order[i]->next = order[i + 1];
4633 }
4634 xmlFree(order);
4635 }
4636 if (children != NULL)
4637 xmlFree(children);
4638 return(-1);
4639}
4640
4641/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004642 * xmlRelaxNGValidateElementContent:
4643 * @ctxt: a Relax-NG validation context
4644 * @define: the list of definition to verify
4645 *
4646 * Validate the given node content against the (list) of definitions
4647 *
4648 * Returns 0 if the validation succeeded or an error code.
4649 */
4650static int
4651xmlRelaxNGValidateElementContent(xmlRelaxNGValidCtxtPtr ctxt,
4652 xmlRelaxNGDefinePtr defines) {
4653 int ret = 0, res;
4654
4655 if (ctxt->state == NULL) {
4656 VALID_CTXT();
4657 VALID_ERROR("Internal: no state\n");
4658 return(-1);
4659 }
4660 while (defines != NULL) {
4661 res = xmlRelaxNGValidateDefinition(ctxt, defines);
4662 if (res < 0)
4663 ret = -1;
4664 defines = defines->next;
4665 }
4666
4667 return(ret);
4668}
4669
4670/**
4671 * xmlRelaxNGValidateDefinition:
4672 * @ctxt: a Relax-NG validation context
4673 * @define: the definition to verify
4674 *
4675 * Validate the current node against the definition
4676 *
4677 * Returns 0 if the validation succeeded or an error code.
4678 */
4679static int
4680xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
4681 xmlRelaxNGDefinePtr define) {
4682 xmlNodePtr node;
4683 int ret = 0, i, tmp, oldflags;
4684 xmlRelaxNGValidStatePtr oldstate, state;
4685
4686 if (define == NULL) {
4687 VALID_CTXT();
4688 VALID_ERROR("internal error: define == NULL\n");
4689 return(-1);
4690 }
4691 if (ctxt->state != NULL) {
4692 node = ctxt->state->seq;
4693 } else {
4694 node = NULL;
4695 }
4696 switch (define->type) {
4697 case XML_RELAXNG_EMPTY:
4698 if (node != NULL) {
4699 VALID_CTXT();
4700 VALID_ERROR("Expecting an empty element\n");
4701 return(-1);
4702 }
4703#ifdef DEBUG
4704 xmlGenericError(xmlGenericErrorContext,
4705 "xmlRelaxNGValidateDefinition(): validated empty\n");
4706#endif
4707 return(0);
4708 case XML_RELAXNG_NOT_ALLOWED:
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004709 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004710 case XML_RELAXNG_TEXT:
4711 if (node == NULL)
4712 return(0);
4713 while ((node != NULL) &&
4714 ((node->type == XML_TEXT_NODE) ||
4715 (node->type == XML_CDATA_SECTION_NODE)))
4716 node = node->next;
Daniel Veillard276be4a2003-01-24 01:03:34 +00004717 if (node == ctxt->state->seq) {
4718 VALID_CTXT();
4719 VALID_ERROR("Expecting text content\n");
4720 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004721 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00004722 ctxt->state->seq = node;
4723 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004724 case XML_RELAXNG_ELEMENT:
4725 node = xmlRelaxNGSkipIgnored(ctxt, node);
4726 if ((node == NULL) || (node->type != XML_ELEMENT_NODE)) {
4727 VALID_CTXT();
4728 VALID_ERROR("Expecting an element\n");
4729 return(-1);
4730 }
4731 if (define->name != NULL) {
4732 if (!xmlStrEqual(node->name, define->name)) {
4733 VALID_CTXT();
4734 VALID_ERROR("Expecting element %s, got %s\n",
4735 define->name, node->name);
4736 return(-1);
4737 }
4738 }
4739 if ((define->ns != NULL) && (define->ns[0] != 0)) {
4740 if (node->ns == NULL) {
4741 VALID_CTXT();
4742 VALID_ERROR("Expecting a namespace for element %s\n",
4743 node->name);
4744 return(-1);
4745 } else if (!xmlStrEqual(node->ns->href, define->ns)) {
4746 VALID_CTXT();
4747 VALID_ERROR("Expecting element %s has wrong namespace: expecting %s\n",
4748 node->name, define->ns);
4749 return(-1);
4750 }
4751 } else {
4752 if (node->ns != NULL) {
4753 VALID_CTXT();
4754 VALID_ERROR("Expecting no namespace for element %s\n",
4755 node->name);
4756 return(-1);
4757 }
4758 }
4759
4760 state = xmlRelaxNGNewValidState(ctxt, node);
4761 if (state == NULL) {
4762 return(-1);
4763 }
4764
4765 oldstate = ctxt->state;
4766 ctxt->state = state;
4767 if (define->attrs != NULL) {
4768 tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
4769 if (tmp != 0)
4770 ret = -1;
4771 }
4772 if (define->content != NULL) {
4773 tmp = xmlRelaxNGValidateElementContent(ctxt, define->content);
4774 if (tmp != 0)
4775 ret = -1;
4776 }
4777 state = ctxt->state;
4778 if (state->seq != NULL) {
4779 state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq);
4780 if (state->seq != NULL) {
4781 VALID_CTXT();
4782 VALID_ERROR("Extra content for element %s\n",
4783 node->name);
4784 ret = -1;
4785 }
4786 }
4787 for (i = 0;i < state->nbAttrs;i++) {
4788 if (state->attrs[i] != NULL) {
4789 VALID_CTXT();
Daniel Veillardea3f3982003-01-26 19:45:18 +00004790 VALID_ERROR("Invalid attribute %s for element %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00004791 state->attrs[i]->name, node->name);
4792 ret = -1;
4793 }
4794 }
4795 ctxt->state = oldstate;
4796 xmlRelaxNGFreeValidState(state);
4797 if (oldstate != NULL)
4798 oldstate->seq = node->next;
4799
4800
4801#ifdef DEBUG
4802 xmlGenericError(xmlGenericErrorContext,
4803 "xmlRelaxNGValidateDefinition(): validated %s : %d\n",
4804 node->name, ret);
4805#endif
4806 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004807 case XML_RELAXNG_OPTIONAL:
4808 oldflags = ctxt->flags;
4809 ctxt->flags |= FLAGS_IGNORABLE;
4810 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
4811 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
4812 if (ret != 0) {
4813 xmlRelaxNGFreeValidState(ctxt->state);
4814 ctxt->state = oldstate;
4815 ret = 0;
4816 break;
4817 }
4818 xmlRelaxNGFreeValidState(oldstate);
4819 ctxt->flags = oldflags;
4820 ret = 0;
4821 break;
4822 case XML_RELAXNG_ONEORMORE:
4823 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
4824 if (ret != 0) {
4825 break;
4826 }
4827 /* no break on purpose */
Daniel Veillard276be4a2003-01-24 01:03:34 +00004828 case XML_RELAXNG_ZEROORMORE: {
4829 xmlNodePtr cur, temp;
4830
Daniel Veillard6eadf632003-01-23 18:29:16 +00004831 oldflags = ctxt->flags;
4832 ctxt->flags |= FLAGS_IGNORABLE;
Daniel Veillard276be4a2003-01-24 01:03:34 +00004833 cur = ctxt->state->seq;
4834 temp = NULL;
4835 while ((cur != NULL) && (temp != cur)) {
4836 temp = cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004837 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
4838 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
4839 if (ret != 0) {
4840 xmlRelaxNGFreeValidState(ctxt->state);
4841 ctxt->state = oldstate;
4842 ret = 0;
4843 break;
4844 }
4845 xmlRelaxNGFreeValidState(oldstate);
Daniel Veillard276be4a2003-01-24 01:03:34 +00004846 cur = ctxt->state->seq;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004847 }
4848 ctxt->flags = oldflags;
4849 break;
Daniel Veillard276be4a2003-01-24 01:03:34 +00004850 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004851 case XML_RELAXNG_CHOICE: {
4852 xmlRelaxNGDefinePtr list = define->content;
4853
4854 oldflags = ctxt->flags;
4855 ctxt->flags |= FLAGS_IGNORABLE;
4856
4857 while (list != NULL) {
4858 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
4859 ret = xmlRelaxNGValidateDefinition(ctxt, list);
4860 if (ret == 0) {
4861 xmlRelaxNGFreeValidState(oldstate);
4862 break;
4863 }
4864 xmlRelaxNGFreeValidState(ctxt->state);
4865 ctxt->state = oldstate;
4866 list = list->next;
4867 }
4868 ctxt->flags = oldflags;
4869 break;
4870 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004871 case XML_RELAXNG_DEF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00004872 case XML_RELAXNG_GROUP: {
4873 xmlRelaxNGDefinePtr list = define->content;
4874
4875 while (list != NULL) {
4876 ret = xmlRelaxNGValidateDefinition(ctxt, list);
4877 if (ret != 0)
4878 break;
4879 list = list->next;
4880 }
4881 break;
4882 }
4883 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004884 ret = xmlRelaxNGValidateInterleave(ctxt, define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004885 break;
4886 case XML_RELAXNG_ATTRIBUTE:
4887 ret = xmlRelaxNGValidateAttribute(ctxt, define);
4888 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004889 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00004890 case XML_RELAXNG_REF:
4891 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
4892 break;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004893 case XML_RELAXNG_DATATYPE: {
4894 xmlChar *content;
4895
4896 content = xmlNodeGetContent(node);
4897 ret = xmlRelaxNGValidateDatatype(ctxt, content, define);
4898 if (ret == -1) {
4899 VALID_CTXT();
4900 VALID_ERROR("internal error validating %s\n", define->name);
4901 } else if (ret == 0) {
4902 ctxt->state->seq = node->next;
4903 }
4904 /*
4905 * TODO cover the problems with
4906 * <p>12<!-- comment -->34</p>
4907 * TODO detect full element coverage at compilation time.
4908 */
4909 if ((node != NULL) && (node->next != NULL)) {
4910 VALID_CTXT();
4911 VALID_ERROR("The data does not cover the full element %s\n",
4912 node->parent->name);
4913 ret = -1;
4914 }
4915 if (content != NULL)
4916 xmlFree(content);
4917 break;
4918 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00004919 case XML_RELAXNG_VALUE: {
4920 xmlChar *content;
4921 xmlChar *oldvalue;
4922
4923 content = xmlNodeGetContent(node);
4924 oldvalue = ctxt->state->value;
4925 ctxt->state->value = content;
4926 ret = xmlRelaxNGValidateValue(ctxt, define);
4927 ctxt->state->value = oldvalue;
4928 if (ret == -1) {
4929 VALID_CTXT();
4930 VALID_ERROR("internal error validating %s\n", define->name);
4931 } else if (ret == 0) {
4932 ctxt->state->seq = node->next;
4933 }
4934 /*
4935 * TODO cover the problems with
4936 * <p>12<!-- comment -->34</p>
4937 * TODO detect full element coverage at compilation time.
4938 */
4939 if ((node != NULL) && (node->next != NULL)) {
4940 VALID_CTXT();
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004941 VALID_ERROR("The value does not cover the full element %s\n",
Daniel Veillardea3f3982003-01-26 19:45:18 +00004942 node->parent->name);
4943 ret = -1;
4944 }
4945 if (content != NULL)
4946 xmlFree(content);
4947 break;
4948 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004949 case XML_RELAXNG_LIST: {
4950 xmlChar *content;
4951 xmlChar *oldvalue, *oldendvalue;
4952 int len;
Daniel Veillardea3f3982003-01-26 19:45:18 +00004953
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004954 content = xmlNodeGetContent(node);
4955 len = xmlStrlen(content);
4956 oldvalue = ctxt->state->value;
4957 oldendvalue = ctxt->state->endvalue;
4958 ctxt->state->value = content;
4959 ctxt->state->endvalue = content + len;
4960 ret = xmlRelaxNGValidateValue(ctxt, define);
4961 ctxt->state->value = oldvalue;
4962 ctxt->state->endvalue = oldendvalue;
4963 if (ret == -1) {
4964 VALID_CTXT();
4965 VALID_ERROR("internal error validating list\n");
4966 } else if (ret == 0) {
4967 ctxt->state->seq = node->next;
4968 }
4969 /*
4970 * TODO cover the problems with
4971 * <p>12<!-- comment -->34</p>
4972 * TODO detect full element coverage at compilation time.
4973 */
4974 if ((node != NULL) && (node->next != NULL)) {
4975 VALID_CTXT();
4976 VALID_ERROR("The list does not cover the full element %s\n",
4977 node->parent->name);
4978 ret = -1;
4979 }
4980 if (content != NULL)
4981 xmlFree(content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004982 break;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004983 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004984 case XML_RELAXNG_START:
4985 TODO
4986 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004987 }
4988 return(ret);
4989}
4990
4991/**
4992 * xmlRelaxNGValidateDocument:
4993 * @ctxt: a Relax-NG validation context
4994 * @doc: the document
4995 *
4996 * Validate the given document
4997 *
4998 * Returns 0 if the validation succeeded or an error code.
4999 */
5000static int
5001xmlRelaxNGValidateDocument(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
5002 int ret;
5003 xmlRelaxNGPtr schema;
5004 xmlRelaxNGGrammarPtr grammar;
5005 xmlRelaxNGValidStatePtr state;
5006
5007 if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL))
5008 return(-1);
5009
5010 schema = ctxt->schema;
5011 grammar = schema->topgrammar;
5012 if (grammar == NULL) {
5013 VALID_CTXT();
5014 VALID_ERROR("No top grammar defined\n");
5015 return(-1);
5016 }
5017 state = xmlRelaxNGNewValidState(ctxt, NULL);
5018 ctxt->state = state;
5019 ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start);
5020 state = ctxt->state;
5021 if ((state != NULL) && (state->seq != NULL)) {
5022 xmlNodePtr node;
5023
5024 node = state->seq;
5025 node = xmlRelaxNGSkipIgnored(ctxt, node);
5026 if (node != NULL) {
5027 VALID_CTXT();
5028 VALID_ERROR("extra data on the document\n");
5029 ret = -1;
5030 }
5031 }
5032 xmlRelaxNGFreeValidState(state);
5033
5034 return(ret);
5035}
5036
5037/************************************************************************
5038 * *
5039 * Validation interfaces *
5040 * *
5041 ************************************************************************/
5042/**
5043 * xmlRelaxNGNewValidCtxt:
5044 * @schema: a precompiled XML RelaxNGs
5045 *
5046 * Create an XML RelaxNGs validation context based on the given schema
5047 *
5048 * Returns the validation context or NULL in case of error
5049 */
5050xmlRelaxNGValidCtxtPtr
5051xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema) {
5052 xmlRelaxNGValidCtxtPtr ret;
5053
5054 ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt));
5055 if (ret == NULL) {
5056 xmlGenericError(xmlGenericErrorContext,
5057 "Failed to allocate new schama validation context\n");
5058 return (NULL);
5059 }
5060 memset(ret, 0, sizeof(xmlRelaxNGValidCtxt));
5061 ret->schema = schema;
5062 return (ret);
5063}
5064
5065/**
5066 * xmlRelaxNGFreeValidCtxt:
5067 * @ctxt: the schema validation context
5068 *
5069 * Free the resources associated to the schema validation context
5070 */
5071void
5072xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt) {
5073 if (ctxt == NULL)
5074 return;
5075 xmlFree(ctxt);
5076}
5077
5078/**
5079 * xmlRelaxNGSetValidErrors:
5080 * @ctxt: a Relax-NG validation context
5081 * @err: the error function
5082 * @warn: the warning function
5083 * @ctx: the functions context
5084 *
5085 * Set the error and warning callback informations
5086 */
5087void
5088xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
5089 xmlRelaxNGValidityErrorFunc err,
5090 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
5091 if (ctxt == NULL)
5092 return;
5093 ctxt->error = err;
5094 ctxt->warning = warn;
5095 ctxt->userData = ctx;
5096}
5097
5098/**
5099 * xmlRelaxNGValidateDoc:
5100 * @ctxt: a Relax-NG validation context
5101 * @doc: a parsed document tree
5102 *
5103 * Validate a document tree in memory.
5104 *
5105 * Returns 0 if the document is valid, a positive error code
5106 * number otherwise and -1 in case of internal or API error.
5107 */
5108int
5109xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
5110 int ret;
5111
5112 if ((ctxt == NULL) || (doc == NULL))
5113 return(-1);
5114
5115 ctxt->doc = doc;
5116
5117 ret = xmlRelaxNGValidateDocument(ctxt, doc);
5118 return(ret);
5119}
5120
5121#endif /* LIBXML_SCHEMAS_ENABLED */
5122