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