blob: c6953c7bffa26c1bc716c1f8db122bd42d1d9d6f [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 Veillard1ed7f362003-02-03 10:57:45 +000015 * - handle namespace declarations as attributes.
Daniel Veillardd41f4f42003-01-29 21:07:52 +000016 */
17
Daniel Veillard6eadf632003-01-23 18:29:16 +000018#define IN_LIBXML
19#include "libxml.h"
20
21#ifdef LIBXML_SCHEMAS_ENABLED
22
23#include <string.h>
24#include <stdio.h>
25#include <libxml/xmlmemory.h>
26#include <libxml/parser.h>
27#include <libxml/parserInternals.h>
28#include <libxml/hash.h>
29#include <libxml/uri.h>
30
31#include <libxml/relaxng.h>
32
33#include <libxml/xmlschemastypes.h>
34#include <libxml/xmlautomata.h>
35#include <libxml/xmlregexp.h>
Daniel Veillardc6e997c2003-01-27 12:35:42 +000036#include <libxml/xmlschemastypes.h>
Daniel Veillard6eadf632003-01-23 18:29:16 +000037
38/*
39 * The Relax-NG namespace
40 */
41static const xmlChar *xmlRelaxNGNs = (const xmlChar *)
42 "http://relaxng.org/ns/structure/1.0";
43
44#define IS_RELAXNG(node, type) \
45 ((node != NULL) && (node->ns != NULL) && \
46 (xmlStrEqual(node->name, (const xmlChar *) type)) && \
47 (xmlStrEqual(node->ns->href, xmlRelaxNGNs)))
48
49
50#define DEBUG 1 /* very verbose output */
51#define DEBUG_CONTENT 1
52#define DEBUG_TYPE 1
Daniel Veillard276be4a2003-01-24 01:03:34 +000053#define DEBUG_VALID 1
Daniel Veillardb08c9812003-01-28 23:09:49 +000054#define DEBUG_INTERLEAVE 1 */
Daniel Veillard6eadf632003-01-23 18:29:16 +000055
56#define UNBOUNDED (1 << 30)
57#define TODO \
58 xmlGenericError(xmlGenericErrorContext, \
59 "Unimplemented block at %s:%d\n", \
60 __FILE__, __LINE__);
61
62typedef struct _xmlRelaxNGSchema xmlRelaxNGSchema;
63typedef xmlRelaxNGSchema *xmlRelaxNGSchemaPtr;
64
65typedef struct _xmlRelaxNGDefine xmlRelaxNGDefine;
66typedef xmlRelaxNGDefine *xmlRelaxNGDefinePtr;
67
Daniel Veillardd41f4f42003-01-29 21:07:52 +000068typedef struct _xmlRelaxNGDocument xmlRelaxNGDocument;
69typedef xmlRelaxNGDocument *xmlRelaxNGDocumentPtr;
70
Daniel Veillarda9d912d2003-02-01 17:43:10 +000071typedef struct _xmlRelaxNGInclude xmlRelaxNGInclude;
72typedef xmlRelaxNGInclude *xmlRelaxNGIncludePtr;
73
Daniel Veillard6eadf632003-01-23 18:29:16 +000074typedef enum {
75 XML_RELAXNG_COMBINE_UNDEFINED = 0, /* undefined */
76 XML_RELAXNG_COMBINE_CHOICE, /* choice */
77 XML_RELAXNG_COMBINE_INTERLEAVE /* interleave */
78} xmlRelaxNGCombine;
79
80typedef struct _xmlRelaxNGGrammar xmlRelaxNGGrammar;
81typedef xmlRelaxNGGrammar *xmlRelaxNGGrammarPtr;
82
83struct _xmlRelaxNGGrammar {
84 xmlRelaxNGGrammarPtr parent;/* the parent grammar if any */
85 xmlRelaxNGGrammarPtr children;/* the children grammar if any */
86 xmlRelaxNGGrammarPtr next; /* the next grammar if any */
87 xmlRelaxNGDefinePtr start; /* <start> content */
88 xmlRelaxNGCombine combine; /* the default combine value */
Daniel Veillardd41f4f42003-01-29 21:07:52 +000089 xmlRelaxNGDefinePtr startList;/* list of <start> definitions */
Daniel Veillard6eadf632003-01-23 18:29:16 +000090 xmlHashTablePtr defs; /* define* */
91 xmlHashTablePtr refs; /* references */
92};
93
94
Daniel Veillard6eadf632003-01-23 18:29:16 +000095typedef enum {
96 XML_RELAXNG_EMPTY = 0, /* an empty pattern */
97 XML_RELAXNG_NOT_ALLOWED, /* not allowed top */
Daniel Veillard144fae12003-02-03 13:17:57 +000098 XML_RELAXNG_EXCEPT, /* except present in nameclass defs */
Daniel Veillard6eadf632003-01-23 18:29:16 +000099 XML_RELAXNG_TEXT, /* textual content */
100 XML_RELAXNG_ELEMENT, /* an element */
101 XML_RELAXNG_DATATYPE, /* extenal data type definition */
102 XML_RELAXNG_VALUE, /* value from an extenal data type definition */
103 XML_RELAXNG_LIST, /* a list of patterns */
104 XML_RELAXNG_ATTRIBUTE, /* an attrbute following a pattern */
105 XML_RELAXNG_DEF, /* a definition */
106 XML_RELAXNG_REF, /* reference to a definition */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000107 XML_RELAXNG_EXTERNALREF, /* reference to an external def */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000108 XML_RELAXNG_OPTIONAL, /* optional patterns */
109 XML_RELAXNG_ZEROORMORE, /* zero or more non empty patterns */
110 XML_RELAXNG_ONEORMORE, /* one or more non empty patterns */
111 XML_RELAXNG_CHOICE, /* a choice between non empty patterns */
112 XML_RELAXNG_GROUP, /* a pair/group of non empty patterns */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000113 XML_RELAXNG_INTERLEAVE, /* interleaving choice of non-empty patterns */
114 XML_RELAXNG_START /* Used to keep track of starts on grammars */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000115} xmlRelaxNGType;
116
117struct _xmlRelaxNGDefine {
118 xmlRelaxNGType type; /* the type of definition */
119 xmlNodePtr node; /* the node in the source */
120 xmlChar *name; /* the element local name if present */
121 xmlChar *ns; /* the namespace local name if present */
Daniel Veillardedc91922003-01-26 00:52:04 +0000122 xmlChar *value; /* value when available */
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000123 void *data; /* data lib or specific pointer */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000124 xmlRelaxNGDefinePtr content;/* the expected content */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000125 xmlRelaxNGDefinePtr parent; /* the parent definition, if any */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000126 xmlRelaxNGDefinePtr next; /* list within grouping sequences */
127 xmlRelaxNGDefinePtr attrs; /* list of attributes for elements */
Daniel Veillard3b2e4e12003-02-03 08:52:58 +0000128 xmlRelaxNGDefinePtr nameClass;/* the nameClass definition if any */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000129 xmlRelaxNGDefinePtr nextHash;/* next define in defs/refs hash tables */
130};
131
132/**
133 * _xmlRelaxNG:
134 *
135 * A RelaxNGs definition
136 */
137struct _xmlRelaxNG {
138 xmlRelaxNGGrammarPtr topgrammar;
139 xmlDocPtr doc;
140
141 xmlHashTablePtr defs; /* define */
142 xmlHashTablePtr refs; /* references */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000143 xmlHashTablePtr documents; /* all the documents loaded */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000144 xmlHashTablePtr includes; /* all the includes loaded */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000145 void *_private; /* unused by the library for users or bindings */
146};
147
148typedef enum {
149 XML_RELAXNG_ERR_OK = 0,
150 XML_RELAXNG_ERR_NOROOT = 1,
151 XML_RELAXNG_ERR_
152} xmlRelaxNGValidError;
153
154#define XML_RELAXNG_IN_ATTRIBUTE 1
155
156struct _xmlRelaxNGParserCtxt {
157 void *userData; /* user specific data block */
158 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
159 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
160 xmlRelaxNGValidError err;
161
162 xmlRelaxNGPtr schema; /* The schema in use */
163 xmlRelaxNGGrammarPtr grammar; /* the current grammar */
164 int flags; /* parser flags */
165 int nbErrors; /* number of errors at parse time */
166 int nbWarnings; /* number of warnings at parse time */
Daniel Veillard276be4a2003-01-24 01:03:34 +0000167 const xmlChar *define; /* the current define scope */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000168 xmlRelaxNGDefinePtr def; /* the current define */
169
170 int nbInterleaves;
171 xmlHashTablePtr interleaves; /* keep track of all the interleaves */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000172
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000173 xmlHashTablePtr documents; /* all the documents loaded */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000174 xmlHashTablePtr includes; /* all the includes loaded */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000175 xmlChar *URL;
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000176 xmlDocPtr document;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000177
178 const char *buffer;
179 int size;
180
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000181 /* the document stack */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000182 xmlRelaxNGDocumentPtr doc; /* Current parsed external ref */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000183 int docNr; /* Depth of the parsing stack */
184 int docMax; /* Max depth of the parsing stack */
185 xmlRelaxNGDocumentPtr *docTab; /* array of docs */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000186
187 /* the include stack */
188 xmlRelaxNGIncludePtr inc; /* Current parsed include */
189 int incNr; /* Depth of the include parsing stack */
190 int incMax; /* Max depth of the parsing stack */
191 xmlRelaxNGIncludePtr *incTab; /* array of incs */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000192};
193
194#define FLAGS_IGNORABLE 1
195#define FLAGS_NEGATIVE 2
196
197/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000198 * xmlRelaxNGInterleaveGroup:
199 *
200 * A RelaxNGs partition set associated to lists of definitions
201 */
202typedef struct _xmlRelaxNGInterleaveGroup xmlRelaxNGInterleaveGroup;
203typedef xmlRelaxNGInterleaveGroup *xmlRelaxNGInterleaveGroupPtr;
204struct _xmlRelaxNGInterleaveGroup {
205 xmlRelaxNGDefinePtr rule; /* the rule to satisfy */
206 xmlRelaxNGDefinePtr *defs; /* the array of element definitions */
207};
208
209/**
210 * xmlRelaxNGPartitions:
211 *
212 * A RelaxNGs partition associated to an interleave group
213 */
214typedef struct _xmlRelaxNGPartition xmlRelaxNGPartition;
215typedef xmlRelaxNGPartition *xmlRelaxNGPartitionPtr;
216struct _xmlRelaxNGPartition {
217 int nbgroups; /* number of groups in the partitions */
218 xmlRelaxNGInterleaveGroupPtr *groups;
219};
220
221/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000222 * xmlRelaxNGValidState:
223 *
224 * A RelaxNGs validation state
225 */
226#define MAX_ATTR 20
227typedef struct _xmlRelaxNGValidState xmlRelaxNGValidState;
228typedef xmlRelaxNGValidState *xmlRelaxNGValidStatePtr;
229struct _xmlRelaxNGValidState {
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000230 xmlNodePtr node; /* the current node */
231 xmlNodePtr seq; /* the sequence of children left to validate */
232 int nbAttrs; /* the number of attributes */
Daniel Veillard1ed7f362003-02-03 10:57:45 +0000233 int nbAttrLeft; /* the number of attributes left to validate */
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000234 xmlChar *value; /* the value when operating on string */
235 xmlChar *endvalue; /* the end value when operating on string */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000236 xmlAttrPtr attrs[1]; /* the array of attributes */
237};
238
239/**
240 * xmlRelaxNGValidCtxt:
241 *
242 * A RelaxNGs validation context
243 */
244
245struct _xmlRelaxNGValidCtxt {
246 void *userData; /* user specific data block */
247 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
248 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
249
250 xmlRelaxNGPtr schema; /* The schema in use */
251 xmlDocPtr doc; /* the document being validated */
252 xmlRelaxNGValidStatePtr state; /* the current validation state */
253 int flags; /* validation flags */
254};
255
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000256/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000257 * xmlRelaxNGInclude:
258 *
259 * Structure associated to a RelaxNGs document element
260 */
261struct _xmlRelaxNGInclude {
262 xmlChar *href; /* the normalized href value */
263 xmlDocPtr doc; /* the associated XML document */
264 xmlRelaxNGDefinePtr content;/* the definitions */
265 xmlRelaxNGPtr schema; /* the schema */
266};
267
268/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000269 * xmlRelaxNGDocument:
270 *
271 * Structure associated to a RelaxNGs document element
272 */
273struct _xmlRelaxNGDocument {
274 xmlChar *href; /* the normalized href value */
275 xmlDocPtr doc; /* the associated XML document */
276 xmlRelaxNGDefinePtr content;/* the definitions */
277 xmlRelaxNGPtr schema; /* the schema */
278};
279
Daniel Veillard6eadf632003-01-23 18:29:16 +0000280/************************************************************************
281 * *
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000282 * Preliminary type checking interfaces *
283 * *
284 ************************************************************************/
285/**
286 * xmlRelaxNGTypeHave:
287 * @data: data needed for the library
288 * @type: the type name
289 * @value: the value to check
290 *
291 * Function provided by a type library to check if a type is exported
292 *
293 * Returns 1 if yes, 0 if no and -1 in case of error.
294 */
295typedef int (*xmlRelaxNGTypeHave) (void *data, const xmlChar *type);
296
297/**
298 * xmlRelaxNGTypeCheck:
299 * @data: data needed for the library
300 * @type: the type name
301 * @value: the value to check
302 *
303 * Function provided by a type library to check if a value match a type
304 *
305 * Returns 1 if yes, 0 if no and -1 in case of error.
306 */
307typedef int (*xmlRelaxNGTypeCheck) (void *data, const xmlChar *type,
308 const xmlChar *value);
309
310/**
311 * xmlRelaxNGTypeCompare:
312 * @data: data needed for the library
313 * @type: the type name
314 * @value1: the first value
315 * @value2: the second value
316 *
317 * Function provided by a type library to compare two values accordingly
318 * to a type.
319 *
320 * Returns 1 if yes, 0 if no and -1 in case of error.
321 */
322typedef int (*xmlRelaxNGTypeCompare) (void *data, const xmlChar *type,
323 const xmlChar *value1,
324 const xmlChar *value2);
325typedef struct _xmlRelaxNGTypeLibrary xmlRelaxNGTypeLibrary;
326typedef xmlRelaxNGTypeLibrary *xmlRelaxNGTypeLibraryPtr;
327struct _xmlRelaxNGTypeLibrary {
328 const xmlChar *namespace; /* the datatypeLibrary value */
329 void *data; /* data needed for the library */
330 xmlRelaxNGTypeHave have; /* the export function */
331 xmlRelaxNGTypeCheck check; /* the checking function */
332 xmlRelaxNGTypeCompare comp; /* the compare function */
333};
334
335/************************************************************************
336 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +0000337 * Allocation functions *
338 * *
339 ************************************************************************/
340static void xmlRelaxNGFreeDefineList(xmlRelaxNGDefinePtr defines);
341static void xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar);
342static void xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define);
343
344/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000345 * xmlRelaxNGFreeDocument:
346 * @docu: a document structure
347 *
348 * Deallocate a RelaxNG document structure.
349 */
350static void
351xmlRelaxNGFreeDocument(xmlRelaxNGDocumentPtr docu)
352{
353 if (docu == NULL)
354 return;
355
356 if (docu->href != NULL)
357 xmlFree(docu->href);
358 if (docu->doc != NULL)
359 xmlFreeDoc(docu->doc);
360 if (docu->schema != NULL)
361 xmlRelaxNGFree(docu->schema);
362 xmlFree(docu);
363}
364
365/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000366 * xmlRelaxNGFreeInclude:
367 * @incl: a include structure
368 *
369 * Deallocate a RelaxNG include structure.
370 */
371static void
372xmlRelaxNGFreeInclude(xmlRelaxNGIncludePtr incl)
373{
374 if (incl == NULL)
375 return;
376
377 if (incl->href != NULL)
378 xmlFree(incl->href);
379 if (incl->doc != NULL)
380 xmlFreeDoc(incl->doc);
381 if (incl->schema != NULL)
382 xmlRelaxNGFree(incl->schema);
383 xmlFree(incl);
384}
385
386/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000387 * xmlRelaxNGNewRelaxNG:
388 * @ctxt: a Relax-NG validation context (optional)
389 *
390 * Allocate a new RelaxNG structure.
391 *
392 * Returns the newly allocated structure or NULL in case or error
393 */
394static xmlRelaxNGPtr
395xmlRelaxNGNewRelaxNG(xmlRelaxNGParserCtxtPtr ctxt)
396{
397 xmlRelaxNGPtr ret;
398
399 ret = (xmlRelaxNGPtr) xmlMalloc(sizeof(xmlRelaxNG));
400 if (ret == NULL) {
401 if ((ctxt != NULL) && (ctxt->error != NULL))
402 ctxt->error(ctxt->userData, "Out of memory\n");
403 ctxt->nbErrors++;
404 return (NULL);
405 }
406 memset(ret, 0, sizeof(xmlRelaxNG));
407
408 return (ret);
409}
410
411/**
412 * xmlRelaxNGFree:
413 * @schema: a schema structure
414 *
415 * Deallocate a RelaxNG structure.
416 */
417void
418xmlRelaxNGFree(xmlRelaxNGPtr schema)
419{
420 if (schema == NULL)
421 return;
422
Daniel Veillard6eadf632003-01-23 18:29:16 +0000423 if (schema->topgrammar != NULL)
424 xmlRelaxNGFreeGrammar(schema->topgrammar);
425 if (schema->doc != NULL)
426 xmlFreeDoc(schema->doc);
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000427 if (schema->documents != NULL)
428 xmlHashFree(schema->documents, (xmlHashDeallocator)
429 xmlRelaxNGFreeDocument);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000430 if (schema->includes != NULL)
431 xmlHashFree(schema->includes, (xmlHashDeallocator)
432 xmlRelaxNGFreeInclude);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000433
434 xmlFree(schema);
435}
436
437/**
438 * xmlRelaxNGNewGrammar:
439 * @ctxt: a Relax-NG validation context (optional)
440 *
441 * Allocate a new RelaxNG grammar.
442 *
443 * Returns the newly allocated structure or NULL in case or error
444 */
445static xmlRelaxNGGrammarPtr
446xmlRelaxNGNewGrammar(xmlRelaxNGParserCtxtPtr ctxt)
447{
448 xmlRelaxNGGrammarPtr ret;
449
450 ret = (xmlRelaxNGGrammarPtr) xmlMalloc(sizeof(xmlRelaxNGGrammar));
451 if (ret == NULL) {
452 if ((ctxt != NULL) && (ctxt->error != NULL))
453 ctxt->error(ctxt->userData, "Out of memory\n");
454 ctxt->nbErrors++;
455 return (NULL);
456 }
457 memset(ret, 0, sizeof(xmlRelaxNGGrammar));
458
459 return (ret);
460}
461
462/**
Daniel Veillard276be4a2003-01-24 01:03:34 +0000463 * xmlRelaxNGFreeDefineHash:
464 * @defines: a list of define structures
465 *
466 * Deallocate a RelaxNG definition in the hash table
467 */
468static void
469xmlRelaxNGFreeDefineHash(xmlRelaxNGDefinePtr defines)
470{
471 xmlRelaxNGDefinePtr next;
472
473 while (defines != NULL) {
474 next = defines->nextHash;
475 xmlRelaxNGFreeDefine(defines);
476 defines = next;
477 }
478}
479
480/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000481 * xmlRelaxNGFreeGrammar:
482 * @grammar: a grammar structure
483 *
484 * Deallocate a RelaxNG grammar structure.
485 */
486static void
487xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar)
488{
489 if (grammar == NULL)
490 return;
491
492 if (grammar->start != NULL)
493 xmlRelaxNGFreeDefine(grammar->start);
494 if (grammar->refs != NULL) {
495 xmlHashFree(grammar->refs, NULL);
496 }
497 if (grammar->defs != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +0000498 xmlHashFree(grammar->defs, (xmlHashDeallocator)
499 xmlRelaxNGFreeDefineHash);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000500 }
501
502 xmlFree(grammar);
503}
504
505/**
506 * xmlRelaxNGNewDefine:
507 * @ctxt: a Relax-NG validation context
508 * @node: the node in the input document.
509 *
510 * Allocate a new RelaxNG define.
511 *
512 * Returns the newly allocated structure or NULL in case or error
513 */
514static xmlRelaxNGDefinePtr
515xmlRelaxNGNewDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
516{
517 xmlRelaxNGDefinePtr ret;
518
519 ret = (xmlRelaxNGDefinePtr) xmlMalloc(sizeof(xmlRelaxNGDefine));
520 if (ret == NULL) {
521 if ((ctxt != NULL) && (ctxt->error != NULL))
522 ctxt->error(ctxt->userData, "Out of memory\n");
523 ctxt->nbErrors++;
524 return (NULL);
525 }
526 memset(ret, 0, sizeof(xmlRelaxNGDefine));
527 ret->node = node;
528
529 return (ret);
530}
531
532/**
533 * xmlRelaxNGFreeDefineList:
534 * @defines: a list of define structures
535 *
536 * Deallocate a RelaxNG define structures.
537 */
538static void
539xmlRelaxNGFreeDefineList(xmlRelaxNGDefinePtr defines)
540{
541 xmlRelaxNGDefinePtr next;
542
543 while (defines != NULL) {
544 next = defines->next;
545 xmlRelaxNGFreeDefine(defines);
546 defines = next;
547 }
548}
549
550/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000551 * xmlRelaxNGFreePartition:
552 * @partitions: a partition set structure
553 *
554 * Deallocate RelaxNG partition set structures.
555 */
556static void
557xmlRelaxNGFreePartition(xmlRelaxNGPartitionPtr partitions) {
558 xmlRelaxNGInterleaveGroupPtr group;
559 int j;
560
561 if (partitions != NULL) {
562 if (partitions->groups != NULL) {
563 for (j = 0;j < partitions->nbgroups;j++) {
564 group = partitions->groups[j];
565 if (group != NULL) {
566 if (group->defs != NULL)
567 xmlFree(group->defs);
568 xmlFree(group);
569 }
570 }
571 xmlFree(partitions->groups);
572 }
573 xmlFree(partitions);
574 }
575}
576/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000577 * xmlRelaxNGFreeDefine:
578 * @define: a define structure
579 *
580 * Deallocate a RelaxNG define structure.
581 */
582static void
583xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define)
584{
585 if (define == NULL)
586 return;
587
588 if (define->name != NULL)
589 xmlFree(define->name);
590 if (define->ns != NULL)
591 xmlFree(define->ns);
Daniel Veillardedc91922003-01-26 00:52:04 +0000592 if (define->value != NULL)
593 xmlFree(define->value);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000594 if (define->attrs != NULL)
595 xmlRelaxNGFreeDefineList(define->attrs);
Daniel Veillard276be4a2003-01-24 01:03:34 +0000596 if ((define->content != NULL) &&
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000597 (define->type != XML_RELAXNG_REF) &&
598 (define->type != XML_RELAXNG_EXTERNALREF))
Daniel Veillard6eadf632003-01-23 18:29:16 +0000599 xmlRelaxNGFreeDefineList(define->content);
Daniel Veillard144fae12003-02-03 13:17:57 +0000600 if (define->nameClass != NULL)
601 xmlRelaxNGFreeDefineList(define->nameClass);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000602 if ((define->data != NULL) &&
603 (define->type == XML_RELAXNG_INTERLEAVE))
604 xmlRelaxNGFreePartition((xmlRelaxNGPartitionPtr) define->data);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000605 xmlFree(define);
606}
607
608/**
609 * xmlRelaxNGNewValidState:
610 * @ctxt: a Relax-NG validation context
611 * @node: the current node or NULL for the document
612 *
613 * Allocate a new RelaxNG validation state
614 *
615 * Returns the newly allocated structure or NULL in case or error
616 */
617static xmlRelaxNGValidStatePtr
618xmlRelaxNGNewValidState(xmlRelaxNGValidCtxtPtr ctxt, xmlNodePtr node)
619{
620 xmlRelaxNGValidStatePtr ret;
621 xmlAttrPtr attr;
622 xmlAttrPtr attrs[MAX_ATTR];
623 int nbAttrs = 0;
624 xmlNodePtr root = NULL;
625
626 if (node == NULL) {
627 root = xmlDocGetRootElement(ctxt->doc);
628 if (root == NULL)
629 return(NULL);
630 } else {
631 attr = node->properties;
632 while (attr != NULL) {
633 if (nbAttrs < MAX_ATTR)
634 attrs[nbAttrs++] = attr;
635 else
636 nbAttrs++;
637 attr = attr->next;
638 }
639 }
640
641 if (nbAttrs < MAX_ATTR)
642 attrs[nbAttrs] = NULL;
643 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(sizeof(xmlRelaxNGValidState) +
644 nbAttrs * sizeof(xmlAttrPtr));
645 if (ret == NULL) {
646 if ((ctxt != NULL) && (ctxt->error != NULL))
647 ctxt->error(ctxt->userData, "Out of memory\n");
648 return (NULL);
649 }
650 if (node == NULL) {
651 ret->node = (xmlNodePtr) ctxt->doc;
652 ret->seq = root;
653 ret->nbAttrs = 0;
654 } else {
655 ret->node = node;
656 ret->seq = node->children;
657 ret->nbAttrs = nbAttrs;
658 if (nbAttrs > 0) {
659 if (nbAttrs < MAX_ATTR) {
660 memcpy(&(ret->attrs[0]), attrs,
661 sizeof(xmlAttrPtr) * (nbAttrs + 1));
662 } else {
663 attr = node->properties;
664 nbAttrs = 0;
665 while (attr != NULL) {
666 ret->attrs[nbAttrs++] = attr;
667 attr = attr->next;
668 }
669 ret->attrs[nbAttrs] = NULL;
670 }
671 }
672 }
Daniel Veillard1ed7f362003-02-03 10:57:45 +0000673 ret->nbAttrLeft = ret->nbAttrs;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000674 return (ret);
675}
676
677/**
678 * xmlRelaxNGCopyValidState:
679 * @ctxt: a Relax-NG validation context
680 * @state: a validation state
681 *
682 * Copy the validation state
683 *
684 * Returns the newly allocated structure or NULL in case or error
685 */
686static xmlRelaxNGValidStatePtr
687xmlRelaxNGCopyValidState(xmlRelaxNGValidCtxtPtr ctxt,
688 xmlRelaxNGValidStatePtr state)
689{
690 xmlRelaxNGValidStatePtr ret;
691 unsigned int size;
692
693 if (state == NULL)
694 return(NULL);
695
696 size = sizeof(xmlRelaxNGValidState) +
697 state->nbAttrs * sizeof(xmlAttrPtr);
698 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(size);
699 if (ret == NULL) {
700 if ((ctxt != NULL) && (ctxt->error != NULL))
701 ctxt->error(ctxt->userData, "Out of memory\n");
702 return (NULL);
703 }
704 memcpy(ret, state, size);
705 return(ret);
706}
707
708/**
709 * xmlRelaxNGFreeValidState:
710 * @state: a validation state structure
711 *
712 * Deallocate a RelaxNG validation state structure.
713 */
714static void
715xmlRelaxNGFreeValidState(xmlRelaxNGValidStatePtr state)
716{
717 if (state == NULL)
718 return;
719
720 xmlFree(state);
721}
722
723/************************************************************************
724 * *
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000725 * Document functions *
726 * *
727 ************************************************************************/
728static xmlDocPtr xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt,
729 xmlDocPtr doc);
730
731/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000732 * xmlRelaxNGIncludePush:
733 * @ctxt: the parser context
734 * @value: the element doc
735 *
736 * Pushes a new include on top of the include stack
737 *
738 * Returns 0 in case of error, the index in the stack otherwise
739 */
740static int
741xmlRelaxNGIncludePush(xmlRelaxNGParserCtxtPtr ctxt,
742 xmlRelaxNGIncludePtr value)
743{
744 if (ctxt->incTab == NULL) {
745 ctxt->incMax = 4;
746 ctxt->incNr = 0;
747 ctxt->incTab = (xmlRelaxNGIncludePtr *) xmlMalloc(
748 ctxt->incMax * sizeof(ctxt->incTab[0]));
749 if (ctxt->incTab == NULL) {
750 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
751 return (0);
752 }
753 }
754 if (ctxt->incNr >= ctxt->incMax) {
755 ctxt->incMax *= 2;
756 ctxt->incTab =
757 (xmlRelaxNGIncludePtr *) xmlRealloc(ctxt->incTab,
758 ctxt->incMax *
759 sizeof(ctxt->incTab[0]));
760 if (ctxt->incTab == NULL) {
761 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
762 return (0);
763 }
764 }
765 ctxt->incTab[ctxt->incNr] = value;
766 ctxt->inc = value;
767 return (ctxt->incNr++);
768}
769
770/**
771 * xmlRelaxNGIncludePop:
772 * @ctxt: the parser context
773 *
774 * Pops the top include from the include stack
775 *
776 * Returns the include just removed
777 */
778static xmlRelaxNGIncludePtr
779xmlRelaxNGIncludePop(xmlRelaxNGParserCtxtPtr ctxt)
780{
781 xmlRelaxNGIncludePtr ret;
782
783 if (ctxt->incNr <= 0)
784 return (0);
785 ctxt->incNr--;
786 if (ctxt->incNr > 0)
787 ctxt->inc = ctxt->incTab[ctxt->incNr - 1];
788 else
789 ctxt->inc = NULL;
790 ret = ctxt->incTab[ctxt->incNr];
791 ctxt->incTab[ctxt->incNr] = 0;
792 return (ret);
793}
794
795/**
796 * xmlRelaxNGLoadInclude:
797 * @ctxt: the parser context
798 * @URL: the normalized URL
799 * @node: the include node.
800 *
801 * First lookup if the document is already loaded into the parser context,
802 * check against recursion. If not found the resource is loaded and
803 * the content is preprocessed before being returned back to the caller.
804 *
805 * Returns the xmlRelaxNGIncludePtr or NULL in case of error
806 */
807static xmlRelaxNGIncludePtr
808xmlRelaxNGLoadInclude(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
809 xmlNodePtr node) {
810 xmlRelaxNGIncludePtr ret = NULL;
811 xmlDocPtr doc;
812 int i;
813 xmlNodePtr root, tmp, tmp2, cur;
814
815 /*
816 * check against recursion in the stack
817 */
818 for (i = 0;i < ctxt->incNr;i++) {
819 if (xmlStrEqual(ctxt->incTab[i]->href, URL)) {
820 if (ctxt->error != NULL)
821 ctxt->error(ctxt->userData,
822 "Detected an externalRef recursion for %s\n",
823 URL);
824 ctxt->nbErrors++;
825 return(NULL);
826 }
827 }
828
829 /*
830 * Lookup in the hash table
831 */
832 if (ctxt->includes == NULL) {
833 ctxt->includes = xmlHashCreate(10);
834 if (ctxt->includes == NULL) {
835 if (ctxt->error != NULL)
836 ctxt->error(ctxt->userData,
837 "Failed to allocate hash table for document\n");
838 ctxt->nbErrors++;
839 return(NULL);
840 }
841 } else {
842 ret = xmlHashLookup(ctxt->includes, URL);
843 if (ret != NULL)
844 return(ret);
845 }
846
847
848 /*
849 * load the document
850 */
851 doc = xmlParseFile((const char *) URL);
852 if (doc == NULL) {
853 if (ctxt->error != NULL)
854 ctxt->error(ctxt->userData,
855 "xmlRelaxNG: could not load %s\n", URL);
856 ctxt->nbErrors++;
857 return (NULL);
858 }
859
860 /*
861 * Allocate the document structures and register it first.
862 */
863 ret = (xmlRelaxNGIncludePtr) xmlMalloc(sizeof(xmlRelaxNGInclude));
864 if (ret == NULL) {
865 if (ctxt->error != NULL)
866 ctxt->error(ctxt->userData,
867 "xmlRelaxNG: allocate memory for doc %s\n", URL);
868 ctxt->nbErrors++;
869 xmlFreeDoc(doc);
870 return (NULL);
871 }
872 memset(ret, 0, sizeof(xmlRelaxNGInclude));
873 ret->doc = doc;
874 ret->href = xmlStrdup(URL);
875
876 /*
877 * push it on the stack and register it in the hash table
878 */
879 xmlHashAddEntry(ctxt->includes, URL, ret);
880 xmlRelaxNGIncludePush(ctxt, ret);
881
882 /*
883 * Some preprocessing of the document content, this include recursing
884 * in the include stack.
885 */
886 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
887 if (doc == NULL) {
888 /* xmlFreeDoc(ctxt->include); */
889 ctxt->inc = NULL;
890 return(NULL);
891 }
892
893 /*
894 * Pop up the include from the stack
895 */
896 xmlRelaxNGIncludePop(ctxt);
897
898 /*
899 * Check that the top element is a grammar
900 */
901 root = xmlDocGetRootElement(doc);
902 if (root == NULL) {
903 if (ctxt->error != NULL)
904 ctxt->error(ctxt->userData,
905 "xmlRelaxNG: included document is empty %s\n", URL);
906 ctxt->nbErrors++;
907 xmlFreeDoc(doc);
908 return (NULL);
909 }
910 if (!IS_RELAXNG(root, "grammar")) {
911 if (ctxt->error != NULL)
912 ctxt->error(ctxt->userData,
913 "xmlRelaxNG: included document %s root is not a grammar\n",
914 URL);
915 ctxt->nbErrors++;
916 xmlFreeDoc(doc);
917 return (NULL);
918 }
919
920 /*
921 * Elimination of redefined rules in the include.
922 */
923 cur = node->children;
924 while (cur != NULL) {
925 if (IS_RELAXNG(cur, "start")) {
926 int found = 0;
927
928 tmp = root->children;
929 while (tmp != NULL) {
930 tmp2 = tmp->next;
931 if (IS_RELAXNG(tmp, "start")) {
932 found = 1;
933 xmlUnlinkNode(tmp);
934 xmlFreeNode(tmp);
935 }
936 tmp = tmp2;
937 }
938 if (!found) {
939 if (ctxt->error != NULL)
940 ctxt->error(ctxt->userData,
941 "xmlRelaxNG: include %s has a start but not the included grammar\n",
942 URL);
943 ctxt->nbErrors++;
944 }
945 } else if (IS_RELAXNG(cur, "define")) {
946 xmlChar *name, *name2;
947
948 name = xmlGetProp(cur, BAD_CAST "name");
949 if (name == NULL) {
950 if (ctxt->error != NULL)
951 ctxt->error(ctxt->userData,
952 "xmlRelaxNG: include %s has define without name\n",
953 URL);
954 ctxt->nbErrors++;
955 } else {
956 int found = 0;
957
958 tmp = root->children;
959 while (tmp != NULL) {
960 tmp2 = tmp->next;
961 if (IS_RELAXNG(tmp, "define")) {
962 name2 = xmlGetProp(tmp, BAD_CAST "name");
963 if (name2 != NULL) {
964 if (xmlStrEqual(name, name2)) {
965 found = 1;
966 xmlUnlinkNode(tmp);
967 xmlFreeNode(tmp);
968 }
969 xmlFree(name2);
970 }
971 }
972 tmp = tmp2;
973 }
974 if (!found) {
975 if (ctxt->error != NULL)
976 ctxt->error(ctxt->userData,
977 "xmlRelaxNG: include %s has a define %s but not the included grammar\n",
978 URL, name);
979 ctxt->nbErrors++;
980 }
981 xmlFree(name);
982 }
983 }
984 cur = cur->next;
985 }
986
987
988 return(ret);
989}
990
991/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000992 * xmlRelaxNGDocumentPush:
993 * @ctxt: the parser context
994 * @value: the element doc
995 *
996 * Pushes a new doc on top of the doc stack
997 *
998 * Returns 0 in case of error, the index in the stack otherwise
999 */
1000static int
1001xmlRelaxNGDocumentPush(xmlRelaxNGParserCtxtPtr ctxt,
1002 xmlRelaxNGDocumentPtr value)
1003{
1004 if (ctxt->docTab == NULL) {
1005 ctxt->docMax = 4;
1006 ctxt->docNr = 0;
1007 ctxt->docTab = (xmlRelaxNGDocumentPtr *) xmlMalloc(
1008 ctxt->docMax * sizeof(ctxt->docTab[0]));
1009 if (ctxt->docTab == NULL) {
1010 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
1011 return (0);
1012 }
1013 }
1014 if (ctxt->docNr >= ctxt->docMax) {
1015 ctxt->docMax *= 2;
1016 ctxt->docTab =
1017 (xmlRelaxNGDocumentPtr *) xmlRealloc(ctxt->docTab,
1018 ctxt->docMax *
1019 sizeof(ctxt->docTab[0]));
1020 if (ctxt->docTab == NULL) {
1021 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
1022 return (0);
1023 }
1024 }
1025 ctxt->docTab[ctxt->docNr] = value;
1026 ctxt->doc = value;
1027 return (ctxt->docNr++);
1028}
1029
1030/**
1031 * xmlRelaxNGDocumentPop:
1032 * @ctxt: the parser context
1033 *
1034 * Pops the top doc from the doc stack
1035 *
1036 * Returns the doc just removed
1037 */
1038static xmlRelaxNGDocumentPtr
1039xmlRelaxNGDocumentPop(xmlRelaxNGParserCtxtPtr ctxt)
1040{
1041 xmlRelaxNGDocumentPtr ret;
1042
1043 if (ctxt->docNr <= 0)
1044 return (0);
1045 ctxt->docNr--;
1046 if (ctxt->docNr > 0)
1047 ctxt->doc = ctxt->docTab[ctxt->docNr - 1];
1048 else
1049 ctxt->doc = NULL;
1050 ret = ctxt->docTab[ctxt->docNr];
1051 ctxt->docTab[ctxt->docNr] = 0;
1052 return (ret);
1053}
1054
1055/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001056 * xmlRelaxNGLoadExternalRef:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001057 * @ctxt: the parser context
1058 * @URL: the normalized URL
1059 * @ns: the inherited ns if any
1060 *
1061 * First lookup if the document is already loaded into the parser context,
1062 * check against recursion. If not found the resource is loaded and
1063 * the content is preprocessed before being returned back to the caller.
1064 *
1065 * Returns the xmlRelaxNGDocumentPtr or NULL in case of error
1066 */
1067static xmlRelaxNGDocumentPtr
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001068xmlRelaxNGLoadExternalRef(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001069 const xmlChar *ns) {
1070 xmlRelaxNGDocumentPtr ret = NULL;
1071 xmlDocPtr doc;
1072 xmlNodePtr root;
1073 int i;
1074
1075 /*
1076 * check against recursion in the stack
1077 */
1078 for (i = 0;i < ctxt->docNr;i++) {
1079 if (xmlStrEqual(ctxt->docTab[i]->href, URL)) {
1080 if (ctxt->error != NULL)
1081 ctxt->error(ctxt->userData,
1082 "Detected an externalRef recursion for %s\n",
1083 URL);
1084 ctxt->nbErrors++;
1085 return(NULL);
1086 }
1087 }
1088
1089 /*
1090 * Lookup in the hash table
1091 */
1092 if (ctxt->documents == NULL) {
1093 ctxt->documents = xmlHashCreate(10);
1094 if (ctxt->documents == NULL) {
1095 if (ctxt->error != NULL)
1096 ctxt->error(ctxt->userData,
1097 "Failed to allocate hash table for document\n");
1098 ctxt->nbErrors++;
1099 return(NULL);
1100 }
1101 } else {
1102 if (ns == NULL)
1103 ret = xmlHashLookup2(ctxt->documents, BAD_CAST "", URL);
1104 else
1105 ret = xmlHashLookup2(ctxt->documents, ns, URL);
1106 if (ret != NULL)
1107 return(ret);
1108 }
1109
1110
1111 /*
1112 * load the document
1113 */
1114 doc = xmlParseFile((const char *) URL);
1115 if (doc == NULL) {
1116 if (ctxt->error != NULL)
1117 ctxt->error(ctxt->userData,
1118 "xmlRelaxNG: could not load %s\n", URL);
1119 ctxt->nbErrors++;
1120 return (NULL);
1121 }
1122
1123 /*
1124 * Allocate the document structures and register it first.
1125 */
1126 ret = (xmlRelaxNGDocumentPtr) xmlMalloc(sizeof(xmlRelaxNGDocument));
1127 if (ret == NULL) {
1128 if (ctxt->error != NULL)
1129 ctxt->error(ctxt->userData,
1130 "xmlRelaxNG: allocate memory for doc %s\n", URL);
1131 ctxt->nbErrors++;
1132 xmlFreeDoc(doc);
1133 return (NULL);
1134 }
1135 memset(ret, 0, sizeof(xmlRelaxNGDocument));
1136 ret->doc = doc;
1137 ret->href = xmlStrdup(URL);
1138
1139 /*
1140 * transmit the ns if needed
1141 */
1142 if (ns != NULL) {
1143 root = xmlDocGetRootElement(doc);
1144 if (root != NULL) {
1145 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
1146 xmlSetProp(root, BAD_CAST"ns", ns);
1147 }
1148 }
1149 }
1150
1151 /*
1152 * push it on the stack and register it in the hash table
1153 */
1154 if (ns == NULL)
1155 xmlHashAddEntry2(ctxt->documents, BAD_CAST "", URL, ret);
1156 else
1157 xmlHashAddEntry2(ctxt->documents, ns, URL, ret);
1158 xmlRelaxNGDocumentPush(ctxt, ret);
1159
1160 /*
1161 * Some preprocessing of the document content
1162 */
1163 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
1164 if (doc == NULL) {
1165 xmlFreeDoc(ctxt->document);
1166 ctxt->doc = NULL;
1167 return(NULL);
1168 }
1169
1170 xmlRelaxNGDocumentPop(ctxt);
1171
1172 return(ret);
1173}
1174
1175/************************************************************************
1176 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +00001177 * Error functions *
1178 * *
1179 ************************************************************************/
1180
1181#define VALID_CTXT() \
1182 if (ctxt->flags == 0) xmlGenericError(xmlGenericErrorContext, \
1183 "error detected at %s:%d\n", \
1184 __FILE__, __LINE__);
1185#define VALID_ERROR if (ctxt->flags == 0) printf
1186
1187#if 0
1188/**
1189 * xmlRelaxNGErrorContext:
1190 * @ctxt: the parsing context
1191 * @schema: the schema being built
1192 * @node: the node being processed
1193 * @child: the child being processed
1194 *
1195 * Dump a RelaxNGType structure
1196 */
1197static void
1198xmlRelaxNGErrorContext(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGPtr schema,
1199 xmlNodePtr node, xmlNodePtr child)
1200{
1201 int line = 0;
1202 const xmlChar *file = NULL;
1203 const xmlChar *name = NULL;
1204 const char *type = "error";
1205
1206 if ((ctxt == NULL) || (ctxt->error == NULL))
1207 return;
1208
1209 if (child != NULL)
1210 node = child;
1211
1212 if (node != NULL) {
1213 if ((node->type == XML_DOCUMENT_NODE) ||
1214 (node->type == XML_HTML_DOCUMENT_NODE)) {
1215 xmlDocPtr doc = (xmlDocPtr) node;
1216
1217 file = doc->URL;
1218 } else {
1219 /*
1220 * Try to find contextual informations to report
1221 */
1222 if (node->type == XML_ELEMENT_NODE) {
1223 line = (int) node->content;
1224 } else if ((node->prev != NULL) &&
1225 (node->prev->type == XML_ELEMENT_NODE)) {
1226 line = (int) node->prev->content;
1227 } else if ((node->parent != NULL) &&
1228 (node->parent->type == XML_ELEMENT_NODE)) {
1229 line = (int) node->parent->content;
1230 }
1231 if ((node->doc != NULL) && (node->doc->URL != NULL))
1232 file = node->doc->URL;
1233 if (node->name != NULL)
1234 name = node->name;
1235 }
1236 }
1237
1238 if (ctxt != NULL)
1239 type = "compilation error";
1240 else if (schema != NULL)
1241 type = "runtime error";
1242
1243 if ((file != NULL) && (line != 0) && (name != NULL))
1244 ctxt->error(ctxt->userData, "%s: file %s line %d element %s\n",
1245 type, file, line, name);
1246 else if ((file != NULL) && (name != NULL))
1247 ctxt->error(ctxt->userData, "%s: file %s element %s\n",
1248 type, file, name);
1249 else if ((file != NULL) && (line != 0))
1250 ctxt->error(ctxt->userData, "%s: file %s line %d\n", type, file, line);
1251 else if (file != NULL)
1252 ctxt->error(ctxt->userData, "%s: file %s\n", type, file);
1253 else if (name != NULL)
1254 ctxt->error(ctxt->userData, "%s: element %s\n", type, name);
1255 else
1256 ctxt->error(ctxt->userData, "%s\n", type);
1257}
1258#endif
1259
1260/************************************************************************
1261 * *
1262 * Type library hooks *
1263 * *
1264 ************************************************************************/
Daniel Veillardea3f3982003-01-26 19:45:18 +00001265static xmlChar *xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt,
1266 const xmlChar *str);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001267
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001268/**
1269 * xmlRelaxNGSchemaTypeHave:
1270 * @data: data needed for the library
1271 * @type: the type name
1272 *
1273 * Check if the given type is provided by
1274 * the W3C XMLSchema Datatype library.
1275 *
1276 * Returns 1 if yes, 0 if no and -1 in case of error.
1277 */
1278static int
1279xmlRelaxNGSchemaTypeHave(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001280 const xmlChar *type) {
1281 xmlSchemaTypePtr typ;
1282
1283 if (type == NULL)
1284 return(-1);
1285 typ = xmlSchemaGetPredefinedType(type,
1286 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1287 if (typ == NULL)
1288 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001289 return(1);
1290}
1291
1292/**
1293 * xmlRelaxNGSchemaTypeCheck:
1294 * @data: data needed for the library
1295 * @type: the type name
1296 * @value: the value to check
1297 *
1298 * Check if the given type and value are validated by
1299 * the W3C XMLSchema Datatype library.
1300 *
1301 * Returns 1 if yes, 0 if no and -1 in case of error.
1302 */
1303static int
1304xmlRelaxNGSchemaTypeCheck(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001305 const xmlChar *type,
1306 const xmlChar *value) {
1307 xmlSchemaTypePtr typ;
1308 int ret;
1309
1310 /*
1311 * TODO: the type should be cached ab provided back, interface subject
1312 * to changes.
1313 * TODO: handle facets, may require an additional interface and keep
1314 * the value returned from the validation.
1315 */
1316 if ((type == NULL) || (value == NULL))
1317 return(-1);
1318 typ = xmlSchemaGetPredefinedType(type,
1319 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1320 if (typ == NULL)
1321 return(-1);
1322 ret = xmlSchemaValidatePredefinedType(typ, value, NULL);
1323 if (ret == 0)
1324 return(1);
1325 if (ret > 0)
1326 return(0);
1327 return(-1);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001328}
1329
1330/**
1331 * xmlRelaxNGSchemaTypeCompare:
1332 * @data: data needed for the library
1333 * @type: the type name
1334 * @value1: the first value
1335 * @value2: the second value
1336 *
1337 * Compare two values accordingly a type from the W3C XMLSchema
1338 * Datatype library.
1339 *
1340 * Returns 1 if yes, 0 if no and -1 in case of error.
1341 */
1342static int
1343xmlRelaxNGSchemaTypeCompare(void *data ATTRIBUTE_UNUSED,
1344 const xmlChar *type ATTRIBUTE_UNUSED,
1345 const xmlChar *value1 ATTRIBUTE_UNUSED,
1346 const xmlChar *value2 ATTRIBUTE_UNUSED) {
1347 TODO
1348 return(1);
1349}
1350
1351/**
1352 * xmlRelaxNGDefaultTypeHave:
1353 * @data: data needed for the library
1354 * @type: the type name
1355 *
1356 * Check if the given type is provided by
1357 * the default datatype library.
1358 *
1359 * Returns 1 if yes, 0 if no and -1 in case of error.
1360 */
1361static int
1362xmlRelaxNGDefaultTypeHave(void *data ATTRIBUTE_UNUSED, const xmlChar *type) {
1363 if (type == NULL)
1364 return(-1);
1365 if (xmlStrEqual(type, BAD_CAST "string"))
1366 return(1);
1367 if (xmlStrEqual(type, BAD_CAST "token"))
1368 return(1);
1369 return(0);
1370}
1371
1372/**
1373 * xmlRelaxNGDefaultTypeCheck:
1374 * @data: data needed for the library
1375 * @type: the type name
1376 * @value: the value to check
1377 *
1378 * Check if the given type and value are validated by
1379 * the default datatype library.
1380 *
1381 * Returns 1 if yes, 0 if no and -1 in case of error.
1382 */
1383static int
1384xmlRelaxNGDefaultTypeCheck(void *data ATTRIBUTE_UNUSED,
1385 const xmlChar *type ATTRIBUTE_UNUSED,
1386 const xmlChar *value ATTRIBUTE_UNUSED) {
1387 return(1);
1388}
1389
1390/**
1391 * xmlRelaxNGDefaultTypeCompare:
1392 * @data: data needed for the library
1393 * @type: the type name
1394 * @value1: the first value
1395 * @value2: the second value
1396 *
1397 * Compare two values accordingly a type from the default
1398 * datatype library.
1399 *
1400 * Returns 1 if yes, 0 if no and -1 in case of error.
1401 */
1402static int
1403xmlRelaxNGDefaultTypeCompare(void *data ATTRIBUTE_UNUSED,
1404 const xmlChar *type ATTRIBUTE_UNUSED,
1405 const xmlChar *value1 ATTRIBUTE_UNUSED,
1406 const xmlChar *value2 ATTRIBUTE_UNUSED) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00001407 int ret = -1;
1408
1409 if (xmlStrEqual(type, BAD_CAST "string")) {
1410 ret = xmlStrEqual(value1, value2);
1411 } else if (xmlStrEqual(type, BAD_CAST "token")) {
1412 if (!xmlStrEqual(value1, value2)) {
1413 xmlChar *nval, *nvalue;
1414
1415 /*
1416 * TODO: trivial optimizations are possible by
1417 * computing at compile-time
1418 */
1419 nval = xmlRelaxNGNormalize(NULL, value1);
1420 nvalue = xmlRelaxNGNormalize(NULL, value2);
1421
1422 if ((nval == NULL) || (nvalue == NULL) ||
1423 (!xmlStrEqual(nval, nvalue)))
1424 ret = -1;
1425 if (nval != NULL)
1426 xmlFree(nval);
1427 if (nvalue != NULL)
1428 xmlFree(nvalue);
1429 }
1430 }
1431 return(ret);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001432}
1433
1434static int xmlRelaxNGTypeInitialized = 0;
1435static xmlHashTablePtr xmlRelaxNGRegisteredTypes = NULL;
1436
1437/**
1438 * xmlRelaxNGFreeTypeLibrary:
1439 * @lib: the type library structure
1440 * @namespace: the URI bound to the library
1441 *
1442 * Free the structure associated to the type library
1443 */
Daniel Veillard6eadf632003-01-23 18:29:16 +00001444static void
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001445xmlRelaxNGFreeTypeLibrary(xmlRelaxNGTypeLibraryPtr lib,
1446 const xmlChar *namespace ATTRIBUTE_UNUSED) {
1447 if (lib == NULL)
1448 return;
1449 if (lib->namespace != NULL)
1450 xmlFree((xmlChar *)lib->namespace);
1451 xmlFree(lib);
1452}
1453
1454/**
1455 * xmlRelaxNGRegisterTypeLibrary:
1456 * @namespace: the URI bound to the library
1457 * @data: data associated to the library
1458 * @have: the provide function
1459 * @check: the checking function
1460 * @comp: the comparison function
1461 *
1462 * Register a new type library
1463 *
1464 * Returns 0 in case of success and -1 in case of error.
1465 */
1466static int
1467xmlRelaxNGRegisterTypeLibrary(const xmlChar *namespace, void *data,
1468 xmlRelaxNGTypeHave have, xmlRelaxNGTypeCheck check,
1469 xmlRelaxNGTypeCompare comp) {
1470 xmlRelaxNGTypeLibraryPtr lib;
1471 int ret;
1472
1473 if ((xmlRelaxNGRegisteredTypes == NULL) || (namespace == NULL) ||
1474 (check == NULL) || (comp == NULL))
1475 return(-1);
1476 if (xmlHashLookup(xmlRelaxNGRegisteredTypes, namespace) != NULL) {
1477 xmlGenericError(xmlGenericErrorContext,
1478 "Relax-NG types library '%s' already registered\n",
1479 namespace);
1480 return(-1);
1481 }
1482 lib = (xmlRelaxNGTypeLibraryPtr) xmlMalloc(sizeof(xmlRelaxNGTypeLibrary));
1483 if (lib == NULL) {
1484 xmlGenericError(xmlGenericErrorContext,
1485 "Relax-NG types library '%s' malloc() failed\n",
1486 namespace);
1487 return (-1);
1488 }
1489 memset(lib, 0, sizeof(xmlRelaxNGTypeLibrary));
1490 lib->namespace = xmlStrdup(namespace);
1491 lib->data = data;
1492 lib->have = have;
1493 lib->comp = comp;
1494 lib->check = check;
1495 ret = xmlHashAddEntry(xmlRelaxNGRegisteredTypes, namespace, lib);
1496 if (ret < 0) {
1497 xmlGenericError(xmlGenericErrorContext,
1498 "Relax-NG types library failed to register '%s'\n",
1499 namespace);
1500 xmlRelaxNGFreeTypeLibrary(lib, namespace);
1501 return(-1);
1502 }
1503 return(0);
1504}
1505
1506/**
1507 * xmlRelaxNGInitTypes:
1508 *
1509 * Initilize the default type libraries.
1510 *
1511 * Returns 0 in case of success and -1 in case of error.
1512 */
1513static int
Daniel Veillard6eadf632003-01-23 18:29:16 +00001514xmlRelaxNGInitTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001515 if (xmlRelaxNGTypeInitialized != 0)
1516 return(0);
1517 xmlRelaxNGRegisteredTypes = xmlHashCreate(10);
1518 if (xmlRelaxNGRegisteredTypes == NULL) {
1519 xmlGenericError(xmlGenericErrorContext,
1520 "Failed to allocate sh table for Relax-NG types\n");
1521 return(-1);
1522 }
1523 xmlRelaxNGRegisterTypeLibrary(
1524 BAD_CAST "http://www.w3.org/2001/XMLSchema-datatypes",
1525 NULL,
1526 xmlRelaxNGSchemaTypeHave,
1527 xmlRelaxNGSchemaTypeCheck,
1528 xmlRelaxNGSchemaTypeCompare);
1529 xmlRelaxNGRegisterTypeLibrary(
1530 xmlRelaxNGNs,
1531 NULL,
1532 xmlRelaxNGDefaultTypeHave,
1533 xmlRelaxNGDefaultTypeCheck,
1534 xmlRelaxNGDefaultTypeCompare);
1535 xmlRelaxNGTypeInitialized = 1;
1536 return(0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001537}
1538
1539/**
1540 * xmlRelaxNGCleanupTypes:
1541 *
1542 * Cleanup the default Schemas type library associated to RelaxNG
1543 */
1544void
1545xmlRelaxNGCleanupTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001546 if (xmlRelaxNGTypeInitialized == 0)
1547 return;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001548 xmlSchemaCleanupTypes();
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001549 xmlHashFree(xmlRelaxNGRegisteredTypes, (xmlHashDeallocator)
1550 xmlRelaxNGFreeTypeLibrary);
1551 xmlRelaxNGTypeInitialized = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001552}
1553
1554/************************************************************************
1555 * *
1556 * Parsing functions *
1557 * *
1558 ************************************************************************/
1559
1560static xmlRelaxNGDefinePtr xmlRelaxNGParseAttribute(
1561 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1562static xmlRelaxNGDefinePtr xmlRelaxNGParseElement(
1563 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1564static xmlRelaxNGDefinePtr xmlRelaxNGParsePatterns(
Daniel Veillard154877e2003-01-30 12:17:05 +00001565 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes, int group);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001566static xmlRelaxNGDefinePtr xmlRelaxNGParsePattern(
1567 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001568static xmlRelaxNGPtr xmlRelaxNGParseDocument(
1569 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00001570static int xmlRelaxNGParseGrammarContent(
1571 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00001572static xmlRelaxNGDefinePtr xmlRelaxNGParseNameClass(
1573 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
1574 xmlRelaxNGDefinePtr def);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001575
1576
1577#define IS_BLANK_NODE(n) \
1578 (((n)->type == XML_TEXT_NODE) && (xmlRelaxNGIsBlank((n)->content)))
1579
1580/**
1581 * xmlRelaxNGIsBlank:
1582 * @str: a string
1583 *
1584 * Check if a string is ignorable c.f. 4.2. Whitespace
1585 *
1586 * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
1587 */
1588static int
1589xmlRelaxNGIsBlank(xmlChar *str) {
1590 if (str == NULL)
1591 return(1);
1592 while (*str != 0) {
1593 if (!(IS_BLANK(*str))) return(0);
1594 str++;
1595 }
1596 return(1);
1597}
1598
Daniel Veillard6eadf632003-01-23 18:29:16 +00001599/**
1600 * xmlRelaxNGGetDataTypeLibrary:
1601 * @ctxt: a Relax-NG parser context
1602 * @node: the current data or value element
1603 *
1604 * Applies algorithm from 4.3. datatypeLibrary attribute
1605 *
1606 * Returns the datatypeLibary value or NULL if not found
1607 */
1608static xmlChar *
1609xmlRelaxNGGetDataTypeLibrary(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1610 xmlNodePtr node) {
1611 xmlChar *ret, *escape;
1612
Daniel Veillard6eadf632003-01-23 18:29:16 +00001613 if ((IS_RELAXNG(node, "data")) || (IS_RELAXNG(node, "value"))) {
1614 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1615 if (ret != NULL) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001616 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +00001617 if (escape == NULL) {
1618 return(ret);
1619 }
1620 xmlFree(ret);
1621 return(escape);
1622 }
1623 }
1624 node = node->parent;
1625 while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) {
1626 if (IS_RELAXNG(node, "element")) {
1627 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1628 if (ret != NULL) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001629 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +00001630 if (escape == NULL) {
1631 return(ret);
1632 }
1633 xmlFree(ret);
1634 return(escape);
1635 }
1636 }
1637 node = node->parent;
1638 }
1639 return(NULL);
1640}
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001641
1642/**
Daniel Veillardedc91922003-01-26 00:52:04 +00001643 * xmlRelaxNGParseValue:
1644 * @ctxt: a Relax-NG parser context
1645 * @node: the data node.
1646 *
1647 * parse the content of a RelaxNG value node.
1648 *
1649 * Returns the definition pointer or NULL in case of error
1650 */
1651static xmlRelaxNGDefinePtr
1652xmlRelaxNGParseValue(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1653 xmlRelaxNGDefinePtr def = NULL;
1654 xmlRelaxNGTypeLibraryPtr lib;
1655 xmlChar *type;
1656 xmlChar *library;
1657 int tmp;
1658
1659 def = xmlRelaxNGNewDefine(ctxt, node);
1660 if (def == NULL)
1661 return(NULL);
1662 def->type = XML_RELAXNG_VALUE;
1663
1664 type = xmlGetProp(node, BAD_CAST "type");
1665 if (type != NULL) {
1666 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1667 if (library == NULL)
1668 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1669
1670 def->name = type;
1671 def->ns = library;
1672
1673 lib = (xmlRelaxNGTypeLibraryPtr)
1674 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1675 if (lib == NULL) {
1676 if (ctxt->error != NULL)
1677 ctxt->error(ctxt->userData,
1678 "Use of unregistered type library '%s'\n",
1679 library);
1680 ctxt->nbErrors++;
1681 def->data = NULL;
1682 } else {
1683 def->data = lib;
1684 if (lib->have == NULL) {
1685 ctxt->error(ctxt->userData,
1686 "Internal error with type library '%s': no 'have'\n",
1687 library);
1688 ctxt->nbErrors++;
1689 } else {
1690 tmp = lib->have(lib->data, def->name);
1691 if (tmp != 1) {
1692 ctxt->error(ctxt->userData,
1693 "Error type '%s' is not exported by type library '%s'\n",
1694 def->name, library);
1695 ctxt->nbErrors++;
1696 }
1697 }
1698 }
1699 }
1700 if (node->children == NULL) {
1701 if (ctxt->error != NULL)
1702 ctxt->error(ctxt->userData,
1703 "Element <value> has no content\n");
1704 ctxt->nbErrors++;
1705 } else if ((node->children->type != XML_TEXT_NODE) ||
1706 (node->children->next != NULL)) {
1707 if (ctxt->error != NULL)
1708 ctxt->error(ctxt->userData,
1709 "Expecting a single text value for <value>content\n");
1710 ctxt->nbErrors++;
1711 } else {
1712 def->value = xmlNodeGetContent(node);
1713 if (def->value == NULL) {
1714 if (ctxt->error != NULL)
1715 ctxt->error(ctxt->userData,
1716 "Element <value> has no content\n");
1717 ctxt->nbErrors++;
1718 }
1719 }
1720 /* TODO check ahead of time that the value is okay per the type */
1721 return(def);
1722}
1723
1724/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001725 * xmlRelaxNGParseData:
1726 * @ctxt: a Relax-NG parser context
1727 * @node: the data node.
1728 *
1729 * parse the content of a RelaxNG data node.
1730 *
1731 * Returns the definition pointer or NULL in case of error
1732 */
1733static xmlRelaxNGDefinePtr
1734xmlRelaxNGParseData(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1735 xmlRelaxNGDefinePtr def = NULL;
1736 xmlRelaxNGTypeLibraryPtr lib;
1737 xmlChar *type;
1738 xmlChar *library;
1739 xmlNodePtr content;
1740 int tmp;
1741
1742 type = xmlGetProp(node, BAD_CAST "type");
1743 if (type == NULL) {
1744 if (ctxt->error != NULL)
1745 ctxt->error(ctxt->userData,
1746 "data has no type\n");
1747 ctxt->nbErrors++;
1748 return(NULL);
1749 }
1750 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1751 if (library == NULL)
1752 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1753
1754 def = xmlRelaxNGNewDefine(ctxt, node);
1755 if (def == NULL) {
1756 xmlFree(type);
1757 return(NULL);
1758 }
1759 def->type = XML_RELAXNG_DATATYPE;
1760 def->name = type;
1761 def->ns = library;
1762
1763 lib = (xmlRelaxNGTypeLibraryPtr)
1764 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1765 if (lib == NULL) {
1766 if (ctxt->error != NULL)
1767 ctxt->error(ctxt->userData,
1768 "Use of unregistered type library '%s'\n",
1769 library);
1770 ctxt->nbErrors++;
1771 def->data = NULL;
1772 } else {
1773 def->data = lib;
1774 if (lib->have == NULL) {
1775 ctxt->error(ctxt->userData,
1776 "Internal error with type library '%s': no 'have'\n",
1777 library);
1778 ctxt->nbErrors++;
1779 } else {
1780 tmp = lib->have(lib->data, def->name);
1781 if (tmp != 1) {
1782 ctxt->error(ctxt->userData,
1783 "Error type '%s' is not exported by type library '%s'\n",
1784 def->name, library);
1785 ctxt->nbErrors++;
1786 }
1787 }
1788 }
1789 content = node->children;
1790 while (content != NULL) {
1791 TODO
1792 content = content->next;
1793 }
1794
1795 return(def);
1796}
1797
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001798/**
1799 * xmlRelaxNGCompareElemDefLists:
1800 * @ctxt: a Relax-NG parser context
1801 * @defs1: the first list of element defs
1802 * @defs2: the second list of element defs
1803 *
1804 * Compare the 2 lists of element definitions. The comparison is
1805 * that if both lists do not accept the same QNames, it returns 1
1806 * If the 2 lists can accept the same QName the comparison returns 0
1807 *
1808 * Returns 1 disttinct, 0 if equal
1809 */
1810static int
1811xmlRelaxNGCompareElemDefLists(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1812 xmlRelaxNGDefinePtr *def1,
1813 xmlRelaxNGDefinePtr *def2) {
1814 xmlRelaxNGDefinePtr *basedef2 = def2;
1815
Daniel Veillard154877e2003-01-30 12:17:05 +00001816 if ((def1 == NULL) || (def2 == NULL))
1817 return(1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001818 if ((*def1 == NULL) || (*def2 == NULL))
1819 return(1);
1820 while (*def1 != NULL) {
1821 while ((*def2) != NULL) {
1822 if ((*def1)->name == NULL) {
1823 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1824 return(0);
1825 } else if ((*def2)->name == NULL) {
1826 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1827 return(0);
1828 } else if (xmlStrEqual((*def1)->name, (*def2)->name)) {
1829 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1830 return(0);
1831 }
1832 def2++;
1833 }
1834 def2 = basedef2;
1835 def1++;
1836 }
1837 return(1);
1838}
1839
1840/**
1841 * xmlRelaxNGGetElements:
1842 * @ctxt: a Relax-NG parser context
1843 * @def: the interleave definition
1844 *
1845 * Compute the list of top elements a definition can generate
1846 *
1847 * Returns a list of elements or NULL if none was found.
1848 */
1849static xmlRelaxNGDefinePtr *
1850xmlRelaxNGGetElements(xmlRelaxNGParserCtxtPtr ctxt,
1851 xmlRelaxNGDefinePtr def) {
1852 xmlRelaxNGDefinePtr *ret = NULL, parent, cur, tmp;
1853 int len = 0;
1854 int max = 0;
1855
1856 parent = NULL;
1857 cur = def;
1858 while (cur != NULL) {
Daniel Veillardb08c9812003-01-28 23:09:49 +00001859 if ((cur->type == XML_RELAXNG_ELEMENT) ||
1860 (cur->type == XML_RELAXNG_TEXT)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001861 if (ret == NULL) {
1862 max = 10;
1863 ret = (xmlRelaxNGDefinePtr *)
1864 xmlMalloc((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 } else if (max <= len) {
1873 max *= 2;
1874 ret = xmlRealloc(ret, (max + 1) * sizeof(xmlRelaxNGDefinePtr));
1875 if (ret == NULL) {
1876 if (ctxt->error != NULL)
1877 ctxt->error(ctxt->userData,
1878 "Out of memory in element search\n");
1879 ctxt->nbErrors++;
1880 return(NULL);
1881 }
1882 }
Daniel Veillardb08c9812003-01-28 23:09:49 +00001883 ret[len++] = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001884 ret[len] = NULL;
1885 } else if ((cur->type == XML_RELAXNG_CHOICE) ||
1886 (cur->type == XML_RELAXNG_INTERLEAVE) ||
1887 (cur->type == XML_RELAXNG_GROUP) ||
1888 (cur->type == XML_RELAXNG_ONEORMORE) ||
Daniel Veillardb08c9812003-01-28 23:09:49 +00001889 (cur->type == XML_RELAXNG_ZEROORMORE) ||
1890 (cur->type == XML_RELAXNG_OPTIONAL) ||
1891 (cur->type == XML_RELAXNG_REF) ||
1892 (cur->type == XML_RELAXNG_DEF)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001893 /*
1894 * Don't go within elements or attributes or string values.
1895 * Just gather the element top list
1896 */
1897 if (cur->content != NULL) {
1898 parent = cur;
1899 cur = cur->content;
1900 tmp = cur;
1901 while (tmp != NULL) {
1902 tmp->parent = parent;
1903 tmp = tmp->next;
1904 }
1905 continue;
1906 }
1907 }
Daniel Veillard154877e2003-01-30 12:17:05 +00001908 if (cur == def)
1909 return(ret);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001910 if (cur->next != NULL) {
1911 cur = cur->next;
1912 continue;
1913 }
1914 do {
1915 cur = cur->parent;
1916 if (cur == NULL) break;
1917 if (cur == def) return(ret);
1918 if (cur->next != NULL) {
1919 cur = cur->next;
1920 break;
1921 }
1922 } while (cur != NULL);
1923 }
1924 return(ret);
1925}
1926
1927/**
1928 * xmlRelaxNGComputeInterleaves:
1929 * @def: the interleave definition
1930 * @ctxt: a Relax-NG parser context
1931 * @node: the data node.
1932 *
1933 * A lot of work for preprocessing interleave definitions
1934 * is potentially needed to get a decent execution speed at runtime
1935 * - trying to get a total order on the element nodes generated
1936 * by the interleaves, order the list of interleave definitions
1937 * following that order.
1938 * - if <text/> is used to handle mixed content, it is better to
1939 * flag this in the define and simplify the runtime checking
1940 * algorithm
1941 */
1942static void
1943xmlRelaxNGComputeInterleaves(xmlRelaxNGDefinePtr def,
1944 xmlRelaxNGParserCtxtPtr ctxt,
1945 xmlChar *name ATTRIBUTE_UNUSED) {
1946 xmlRelaxNGDefinePtr cur;
1947
1948 xmlRelaxNGDefinePtr *list = NULL;
1949 xmlRelaxNGPartitionPtr partitions = NULL;
1950 xmlRelaxNGInterleaveGroupPtr *groups = NULL;
1951 xmlRelaxNGInterleaveGroupPtr group;
1952 int i,j,ret;
1953 int nbgroups = 0;
1954 int nbchild = 0;
1955
1956#ifdef DEBUG_INTERLEAVE
1957 xmlGenericError(xmlGenericErrorContext,
1958 "xmlRelaxNGComputeInterleaves(%s)\n",
1959 name);
1960#endif
1961 cur = def->content;
1962 while (cur != NULL) {
1963 nbchild++;
1964 cur = cur->next;
1965 }
1966
1967#ifdef DEBUG_INTERLEAVE
1968 xmlGenericError(xmlGenericErrorContext, " %d child\n", nbchild);
1969#endif
1970 groups = (xmlRelaxNGInterleaveGroupPtr *)
1971 xmlMalloc(nbchild * sizeof(xmlRelaxNGInterleaveGroupPtr));
1972 if (groups == NULL)
1973 goto error;
1974 cur = def->content;
1975 while (cur != NULL) {
Daniel Veillard154877e2003-01-30 12:17:05 +00001976 groups[nbgroups] = (xmlRelaxNGInterleaveGroupPtr)
1977 xmlMalloc(sizeof(xmlRelaxNGInterleaveGroup));
1978 if (groups[nbgroups] == NULL)
1979 goto error;
1980 groups[nbgroups]->rule = cur;
1981 groups[nbgroups]->defs = xmlRelaxNGGetElements(ctxt, cur);
1982 nbgroups++;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001983 cur = cur->next;
1984 }
1985 list = NULL;
1986#ifdef DEBUG_INTERLEAVE
1987 xmlGenericError(xmlGenericErrorContext, " %d groups\n", nbgroups);
1988#endif
1989
1990 /*
1991 * Let's check that all rules makes a partitions according to 7.4
1992 */
1993 partitions = (xmlRelaxNGPartitionPtr)
1994 xmlMalloc(sizeof(xmlRelaxNGPartition));
1995 if (partitions == NULL)
1996 goto error;
1997 partitions->nbgroups = nbgroups;
1998 for (i = 0;i < nbgroups;i++) {
1999 group = groups[i];
2000 for (j = i+1;j < nbgroups;j++) {
2001 if (groups[j] == NULL)
2002 continue;
2003 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->defs,
2004 groups[j]->defs);
2005 if (ret == 0) {
2006 if (ctxt->error != NULL)
2007 ctxt->error(ctxt->userData,
2008 "Element or text conflicts in interleave\n");
2009 ctxt->nbErrors++;
2010 }
2011 }
2012 }
2013 partitions->groups = groups;
2014
2015 /*
2016 * Free Up the child list, and save the partition list back in the def
2017 */
2018 def->data = partitions;
2019 return;
2020
2021error:
2022 if (ctxt->error != NULL)
2023 ctxt->error(ctxt->userData,
2024 "Out of memory in interleave computation\n");
2025 ctxt->nbErrors++;
2026 if (list == NULL)
2027 xmlFree(list);
2028 if (groups != NULL) {
2029 for (i = 0;i < nbgroups;i++)
2030 if (groups[i] != NULL) {
2031 if (groups[i]->defs != NULL)
2032 xmlFree(groups[i]->defs);
2033 xmlFree(groups[i]);
2034 }
2035 xmlFree(groups);
2036 }
2037 xmlRelaxNGFreePartition(partitions);
2038}
2039
2040/**
2041 * xmlRelaxNGParseInterleave:
2042 * @ctxt: a Relax-NG parser context
2043 * @node: the data node.
2044 *
2045 * parse the content of a RelaxNG interleave node.
2046 *
2047 * Returns the definition pointer or NULL in case of error
2048 */
2049static xmlRelaxNGDefinePtr
2050xmlRelaxNGParseInterleave(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2051 xmlRelaxNGDefinePtr def = NULL;
2052 xmlRelaxNGDefinePtr last = NULL, cur;
2053 xmlNodePtr child;
2054
2055 def = xmlRelaxNGNewDefine(ctxt, node);
2056 if (def == NULL) {
2057 return(NULL);
2058 }
2059 def->type = XML_RELAXNG_INTERLEAVE;
2060
2061 if (ctxt->interleaves == NULL)
2062 ctxt->interleaves = xmlHashCreate(10);
2063 if (ctxt->interleaves == NULL) {
2064 if (ctxt->error != NULL)
2065 ctxt->error(ctxt->userData,
2066 "Failed to create interleaves hash table\n");
2067 ctxt->nbErrors++;
2068 } else {
2069 char name[32];
2070
2071 snprintf(name, 32, "interleave%d", ctxt->nbInterleaves++);
2072 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST name, def) < 0) {
2073 if (ctxt->error != NULL)
2074 ctxt->error(ctxt->userData,
2075 "Failed to add %s to hash table\n", name);
2076 ctxt->nbErrors++;
2077 }
2078 }
2079 child = node->children;
2080 while (child != NULL) {
2081 if (IS_RELAXNG(child, "element")) {
2082 cur = xmlRelaxNGParseElement(ctxt, child);
2083 } else {
2084 cur = xmlRelaxNGParsePattern(ctxt, child);
2085 }
2086 if (cur != NULL) {
2087 cur->parent = def;
2088 if (last == NULL) {
2089 def->content = last = cur;
2090 } else {
2091 last->next = cur;
2092 last = cur;
2093 }
2094 }
2095 child = child->next;
2096 }
2097
2098 return(def);
2099}
Daniel Veillard6eadf632003-01-23 18:29:16 +00002100
2101/**
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002102 * xmlRelaxNGParseInclude:
2103 * @ctxt: a Relax-NG parser context
2104 * @node: the include node
2105 *
2106 * Integrate the content of an include node in the current grammar
2107 *
2108 * Returns 0 in case of success or -1 in case of error
2109 */
2110static int
2111xmlRelaxNGParseInclude(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2112 xmlRelaxNGIncludePtr incl;
2113 xmlNodePtr root;
2114 int ret = 0, tmp;
2115
2116 incl = node->_private;
2117 if (incl == NULL) {
2118 if (ctxt->error != NULL)
2119 ctxt->error(ctxt->userData,
2120 "Include node has no data\n");
2121 ctxt->nbErrors++;
2122 return(-1);
2123 }
2124 root = xmlDocGetRootElement(incl->doc);
2125 if (root == NULL) {
2126 if (ctxt->error != NULL)
2127 ctxt->error(ctxt->userData,
2128 "Include document is empty\n");
2129 ctxt->nbErrors++;
2130 return(-1);
2131 }
2132 if (!xmlStrEqual(root->name, BAD_CAST "grammar")) {
2133 if (ctxt->error != NULL)
2134 ctxt->error(ctxt->userData,
2135 "Include document root is not a grammar\n");
2136 ctxt->nbErrors++;
2137 return(-1);
2138 }
2139
2140 /*
2141 * Merge the definition from both the include and the internal list
2142 */
2143 if (root->children != NULL) {
2144 tmp = xmlRelaxNGParseGrammarContent(ctxt, root->children);
2145 if (tmp != 0)
2146 ret = -1;
2147 }
2148 if (node->children != NULL) {
2149 tmp = xmlRelaxNGParseGrammarContent(ctxt, node->children);
2150 if (tmp != 0)
2151 ret = -1;
2152 }
2153 return(ret);
2154}
2155
2156/**
Daniel Veillard276be4a2003-01-24 01:03:34 +00002157 * xmlRelaxNGParseDefine:
2158 * @ctxt: a Relax-NG parser context
2159 * @node: the define node
2160 *
2161 * parse the content of a RelaxNG define element node.
2162 *
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002163 * Returns 0 in case of success or -1 in case of error
Daniel Veillard276be4a2003-01-24 01:03:34 +00002164 */
2165static int
2166xmlRelaxNGParseDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2167 xmlChar *name;
2168 int ret = 0, tmp;
2169 xmlRelaxNGDefinePtr def;
2170 const xmlChar *olddefine;
2171
2172 name = xmlGetProp(node, BAD_CAST "name");
2173 if (name == NULL) {
2174 if (ctxt->error != NULL)
2175 ctxt->error(ctxt->userData,
2176 "define has no name\n");
2177 ctxt->nbErrors++;
2178 } else {
2179 def = xmlRelaxNGNewDefine(ctxt, node);
2180 if (def == NULL) {
2181 xmlFree(name);
2182 return(-1);
2183 }
2184 def->type = XML_RELAXNG_DEF;
2185 def->name = name;
2186 if (node->children == NULL) {
2187 if (ctxt->error != NULL)
2188 ctxt->error(ctxt->userData,
2189 "define has no children\n");
2190 ctxt->nbErrors++;
2191 } else {
2192 olddefine = ctxt->define;
2193 ctxt->define = name;
Daniel Veillard154877e2003-01-30 12:17:05 +00002194 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard276be4a2003-01-24 01:03:34 +00002195 ctxt->define = olddefine;
2196 }
2197 if (ctxt->grammar->defs == NULL)
2198 ctxt->grammar->defs = xmlHashCreate(10);
2199 if (ctxt->grammar->defs == NULL) {
2200 if (ctxt->error != NULL)
2201 ctxt->error(ctxt->userData,
2202 "Could not create definition hash\n");
2203 ctxt->nbErrors++;
2204 ret = -1;
2205 xmlRelaxNGFreeDefine(def);
2206 } else {
2207 tmp = xmlHashAddEntry(ctxt->grammar->defs, name, def);
2208 if (tmp < 0) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002209 xmlRelaxNGDefinePtr prev;
2210
2211 prev = xmlHashLookup(ctxt->grammar->defs, name);
2212 if (prev == NULL) {
2213 if (ctxt->error != NULL)
2214 ctxt->error(ctxt->userData,
2215 "Internal error on define aggregation of %s\n",
2216 name);
2217 ctxt->nbErrors++;
2218 ret = -1;
2219 xmlRelaxNGFreeDefine(def);
2220 } else {
2221 while (prev->nextHash != NULL)
2222 prev = prev->nextHash;
2223 prev->nextHash = def;
2224 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002225 }
2226 }
2227 }
2228 return(ret);
2229}
2230
2231/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00002232 * xmlRelaxNGParsePattern:
2233 * @ctxt: a Relax-NG parser context
2234 * @node: the pattern node.
2235 *
2236 * parse the content of a RelaxNG pattern node.
2237 *
Daniel Veillard276be4a2003-01-24 01:03:34 +00002238 * Returns the definition pointer or NULL in case of error or if no
2239 * pattern is generated.
Daniel Veillard6eadf632003-01-23 18:29:16 +00002240 */
2241static xmlRelaxNGDefinePtr
2242xmlRelaxNGParsePattern(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2243 xmlRelaxNGDefinePtr def = NULL;
2244
2245 if (IS_RELAXNG(node, "element")) {
2246 def = xmlRelaxNGParseElement(ctxt, node);
2247 } else if (IS_RELAXNG(node, "attribute")) {
2248 def = xmlRelaxNGParseAttribute(ctxt, node);
2249 } else if (IS_RELAXNG(node, "empty")) {
2250 def = xmlRelaxNGNewDefine(ctxt, node);
2251 if (def == NULL)
2252 return(NULL);
2253 def->type = XML_RELAXNG_EMPTY;
2254 } else if (IS_RELAXNG(node, "text")) {
2255 def = xmlRelaxNGNewDefine(ctxt, node);
2256 if (def == NULL)
2257 return(NULL);
2258 def->type = XML_RELAXNG_TEXT;
2259 if (node->children != NULL) {
2260 if (ctxt->error != NULL)
2261 ctxt->error(ctxt->userData, "text: had a child node\n");
2262 ctxt->nbErrors++;
2263 }
2264 } else if (IS_RELAXNG(node, "zeroOrMore")) {
2265 def = xmlRelaxNGNewDefine(ctxt, node);
2266 if (def == NULL)
2267 return(NULL);
2268 def->type = XML_RELAXNG_ZEROORMORE;
Daniel Veillard154877e2003-01-30 12:17:05 +00002269 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002270 } else if (IS_RELAXNG(node, "oneOrMore")) {
2271 def = xmlRelaxNGNewDefine(ctxt, node);
2272 if (def == NULL)
2273 return(NULL);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002274 def->type = XML_RELAXNG_ONEORMORE;
Daniel Veillard154877e2003-01-30 12:17:05 +00002275 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002276 } else if (IS_RELAXNG(node, "optional")) {
2277 def = xmlRelaxNGNewDefine(ctxt, node);
2278 if (def == NULL)
2279 return(NULL);
2280 def->type = XML_RELAXNG_OPTIONAL;
Daniel Veillard154877e2003-01-30 12:17:05 +00002281 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002282 } else if (IS_RELAXNG(node, "choice")) {
2283 def = xmlRelaxNGNewDefine(ctxt, node);
2284 if (def == NULL)
2285 return(NULL);
2286 def->type = XML_RELAXNG_CHOICE;
Daniel Veillard154877e2003-01-30 12:17:05 +00002287 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002288 } else if (IS_RELAXNG(node, "group")) {
2289 def = xmlRelaxNGNewDefine(ctxt, node);
2290 if (def == NULL)
2291 return(NULL);
2292 def->type = XML_RELAXNG_GROUP;
Daniel Veillard154877e2003-01-30 12:17:05 +00002293 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002294 } else if (IS_RELAXNG(node, "ref")) {
2295 def = xmlRelaxNGNewDefine(ctxt, node);
2296 if (def == NULL)
2297 return(NULL);
2298 def->type = XML_RELAXNG_REF;
2299 def->name = xmlGetProp(node, BAD_CAST "name");
2300 if (def->name == NULL) {
2301 if (ctxt->error != NULL)
2302 ctxt->error(ctxt->userData,
2303 "ref has no name\n");
2304 ctxt->nbErrors++;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002305 } else {
2306 if ((ctxt->define != NULL) &&
2307 (xmlStrEqual(ctxt->define, def->name))) {
2308 if (ctxt->error != NULL)
2309 ctxt->error(ctxt->userData,
2310 "Recursive reference to %s not in an element\n",
2311 def->name);
2312 ctxt->nbErrors++;
2313 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002314 }
2315 if (node->children != NULL) {
2316 if (ctxt->error != NULL)
2317 ctxt->error(ctxt->userData,
2318 "ref is not empty\n");
2319 ctxt->nbErrors++;
2320 }
2321 if (ctxt->grammar->refs == NULL)
2322 ctxt->grammar->refs = xmlHashCreate(10);
2323 if (ctxt->grammar->refs == NULL) {
2324 if (ctxt->error != NULL)
2325 ctxt->error(ctxt->userData,
2326 "Could not create references hash\n");
2327 ctxt->nbErrors++;
2328 xmlRelaxNGFreeDefine(def);
2329 def = NULL;
2330 } else {
2331 int tmp;
2332
2333 tmp = xmlHashAddEntry(ctxt->grammar->refs, def->name, def);
2334 if (tmp < 0) {
2335 xmlRelaxNGDefinePtr prev;
2336
2337 prev = (xmlRelaxNGDefinePtr)
2338 xmlHashLookup(ctxt->grammar->refs, def->name);
2339 if (prev == NULL) {
2340 if (ctxt->error != NULL)
2341 ctxt->error(ctxt->userData,
2342 "Internal error refs definitions '%s'\n",
2343 def->name);
2344 ctxt->nbErrors++;
2345 xmlRelaxNGFreeDefine(def);
2346 def = NULL;
2347 } else {
2348 def->nextHash = prev->nextHash;
2349 prev->nextHash = def;
2350 }
2351 }
2352 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002353 } else if (IS_RELAXNG(node, "data")) {
2354 def = xmlRelaxNGParseData(ctxt, node);
Daniel Veillard276be4a2003-01-24 01:03:34 +00002355 } else if (IS_RELAXNG(node, "define")) {
2356 xmlRelaxNGParseDefine(ctxt, node);
2357 def = NULL;
Daniel Veillardedc91922003-01-26 00:52:04 +00002358 } else if (IS_RELAXNG(node, "value")) {
2359 def = xmlRelaxNGParseValue(ctxt, node);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002360 } else if (IS_RELAXNG(node, "list")) {
2361 def = xmlRelaxNGNewDefine(ctxt, node);
2362 if (def == NULL)
2363 return(NULL);
2364 def->type = XML_RELAXNG_LIST;
Daniel Veillard154877e2003-01-30 12:17:05 +00002365 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002366 } else if (IS_RELAXNG(node, "interleave")) {
2367 def = xmlRelaxNGParseInterleave(ctxt, node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002368 } else if (IS_RELAXNG(node, "externalRef")) {
2369 xmlRelaxNGDocumentPtr docu;
2370 xmlNodePtr root;
2371
2372 docu = node->_private;
2373 if (docu != NULL) {
2374 def = xmlRelaxNGNewDefine(ctxt, node);
2375 if (def == NULL)
2376 return(NULL);
2377 def->type = XML_RELAXNG_EXTERNALREF;
2378
2379 if (docu->content == NULL) {
2380 /*
2381 * Then do the parsing for good
2382 */
2383 root = xmlDocGetRootElement(docu->doc);
2384 if (root == NULL) {
2385 if (ctxt->error != NULL)
2386 ctxt->error(ctxt->userData,
2387 "xmlRelaxNGParse: %s is empty\n",
2388 ctxt->URL);
2389 ctxt->nbErrors++;
2390 return (NULL);
2391 }
2392 docu->schema = xmlRelaxNGParseDocument(ctxt, root);
2393 if ((docu->schema != NULL) &&
2394 (docu->schema->topgrammar != NULL)) {
2395 docu->content = docu->schema->topgrammar->start;
2396 }
2397 }
2398 def->content = docu->content;
2399 } else {
2400 def = NULL;
2401 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002402 } else if (IS_RELAXNG(node, "notAllowed")) {
2403 def = xmlRelaxNGNewDefine(ctxt, node);
2404 if (def == NULL)
2405 return(NULL);
2406 def->type = XML_RELAXNG_NOT_ALLOWED;
2407 if (node->children != NULL) {
2408 if (ctxt->error != NULL)
2409 ctxt->error(ctxt->userData,
2410 "xmlRelaxNGParse: notAllowed element is not empty\n");
2411 ctxt->nbErrors++;
2412 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002413 } else {
2414 TODO
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002415 def = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002416 }
2417 return(def);
2418}
2419
2420/**
2421 * xmlRelaxNGParseAttribute:
2422 * @ctxt: a Relax-NG parser context
2423 * @node: the element node
2424 *
2425 * parse the content of a RelaxNG attribute node.
2426 *
2427 * Returns the definition pointer or NULL in case of error.
2428 */
2429static xmlRelaxNGDefinePtr
2430xmlRelaxNGParseAttribute(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2431 xmlRelaxNGDefinePtr ret, cur, last;
2432 xmlNodePtr child;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002433 int old_flags;
2434
2435 ret = xmlRelaxNGNewDefine(ctxt, node);
2436 if (ret == NULL)
2437 return(NULL);
2438 ret->type = XML_RELAXNG_ATTRIBUTE;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002439 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002440 child = node->children;
2441 if (child == NULL) {
2442 if (ctxt->error != NULL)
2443 ctxt->error(ctxt->userData,
2444 "xmlRelaxNGParseattribute: attribute has no children\n");
2445 ctxt->nbErrors++;
2446 return(ret);
2447 }
2448 old_flags = ctxt->flags;
2449 ctxt->flags |= XML_RELAXNG_IN_ATTRIBUTE;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002450 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
2451 if (cur != NULL)
2452 child = child->next;
2453
Daniel Veillard6eadf632003-01-23 18:29:16 +00002454 last = NULL;
2455 while (child != NULL) {
2456 cur = xmlRelaxNGParsePattern(ctxt, child);
2457 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002458 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002459 switch (cur->type) {
2460 case XML_RELAXNG_EMPTY:
2461 case XML_RELAXNG_NOT_ALLOWED:
2462 case XML_RELAXNG_TEXT:
2463 case XML_RELAXNG_ELEMENT:
2464 case XML_RELAXNG_DATATYPE:
2465 case XML_RELAXNG_VALUE:
2466 case XML_RELAXNG_LIST:
2467 case XML_RELAXNG_REF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002468 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00002469 case XML_RELAXNG_DEF:
2470 case XML_RELAXNG_ONEORMORE:
2471 case XML_RELAXNG_ZEROORMORE:
2472 case XML_RELAXNG_OPTIONAL:
2473 case XML_RELAXNG_CHOICE:
2474 case XML_RELAXNG_GROUP:
2475 case XML_RELAXNG_INTERLEAVE:
2476 if (last == NULL) {
2477 ret->content = last = cur;
2478 } else {
2479 if ((last->type == XML_RELAXNG_ELEMENT) &&
2480 (ret->content == last)) {
2481 ret->content = xmlRelaxNGNewDefine(ctxt, node);
2482 if (ret->content != NULL) {
2483 ret->content->type = XML_RELAXNG_GROUP;
2484 ret->content->content = last;
2485 } else {
2486 ret->content = last;
2487 }
2488 }
2489 last->next = cur;
2490 last = cur;
2491 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002492 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002493 break;
2494 case XML_RELAXNG_ATTRIBUTE:
2495 cur->next = ret->attrs;
2496 ret->attrs = cur;
2497 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002498 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00002499 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002500 TODO
2501 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002502 }
2503 }
2504 child = child->next;
2505 }
2506 ctxt->flags = old_flags;
2507 return(ret);
2508}
2509
2510/**
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002511 * xmlRelaxNGParseExceptNameClass:
2512 * @ctxt: a Relax-NG parser context
2513 * @node: the except node
Daniel Veillard144fae12003-02-03 13:17:57 +00002514 * @attr: 1 if within an attribute, 0 if within an element
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002515 *
2516 * parse the content of a RelaxNG nameClass node.
2517 *
2518 * Returns the definition pointer or NULL in case of error.
2519 */
2520static xmlRelaxNGDefinePtr
Daniel Veillard144fae12003-02-03 13:17:57 +00002521xmlRelaxNGParseExceptNameClass(xmlRelaxNGParserCtxtPtr ctxt,
2522 xmlNodePtr node, int attr) {
2523 xmlRelaxNGDefinePtr ret, cur, last = NULL;
2524 xmlNodePtr child;
2525
2526 if (!IS_RELAXNG(node, "except"))
2527 return(NULL);
2528 if (node->children == NULL) {
2529 if (ctxt->error != NULL)
2530 ctxt->error(ctxt->userData,
2531 "except has no content\n");
2532 ctxt->nbErrors++;
2533 return(NULL);
2534 }
2535
2536 ret = xmlRelaxNGNewDefine(ctxt, node);
2537 if (ret == NULL)
2538 return(NULL);
2539 ret->type = XML_RELAXNG_EXCEPT;
2540 child = node->children;
2541 while (child != NULL) {
2542 cur = xmlRelaxNGNewDefine(ctxt, child);
2543 if (cur == NULL)
2544 break;
2545 if (attr)
2546 cur->type = XML_RELAXNG_ATTRIBUTE;
2547 else
2548 cur->type = XML_RELAXNG_ELEMENT;
2549
2550 if (xmlRelaxNGParseNameClass(ctxt, child, cur) == NULL) {
2551 xmlRelaxNGFreeDefine(cur);
2552 } else {
2553 if (last == NULL) {
2554 ret->content = cur;
2555 } else {
2556 last->next = cur;
2557 }
2558 last = cur;
2559 }
2560 child = child->next;
2561 }
2562
2563 return(ret);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002564}
2565
2566/**
2567 * xmlRelaxNGParseNameClass:
2568 * @ctxt: a Relax-NG parser context
2569 * @node: the nameClass node
2570 * @def: the current definition
2571 *
2572 * parse the content of a RelaxNG nameClass node.
2573 *
2574 * Returns the definition pointer or NULL in case of error.
2575 */
2576static xmlRelaxNGDefinePtr
2577xmlRelaxNGParseNameClass(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
2578 xmlRelaxNGDefinePtr def) {
2579 xmlRelaxNGDefinePtr ret = def;
2580 xmlChar *val;
2581
2582 if (IS_RELAXNG(node, "name")) {
2583 val = xmlNodeGetContent(node);
2584 ret->name = val;
2585 val = xmlGetProp(node, BAD_CAST "ns");
2586 ret->ns = val;
2587 } else if (IS_RELAXNG(node, "anyName")) {
2588 ret->name = NULL;
2589 ret->ns = NULL;
2590 if (node->children != NULL) {
2591 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00002592 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
2593 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002594 }
2595 } else if (IS_RELAXNG(node, "nsName")) {
2596 ret->name = NULL;
2597 ret->ns = xmlGetProp(node, BAD_CAST "ns");
2598 if (ret->ns == NULL) {
2599 if (ctxt->error != NULL)
2600 ctxt->error(ctxt->userData,
2601 "nsName has no ns attribute\n");
2602 ctxt->nbErrors++;
2603 }
2604 if (node->children != NULL) {
2605 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00002606 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
2607 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002608 }
2609 } else if (IS_RELAXNG(node, "choice")) {
2610 TODO
2611 } else {
2612 if (ctxt->error != NULL)
2613 ctxt->error(ctxt->userData,
2614 "expecting name, anyName, nsName or choice : got %s\n",
2615 node->name);
2616 ctxt->nbErrors++;
2617 return(NULL);
2618 }
2619 return(ret);
2620}
2621
2622/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00002623 * xmlRelaxNGParseElement:
2624 * @ctxt: a Relax-NG parser context
2625 * @node: the element node
2626 *
2627 * parse the content of a RelaxNG element node.
2628 *
2629 * Returns the definition pointer or NULL in case of error.
2630 */
2631static xmlRelaxNGDefinePtr
2632xmlRelaxNGParseElement(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2633 xmlRelaxNGDefinePtr ret, cur, last;
2634 xmlNodePtr child;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002635 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002636
2637 ret = xmlRelaxNGNewDefine(ctxt, node);
2638 if (ret == NULL)
2639 return(NULL);
2640 ret->type = XML_RELAXNG_ELEMENT;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002641 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002642 child = node->children;
2643 if (child == NULL) {
2644 if (ctxt->error != NULL)
2645 ctxt->error(ctxt->userData,
2646 "xmlRelaxNGParseElement: element has no children\n");
2647 ctxt->nbErrors++;
2648 return(ret);
2649 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002650 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
2651 if (cur != NULL)
2652 child = child->next;
2653
Daniel Veillard6eadf632003-01-23 18:29:16 +00002654 if (child == NULL) {
2655 if (ctxt->error != NULL)
2656 ctxt->error(ctxt->userData,
2657 "xmlRelaxNGParseElement: element has no content\n");
2658 ctxt->nbErrors++;
2659 return(ret);
2660 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002661 olddefine = ctxt->define;
2662 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002663 last = NULL;
2664 while (child != NULL) {
2665 cur = xmlRelaxNGParsePattern(ctxt, child);
2666 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002667 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002668 switch (cur->type) {
2669 case XML_RELAXNG_EMPTY:
2670 case XML_RELAXNG_NOT_ALLOWED:
2671 case XML_RELAXNG_TEXT:
2672 case XML_RELAXNG_ELEMENT:
2673 case XML_RELAXNG_DATATYPE:
2674 case XML_RELAXNG_VALUE:
2675 case XML_RELAXNG_LIST:
2676 case XML_RELAXNG_REF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002677 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00002678 case XML_RELAXNG_DEF:
2679 case XML_RELAXNG_ZEROORMORE:
2680 case XML_RELAXNG_ONEORMORE:
2681 case XML_RELAXNG_OPTIONAL:
2682 case XML_RELAXNG_CHOICE:
2683 case XML_RELAXNG_GROUP:
2684 case XML_RELAXNG_INTERLEAVE:
2685 if (last == NULL) {
2686 ret->content = last = cur;
2687 } else {
2688 if ((last->type == XML_RELAXNG_ELEMENT) &&
2689 (ret->content == last)) {
2690 ret->content = xmlRelaxNGNewDefine(ctxt, node);
2691 if (ret->content != NULL) {
2692 ret->content->type = XML_RELAXNG_GROUP;
2693 ret->content->content = last;
2694 } else {
2695 ret->content = last;
2696 }
2697 }
2698 last->next = cur;
2699 last = cur;
2700 }
2701 break;
2702 case XML_RELAXNG_ATTRIBUTE:
2703 cur->next = ret->attrs;
2704 ret->attrs = cur;
2705 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002706 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00002707 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002708 TODO
2709 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002710 }
2711 }
2712 child = child->next;
2713 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002714 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002715 return(ret);
2716}
2717
2718/**
2719 * xmlRelaxNGParsePatterns:
2720 * @ctxt: a Relax-NG parser context
2721 * @nodes: list of nodes
Daniel Veillard154877e2003-01-30 12:17:05 +00002722 * @group: use an implicit <group> for elements
Daniel Veillard6eadf632003-01-23 18:29:16 +00002723 *
2724 * parse the content of a RelaxNG start node.
2725 *
2726 * Returns the definition pointer or NULL in case of error.
2727 */
2728static xmlRelaxNGDefinePtr
Daniel Veillard154877e2003-01-30 12:17:05 +00002729xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes,
2730 int group) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002731 xmlRelaxNGDefinePtr def = NULL, last = NULL, cur, parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002732
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002733 parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002734 while (nodes != NULL) {
2735 if (IS_RELAXNG(nodes, "element")) {
2736 cur = xmlRelaxNGParseElement(ctxt, nodes);
2737 if (def == NULL) {
2738 def = last = cur;
2739 } else {
Daniel Veillard154877e2003-01-30 12:17:05 +00002740 if ((group == 1) && (def->type == XML_RELAXNG_ELEMENT) &&
2741 (def == last)) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00002742 def = xmlRelaxNGNewDefine(ctxt, nodes);
2743 def->type = XML_RELAXNG_GROUP;
2744 def->content = last;
2745 }
2746 last->next = cur;
2747 last = cur;
2748 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002749 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002750 } else {
2751 cur = xmlRelaxNGParsePattern(ctxt, nodes);
2752 if (def == NULL) {
2753 def = last = cur;
2754 } else {
2755 last->next = cur;
2756 last = cur;
2757 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002758 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002759 }
2760 nodes = nodes->next;
2761 }
2762 return(def);
2763}
2764
2765/**
2766 * xmlRelaxNGParseStart:
2767 * @ctxt: a Relax-NG parser context
2768 * @nodes: start children nodes
2769 *
2770 * parse the content of a RelaxNG start node.
2771 *
2772 * Returns 0 in case of success, -1 in case of error
2773 */
2774static int
2775xmlRelaxNGParseStart(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
2776 int ret = 0;
2777 xmlRelaxNGDefinePtr def = NULL;
2778
2779 while (nodes != NULL) {
2780 if (IS_RELAXNG(nodes, "empty")) {
2781 TODO
2782 xmlElemDump(stdout, nodes->doc, nodes);
2783 } else if (IS_RELAXNG(nodes, "notAllowed")) {
2784 TODO
2785 xmlElemDump(stdout, nodes->doc, nodes);
2786 } else {
Daniel Veillard154877e2003-01-30 12:17:05 +00002787 def = xmlRelaxNGParsePatterns(ctxt, nodes, 1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002788 ctxt->grammar->start = def;
2789 }
2790 nodes = nodes->next;
2791 }
2792 return(ret);
2793}
2794
2795/**
2796 * xmlRelaxNGParseGrammarContent:
2797 * @ctxt: a Relax-NG parser context
2798 * @nodes: grammar children nodes
2799 *
2800 * parse the content of a RelaxNG grammar node.
2801 *
2802 * Returns 0 in case of success, -1 in case of error
2803 */
2804static int
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002805xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes)
Daniel Veillard6eadf632003-01-23 18:29:16 +00002806{
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002807 int ret = 0, tmp;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002808
2809 if (nodes == NULL) {
2810 if (ctxt->error != NULL)
2811 ctxt->error(ctxt->userData,
2812 "grammar has no children\n");
2813 ctxt->nbErrors++;
2814 return(-1);
2815 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002816 while (nodes != NULL) {
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002817 if (IS_RELAXNG(nodes, "start")) {
2818 if (nodes->children == NULL) {
2819 if (ctxt->error != NULL)
2820 ctxt->error(ctxt->userData,
2821 "grammar has no children\n");
2822 ctxt->nbErrors++;
2823 } else {
2824 tmp = xmlRelaxNGParseStart(ctxt, nodes->children);
2825 if (tmp != 0)
2826 ret = -1;
2827 }
2828 } else if (IS_RELAXNG(nodes, "define")) {
2829 tmp = xmlRelaxNGParseDefine(ctxt, nodes);
2830 if (tmp != 0)
2831 ret = -1;
2832 } else if (IS_RELAXNG(nodes, "include")) {
2833 tmp = xmlRelaxNGParseInclude(ctxt, nodes);
2834 if (tmp != 0)
2835 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002836 } else {
2837 if (ctxt->error != NULL)
2838 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002839 "grammar has unexpected child %s\n", nodes->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002840 ctxt->nbErrors++;
2841 ret = -1;
2842 }
2843 nodes = nodes->next;
2844 }
2845 return (ret);
2846}
2847
2848/**
2849 * xmlRelaxNGCheckReference:
2850 * @ref: the ref
2851 * @ctxt: a Relax-NG parser context
2852 * @name: the name associated to the defines
2853 *
2854 * Applies the 4.17. combine attribute rule for all the define
2855 * element of a given grammar using the same name.
2856 */
2857static void
2858xmlRelaxNGCheckReference(xmlRelaxNGDefinePtr ref,
2859 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
2860 xmlRelaxNGGrammarPtr grammar;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002861 xmlRelaxNGDefinePtr def, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002862
2863 grammar = ctxt->grammar;
2864 if (grammar == NULL) {
2865 if (ctxt->error != NULL)
2866 ctxt->error(ctxt->userData,
2867 "Internal error: no grammar in CheckReference %s\n",
2868 name);
2869 ctxt->nbErrors++;
2870 return;
2871 }
2872 if (ref->content != NULL) {
2873 if (ctxt->error != NULL)
2874 ctxt->error(ctxt->userData,
2875 "Internal error: reference has content in CheckReference %s\n",
2876 name);
2877 ctxt->nbErrors++;
2878 return;
2879 }
2880 if (grammar->defs != NULL) {
2881 def = xmlHashLookup(grammar->defs, name);
2882 if (def != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00002883 cur = ref;
2884 while (cur != NULL) {
2885 cur->content = def;
2886 cur = cur->nextHash;
2887 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002888 } else {
2889 TODO
2890 }
2891 }
2892 /*
2893 * TODO: make a closure and verify there is no loop !
2894 */
2895}
2896
2897/**
2898 * xmlRelaxNGCheckCombine:
2899 * @define: the define(s) list
2900 * @ctxt: a Relax-NG parser context
2901 * @name: the name associated to the defines
2902 *
2903 * Applies the 4.17. combine attribute rule for all the define
2904 * element of a given grammar using the same name.
2905 */
2906static void
2907xmlRelaxNGCheckCombine(xmlRelaxNGDefinePtr define,
2908 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
2909 xmlChar *combine;
2910 int choiceOrInterleave = -1;
2911 int missing = 0;
2912 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
2913
2914 if (define->nextHash == NULL)
2915 return;
2916 cur = define;
2917 while (cur != NULL) {
2918 combine = xmlGetProp(cur->node, BAD_CAST "combine");
2919 if (combine != NULL) {
2920 if (xmlStrEqual(combine, BAD_CAST "choice")) {
2921 if (choiceOrInterleave == -1)
2922 choiceOrInterleave = 1;
2923 else if (choiceOrInterleave == 0) {
2924 if (ctxt->error != NULL)
2925 ctxt->error(ctxt->userData,
2926 "Defines for %s use both 'choice' and 'interleave'\n",
2927 name);
2928 ctxt->nbErrors++;
2929 }
Daniel Veillard154877e2003-01-30 12:17:05 +00002930 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00002931 if (choiceOrInterleave == -1)
2932 choiceOrInterleave = 0;
2933 else if (choiceOrInterleave == 1) {
2934 if (ctxt->error != NULL)
2935 ctxt->error(ctxt->userData,
2936 "Defines for %s use both 'choice' and 'interleave'\n",
2937 name);
2938 ctxt->nbErrors++;
2939 }
2940 } else {
2941 if (ctxt->error != NULL)
2942 ctxt->error(ctxt->userData,
2943 "Defines for %s use unknown combine value '%s''\n",
2944 name, combine);
2945 ctxt->nbErrors++;
2946 }
2947 xmlFree(combine);
2948 } else {
2949 if (missing == 0)
2950 missing = 1;
2951 else {
2952 if (ctxt->error != NULL)
2953 ctxt->error(ctxt->userData,
2954 "Some defines for %s lacks the combine attribute\n",
2955 name);
2956 ctxt->nbErrors++;
2957 }
2958 }
2959
2960 cur = cur->nextHash;
2961 }
2962#ifdef DEBUG
2963 xmlGenericError(xmlGenericErrorContext,
2964 "xmlRelaxNGCheckCombine(): merging %s defines: %d\n",
2965 name, choiceOrInterleave);
2966#endif
2967 if (choiceOrInterleave == -1)
2968 choiceOrInterleave = 0;
2969 cur = xmlRelaxNGNewDefine(ctxt, define->node);
2970 if (cur == NULL)
2971 return;
2972 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00002973 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard154877e2003-01-30 12:17:05 +00002974 else
2975 cur->type = XML_RELAXNG_CHOICE;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002976 tmp = define;
2977 last = NULL;
2978 while (tmp != NULL) {
2979 if (tmp->content != NULL) {
2980 if (tmp->content->next != NULL) {
2981 /*
2982 * we need first to create a wrapper.
2983 */
2984 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
2985 if (tmp2 == NULL)
2986 break;
2987 tmp2->type = XML_RELAXNG_GROUP;
2988 tmp2->content = tmp->content;
2989 } else {
2990 tmp2 = tmp->content;
2991 }
2992 if (last == NULL) {
2993 cur->content = tmp2;
2994 } else {
2995 last->next = tmp2;
2996 }
2997 last = tmp2;
2998 tmp->content = NULL;
2999 }
3000 tmp = tmp->nextHash;
3001 }
3002 define->content = cur;
Daniel Veillard154877e2003-01-30 12:17:05 +00003003 if (choiceOrInterleave == 0) {
3004 if (ctxt->interleaves == NULL)
3005 ctxt->interleaves = xmlHashCreate(10);
3006 if (ctxt->interleaves == NULL) {
3007 if (ctxt->error != NULL)
3008 ctxt->error(ctxt->userData,
3009 "Failed to create interleaves hash table\n");
3010 ctxt->nbErrors++;
3011 } else {
3012 char tmpname[32];
3013
3014 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
3015 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
3016 if (ctxt->error != NULL)
3017 ctxt->error(ctxt->userData,
3018 "Failed to add %s to hash table\n", tmpname);
3019 ctxt->nbErrors++;
3020 }
3021 }
3022 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003023}
3024
3025/**
3026 * xmlRelaxNGCombineStart:
3027 * @ctxt: a Relax-NG parser context
3028 * @grammar: the grammar
3029 *
3030 * Applies the 4.17. combine rule for all the start
3031 * element of a given grammar.
3032 */
3033static void
3034xmlRelaxNGCombineStart(xmlRelaxNGParserCtxtPtr ctxt,
3035 xmlRelaxNGGrammarPtr grammar) {
3036 xmlRelaxNGDefinePtr starts;
3037 xmlChar *combine;
3038 int choiceOrInterleave = -1;
3039 int missing = 0;
3040 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
3041
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003042 starts = grammar->startList;
3043 if ((starts == NULL) || (starts->nextHash == NULL))
Daniel Veillard6eadf632003-01-23 18:29:16 +00003044 return;
3045 cur = starts;
3046 while (cur != NULL) {
3047 combine = xmlGetProp(cur->node, BAD_CAST "combine");
3048 if (combine != NULL) {
3049 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3050 if (choiceOrInterleave == -1)
3051 choiceOrInterleave = 1;
3052 else if (choiceOrInterleave == 0) {
3053 if (ctxt->error != NULL)
3054 ctxt->error(ctxt->userData,
3055 "<start> use both 'choice' and 'interleave'\n");
3056 ctxt->nbErrors++;
3057 }
3058 } else if (xmlStrEqual(combine, BAD_CAST "choice")) {
3059 if (choiceOrInterleave == -1)
3060 choiceOrInterleave = 0;
3061 else if (choiceOrInterleave == 1) {
3062 if (ctxt->error != NULL)
3063 ctxt->error(ctxt->userData,
3064 "<start> use both 'choice' and 'interleave'\n");
3065 ctxt->nbErrors++;
3066 }
3067 } else {
3068 if (ctxt->error != NULL)
3069 ctxt->error(ctxt->userData,
3070 "<start> uses unknown combine value '%s''\n", combine);
3071 ctxt->nbErrors++;
3072 }
3073 xmlFree(combine);
3074 } else {
3075 if (missing == 0)
3076 missing = 1;
3077 else {
3078 if (ctxt->error != NULL)
3079 ctxt->error(ctxt->userData,
3080 "Some <start> elements lacks the combine attribute\n");
3081 ctxt->nbErrors++;
3082 }
3083 }
3084
3085 cur = cur->nextHash;
3086 }
3087#ifdef DEBUG
3088 xmlGenericError(xmlGenericErrorContext,
3089 "xmlRelaxNGCombineStart(): merging <start>: %d\n",
3090 choiceOrInterleave);
3091#endif
3092 if (choiceOrInterleave == -1)
3093 choiceOrInterleave = 0;
3094 cur = xmlRelaxNGNewDefine(ctxt, starts->node);
3095 if (cur == NULL)
3096 return;
3097 if (choiceOrInterleave == 0)
3098 cur->type = XML_RELAXNG_CHOICE;
3099 else
3100 cur->type = XML_RELAXNG_INTERLEAVE;
3101 tmp = starts;
3102 last = NULL;
3103 while (tmp != NULL) {
3104 if (tmp->content != NULL) {
3105 if (tmp->content->next != NULL) {
3106 /*
3107 * we need first to create a wrapper.
3108 */
3109 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
3110 if (tmp2 == NULL)
3111 break;
3112 tmp2->type = XML_RELAXNG_GROUP;
3113 tmp2->content = tmp->content;
3114 } else {
3115 tmp2 = tmp->content;
3116 }
3117 if (last == NULL) {
3118 cur->content = tmp2;
3119 } else {
3120 last->next = tmp2;
3121 }
3122 last = tmp2;
3123 tmp->content = NULL;
3124 }
3125 tmp = tmp->nextHash;
3126 }
3127 starts->content = cur;
3128}
3129
3130/**
3131 * xmlRelaxNGParseGrammar:
3132 * @ctxt: a Relax-NG parser context
3133 * @nodes: grammar children nodes
3134 *
3135 * parse a Relax-NG <grammar> node
3136 *
3137 * Returns the internal xmlRelaxNGGrammarPtr built or
3138 * NULL in case of error
3139 */
3140static xmlRelaxNGGrammarPtr
3141xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
3142 xmlRelaxNGGrammarPtr ret, tmp, old;
3143
Daniel Veillard6eadf632003-01-23 18:29:16 +00003144 ret = xmlRelaxNGNewGrammar(ctxt);
3145 if (ret == NULL)
3146 return(NULL);
3147
3148 /*
3149 * Link the new grammar in the tree
3150 */
3151 ret->parent = ctxt->grammar;
3152 if (ctxt->grammar != NULL) {
3153 tmp = ctxt->grammar->children;
3154 if (tmp == NULL) {
3155 ctxt->grammar->children = ret;
3156 } else {
3157 while (tmp->next != NULL)
3158 tmp = tmp->next;
3159 tmp->next = ret;
3160 }
3161 }
3162
3163 old = ctxt->grammar;
3164 ctxt->grammar = ret;
3165 xmlRelaxNGParseGrammarContent(ctxt, nodes);
3166 ctxt->grammar = ret;
3167
3168 /*
3169 * Apply 4.17 mergingd rules to defines and starts
3170 */
3171 xmlRelaxNGCombineStart(ctxt, ret);
3172 if (ret->defs != NULL) {
3173 xmlHashScan(ret->defs, (xmlHashScanner) xmlRelaxNGCheckCombine,
3174 ctxt);
3175 }
3176
3177 /*
3178 * link together defines and refs in this grammar
3179 */
3180 if (ret->refs != NULL) {
3181 xmlHashScan(ret->refs, (xmlHashScanner) xmlRelaxNGCheckReference,
3182 ctxt);
3183 }
3184 ctxt->grammar = old;
3185 return(ret);
3186}
3187
3188/**
3189 * xmlRelaxNGParseDocument:
3190 * @ctxt: a Relax-NG parser context
3191 * @node: the root node of the RelaxNG schema
3192 *
3193 * parse a Relax-NG definition resource and build an internal
3194 * xmlRelaxNG struture which can be used to validate instances.
3195 *
3196 * Returns the internal XML RelaxNG structure built or
3197 * NULL in case of error
3198 */
3199static xmlRelaxNGPtr
3200xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3201 xmlRelaxNGPtr schema = NULL;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003202 const xmlChar *olddefine;
Daniel Veillarde431a272003-01-29 23:02:33 +00003203 xmlRelaxNGGrammarPtr old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003204
3205 if ((ctxt == NULL) || (node == NULL))
3206 return (NULL);
3207
3208 schema = xmlRelaxNGNewRelaxNG(ctxt);
3209 if (schema == NULL)
3210 return(NULL);
3211
Daniel Veillard276be4a2003-01-24 01:03:34 +00003212 olddefine = ctxt->define;
3213 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003214 if (IS_RELAXNG(node, "grammar")) {
3215 schema->topgrammar = xmlRelaxNGParseGrammar(ctxt, node->children);
3216 } else {
3217 schema->topgrammar = xmlRelaxNGNewGrammar(ctxt);
3218 if (schema->topgrammar == NULL) {
3219 return(schema);
3220 }
3221 schema->topgrammar->parent = NULL;
Daniel Veillarde431a272003-01-29 23:02:33 +00003222 old = ctxt->grammar;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003223 ctxt->grammar = schema->topgrammar;
3224 xmlRelaxNGParseStart(ctxt, node);
Daniel Veillarde431a272003-01-29 23:02:33 +00003225 if (old != NULL)
3226 ctxt->grammar = old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003227 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003228 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003229
3230#ifdef DEBUG
3231 if (schema == NULL)
3232 xmlGenericError(xmlGenericErrorContext,
3233 "xmlRelaxNGParseDocument() failed\n");
3234#endif
3235
3236 return (schema);
3237}
3238
3239/************************************************************************
3240 * *
3241 * Reading RelaxNGs *
3242 * *
3243 ************************************************************************/
3244
3245/**
3246 * xmlRelaxNGNewParserCtxt:
3247 * @URL: the location of the schema
3248 *
3249 * Create an XML RelaxNGs parse context for that file/resource expected
3250 * to contain an XML RelaxNGs file.
3251 *
3252 * Returns the parser context or NULL in case of error
3253 */
3254xmlRelaxNGParserCtxtPtr
3255xmlRelaxNGNewParserCtxt(const char *URL) {
3256 xmlRelaxNGParserCtxtPtr ret;
3257
3258 if (URL == NULL)
3259 return(NULL);
3260
3261 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
3262 if (ret == NULL) {
3263 xmlGenericError(xmlGenericErrorContext,
3264 "Failed to allocate new schama parser context for %s\n", URL);
3265 return (NULL);
3266 }
3267 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
3268 ret->URL = xmlStrdup((const xmlChar *)URL);
3269 return (ret);
3270}
3271
3272/**
3273 * xmlRelaxNGNewMemParserCtxt:
3274 * @buffer: a pointer to a char array containing the schemas
3275 * @size: the size of the array
3276 *
3277 * Create an XML RelaxNGs parse context for that memory buffer expected
3278 * to contain an XML RelaxNGs file.
3279 *
3280 * Returns the parser context or NULL in case of error
3281 */
3282xmlRelaxNGParserCtxtPtr
3283xmlRelaxNGNewMemParserCtxt(const char *buffer, int size) {
3284 xmlRelaxNGParserCtxtPtr ret;
3285
3286 if ((buffer == NULL) || (size <= 0))
3287 return(NULL);
3288
3289 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
3290 if (ret == NULL) {
3291 xmlGenericError(xmlGenericErrorContext,
3292 "Failed to allocate new schama parser context\n");
3293 return (NULL);
3294 }
3295 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
3296 ret->buffer = buffer;
3297 ret->size = size;
3298 return (ret);
3299}
3300
3301/**
3302 * xmlRelaxNGFreeParserCtxt:
3303 * @ctxt: the schema parser context
3304 *
3305 * Free the resources associated to the schema parser context
3306 */
3307void
3308xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxtPtr ctxt) {
3309 if (ctxt == NULL)
3310 return;
3311 if (ctxt->URL != NULL)
3312 xmlFree(ctxt->URL);
3313 if (ctxt->doc != NULL)
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003314 xmlFreeDoc(ctxt->document);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003315 if (ctxt->interleaves != NULL)
3316 xmlHashFree(ctxt->interleaves, NULL);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003317 if (ctxt->documents != NULL)
3318 xmlHashFree(ctxt->documents, (xmlHashDeallocator)
3319 xmlRelaxNGFreeDocument);
3320 if (ctxt->docTab != NULL)
3321 xmlFree(ctxt->docTab);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003322 if (ctxt->incTab != NULL)
3323 xmlFree(ctxt->incTab);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003324 xmlFree(ctxt);
3325}
3326
Daniel Veillard6eadf632003-01-23 18:29:16 +00003327/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003328 * xmlRelaxNGCleanupDoc:
3329 * @ctxt: a Relax-NG parser context
3330 * @doc: an xmldocPtr document pointer
Daniel Veillard6eadf632003-01-23 18:29:16 +00003331 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003332 * Cleanup the document from unwanted nodes for parsing, resolve
3333 * Include and externalRef lookups.
Daniel Veillard6eadf632003-01-23 18:29:16 +00003334 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003335 * Returns the cleaned up document or NULL in case of error
Daniel Veillard6eadf632003-01-23 18:29:16 +00003336 */
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003337static xmlDocPtr
3338xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt, xmlDocPtr doc) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003339 xmlNodePtr root, cur, delete;
3340
Daniel Veillard6eadf632003-01-23 18:29:16 +00003341 /*
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003342 * Extract the root
Daniel Veillard6eadf632003-01-23 18:29:16 +00003343 */
3344 root = xmlDocGetRootElement(doc);
3345 if (root == NULL) {
3346 if (ctxt->error != NULL)
3347 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
3348 ctxt->URL);
3349 ctxt->nbErrors++;
3350 return (NULL);
3351 }
3352
3353 /*
3354 * Remove all the blank text nodes
3355 */
3356 delete = NULL;
3357 cur = root;
3358 while (cur != NULL) {
3359 if (delete != NULL) {
3360 xmlUnlinkNode(delete);
3361 xmlFreeNode(delete);
3362 delete = NULL;
3363 }
3364 if (cur->type == XML_ELEMENT_NODE) {
3365 /*
3366 * Simplification 4.1. Annotations
3367 */
3368 if ((cur->ns == NULL) ||
3369 (!xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
3370 delete = cur;
3371 goto skip_children;
3372 } else {
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003373 if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003374 xmlChar *href, *ns, *base, *URL;
3375 xmlRelaxNGDocumentPtr docu;
3376
3377 ns = xmlGetProp(cur, BAD_CAST "ns");
3378 href = xmlGetProp(cur, BAD_CAST "href");
3379 if (href == NULL) {
3380 if (ctxt->error != NULL)
3381 ctxt->error(ctxt->userData,
3382 "xmlRelaxNGParse: externalRef has no href attribute\n");
3383 ctxt->nbErrors++;
3384 delete = cur;
3385 goto skip_children;
3386 }
3387 base = xmlNodeGetBase(cur->doc, cur);
3388 URL = xmlBuildURI(href, base);
3389 if (URL == NULL) {
3390 if (ctxt->error != NULL)
3391 ctxt->error(ctxt->userData,
3392 "Failed to compute URL for externalRef %s\n", href);
3393 ctxt->nbErrors++;
3394 if (href != NULL)
3395 xmlFree(href);
3396 if (base != NULL)
3397 xmlFree(base);
3398 delete = cur;
3399 goto skip_children;
3400 }
3401 if (href != NULL)
3402 xmlFree(href);
3403 if (base != NULL)
3404 xmlFree(base);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003405 docu = xmlRelaxNGLoadExternalRef(ctxt, URL, ns);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003406 if (docu == NULL) {
3407 if (ctxt->error != NULL)
3408 ctxt->error(ctxt->userData,
3409 "Failed to load externalRef %s\n", URL);
3410 ctxt->nbErrors++;
3411 xmlFree(URL);
3412 delete = cur;
3413 goto skip_children;
3414 }
3415 xmlFree(URL);
3416 cur->_private = docu;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003417 } else if (xmlStrEqual(cur->name, BAD_CAST "include")) {
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003418 xmlChar *href, *base, *URL;
3419 xmlRelaxNGIncludePtr incl;
3420
3421 href = xmlGetProp(cur, BAD_CAST "href");
3422 if (href == NULL) {
3423 if (ctxt->error != NULL)
3424 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003425 "xmlRelaxNGParse: include has no href attribute\n");
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003426 ctxt->nbErrors++;
3427 delete = cur;
3428 goto skip_children;
3429 }
3430 base = xmlNodeGetBase(cur->doc, cur);
3431 URL = xmlBuildURI(href, base);
3432 if (URL == NULL) {
3433 if (ctxt->error != NULL)
3434 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003435 "Failed to compute URL for include %s\n", href);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003436 ctxt->nbErrors++;
3437 if (href != NULL)
3438 xmlFree(href);
3439 if (base != NULL)
3440 xmlFree(base);
3441 delete = cur;
3442 goto skip_children;
3443 }
3444 if (href != NULL)
3445 xmlFree(href);
3446 if (base != NULL)
3447 xmlFree(base);
3448 incl = xmlRelaxNGLoadInclude(ctxt, URL, cur);
3449 if (incl == NULL) {
3450 if (ctxt->error != NULL)
3451 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003452 "Failed to load include %s\n", URL);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003453 ctxt->nbErrors++;
3454 xmlFree(URL);
3455 delete = cur;
3456 goto skip_children;
3457 }
3458 xmlFree(URL);
3459 cur->_private = incl;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003460 } else if ((xmlStrEqual(cur->name, BAD_CAST "element")) ||
3461 (xmlStrEqual(cur->name, BAD_CAST "attribute"))) {
3462 xmlChar *name;
3463 xmlNodePtr text = NULL;
3464
3465 /*
3466 * Simplification 4.8. name attribute of element
3467 * and attribute elements
3468 */
3469 name = xmlGetProp(cur, BAD_CAST "name");
3470 if (name != NULL) {
3471 if (cur->children == NULL) {
3472 text = xmlNewChild(cur, cur->ns, BAD_CAST "name",
3473 name);
3474 } else {
3475 xmlNodePtr node;
3476 node = xmlNewNode(cur->ns, BAD_CAST "name");
3477 if (node != NULL) {
3478 xmlAddPrevSibling(cur->children, node);
3479 text = xmlNewText(name);
3480 xmlAddChild(node, text);
3481 text = node;
3482 }
3483 }
3484 xmlUnsetProp(cur, BAD_CAST "name");
3485 xmlFree(name);
3486 }
3487 if (xmlStrEqual(cur->name, BAD_CAST "attribute")) {
3488 if (text == NULL) {
3489 text = cur->children;
3490 while (text != NULL) {
3491 if ((text->type == XML_ELEMENT_NODE) &&
3492 (xmlStrEqual(text->name, BAD_CAST "name")))
3493 break;
3494 text = text->next;
3495 }
3496 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003497 if (text != NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003498 xmlSetProp(text, BAD_CAST "ns", BAD_CAST "");
3499 }
3500 }
3501 } else if ((xmlStrEqual(cur->name, BAD_CAST "name")) ||
3502 (xmlStrEqual(cur->name, BAD_CAST "nsName")) ||
3503 (xmlStrEqual(cur->name, BAD_CAST "value"))) {
3504 /*
3505 * Simplification 4.8. name attribute of element
3506 * and attribute elements
3507 */
3508 if (xmlHasProp(cur, BAD_CAST "ns") == NULL) {
3509 xmlNodePtr node;
3510 xmlChar *ns = NULL;
3511
3512 node = cur->parent;
3513 while ((node != NULL) &&
3514 (node->type == XML_ELEMENT_NODE)) {
3515 ns = xmlGetProp(node, BAD_CAST "ns");
3516 if (ns != NULL) {
3517 break;
3518 }
3519 node = node->parent;
3520 }
3521 if (ns == NULL) {
3522 xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
3523 } else {
3524 xmlSetProp(cur, BAD_CAST "ns", ns);
3525 xmlFree(ns);
3526 }
3527 }
3528 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
3529 xmlChar *name, *local, *prefix;
3530
3531 /*
3532 * Simplification: 4.10. QNames
3533 */
3534 name = xmlNodeGetContent(cur);
3535 if (name != NULL) {
3536 local = xmlSplitQName2(name, &prefix);
3537 if (local != NULL) {
3538 xmlNsPtr ns;
3539
3540 ns = xmlSearchNs(cur->doc, cur, prefix);
3541 if (ns == NULL) {
3542 if (ctxt->error != NULL)
3543 ctxt->error(ctxt->userData,
3544 "xmlRelaxNGParse: no namespace for prefix %s\n", prefix);
3545 ctxt->nbErrors++;
3546 } else {
3547 xmlSetProp(cur, BAD_CAST "ns", ns->href);
3548 xmlNodeSetContent(cur, local);
3549 }
3550 xmlFree(local);
3551 xmlFree(prefix);
3552 }
3553 xmlFree(name);
3554 }
3555 }
3556 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003557 /*
3558 * Thisd is not an else since "include" is transformed
3559 * into a div
3560 */
3561 if (xmlStrEqual(cur->name, BAD_CAST "div")) {
3562 /*
3563 * implements rule 4.11
3564 */
3565 xmlNodePtr child, ins, tmp;
3566
3567 child = cur->children;
3568 ins = child;
3569 while (child != NULL) {
3570 tmp = child->next;
3571 xmlUnlinkNode(child);
3572 ins = xmlAddNextSibling(ins, child);
3573 child = tmp;
3574 }
3575 delete = cur;
3576 goto skip_children;
3577 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003578 }
3579 }
3580 /*
3581 * Simplification 4.2 whitespaces
3582 */
3583 else if (cur->type == XML_TEXT_NODE) {
3584 if (IS_BLANK_NODE(cur)) {
3585 if (cur->parent->type == XML_ELEMENT_NODE) {
3586 if ((!xmlStrEqual(cur->parent->name, BAD_CAST "value")) &&
3587 (!xmlStrEqual(cur->parent->name, BAD_CAST "param")))
3588 delete = cur;
3589 } else {
3590 delete = cur;
3591 goto skip_children;
3592 }
3593 }
3594 } else if (cur->type != XML_CDATA_SECTION_NODE) {
3595 delete = cur;
3596 goto skip_children;
3597 }
3598
3599 /*
3600 * Skip to next node
3601 */
3602 if (cur->children != NULL) {
3603 if ((cur->children->type != XML_ENTITY_DECL) &&
3604 (cur->children->type != XML_ENTITY_REF_NODE) &&
3605 (cur->children->type != XML_ENTITY_NODE)) {
3606 cur = cur->children;
3607 continue;
3608 }
3609 }
3610skip_children:
3611 if (cur->next != NULL) {
3612 cur = cur->next;
3613 continue;
3614 }
3615
3616 do {
3617 cur = cur->parent;
3618 if (cur == NULL)
3619 break;
3620 if (cur == root) {
3621 cur = NULL;
3622 break;
3623 }
3624 if (cur->next != NULL) {
3625 cur = cur->next;
3626 break;
3627 }
3628 } while (cur != NULL);
3629 }
3630 if (delete != NULL) {
3631 xmlUnlinkNode(delete);
3632 xmlFreeNode(delete);
3633 delete = NULL;
3634 }
3635
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003636 return(doc);
3637}
3638
3639/**
3640 * xmlRelaxNGParse:
3641 * @ctxt: a Relax-NG parser context
3642 *
3643 * parse a schema definition resource and build an internal
3644 * XML Shema struture which can be used to validate instances.
3645 * *WARNING* this interface is highly subject to change
3646 *
3647 * Returns the internal XML RelaxNG structure built from the resource or
3648 * NULL in case of error
3649 */
3650xmlRelaxNGPtr
3651xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt)
3652{
3653 xmlRelaxNGPtr ret = NULL;
3654 xmlDocPtr doc;
3655 xmlNodePtr root;
3656
3657 xmlRelaxNGInitTypes();
3658
3659 if (ctxt == NULL)
3660 return (NULL);
3661
3662 /*
3663 * First step is to parse the input document into an DOM/Infoset
3664 */
3665 if (ctxt->URL != NULL) {
3666 doc = xmlParseFile((const char *) ctxt->URL);
3667 if (doc == NULL) {
3668 if (ctxt->error != NULL)
3669 ctxt->error(ctxt->userData,
3670 "xmlRelaxNGParse: could not load %s\n", ctxt->URL);
3671 ctxt->nbErrors++;
3672 return (NULL);
3673 }
3674 } else if (ctxt->buffer != NULL) {
3675 doc = xmlParseMemory(ctxt->buffer, ctxt->size);
3676 if (doc == NULL) {
3677 if (ctxt->error != NULL)
3678 ctxt->error(ctxt->userData,
3679 "xmlRelaxNGParse: could not parse schemas\n");
3680 ctxt->nbErrors++;
3681 return (NULL);
3682 }
3683 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
3684 ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
3685 } else {
3686 if (ctxt->error != NULL)
3687 ctxt->error(ctxt->userData,
3688 "xmlRelaxNGParse: nothing to parse\n");
3689 ctxt->nbErrors++;
3690 return (NULL);
3691 }
3692 ctxt->document = doc;
3693
3694 /*
3695 * Some preprocessing of the document content
3696 */
3697 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
3698 if (doc == NULL) {
3699 xmlFreeDoc(ctxt->document);
3700 ctxt->document = NULL;
3701 return(NULL);
3702 }
3703
Daniel Veillard6eadf632003-01-23 18:29:16 +00003704 /*
3705 * Then do the parsing for good
3706 */
3707 root = xmlDocGetRootElement(doc);
3708 if (root == NULL) {
3709 if (ctxt->error != NULL)
3710 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
3711 ctxt->URL);
3712 ctxt->nbErrors++;
3713 return (NULL);
3714 }
3715 ret = xmlRelaxNGParseDocument(ctxt, root);
3716 if (ret == NULL)
3717 return(NULL);
3718
3719 /*
3720 * Check the ref/defines links
3721 */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003722 /*
3723 * try to preprocess interleaves
3724 */
3725 if (ctxt->interleaves != NULL) {
3726 xmlHashScan(ctxt->interleaves,
3727 (xmlHashScanner)xmlRelaxNGComputeInterleaves, ctxt);
3728 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003729
3730 /*
3731 * if there was a parsing error return NULL
3732 */
3733 if (ctxt->nbErrors > 0) {
3734 xmlRelaxNGFree(ret);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003735 ctxt->document = NULL;
3736 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003737 return(NULL);
3738 }
3739
3740 /*
3741 * Transfer the pointer for cleanup at the schema level.
3742 */
3743 ret->doc = doc;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003744 ctxt->document = NULL;
3745 ret->documents = ctxt->documents;
3746 ctxt->documents = NULL;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003747 ret->includes = ctxt->includes;
3748 ctxt->includes = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003749
3750 return (ret);
3751}
3752
3753/**
3754 * xmlRelaxNGSetParserErrors:
3755 * @ctxt: a Relax-NG validation context
3756 * @err: the error callback
3757 * @warn: the warning callback
3758 * @ctx: contextual data for the callbacks
3759 *
3760 * Set the callback functions used to handle errors for a validation context
3761 */
3762void
3763xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
3764 xmlRelaxNGValidityErrorFunc err,
3765 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
3766 if (ctxt == NULL)
3767 return;
3768 ctxt->error = err;
3769 ctxt->warning = warn;
3770 ctxt->userData = ctx;
3771}
3772/************************************************************************
3773 * *
3774 * Dump back a compiled form *
3775 * *
3776 ************************************************************************/
3777static void xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define);
3778
3779/**
3780 * xmlRelaxNGDumpDefines:
3781 * @output: the file output
3782 * @defines: a list of define structures
3783 *
3784 * Dump a RelaxNG structure back
3785 */
3786static void
3787xmlRelaxNGDumpDefines(FILE * output, xmlRelaxNGDefinePtr defines) {
3788 while (defines != NULL) {
3789 xmlRelaxNGDumpDefine(output, defines);
3790 defines = defines->next;
3791 }
3792}
3793
3794/**
3795 * xmlRelaxNGDumpDefine:
3796 * @output: the file output
3797 * @define: a define structure
3798 *
3799 * Dump a RelaxNG structure back
3800 */
3801static void
3802xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define) {
3803 if (define == NULL)
3804 return;
3805 switch(define->type) {
3806 case XML_RELAXNG_EMPTY:
3807 fprintf(output, "<empty/>\n");
3808 break;
3809 case XML_RELAXNG_NOT_ALLOWED:
3810 fprintf(output, "<notAllowed/>\n");
3811 break;
3812 case XML_RELAXNG_TEXT:
3813 fprintf(output, "<text/>\n");
3814 break;
3815 case XML_RELAXNG_ELEMENT:
3816 fprintf(output, "<element>\n");
3817 if (define->name != NULL) {
3818 fprintf(output, "<name");
3819 if (define->ns != NULL)
3820 fprintf(output, " ns=\"%s\"", define->ns);
3821 fprintf(output, ">%s</name>\n", define->name);
3822 }
3823 xmlRelaxNGDumpDefines(output, define->attrs);
3824 xmlRelaxNGDumpDefines(output, define->content);
3825 fprintf(output, "</element>\n");
3826 break;
3827 case XML_RELAXNG_LIST:
3828 fprintf(output, "<list>\n");
3829 xmlRelaxNGDumpDefines(output, define->content);
3830 fprintf(output, "</list>\n");
3831 break;
3832 case XML_RELAXNG_ONEORMORE:
3833 fprintf(output, "<oneOrMore>\n");
3834 xmlRelaxNGDumpDefines(output, define->content);
3835 fprintf(output, "</oneOrMore>\n");
3836 break;
3837 case XML_RELAXNG_ZEROORMORE:
3838 fprintf(output, "<zeroOrMore>\n");
3839 xmlRelaxNGDumpDefines(output, define->content);
3840 fprintf(output, "</zeroOrMore>\n");
3841 break;
3842 case XML_RELAXNG_CHOICE:
3843 fprintf(output, "<choice>\n");
3844 xmlRelaxNGDumpDefines(output, define->content);
3845 fprintf(output, "</choice>\n");
3846 break;
3847 case XML_RELAXNG_GROUP:
3848 fprintf(output, "<group>\n");
3849 xmlRelaxNGDumpDefines(output, define->content);
3850 fprintf(output, "</group>\n");
3851 break;
3852 case XML_RELAXNG_INTERLEAVE:
3853 fprintf(output, "<interleave>\n");
3854 xmlRelaxNGDumpDefines(output, define->content);
3855 fprintf(output, "</interleave>\n");
3856 break;
3857 case XML_RELAXNG_OPTIONAL:
3858 fprintf(output, "<optional>\n");
3859 xmlRelaxNGDumpDefines(output, define->content);
3860 fprintf(output, "</optional>\n");
3861 break;
3862 case XML_RELAXNG_ATTRIBUTE:
3863 fprintf(output, "<attribute>\n");
3864 xmlRelaxNGDumpDefines(output, define->content);
3865 fprintf(output, "</attribute>\n");
3866 break;
3867 case XML_RELAXNG_DEF:
3868 fprintf(output, "<define");
3869 if (define->name != NULL)
3870 fprintf(output, " name=\"%s\"", define->name);
3871 fprintf(output, ">\n");
3872 xmlRelaxNGDumpDefines(output, define->content);
3873 fprintf(output, "</define>\n");
3874 break;
3875 case XML_RELAXNG_REF:
3876 fprintf(output, "<ref");
3877 if (define->name != NULL)
3878 fprintf(output, " name=\"%s\"", define->name);
3879 fprintf(output, ">\n");
3880 xmlRelaxNGDumpDefines(output, define->content);
3881 fprintf(output, "</ref>\n");
3882 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003883 case XML_RELAXNG_EXTERNALREF:
Daniel Veillarde431a272003-01-29 23:02:33 +00003884 fprintf(output, "<externalRef");
3885 xmlRelaxNGDumpDefines(output, define->content);
3886 fprintf(output, "</externalRef>\n");
3887 break;
3888 case XML_RELAXNG_DATATYPE:
Daniel Veillard6eadf632003-01-23 18:29:16 +00003889 case XML_RELAXNG_VALUE:
3890 TODO
3891 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003892 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00003893 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003894 TODO
3895 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003896 }
3897}
3898
3899/**
3900 * xmlRelaxNGDumpGrammar:
3901 * @output: the file output
3902 * @grammar: a grammar structure
3903 * @top: is this a top grammar
3904 *
3905 * Dump a RelaxNG structure back
3906 */
3907static void
3908xmlRelaxNGDumpGrammar(FILE * output, xmlRelaxNGGrammarPtr grammar, int top)
3909{
3910 if (grammar == NULL)
3911 return;
3912
3913 fprintf(output, "<grammar");
3914 if (top)
3915 fprintf(output,
3916 " xmlns=\"http://relaxng.org/ns/structure/1.0\"");
3917 switch(grammar->combine) {
3918 case XML_RELAXNG_COMBINE_UNDEFINED:
3919 break;
3920 case XML_RELAXNG_COMBINE_CHOICE:
3921 fprintf(output, " combine=\"choice\"");
3922 break;
3923 case XML_RELAXNG_COMBINE_INTERLEAVE:
3924 fprintf(output, " combine=\"interleave\"");
3925 break;
3926 default:
3927 fprintf(output, " <!-- invalid combine value -->");
3928 }
3929 fprintf(output, ">\n");
3930 if (grammar->start == NULL) {
3931 fprintf(output, " <!-- grammar had no start -->");
3932 } else {
3933 fprintf(output, "<start>\n");
3934 xmlRelaxNGDumpDefine(output, grammar->start);
3935 fprintf(output, "</start>\n");
3936 }
3937 /* TODO ? Dump the defines ? */
3938 fprintf(output, "</grammar>\n");
3939}
3940
3941/**
3942 * xmlRelaxNGDump:
3943 * @output: the file output
3944 * @schema: a schema structure
3945 *
3946 * Dump a RelaxNG structure back
3947 */
3948void
3949xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema)
3950{
3951 if (schema == NULL) {
3952 fprintf(output, "RelaxNG empty or failed to compile\n");
3953 return;
3954 }
3955 fprintf(output, "RelaxNG: ");
3956 if (schema->doc == NULL) {
3957 fprintf(output, "no document\n");
3958 } else if (schema->doc->URL != NULL) {
3959 fprintf(output, "%s\n", schema->doc->URL);
3960 } else {
3961 fprintf(output, "\n");
3962 }
3963 if (schema->topgrammar == NULL) {
3964 fprintf(output, "RelaxNG has no top grammar\n");
3965 return;
3966 }
3967 xmlRelaxNGDumpGrammar(output, schema->topgrammar, 1);
3968}
3969
3970/************************************************************************
3971 * *
3972 * Validation implementation *
3973 * *
3974 ************************************************************************/
3975static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
3976 xmlRelaxNGDefinePtr define);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00003977static int xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
3978 xmlRelaxNGDefinePtr define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003979
3980/**
3981 * xmlRelaxNGSkipIgnored:
3982 * @ctxt: a schema validation context
3983 * @node: the top node.
3984 *
3985 * Skip ignorable nodes in that context
3986 *
3987 * Returns the new sibling or NULL in case of error.
3988 */
3989static xmlNodePtr
3990xmlRelaxNGSkipIgnored(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
3991 xmlNodePtr node) {
3992 /*
3993 * TODO complete and handle entities
3994 */
3995 while ((node != NULL) &&
3996 ((node->type == XML_COMMENT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00003997 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00003998 ((node->type == XML_TEXT_NODE) &&
3999 (IS_BLANK_NODE(node))))) {
4000 node = node->next;
4001 }
4002 return(node);
4003}
4004
4005/**
Daniel Veillardedc91922003-01-26 00:52:04 +00004006 * xmlRelaxNGNormalize:
4007 * @ctxt: a schema validation context
4008 * @str: the string to normalize
4009 *
4010 * Implements the normalizeWhiteSpace( s ) function from
4011 * section 6.2.9 of the spec
4012 *
4013 * Returns the new string or NULL in case of error.
4014 */
4015static xmlChar *
4016xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *str) {
4017 xmlChar *ret, *p;
4018 const xmlChar *tmp;
4019 int len;
4020
4021 if (str == NULL)
4022 return(NULL);
4023 tmp = str;
4024 while (*tmp != 0) tmp++;
4025 len = tmp - str;
4026
4027 ret = (xmlChar *) xmlMalloc((len + 1) * sizeof(xmlChar));
4028 if (ret == NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00004029 if (ctxt != NULL) {
4030 VALID_CTXT();
4031 VALID_ERROR("xmlRelaxNGNormalize: out of memory\n");
4032 } else {
4033 xmlGenericError(xmlGenericErrorContext,
4034 "xmlRelaxNGNormalize: out of memory\n");
4035 }
Daniel Veillardedc91922003-01-26 00:52:04 +00004036 return(NULL);
4037 }
4038 p = ret;
4039 while (IS_BLANK(*str)) str++;
4040 while (*str != 0) {
4041 if (IS_BLANK(*str)) {
4042 while (IS_BLANK(*str)) str++;
4043 if (*str == 0)
4044 break;
4045 *p++ = ' ';
4046 } else
4047 *p++ = *str++;
4048 }
4049 *p = 0;
4050 return(ret);
4051}
4052
4053/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004054 * xmlRelaxNGValidateDatatype:
4055 * @ctxt: a Relax-NG validation context
4056 * @value: the string value
4057 * @type: the datatype definition
4058 *
4059 * Validate the given value against the dataype
4060 *
4061 * Returns 0 if the validation succeeded or an error code.
4062 */
4063static int
4064xmlRelaxNGValidateDatatype(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *value,
4065 xmlRelaxNGDefinePtr define) {
4066 int ret;
4067 xmlRelaxNGTypeLibraryPtr lib;
4068
4069 if ((define == NULL) || (define->data == NULL)) {
4070 return(-1);
4071 }
4072 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
4073 if (lib->check != NULL)
4074 ret = lib->check(lib->data, define->name, value);
4075 else
4076 ret = -1;
4077 if (ret < 0) {
4078 VALID_CTXT();
4079 VALID_ERROR("Internal: failed to validate type %s\n", define->name);
4080 return(-1);
4081 } else if (ret == 1) {
4082 ret = 0;
4083 } else {
4084 VALID_CTXT();
4085 VALID_ERROR("Type %s doesn't allow value %s\n", define->name, value);
4086 return(-1);
4087 ret = -1;
4088 }
4089 return(ret);
4090}
4091
4092/**
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004093 * xmlRelaxNGNextValue:
4094 * @ctxt: a Relax-NG validation context
4095 *
4096 * Skip to the next value when validating within a list
4097 *
4098 * Returns 0 if the operation succeeded or an error code.
4099 */
4100static int
4101xmlRelaxNGNextValue(xmlRelaxNGValidCtxtPtr ctxt) {
4102 xmlChar *cur;
4103
4104 cur = ctxt->state->value;
4105 if ((cur == NULL) || (ctxt->state->endvalue == NULL)) {
4106 ctxt->state->value = NULL;
4107 return(0);
4108 }
4109 while (*cur != 0) cur++;
4110 while ((cur != ctxt->state->endvalue) && (*cur == 0)) cur++;
4111 if (cur == ctxt->state->endvalue)
4112 ctxt->state->value = NULL;
4113 else
4114 ctxt->state->value = cur;
4115 return(0);
4116}
4117
4118/**
4119 * xmlRelaxNGValidateValueList:
4120 * @ctxt: a Relax-NG validation context
4121 * @defines: the list of definitions to verify
4122 *
4123 * Validate the given set of definitions for the current value
4124 *
4125 * Returns 0 if the validation succeeded or an error code.
4126 */
4127static int
4128xmlRelaxNGValidateValueList(xmlRelaxNGValidCtxtPtr ctxt,
4129 xmlRelaxNGDefinePtr defines) {
4130 int ret = 0;
4131
4132 while (defines != NULL) {
4133 ret = xmlRelaxNGValidateValue(ctxt, defines);
4134 if (ret != 0)
4135 break;
4136 defines = defines->next;
4137 }
4138 return(ret);
4139}
4140
4141/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004142 * xmlRelaxNGValidateValue:
4143 * @ctxt: a Relax-NG validation context
4144 * @define: the definition to verify
4145 *
4146 * Validate the given definition for the current value
4147 *
4148 * Returns 0 if the validation succeeded or an error code.
4149 */
4150static int
4151xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
4152 xmlRelaxNGDefinePtr define) {
Daniel Veillardedc91922003-01-26 00:52:04 +00004153 int ret = 0, oldflags;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004154 xmlChar *value;
4155
4156 value = ctxt->state->value;
4157 switch (define->type) {
4158 case XML_RELAXNG_EMPTY:
4159 if ((value != NULL) && (value[0] != '0'))
4160 ret = -1;
4161 break;
4162 case XML_RELAXNG_TEXT:
4163 break;
Daniel Veillardedc91922003-01-26 00:52:04 +00004164 case XML_RELAXNG_VALUE: {
4165 if (!xmlStrEqual(value, define->value)) {
4166 if (define->name != NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00004167 xmlRelaxNGTypeLibraryPtr lib;
4168
4169 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
4170 if ((lib != NULL) && (lib->comp != NULL))
4171 ret = lib->comp(lib->data, define->name, value,
4172 define->value);
4173 else
4174 ret = -1;
4175 if (ret < 0) {
4176 VALID_CTXT();
4177 VALID_ERROR("Internal: failed to compare type %s\n",
4178 define->name);
4179 return(-1);
4180 } else if (ret == 1) {
4181 ret = 0;
4182 } else {
4183 ret = -1;
4184 }
Daniel Veillardedc91922003-01-26 00:52:04 +00004185 } else {
4186 xmlChar *nval, *nvalue;
4187
4188 /*
4189 * TODO: trivial optimizations are possible by
4190 * computing at compile-time
4191 */
4192 nval = xmlRelaxNGNormalize(ctxt, define->value);
4193 nvalue = xmlRelaxNGNormalize(ctxt, value);
4194
Daniel Veillardea3f3982003-01-26 19:45:18 +00004195 if ((nval == NULL) || (nvalue == NULL) ||
4196 (!xmlStrEqual(nval, nvalue)))
Daniel Veillardedc91922003-01-26 00:52:04 +00004197 ret = -1;
4198 if (nval != NULL)
4199 xmlFree(nval);
4200 if (nvalue != NULL)
4201 xmlFree(nvalue);
4202 }
4203 }
4204 break;
4205 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004206 case XML_RELAXNG_DATATYPE: {
4207 ret = xmlRelaxNGValidateDatatype(ctxt, value, define);
4208 if (ret == 0)
4209 xmlRelaxNGNextValue(ctxt);
4210
4211 break;
4212 }
4213 case XML_RELAXNG_CHOICE: {
4214 xmlRelaxNGDefinePtr list = define->content;
4215 xmlChar *oldvalue;
4216
4217 oldflags = ctxt->flags;
4218 ctxt->flags |= FLAGS_IGNORABLE;
4219
4220 oldvalue = ctxt->state->value;
4221 while (list != NULL) {
4222 ret = xmlRelaxNGValidateValue(ctxt, list);
4223 if (ret == 0) {
4224 break;
4225 }
4226 ctxt->state->value = oldvalue;
4227 list = list->next;
4228 }
4229 ctxt->flags = oldflags;
4230 break;
4231 }
4232 case XML_RELAXNG_LIST: {
4233 xmlRelaxNGDefinePtr list = define->content;
4234 xmlChar *oldvalue, *oldend, *val, *cur;
4235
4236 oldvalue = ctxt->state->value;
4237 oldend = ctxt->state->endvalue;
4238
4239 val = xmlStrdup(oldvalue);
4240 if (val == NULL) {
4241 VALID_CTXT();
4242 VALID_ERROR("Internal: no state\n");
4243 return(-1);
4244 }
4245 cur = val;
4246 while (*cur != 0) {
4247 if (IS_BLANK(*cur))
4248 *cur = 0;
4249 cur++;
4250 }
4251 ctxt->state->endvalue = cur;
4252 cur = val;
4253 while ((*cur == 0) && (cur != ctxt->state->endvalue)) cur++;
4254
4255 ctxt->state->value = cur;
4256
4257 while (list != NULL) {
4258 ret = xmlRelaxNGValidateValue(ctxt, list);
4259 if (ret != 0) {
4260 break;
4261 }
4262 list = list->next;
4263 }
4264 if ((ret == 0) && (ctxt->state->value != NULL) &&
4265 (ctxt->state->value != ctxt->state->endvalue)) {
4266 VALID_CTXT();
4267 VALID_ERROR("Extra data in list: %s\n", ctxt->state->value);
4268 ret = -1;
4269 }
4270 xmlFree(val);
4271 ctxt->state->value = oldvalue;
4272 ctxt->state->endvalue = oldend;
4273 break;
4274 }
4275 case XML_RELAXNG_ONEORMORE:
4276 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
4277 if (ret != 0) {
4278 break;
4279 }
4280 /* no break on purpose */
4281 case XML_RELAXNG_ZEROORMORE: {
4282 xmlChar *cur, *temp;
4283
4284 oldflags = ctxt->flags;
4285 ctxt->flags |= FLAGS_IGNORABLE;
4286 cur = ctxt->state->value;
4287 temp = NULL;
4288 while ((cur != NULL) && (cur != ctxt->state->endvalue) &&
4289 (temp != cur)) {
4290 temp = cur;
4291 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
4292 if (ret != 0) {
4293 ctxt->state->value = temp;
4294 ret = 0;
4295 break;
4296 }
4297 cur = ctxt->state->value;
4298 }
4299 ctxt->flags = oldflags;
4300 break;
4301 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004302 default:
4303 TODO
4304 ret = -1;
4305 }
4306 return(ret);
4307}
4308
4309/**
4310 * xmlRelaxNGValidateValueContent:
4311 * @ctxt: a Relax-NG validation context
4312 * @defines: the list of definitions to verify
4313 *
4314 * Validate the given definitions for the current value
4315 *
4316 * Returns 0 if the validation succeeded or an error code.
4317 */
4318static int
4319xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt,
4320 xmlRelaxNGDefinePtr defines) {
4321 int ret = 0;
4322
4323 while (defines != NULL) {
4324 ret = xmlRelaxNGValidateValue(ctxt, defines);
4325 if (ret != 0)
4326 break;
4327 defines = defines->next;
4328 }
4329 return(ret);
4330}
4331
4332/**
Daniel Veillard144fae12003-02-03 13:17:57 +00004333 * xmlRelaxNGAttributeMatch:
4334 * @ctxt: a Relax-NG validation context
4335 * @define: the definition to check
4336 * @prop: the attribute
4337 *
4338 * Check if the attribute matches the definition nameClass
4339 *
4340 * Returns 1 if the attribute matches, 0 if no, or -1 in case of error
4341 */
4342static int
4343xmlRelaxNGAttributeMatch(xmlRelaxNGValidCtxtPtr ctxt,
4344 xmlRelaxNGDefinePtr define,
4345 xmlAttrPtr prop) {
4346 int ret;
4347
4348 if (define->name != NULL) {
4349 if (!xmlStrEqual(define->name, prop->name))
4350 return(0);
4351 }
4352 if (define->ns != NULL) {
4353 if (define->ns[0] == 0) {
4354 if (prop->ns != NULL)
4355 return(0);
4356 } else {
4357 if ((prop->ns == NULL) ||
4358 (!xmlStrEqual(define->ns, prop->ns->href)))
4359 return(0);
4360 }
4361 }
4362 if (define->nameClass == NULL)
4363 return(1);
4364 define = define->nameClass;
4365 if (define->type == XML_RELAXNG_EXCEPT) {
4366 xmlRelaxNGDefinePtr list;
4367
4368 list = define->content;
4369 while (list != NULL) {
4370 ret = xmlRelaxNGAttributeMatch(ctxt, list, prop);
4371 if (ret == 1)
4372 return(0);
4373 if (ret < 0)
4374 return(ret);
4375 list = list->next;
4376 }
4377 } else {
4378 TODO
4379 }
4380 return(1);
4381}
4382
4383/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004384 * xmlRelaxNGValidateAttribute:
4385 * @ctxt: a Relax-NG validation context
4386 * @define: the definition to verify
4387 *
4388 * Validate the given attribute definition for that node
4389 *
4390 * Returns 0 if the validation succeeded or an error code.
4391 */
4392static int
4393xmlRelaxNGValidateAttribute(xmlRelaxNGValidCtxtPtr ctxt,
4394 xmlRelaxNGDefinePtr define) {
4395 int ret = 0, i;
4396 xmlChar *value, *oldvalue;
4397 xmlAttrPtr prop = NULL, tmp;
4398
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004399 if (ctxt->state->nbAttrLeft <= 0)
4400 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004401 if (define->name != NULL) {
4402 for (i = 0;i < ctxt->state->nbAttrs;i++) {
4403 tmp = ctxt->state->attrs[i];
4404 if ((tmp != NULL) && (xmlStrEqual(define->name, tmp->name))) {
4405 if ((((define->ns == NULL) || (define->ns[0] == 0)) &&
4406 (tmp->ns == NULL)) ||
4407 ((tmp->ns != NULL) &&
4408 (xmlStrEqual(define->ns, tmp->ns->href)))) {
4409 prop = tmp;
4410 break;
4411 }
4412 }
4413 }
4414 if (prop != NULL) {
4415 value = xmlNodeListGetString(prop->doc, prop->children, 1);
4416 oldvalue = ctxt->state->value;
4417 ctxt->state->value = value;
4418 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
4419 value = ctxt->state->value;
4420 ctxt->state->value = oldvalue;
4421 if (value != NULL)
4422 xmlFree(value);
4423 if (ret == 0) {
4424 /*
4425 * flag the attribute as processed
4426 */
4427 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004428 ctxt->state->nbAttrLeft--;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004429 }
4430 } else {
4431 ret = -1;
4432 }
4433#ifdef DEBUG
4434 xmlGenericError(xmlGenericErrorContext,
4435 "xmlRelaxNGValidateAttribute(%s): %d\n", define->name, ret);
4436#endif
4437 } else {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004438 for (i = 0;i < ctxt->state->nbAttrs;i++) {
4439 tmp = ctxt->state->attrs[i];
Daniel Veillard144fae12003-02-03 13:17:57 +00004440 if ((tmp != NULL) &&
4441 (xmlRelaxNGAttributeMatch(ctxt, define, tmp) == 1)) {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004442 prop = tmp;
4443 break;
4444 }
4445 }
4446 if (prop != NULL) {
4447 value = xmlNodeListGetString(prop->doc, prop->children, 1);
4448 oldvalue = ctxt->state->value;
4449 ctxt->state->value = value;
4450 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
4451 value = ctxt->state->value;
4452 ctxt->state->value = oldvalue;
4453 if (value != NULL)
4454 xmlFree(value);
4455 if (ret == 0) {
4456 /*
4457 * flag the attribute as processed
4458 */
4459 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004460 ctxt->state->nbAttrLeft--;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004461 }
4462 } else {
4463 ret = -1;
4464 }
4465#ifdef DEBUG
Daniel Veillard144fae12003-02-03 13:17:57 +00004466 if (define->ns != NULL) {
4467 xmlGenericError(xmlGenericErrorContext,
4468 "xmlRelaxNGValidateAttribute(nsName ns = %s): %d\n",
4469 define->ns, ret);
4470 } else {
4471 xmlGenericError(xmlGenericErrorContext,
4472 "xmlRelaxNGValidateAttribute(anyName): %d\n",
4473 ret);
4474 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004475#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00004476 }
4477
4478 return(ret);
4479}
4480
4481/**
4482 * xmlRelaxNGValidateAttributeList:
4483 * @ctxt: a Relax-NG validation context
4484 * @define: the list of definition to verify
4485 *
4486 * Validate the given node against the list of attribute definitions
4487 *
4488 * Returns 0 if the validation succeeded or an error code.
4489 */
4490static int
4491xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt,
4492 xmlRelaxNGDefinePtr defines) {
4493 int ret = 0;
4494 while (defines != NULL) {
4495 if (xmlRelaxNGValidateAttribute(ctxt, defines) != 0)
4496 ret = -1;
4497 defines = defines->next;
4498 }
4499 return(ret);
4500}
4501
4502/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004503 * xmlRelaxNGValidateTryPermutation:
4504 * @ctxt: a Relax-NG validation context
4505 * @groups: the array of groups
4506 * @nbgroups: the number of groups in the array
4507 * @array: the permutation to try
4508 * @len: the size of the set
4509 *
4510 * Try to validate a permutation for the group of definitions.
4511 *
4512 * Returns 0 if the validation succeeded or an error code.
4513 */
4514static int
4515xmlRelaxNGValidateTryPermutation(xmlRelaxNGValidCtxtPtr ctxt,
4516 xmlRelaxNGDefinePtr rule,
4517 xmlNodePtr *array, int len) {
4518 int i, ret;
4519
4520 if (len > 0) {
4521 /*
4522 * One only need the next pointer set-up to do the validation
4523 */
4524 for (i = 0;i < (len - 1);i++)
4525 array[i]->next = array[i + 1];
4526 array[i]->next = NULL;
4527
4528 /*
4529 * Now try to validate the sequence
4530 */
4531 ctxt->state->seq = array[0];
4532 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
4533 } else {
4534 ctxt->state->seq = NULL;
4535 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
4536 }
4537
4538 /*
4539 * the sequence must be fully consumed
4540 */
4541 if (ctxt->state->seq != NULL)
4542 return(-1);
4543
4544 return(ret);
4545}
4546
4547/**
4548 * xmlRelaxNGValidateWalkPermutations:
4549 * @ctxt: a Relax-NG validation context
4550 * @groups: the array of groups
4551 * @nbgroups: the number of groups in the array
4552 * @nodes: the set of nodes
4553 * @array: the current state of the parmutation
4554 * @len: the size of the set
4555 * @level: a pointer to the level variable
4556 * @k: the index in the array to fill
4557 *
4558 * Validate a set of nodes for a groups of definitions, will try the
4559 * full set of permutations
4560 *
4561 * Returns 0 if the validation succeeded or an error code.
4562 */
4563static int
4564xmlRelaxNGValidateWalkPermutations(xmlRelaxNGValidCtxtPtr ctxt,
4565 xmlRelaxNGDefinePtr rule, xmlNodePtr *nodes,
4566 xmlNodePtr *array, int len,
4567 int *level, int k) {
4568 int i, ret;
4569
4570 if ((k >= 0) && (k < len))
4571 array[k] = nodes[*level];
4572 *level = *level + 1;
4573 if (*level == len) {
4574 ret = xmlRelaxNGValidateTryPermutation(ctxt, rule, array, len);
4575 if (ret == 0)
4576 return(0);
4577 } else {
4578 for (i = 0;i < len;i++) {
4579 if (array[i] == NULL) {
4580 ret = xmlRelaxNGValidateWalkPermutations(ctxt, rule,
4581 nodes, array, len, level, i);
4582 if (ret == 0)
4583 return(0);
4584 }
4585 }
4586 }
4587 *level = *level - 1;
4588 array[k] = NULL;
4589 return(-1);
4590}
4591
4592/**
4593 * xmlRelaxNGNodeMatchesList:
4594 * @node: the node
4595 * @list: a NULL terminated array of definitions
4596 *
4597 * Check if a node can be matched by one of the definitions
4598 *
4599 * Returns 1 if matches 0 otherwise
4600 */
4601static int
4602xmlRelaxNGNodeMatchesList(xmlNodePtr node, xmlRelaxNGDefinePtr *list) {
4603 xmlRelaxNGDefinePtr cur;
4604 int i = 0;
4605
4606 if ((node == NULL) || (list == NULL))
4607 return(0);
4608
4609 cur = list[i++];
4610 while (cur != NULL) {
4611 if ((node->type == XML_ELEMENT_NODE) &&
4612 (cur->type == XML_RELAXNG_ELEMENT)) {
4613 if (cur->name == NULL) {
4614 if ((node->ns != NULL) &&
4615 (xmlStrEqual(node->ns->href, cur->ns)))
4616 return(1);
4617 } else if (xmlStrEqual(cur->name, node->name)) {
4618 if ((cur->ns == NULL) || (cur->ns[0] == 0)) {
4619 if (node->ns == NULL)
4620 return(1);
4621 } else {
4622 if ((node->ns != NULL) &&
4623 (xmlStrEqual(node->ns->href, cur->ns)))
4624 return(1);
4625 }
4626 }
4627 } else if ((node->type == XML_TEXT_NODE) &&
4628 (cur->type == XML_RELAXNG_TEXT)) {
4629 return(1);
4630 }
4631 cur = list[i++];
4632 }
4633 return(0);
4634}
4635
4636/**
4637 * xmlRelaxNGValidatePartGroup:
4638 * @ctxt: a Relax-NG validation context
4639 * @groups: the array of groups
4640 * @nbgroups: the number of groups in the array
4641 * @nodes: the set of nodes
4642 * @len: the size of the set of nodes
4643 *
4644 * Validate a set of nodes for a groups of definitions
4645 *
4646 * Returns 0 if the validation succeeded or an error code.
4647 */
4648static int
4649xmlRelaxNGValidatePartGroup(xmlRelaxNGValidCtxtPtr ctxt,
4650 xmlRelaxNGInterleaveGroupPtr *groups,
4651 int nbgroups, xmlNodePtr *nodes, int len) {
Daniel Veillardb08c9812003-01-28 23:09:49 +00004652 int level, ret = -1, i, j, k;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004653 xmlNodePtr *array = NULL, *list, oldseq;
4654 xmlRelaxNGInterleaveGroupPtr group;
4655
4656 list = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
4657 if (list == NULL) {
4658 return(-1);
4659 }
4660 array = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
4661 if (array == NULL) {
4662 xmlFree(list);
4663 return(-1);
4664 }
4665 memset(array, 0, len * sizeof(xmlNodePtr));
4666
4667 /*
4668 * Partition the elements and validate the subsets.
4669 */
4670 oldseq = ctxt->state->seq;
4671 for (i = 0;i < nbgroups;i++) {
4672 group = groups[i];
4673 if (group == NULL)
4674 continue;
4675 k = 0;
4676 for (j = 0;j < len;j++) {
4677 if (nodes[j] == NULL)
4678 continue;
4679 if (xmlRelaxNGNodeMatchesList(nodes[j], group->defs)) {
4680 list[k++] = nodes[j];
4681 nodes[j] = NULL;
4682 }
4683 }
4684 ctxt->state->seq = oldseq;
4685 if (k > 1) {
4686 memset(array, 0, k * sizeof(xmlNodePtr));
Daniel Veillardb08c9812003-01-28 23:09:49 +00004687 level = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004688 ret = xmlRelaxNGValidateWalkPermutations(ctxt, group->rule,
4689 list, array, k, &level, -1);
4690 } else {
4691 ret = xmlRelaxNGValidateTryPermutation(ctxt, group->rule, list, k);
4692 }
4693 if (ret != 0) {
4694 ctxt->state->seq = oldseq;
4695 break;
4696 }
4697 }
4698
4699 xmlFree(list);
4700 xmlFree(array);
4701 return(ret);
4702}
4703
4704/**
4705 * xmlRelaxNGValidateInterleave:
4706 * @ctxt: a Relax-NG validation context
4707 * @define: the definition to verify
4708 *
4709 * Validate an interleave definition for a node.
4710 *
4711 * Returns 0 if the validation succeeded or an error code.
4712 */
4713static int
4714xmlRelaxNGValidateInterleave(xmlRelaxNGValidCtxtPtr ctxt,
4715 xmlRelaxNGDefinePtr define) {
4716 int ret = 0, nbchildren, nbtot, i, j;
4717 xmlRelaxNGPartitionPtr partitions;
4718 xmlNodePtr *children = NULL;
4719 xmlNodePtr *order = NULL;
4720 xmlNodePtr cur;
4721
4722 if (define->data != NULL) {
4723 partitions = (xmlRelaxNGPartitionPtr) define->data;
4724 } else {
4725 VALID_CTXT();
4726 VALID_ERROR("Internal: interleave block has no data\n");
4727 return(-1);
4728 }
4729
4730 /*
4731 * Build the sequence of child and an array preserving the children
4732 * initial order.
4733 */
4734 cur = ctxt->state->seq;
4735 nbchildren = 0;
4736 nbtot = 0;
4737 while (cur != NULL) {
4738 if ((cur->type == XML_COMMENT_NODE) ||
4739 (cur->type == XML_PI_NODE) ||
4740 ((cur->type == XML_TEXT_NODE) &&
4741 (IS_BLANK_NODE(cur)))) {
4742 nbtot++;
4743 } else {
4744 nbchildren++;
4745 nbtot++;
4746 }
4747 cur = cur->next;
4748 }
4749 children = (xmlNodePtr *) xmlMalloc(nbchildren * sizeof(xmlNodePtr));
4750 if (children == NULL)
4751 goto error;
4752 order = (xmlNodePtr *) xmlMalloc(nbtot * sizeof(xmlNodePtr));
4753 if (order == NULL)
4754 goto error;
4755 cur = ctxt->state->seq;
4756 i = 0;
4757 j = 0;
4758 while (cur != NULL) {
4759 if ((cur->type == XML_COMMENT_NODE) ||
4760 (cur->type == XML_PI_NODE) ||
4761 ((cur->type == XML_TEXT_NODE) &&
4762 (IS_BLANK_NODE(cur)))) {
4763 order[j++] = cur;
4764 } else {
4765 order[j++] = cur;
4766 children[i++] = cur;
4767 }
4768 cur = cur->next;
4769 }
4770
4771 /* TODO: retry with a maller set of child if there is a next... */
4772 ret = xmlRelaxNGValidatePartGroup(ctxt, partitions->groups,
4773 partitions->nbgroups, children, nbchildren);
4774 if (ret == 0) {
4775 ctxt->state->seq = NULL;
4776 }
4777
4778 /*
4779 * Cleanup: rebuid the child sequence and free the structure
4780 */
4781 if (order != NULL) {
4782 for (i = 0;i < nbtot;i++) {
4783 if (i == 0)
4784 order[i]->prev = NULL;
4785 else
4786 order[i]->prev = order[i - 1];
4787 if (i == nbtot - 1)
4788 order[i]->next = NULL;
4789 else
4790 order[i]->next = order[i + 1];
4791 }
4792 xmlFree(order);
4793 }
4794 if (children != NULL)
4795 xmlFree(children);
4796
4797 return(ret);
4798
4799error:
4800 if (order != NULL) {
4801 for (i = 0;i < nbtot;i++) {
4802 if (i == 0)
4803 order[i]->prev = NULL;
4804 else
4805 order[i]->prev = order[i - 1];
4806 if (i == nbtot - 1)
4807 order[i]->next = NULL;
4808 else
4809 order[i]->next = order[i + 1];
4810 }
4811 xmlFree(order);
4812 }
4813 if (children != NULL)
4814 xmlFree(children);
4815 return(-1);
4816}
4817
4818/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004819 * xmlRelaxNGValidateElementContent:
4820 * @ctxt: a Relax-NG validation context
4821 * @define: the list of definition to verify
4822 *
4823 * Validate the given node content against the (list) of definitions
4824 *
4825 * Returns 0 if the validation succeeded or an error code.
4826 */
4827static int
4828xmlRelaxNGValidateElementContent(xmlRelaxNGValidCtxtPtr ctxt,
4829 xmlRelaxNGDefinePtr defines) {
4830 int ret = 0, res;
4831
4832 if (ctxt->state == NULL) {
4833 VALID_CTXT();
4834 VALID_ERROR("Internal: no state\n");
4835 return(-1);
4836 }
4837 while (defines != NULL) {
4838 res = xmlRelaxNGValidateDefinition(ctxt, defines);
4839 if (res < 0)
4840 ret = -1;
4841 defines = defines->next;
4842 }
4843
4844 return(ret);
4845}
4846
4847/**
4848 * xmlRelaxNGValidateDefinition:
4849 * @ctxt: a Relax-NG validation context
4850 * @define: the definition to verify
4851 *
4852 * Validate the current node against the definition
4853 *
4854 * Returns 0 if the validation succeeded or an error code.
4855 */
4856static int
4857xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
4858 xmlRelaxNGDefinePtr define) {
4859 xmlNodePtr node;
4860 int ret = 0, i, tmp, oldflags;
4861 xmlRelaxNGValidStatePtr oldstate, state;
4862
4863 if (define == NULL) {
4864 VALID_CTXT();
4865 VALID_ERROR("internal error: define == NULL\n");
4866 return(-1);
4867 }
4868 if (ctxt->state != NULL) {
4869 node = ctxt->state->seq;
4870 } else {
4871 node = NULL;
4872 }
4873 switch (define->type) {
4874 case XML_RELAXNG_EMPTY:
4875 if (node != NULL) {
4876 VALID_CTXT();
4877 VALID_ERROR("Expecting an empty element\n");
4878 return(-1);
4879 }
4880#ifdef DEBUG
4881 xmlGenericError(xmlGenericErrorContext,
4882 "xmlRelaxNGValidateDefinition(): validated empty\n");
4883#endif
4884 return(0);
4885 case XML_RELAXNG_NOT_ALLOWED:
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004886 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004887 case XML_RELAXNG_TEXT:
4888 if (node == NULL)
4889 return(0);
4890 while ((node != NULL) &&
4891 ((node->type == XML_TEXT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004892 (node->type == XML_COMMENT_NODE) ||
4893 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00004894 (node->type == XML_CDATA_SECTION_NODE)))
4895 node = node->next;
Daniel Veillard276be4a2003-01-24 01:03:34 +00004896 if (node == ctxt->state->seq) {
4897 VALID_CTXT();
4898 VALID_ERROR("Expecting text content\n");
4899 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004900 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00004901 ctxt->state->seq = node;
4902 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004903 case XML_RELAXNG_ELEMENT:
4904 node = xmlRelaxNGSkipIgnored(ctxt, node);
4905 if ((node == NULL) || (node->type != XML_ELEMENT_NODE)) {
4906 VALID_CTXT();
4907 VALID_ERROR("Expecting an element\n");
4908 return(-1);
4909 }
4910 if (define->name != NULL) {
4911 if (!xmlStrEqual(node->name, define->name)) {
4912 VALID_CTXT();
4913 VALID_ERROR("Expecting element %s, got %s\n",
4914 define->name, node->name);
4915 return(-1);
4916 }
4917 }
4918 if ((define->ns != NULL) && (define->ns[0] != 0)) {
4919 if (node->ns == NULL) {
4920 VALID_CTXT();
4921 VALID_ERROR("Expecting a namespace for element %s\n",
4922 node->name);
4923 return(-1);
4924 } else if (!xmlStrEqual(node->ns->href, define->ns)) {
4925 VALID_CTXT();
4926 VALID_ERROR("Expecting element %s has wrong namespace: expecting %s\n",
4927 node->name, define->ns);
4928 return(-1);
4929 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004930 } else if (define->name != NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00004931 if (node->ns != NULL) {
4932 VALID_CTXT();
4933 VALID_ERROR("Expecting no namespace for element %s\n",
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004934 define->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004935 return(-1);
4936 }
4937 }
4938
4939 state = xmlRelaxNGNewValidState(ctxt, node);
4940 if (state == NULL) {
4941 return(-1);
4942 }
4943
4944 oldstate = ctxt->state;
4945 ctxt->state = state;
4946 if (define->attrs != NULL) {
4947 tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
4948 if (tmp != 0)
4949 ret = -1;
4950 }
4951 if (define->content != NULL) {
4952 tmp = xmlRelaxNGValidateElementContent(ctxt, define->content);
4953 if (tmp != 0)
4954 ret = -1;
4955 }
4956 state = ctxt->state;
4957 if (state->seq != NULL) {
4958 state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq);
4959 if (state->seq != NULL) {
4960 VALID_CTXT();
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004961 VALID_ERROR("Extra content for element %s: %s\n",
4962 node->name, state->seq->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004963 ret = -1;
4964 }
4965 }
4966 for (i = 0;i < state->nbAttrs;i++) {
4967 if (state->attrs[i] != NULL) {
4968 VALID_CTXT();
Daniel Veillardea3f3982003-01-26 19:45:18 +00004969 VALID_ERROR("Invalid attribute %s for element %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00004970 state->attrs[i]->name, node->name);
4971 ret = -1;
4972 }
4973 }
4974 ctxt->state = oldstate;
4975 xmlRelaxNGFreeValidState(state);
4976 if (oldstate != NULL)
4977 oldstate->seq = node->next;
4978
4979
4980#ifdef DEBUG
4981 xmlGenericError(xmlGenericErrorContext,
4982 "xmlRelaxNGValidateDefinition(): validated %s : %d\n",
4983 node->name, ret);
4984#endif
4985 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004986 case XML_RELAXNG_OPTIONAL:
4987 oldflags = ctxt->flags;
4988 ctxt->flags |= FLAGS_IGNORABLE;
4989 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
4990 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
4991 if (ret != 0) {
4992 xmlRelaxNGFreeValidState(ctxt->state);
4993 ctxt->state = oldstate;
4994 ret = 0;
4995 break;
4996 }
4997 xmlRelaxNGFreeValidState(oldstate);
4998 ctxt->flags = oldflags;
4999 ret = 0;
5000 break;
5001 case XML_RELAXNG_ONEORMORE:
5002 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5003 if (ret != 0) {
5004 break;
5005 }
5006 /* no break on purpose */
Daniel Veillard276be4a2003-01-24 01:03:34 +00005007 case XML_RELAXNG_ZEROORMORE: {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005008 oldflags = ctxt->flags;
5009 ctxt->flags |= FLAGS_IGNORABLE;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005010 while (ctxt->state->nbAttrLeft != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005011 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5012 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5013 if (ret != 0) {
5014 xmlRelaxNGFreeValidState(ctxt->state);
5015 ctxt->state = oldstate;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005016 break;
5017 }
5018 xmlRelaxNGFreeValidState(oldstate);
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005019 }
5020 if (ret == 0) {
5021 /*
5022 * There is no attribute left to be consumed,
5023 * we can check the closure by looking at ctxt->state->seq
5024 */
5025 xmlNodePtr cur, temp;
5026
Daniel Veillard276be4a2003-01-24 01:03:34 +00005027 cur = ctxt->state->seq;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005028 temp = NULL;
5029 while ((cur != NULL) && (temp != cur)) {
5030 temp = cur;
5031 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5032 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5033 if (ret != 0) {
5034 xmlRelaxNGFreeValidState(ctxt->state);
5035 ctxt->state = oldstate;
5036 break;
5037 }
5038 xmlRelaxNGFreeValidState(oldstate);
5039 cur = ctxt->state->seq;
5040 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005041 }
5042 ctxt->flags = oldflags;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005043 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005044 break;
Daniel Veillard276be4a2003-01-24 01:03:34 +00005045 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005046 case XML_RELAXNG_CHOICE: {
5047 xmlRelaxNGDefinePtr list = define->content;
5048
5049 oldflags = ctxt->flags;
5050 ctxt->flags |= FLAGS_IGNORABLE;
5051
5052 while (list != NULL) {
5053 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5054 ret = xmlRelaxNGValidateDefinition(ctxt, list);
5055 if (ret == 0) {
5056 xmlRelaxNGFreeValidState(oldstate);
5057 break;
5058 }
5059 xmlRelaxNGFreeValidState(ctxt->state);
5060 ctxt->state = oldstate;
5061 list = list->next;
5062 }
5063 ctxt->flags = oldflags;
5064 break;
5065 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005066 case XML_RELAXNG_DEF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00005067 case XML_RELAXNG_GROUP: {
5068 xmlRelaxNGDefinePtr list = define->content;
5069
5070 while (list != NULL) {
5071 ret = xmlRelaxNGValidateDefinition(ctxt, list);
5072 if (ret != 0)
5073 break;
5074 list = list->next;
5075 }
5076 break;
5077 }
5078 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005079 ret = xmlRelaxNGValidateInterleave(ctxt, define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005080 break;
5081 case XML_RELAXNG_ATTRIBUTE:
5082 ret = xmlRelaxNGValidateAttribute(ctxt, define);
5083 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005084 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00005085 case XML_RELAXNG_REF:
5086 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5087 break;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005088 case XML_RELAXNG_DATATYPE: {
5089 xmlChar *content;
5090
5091 content = xmlNodeGetContent(node);
5092 ret = xmlRelaxNGValidateDatatype(ctxt, content, define);
5093 if (ret == -1) {
5094 VALID_CTXT();
5095 VALID_ERROR("internal error validating %s\n", define->name);
5096 } else if (ret == 0) {
5097 ctxt->state->seq = node->next;
5098 }
5099 /*
5100 * TODO cover the problems with
5101 * <p>12<!-- comment -->34</p>
5102 * TODO detect full element coverage at compilation time.
5103 */
5104 if ((node != NULL) && (node->next != NULL)) {
5105 VALID_CTXT();
5106 VALID_ERROR("The data does not cover the full element %s\n",
5107 node->parent->name);
5108 ret = -1;
5109 }
5110 if (content != NULL)
5111 xmlFree(content);
5112 break;
5113 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00005114 case XML_RELAXNG_VALUE: {
5115 xmlChar *content;
5116 xmlChar *oldvalue;
5117
5118 content = xmlNodeGetContent(node);
5119 oldvalue = ctxt->state->value;
5120 ctxt->state->value = content;
5121 ret = xmlRelaxNGValidateValue(ctxt, define);
5122 ctxt->state->value = oldvalue;
5123 if (ret == -1) {
5124 VALID_CTXT();
5125 VALID_ERROR("internal error validating %s\n", define->name);
5126 } else if (ret == 0) {
5127 ctxt->state->seq = node->next;
5128 }
5129 /*
5130 * TODO cover the problems with
5131 * <p>12<!-- comment -->34</p>
5132 * TODO detect full element coverage at compilation time.
5133 */
5134 if ((node != NULL) && (node->next != NULL)) {
5135 VALID_CTXT();
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005136 VALID_ERROR("The value does not cover the full element %s\n",
Daniel Veillardea3f3982003-01-26 19:45:18 +00005137 node->parent->name);
5138 ret = -1;
5139 }
5140 if (content != NULL)
5141 xmlFree(content);
5142 break;
5143 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005144 case XML_RELAXNG_LIST: {
5145 xmlChar *content;
5146 xmlChar *oldvalue, *oldendvalue;
5147 int len;
Daniel Veillardea3f3982003-01-26 19:45:18 +00005148
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005149 content = xmlNodeGetContent(node);
5150 len = xmlStrlen(content);
5151 oldvalue = ctxt->state->value;
5152 oldendvalue = ctxt->state->endvalue;
5153 ctxt->state->value = content;
5154 ctxt->state->endvalue = content + len;
5155 ret = xmlRelaxNGValidateValue(ctxt, define);
5156 ctxt->state->value = oldvalue;
5157 ctxt->state->endvalue = oldendvalue;
5158 if (ret == -1) {
5159 VALID_CTXT();
5160 VALID_ERROR("internal error validating list\n");
5161 } else if (ret == 0) {
5162 ctxt->state->seq = node->next;
5163 }
5164 /*
5165 * TODO cover the problems with
5166 * <p>12<!-- comment -->34</p>
5167 * TODO detect full element coverage at compilation time.
5168 */
5169 if ((node != NULL) && (node->next != NULL)) {
5170 VALID_CTXT();
5171 VALID_ERROR("The list does not cover the full element %s\n",
5172 node->parent->name);
5173 ret = -1;
5174 }
5175 if (content != NULL)
5176 xmlFree(content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005177 break;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005178 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005179 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00005180 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005181 TODO
5182 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005183 }
5184 return(ret);
5185}
5186
5187/**
5188 * xmlRelaxNGValidateDocument:
5189 * @ctxt: a Relax-NG validation context
5190 * @doc: the document
5191 *
5192 * Validate the given document
5193 *
5194 * Returns 0 if the validation succeeded or an error code.
5195 */
5196static int
5197xmlRelaxNGValidateDocument(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
5198 int ret;
5199 xmlRelaxNGPtr schema;
5200 xmlRelaxNGGrammarPtr grammar;
5201 xmlRelaxNGValidStatePtr state;
5202
5203 if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL))
5204 return(-1);
5205
5206 schema = ctxt->schema;
5207 grammar = schema->topgrammar;
5208 if (grammar == NULL) {
5209 VALID_CTXT();
5210 VALID_ERROR("No top grammar defined\n");
5211 return(-1);
5212 }
5213 state = xmlRelaxNGNewValidState(ctxt, NULL);
5214 ctxt->state = state;
5215 ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start);
5216 state = ctxt->state;
5217 if ((state != NULL) && (state->seq != NULL)) {
5218 xmlNodePtr node;
5219
5220 node = state->seq;
5221 node = xmlRelaxNGSkipIgnored(ctxt, node);
5222 if (node != NULL) {
5223 VALID_CTXT();
5224 VALID_ERROR("extra data on the document\n");
5225 ret = -1;
5226 }
5227 }
5228 xmlRelaxNGFreeValidState(state);
5229
5230 return(ret);
5231}
5232
5233/************************************************************************
5234 * *
5235 * Validation interfaces *
5236 * *
5237 ************************************************************************/
5238/**
5239 * xmlRelaxNGNewValidCtxt:
5240 * @schema: a precompiled XML RelaxNGs
5241 *
5242 * Create an XML RelaxNGs validation context based on the given schema
5243 *
5244 * Returns the validation context or NULL in case of error
5245 */
5246xmlRelaxNGValidCtxtPtr
5247xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema) {
5248 xmlRelaxNGValidCtxtPtr ret;
5249
5250 ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt));
5251 if (ret == NULL) {
5252 xmlGenericError(xmlGenericErrorContext,
5253 "Failed to allocate new schama validation context\n");
5254 return (NULL);
5255 }
5256 memset(ret, 0, sizeof(xmlRelaxNGValidCtxt));
5257 ret->schema = schema;
5258 return (ret);
5259}
5260
5261/**
5262 * xmlRelaxNGFreeValidCtxt:
5263 * @ctxt: the schema validation context
5264 *
5265 * Free the resources associated to the schema validation context
5266 */
5267void
5268xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt) {
5269 if (ctxt == NULL)
5270 return;
5271 xmlFree(ctxt);
5272}
5273
5274/**
5275 * xmlRelaxNGSetValidErrors:
5276 * @ctxt: a Relax-NG validation context
5277 * @err: the error function
5278 * @warn: the warning function
5279 * @ctx: the functions context
5280 *
5281 * Set the error and warning callback informations
5282 */
5283void
5284xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
5285 xmlRelaxNGValidityErrorFunc err,
5286 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
5287 if (ctxt == NULL)
5288 return;
5289 ctxt->error = err;
5290 ctxt->warning = warn;
5291 ctxt->userData = ctx;
5292}
5293
5294/**
5295 * xmlRelaxNGValidateDoc:
5296 * @ctxt: a Relax-NG validation context
5297 * @doc: a parsed document tree
5298 *
5299 * Validate a document tree in memory.
5300 *
5301 * Returns 0 if the document is valid, a positive error code
5302 * number otherwise and -1 in case of internal or API error.
5303 */
5304int
5305xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
5306 int ret;
5307
5308 if ((ctxt == NULL) || (doc == NULL))
5309 return(-1);
5310
5311 ctxt->doc = doc;
5312
5313 ret = xmlRelaxNGValidateDocument(ctxt, doc);
5314 return(ret);
5315}
5316
5317#endif /* LIBXML_SCHEMAS_ENABLED */
5318