blob: 86ad215c1c94df9e8c477548dd33a772d8351cdc [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:
11 * - <interleave></interleave><element>...
12 * - error reporting
13 * - module
14 * - fixing ref/def cross grammar contexts
15 */
16
Daniel Veillard6eadf632003-01-23 18:29:16 +000017#define IN_LIBXML
18#include "libxml.h"
19
20#ifdef LIBXML_SCHEMAS_ENABLED
21
22#include <string.h>
23#include <stdio.h>
24#include <libxml/xmlmemory.h>
25#include <libxml/parser.h>
26#include <libxml/parserInternals.h>
27#include <libxml/hash.h>
28#include <libxml/uri.h>
29
30#include <libxml/relaxng.h>
31
32#include <libxml/xmlschemastypes.h>
33#include <libxml/xmlautomata.h>
34#include <libxml/xmlregexp.h>
Daniel Veillardc6e997c2003-01-27 12:35:42 +000035#include <libxml/xmlschemastypes.h>
Daniel Veillard6eadf632003-01-23 18:29:16 +000036
37/*
38 * The Relax-NG namespace
39 */
40static const xmlChar *xmlRelaxNGNs = (const xmlChar *)
41 "http://relaxng.org/ns/structure/1.0";
42
43#define IS_RELAXNG(node, type) \
44 ((node != NULL) && (node->ns != NULL) && \
45 (xmlStrEqual(node->name, (const xmlChar *) type)) && \
46 (xmlStrEqual(node->ns->href, xmlRelaxNGNs)))
47
48
49#define DEBUG 1 /* very verbose output */
50#define DEBUG_CONTENT 1
51#define DEBUG_TYPE 1
Daniel Veillard276be4a2003-01-24 01:03:34 +000052#define DEBUG_VALID 1
Daniel Veillardb08c9812003-01-28 23:09:49 +000053#define DEBUG_INTERLEAVE 1 */
Daniel Veillard6eadf632003-01-23 18:29:16 +000054
55#define UNBOUNDED (1 << 30)
56#define TODO \
57 xmlGenericError(xmlGenericErrorContext, \
58 "Unimplemented block at %s:%d\n", \
59 __FILE__, __LINE__);
60
61typedef struct _xmlRelaxNGSchema xmlRelaxNGSchema;
62typedef xmlRelaxNGSchema *xmlRelaxNGSchemaPtr;
63
64typedef struct _xmlRelaxNGDefine xmlRelaxNGDefine;
65typedef xmlRelaxNGDefine *xmlRelaxNGDefinePtr;
66
Daniel Veillardd41f4f42003-01-29 21:07:52 +000067typedef struct _xmlRelaxNGDocument xmlRelaxNGDocument;
68typedef xmlRelaxNGDocument *xmlRelaxNGDocumentPtr;
69
Daniel Veillard6eadf632003-01-23 18:29:16 +000070typedef enum {
71 XML_RELAXNG_COMBINE_UNDEFINED = 0, /* undefined */
72 XML_RELAXNG_COMBINE_CHOICE, /* choice */
73 XML_RELAXNG_COMBINE_INTERLEAVE /* interleave */
74} xmlRelaxNGCombine;
75
76typedef struct _xmlRelaxNGGrammar xmlRelaxNGGrammar;
77typedef xmlRelaxNGGrammar *xmlRelaxNGGrammarPtr;
78
79struct _xmlRelaxNGGrammar {
80 xmlRelaxNGGrammarPtr parent;/* the parent grammar if any */
81 xmlRelaxNGGrammarPtr children;/* the children grammar if any */
82 xmlRelaxNGGrammarPtr next; /* the next grammar if any */
83 xmlRelaxNGDefinePtr start; /* <start> content */
84 xmlRelaxNGCombine combine; /* the default combine value */
Daniel Veillardd41f4f42003-01-29 21:07:52 +000085 xmlRelaxNGDefinePtr startList;/* list of <start> definitions */
Daniel Veillard6eadf632003-01-23 18:29:16 +000086 xmlHashTablePtr defs; /* define* */
87 xmlHashTablePtr refs; /* references */
88};
89
90
Daniel Veillard6eadf632003-01-23 18:29:16 +000091typedef enum {
92 XML_RELAXNG_EMPTY = 0, /* an empty pattern */
93 XML_RELAXNG_NOT_ALLOWED, /* not allowed top */
94 XML_RELAXNG_TEXT, /* textual content */
95 XML_RELAXNG_ELEMENT, /* an element */
96 XML_RELAXNG_DATATYPE, /* extenal data type definition */
97 XML_RELAXNG_VALUE, /* value from an extenal data type definition */
98 XML_RELAXNG_LIST, /* a list of patterns */
99 XML_RELAXNG_ATTRIBUTE, /* an attrbute following a pattern */
100 XML_RELAXNG_DEF, /* a definition */
101 XML_RELAXNG_REF, /* reference to a definition */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000102 XML_RELAXNG_EXTERNALREF, /* reference to an external def */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000103 XML_RELAXNG_OPTIONAL, /* optional patterns */
104 XML_RELAXNG_ZEROORMORE, /* zero or more non empty patterns */
105 XML_RELAXNG_ONEORMORE, /* one or more non empty patterns */
106 XML_RELAXNG_CHOICE, /* a choice between non empty patterns */
107 XML_RELAXNG_GROUP, /* a pair/group of non empty patterns */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000108 XML_RELAXNG_INTERLEAVE, /* interleaving choice of non-empty patterns */
109 XML_RELAXNG_START /* Used to keep track of starts on grammars */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000110} xmlRelaxNGType;
111
112struct _xmlRelaxNGDefine {
113 xmlRelaxNGType type; /* the type of definition */
114 xmlNodePtr node; /* the node in the source */
115 xmlChar *name; /* the element local name if present */
116 xmlChar *ns; /* the namespace local name if present */
Daniel Veillardedc91922003-01-26 00:52:04 +0000117 xmlChar *value; /* value when available */
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000118 void *data; /* data lib or specific pointer */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000119 xmlRelaxNGDefinePtr content;/* the expected content */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000120 xmlRelaxNGDefinePtr parent; /* the parent definition, if any */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000121 xmlRelaxNGDefinePtr next; /* list within grouping sequences */
122 xmlRelaxNGDefinePtr attrs; /* list of attributes for elements */
123 xmlRelaxNGDefinePtr nextHash;/* next define in defs/refs hash tables */
124};
125
126/**
127 * _xmlRelaxNG:
128 *
129 * A RelaxNGs definition
130 */
131struct _xmlRelaxNG {
132 xmlRelaxNGGrammarPtr topgrammar;
133 xmlDocPtr doc;
134
135 xmlHashTablePtr defs; /* define */
136 xmlHashTablePtr refs; /* references */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000137 xmlHashTablePtr documents; /* all the documents loaded */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000138 void *_private; /* unused by the library for users or bindings */
139};
140
141typedef enum {
142 XML_RELAXNG_ERR_OK = 0,
143 XML_RELAXNG_ERR_NOROOT = 1,
144 XML_RELAXNG_ERR_
145} xmlRelaxNGValidError;
146
147#define XML_RELAXNG_IN_ATTRIBUTE 1
148
149struct _xmlRelaxNGParserCtxt {
150 void *userData; /* user specific data block */
151 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
152 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
153 xmlRelaxNGValidError err;
154
155 xmlRelaxNGPtr schema; /* The schema in use */
156 xmlRelaxNGGrammarPtr grammar; /* the current grammar */
157 int flags; /* parser flags */
158 int nbErrors; /* number of errors at parse time */
159 int nbWarnings; /* number of warnings at parse time */
Daniel Veillard276be4a2003-01-24 01:03:34 +0000160 const xmlChar *define; /* the current define scope */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000161 xmlRelaxNGDefinePtr def; /* the current define */
162
163 int nbInterleaves;
164 xmlHashTablePtr interleaves; /* keep track of all the interleaves */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000165
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000166 xmlHashTablePtr documents; /* all the documents loaded */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000167 xmlChar *URL;
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000168 xmlDocPtr document;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000169
170 const char *buffer;
171 int size;
172
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000173 /* the document stack */
174 xmlRelaxNGDocumentPtr doc; /* Current parsed Node */
175 int docNr; /* Depth of the parsing stack */
176 int docMax; /* Max depth of the parsing stack */
177 xmlRelaxNGDocumentPtr *docTab; /* array of docs */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000178};
179
180#define FLAGS_IGNORABLE 1
181#define FLAGS_NEGATIVE 2
182
183/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000184 * xmlRelaxNGInterleaveGroup:
185 *
186 * A RelaxNGs partition set associated to lists of definitions
187 */
188typedef struct _xmlRelaxNGInterleaveGroup xmlRelaxNGInterleaveGroup;
189typedef xmlRelaxNGInterleaveGroup *xmlRelaxNGInterleaveGroupPtr;
190struct _xmlRelaxNGInterleaveGroup {
191 xmlRelaxNGDefinePtr rule; /* the rule to satisfy */
192 xmlRelaxNGDefinePtr *defs; /* the array of element definitions */
193};
194
195/**
196 * xmlRelaxNGPartitions:
197 *
198 * A RelaxNGs partition associated to an interleave group
199 */
200typedef struct _xmlRelaxNGPartition xmlRelaxNGPartition;
201typedef xmlRelaxNGPartition *xmlRelaxNGPartitionPtr;
202struct _xmlRelaxNGPartition {
203 int nbgroups; /* number of groups in the partitions */
204 xmlRelaxNGInterleaveGroupPtr *groups;
205};
206
207/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000208 * xmlRelaxNGValidState:
209 *
210 * A RelaxNGs validation state
211 */
212#define MAX_ATTR 20
213typedef struct _xmlRelaxNGValidState xmlRelaxNGValidState;
214typedef xmlRelaxNGValidState *xmlRelaxNGValidStatePtr;
215struct _xmlRelaxNGValidState {
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000216 xmlNodePtr node; /* the current node */
217 xmlNodePtr seq; /* the sequence of children left to validate */
218 int nbAttrs; /* the number of attributes */
219 xmlChar *value; /* the value when operating on string */
220 xmlChar *endvalue; /* the end value when operating on string */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000221 xmlAttrPtr attrs[1]; /* the array of attributes */
222};
223
224/**
225 * xmlRelaxNGValidCtxt:
226 *
227 * A RelaxNGs validation context
228 */
229
230struct _xmlRelaxNGValidCtxt {
231 void *userData; /* user specific data block */
232 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
233 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
234
235 xmlRelaxNGPtr schema; /* The schema in use */
236 xmlDocPtr doc; /* the document being validated */
237 xmlRelaxNGValidStatePtr state; /* the current validation state */
238 int flags; /* validation flags */
239};
240
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000241/**
242 * xmlRelaxNGDocument:
243 *
244 * Structure associated to a RelaxNGs document element
245 */
246struct _xmlRelaxNGDocument {
247 xmlChar *href; /* the normalized href value */
248 xmlDocPtr doc; /* the associated XML document */
249 xmlRelaxNGDefinePtr content;/* the definitions */
250 xmlRelaxNGPtr schema; /* the schema */
251};
252
Daniel Veillard6eadf632003-01-23 18:29:16 +0000253/************************************************************************
254 * *
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000255 * Preliminary type checking interfaces *
256 * *
257 ************************************************************************/
258/**
259 * xmlRelaxNGTypeHave:
260 * @data: data needed for the library
261 * @type: the type name
262 * @value: the value to check
263 *
264 * Function provided by a type library to check if a type is exported
265 *
266 * Returns 1 if yes, 0 if no and -1 in case of error.
267 */
268typedef int (*xmlRelaxNGTypeHave) (void *data, const xmlChar *type);
269
270/**
271 * xmlRelaxNGTypeCheck:
272 * @data: data needed for the library
273 * @type: the type name
274 * @value: the value to check
275 *
276 * Function provided by a type library to check if a value match a type
277 *
278 * Returns 1 if yes, 0 if no and -1 in case of error.
279 */
280typedef int (*xmlRelaxNGTypeCheck) (void *data, const xmlChar *type,
281 const xmlChar *value);
282
283/**
284 * xmlRelaxNGTypeCompare:
285 * @data: data needed for the library
286 * @type: the type name
287 * @value1: the first value
288 * @value2: the second value
289 *
290 * Function provided by a type library to compare two values accordingly
291 * to a type.
292 *
293 * Returns 1 if yes, 0 if no and -1 in case of error.
294 */
295typedef int (*xmlRelaxNGTypeCompare) (void *data, const xmlChar *type,
296 const xmlChar *value1,
297 const xmlChar *value2);
298typedef struct _xmlRelaxNGTypeLibrary xmlRelaxNGTypeLibrary;
299typedef xmlRelaxNGTypeLibrary *xmlRelaxNGTypeLibraryPtr;
300struct _xmlRelaxNGTypeLibrary {
301 const xmlChar *namespace; /* the datatypeLibrary value */
302 void *data; /* data needed for the library */
303 xmlRelaxNGTypeHave have; /* the export function */
304 xmlRelaxNGTypeCheck check; /* the checking function */
305 xmlRelaxNGTypeCompare comp; /* the compare function */
306};
307
308/************************************************************************
309 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +0000310 * Allocation functions *
311 * *
312 ************************************************************************/
313static void xmlRelaxNGFreeDefineList(xmlRelaxNGDefinePtr defines);
314static void xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar);
315static void xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define);
316
317/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000318 * xmlRelaxNGFreeDocument:
319 * @docu: a document structure
320 *
321 * Deallocate a RelaxNG document structure.
322 */
323static void
324xmlRelaxNGFreeDocument(xmlRelaxNGDocumentPtr docu)
325{
326 if (docu == NULL)
327 return;
328
329 if (docu->href != NULL)
330 xmlFree(docu->href);
331 if (docu->doc != NULL)
332 xmlFreeDoc(docu->doc);
333 if (docu->schema != NULL)
334 xmlRelaxNGFree(docu->schema);
335 xmlFree(docu);
336}
337
338/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000339 * xmlRelaxNGNewRelaxNG:
340 * @ctxt: a Relax-NG validation context (optional)
341 *
342 * Allocate a new RelaxNG structure.
343 *
344 * Returns the newly allocated structure or NULL in case or error
345 */
346static xmlRelaxNGPtr
347xmlRelaxNGNewRelaxNG(xmlRelaxNGParserCtxtPtr ctxt)
348{
349 xmlRelaxNGPtr ret;
350
351 ret = (xmlRelaxNGPtr) xmlMalloc(sizeof(xmlRelaxNG));
352 if (ret == NULL) {
353 if ((ctxt != NULL) && (ctxt->error != NULL))
354 ctxt->error(ctxt->userData, "Out of memory\n");
355 ctxt->nbErrors++;
356 return (NULL);
357 }
358 memset(ret, 0, sizeof(xmlRelaxNG));
359
360 return (ret);
361}
362
363/**
364 * xmlRelaxNGFree:
365 * @schema: a schema structure
366 *
367 * Deallocate a RelaxNG structure.
368 */
369void
370xmlRelaxNGFree(xmlRelaxNGPtr schema)
371{
372 if (schema == NULL)
373 return;
374
Daniel Veillard6eadf632003-01-23 18:29:16 +0000375 if (schema->topgrammar != NULL)
376 xmlRelaxNGFreeGrammar(schema->topgrammar);
377 if (schema->doc != NULL)
378 xmlFreeDoc(schema->doc);
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000379 if (schema->documents != NULL)
380 xmlHashFree(schema->documents, (xmlHashDeallocator)
381 xmlRelaxNGFreeDocument);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000382
383 xmlFree(schema);
384}
385
386/**
387 * xmlRelaxNGNewGrammar:
388 * @ctxt: a Relax-NG validation context (optional)
389 *
390 * Allocate a new RelaxNG grammar.
391 *
392 * Returns the newly allocated structure or NULL in case or error
393 */
394static xmlRelaxNGGrammarPtr
395xmlRelaxNGNewGrammar(xmlRelaxNGParserCtxtPtr ctxt)
396{
397 xmlRelaxNGGrammarPtr ret;
398
399 ret = (xmlRelaxNGGrammarPtr) xmlMalloc(sizeof(xmlRelaxNGGrammar));
400 if (ret == NULL) {
401 if ((ctxt != NULL) && (ctxt->error != NULL))
402 ctxt->error(ctxt->userData, "Out of memory\n");
403 ctxt->nbErrors++;
404 return (NULL);
405 }
406 memset(ret, 0, sizeof(xmlRelaxNGGrammar));
407
408 return (ret);
409}
410
411/**
Daniel Veillard276be4a2003-01-24 01:03:34 +0000412 * xmlRelaxNGFreeDefineHash:
413 * @defines: a list of define structures
414 *
415 * Deallocate a RelaxNG definition in the hash table
416 */
417static void
418xmlRelaxNGFreeDefineHash(xmlRelaxNGDefinePtr defines)
419{
420 xmlRelaxNGDefinePtr next;
421
422 while (defines != NULL) {
423 next = defines->nextHash;
424 xmlRelaxNGFreeDefine(defines);
425 defines = next;
426 }
427}
428
429/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000430 * xmlRelaxNGFreeGrammar:
431 * @grammar: a grammar structure
432 *
433 * Deallocate a RelaxNG grammar structure.
434 */
435static void
436xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar)
437{
438 if (grammar == NULL)
439 return;
440
441 if (grammar->start != NULL)
442 xmlRelaxNGFreeDefine(grammar->start);
443 if (grammar->refs != NULL) {
444 xmlHashFree(grammar->refs, NULL);
445 }
446 if (grammar->defs != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +0000447 xmlHashFree(grammar->defs, (xmlHashDeallocator)
448 xmlRelaxNGFreeDefineHash);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000449 }
450
451 xmlFree(grammar);
452}
453
454/**
455 * xmlRelaxNGNewDefine:
456 * @ctxt: a Relax-NG validation context
457 * @node: the node in the input document.
458 *
459 * Allocate a new RelaxNG define.
460 *
461 * Returns the newly allocated structure or NULL in case or error
462 */
463static xmlRelaxNGDefinePtr
464xmlRelaxNGNewDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
465{
466 xmlRelaxNGDefinePtr ret;
467
468 ret = (xmlRelaxNGDefinePtr) xmlMalloc(sizeof(xmlRelaxNGDefine));
469 if (ret == NULL) {
470 if ((ctxt != NULL) && (ctxt->error != NULL))
471 ctxt->error(ctxt->userData, "Out of memory\n");
472 ctxt->nbErrors++;
473 return (NULL);
474 }
475 memset(ret, 0, sizeof(xmlRelaxNGDefine));
476 ret->node = node;
477
478 return (ret);
479}
480
481/**
482 * xmlRelaxNGFreeDefineList:
483 * @defines: a list of define structures
484 *
485 * Deallocate a RelaxNG define structures.
486 */
487static void
488xmlRelaxNGFreeDefineList(xmlRelaxNGDefinePtr defines)
489{
490 xmlRelaxNGDefinePtr next;
491
492 while (defines != NULL) {
493 next = defines->next;
494 xmlRelaxNGFreeDefine(defines);
495 defines = next;
496 }
497}
498
499/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000500 * xmlRelaxNGFreePartition:
501 * @partitions: a partition set structure
502 *
503 * Deallocate RelaxNG partition set structures.
504 */
505static void
506xmlRelaxNGFreePartition(xmlRelaxNGPartitionPtr partitions) {
507 xmlRelaxNGInterleaveGroupPtr group;
508 int j;
509
510 if (partitions != NULL) {
511 if (partitions->groups != NULL) {
512 for (j = 0;j < partitions->nbgroups;j++) {
513 group = partitions->groups[j];
514 if (group != NULL) {
515 if (group->defs != NULL)
516 xmlFree(group->defs);
517 xmlFree(group);
518 }
519 }
520 xmlFree(partitions->groups);
521 }
522 xmlFree(partitions);
523 }
524}
525/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000526 * xmlRelaxNGFreeDefine:
527 * @define: a define structure
528 *
529 * Deallocate a RelaxNG define structure.
530 */
531static void
532xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define)
533{
534 if (define == NULL)
535 return;
536
537 if (define->name != NULL)
538 xmlFree(define->name);
539 if (define->ns != NULL)
540 xmlFree(define->ns);
Daniel Veillardedc91922003-01-26 00:52:04 +0000541 if (define->value != NULL)
542 xmlFree(define->value);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000543 if (define->attrs != NULL)
544 xmlRelaxNGFreeDefineList(define->attrs);
Daniel Veillard276be4a2003-01-24 01:03:34 +0000545 if ((define->content != NULL) &&
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000546 (define->type != XML_RELAXNG_REF) &&
547 (define->type != XML_RELAXNG_EXTERNALREF))
Daniel Veillard6eadf632003-01-23 18:29:16 +0000548 xmlRelaxNGFreeDefineList(define->content);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000549 if ((define->data != NULL) &&
550 (define->type == XML_RELAXNG_INTERLEAVE))
551 xmlRelaxNGFreePartition((xmlRelaxNGPartitionPtr) define->data);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000552 xmlFree(define);
553}
554
555/**
556 * xmlRelaxNGNewValidState:
557 * @ctxt: a Relax-NG validation context
558 * @node: the current node or NULL for the document
559 *
560 * Allocate a new RelaxNG validation state
561 *
562 * Returns the newly allocated structure or NULL in case or error
563 */
564static xmlRelaxNGValidStatePtr
565xmlRelaxNGNewValidState(xmlRelaxNGValidCtxtPtr ctxt, xmlNodePtr node)
566{
567 xmlRelaxNGValidStatePtr ret;
568 xmlAttrPtr attr;
569 xmlAttrPtr attrs[MAX_ATTR];
570 int nbAttrs = 0;
571 xmlNodePtr root = NULL;
572
573 if (node == NULL) {
574 root = xmlDocGetRootElement(ctxt->doc);
575 if (root == NULL)
576 return(NULL);
577 } else {
578 attr = node->properties;
579 while (attr != NULL) {
580 if (nbAttrs < MAX_ATTR)
581 attrs[nbAttrs++] = attr;
582 else
583 nbAttrs++;
584 attr = attr->next;
585 }
586 }
587
588 if (nbAttrs < MAX_ATTR)
589 attrs[nbAttrs] = NULL;
590 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(sizeof(xmlRelaxNGValidState) +
591 nbAttrs * sizeof(xmlAttrPtr));
592 if (ret == NULL) {
593 if ((ctxt != NULL) && (ctxt->error != NULL))
594 ctxt->error(ctxt->userData, "Out of memory\n");
595 return (NULL);
596 }
597 if (node == NULL) {
598 ret->node = (xmlNodePtr) ctxt->doc;
599 ret->seq = root;
600 ret->nbAttrs = 0;
601 } else {
602 ret->node = node;
603 ret->seq = node->children;
604 ret->nbAttrs = nbAttrs;
605 if (nbAttrs > 0) {
606 if (nbAttrs < MAX_ATTR) {
607 memcpy(&(ret->attrs[0]), attrs,
608 sizeof(xmlAttrPtr) * (nbAttrs + 1));
609 } else {
610 attr = node->properties;
611 nbAttrs = 0;
612 while (attr != NULL) {
613 ret->attrs[nbAttrs++] = attr;
614 attr = attr->next;
615 }
616 ret->attrs[nbAttrs] = NULL;
617 }
618 }
619 }
620 return (ret);
621}
622
623/**
624 * xmlRelaxNGCopyValidState:
625 * @ctxt: a Relax-NG validation context
626 * @state: a validation state
627 *
628 * Copy the validation state
629 *
630 * Returns the newly allocated structure or NULL in case or error
631 */
632static xmlRelaxNGValidStatePtr
633xmlRelaxNGCopyValidState(xmlRelaxNGValidCtxtPtr ctxt,
634 xmlRelaxNGValidStatePtr state)
635{
636 xmlRelaxNGValidStatePtr ret;
637 unsigned int size;
638
639 if (state == NULL)
640 return(NULL);
641
642 size = sizeof(xmlRelaxNGValidState) +
643 state->nbAttrs * sizeof(xmlAttrPtr);
644 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(size);
645 if (ret == NULL) {
646 if ((ctxt != NULL) && (ctxt->error != NULL))
647 ctxt->error(ctxt->userData, "Out of memory\n");
648 return (NULL);
649 }
650 memcpy(ret, state, size);
651 return(ret);
652}
653
654/**
655 * xmlRelaxNGFreeValidState:
656 * @state: a validation state structure
657 *
658 * Deallocate a RelaxNG validation state structure.
659 */
660static void
661xmlRelaxNGFreeValidState(xmlRelaxNGValidStatePtr state)
662{
663 if (state == NULL)
664 return;
665
666 xmlFree(state);
667}
668
669/************************************************************************
670 * *
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000671 * Document functions *
672 * *
673 ************************************************************************/
674static xmlDocPtr xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt,
675 xmlDocPtr doc);
676
677/**
678 * xmlRelaxNGDocumentPush:
679 * @ctxt: the parser context
680 * @value: the element doc
681 *
682 * Pushes a new doc on top of the doc stack
683 *
684 * Returns 0 in case of error, the index in the stack otherwise
685 */
686static int
687xmlRelaxNGDocumentPush(xmlRelaxNGParserCtxtPtr ctxt,
688 xmlRelaxNGDocumentPtr value)
689{
690 if (ctxt->docTab == NULL) {
691 ctxt->docMax = 4;
692 ctxt->docNr = 0;
693 ctxt->docTab = (xmlRelaxNGDocumentPtr *) xmlMalloc(
694 ctxt->docMax * sizeof(ctxt->docTab[0]));
695 if (ctxt->docTab == NULL) {
696 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
697 return (0);
698 }
699 }
700 if (ctxt->docNr >= ctxt->docMax) {
701 ctxt->docMax *= 2;
702 ctxt->docTab =
703 (xmlRelaxNGDocumentPtr *) xmlRealloc(ctxt->docTab,
704 ctxt->docMax *
705 sizeof(ctxt->docTab[0]));
706 if (ctxt->docTab == NULL) {
707 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
708 return (0);
709 }
710 }
711 ctxt->docTab[ctxt->docNr] = value;
712 ctxt->doc = value;
713 return (ctxt->docNr++);
714}
715
716/**
717 * xmlRelaxNGDocumentPop:
718 * @ctxt: the parser context
719 *
720 * Pops the top doc from the doc stack
721 *
722 * Returns the doc just removed
723 */
724static xmlRelaxNGDocumentPtr
725xmlRelaxNGDocumentPop(xmlRelaxNGParserCtxtPtr ctxt)
726{
727 xmlRelaxNGDocumentPtr ret;
728
729 if (ctxt->docNr <= 0)
730 return (0);
731 ctxt->docNr--;
732 if (ctxt->docNr > 0)
733 ctxt->doc = ctxt->docTab[ctxt->docNr - 1];
734 else
735 ctxt->doc = NULL;
736 ret = ctxt->docTab[ctxt->docNr];
737 ctxt->docTab[ctxt->docNr] = 0;
738 return (ret);
739}
740
741/**
742 * xmlRelaxNGLoadocument:
743 * @ctxt: the parser context
744 * @URL: the normalized URL
745 * @ns: the inherited ns if any
746 *
747 * First lookup if the document is already loaded into the parser context,
748 * check against recursion. If not found the resource is loaded and
749 * the content is preprocessed before being returned back to the caller.
750 *
751 * Returns the xmlRelaxNGDocumentPtr or NULL in case of error
752 */
753static xmlRelaxNGDocumentPtr
754xmlRelaxNGLoaddocument(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
755 const xmlChar *ns) {
756 xmlRelaxNGDocumentPtr ret = NULL;
757 xmlDocPtr doc;
758 xmlNodePtr root;
759 int i;
760
761 /*
762 * check against recursion in the stack
763 */
764 for (i = 0;i < ctxt->docNr;i++) {
765 if (xmlStrEqual(ctxt->docTab[i]->href, URL)) {
766 if (ctxt->error != NULL)
767 ctxt->error(ctxt->userData,
768 "Detected an externalRef recursion for %s\n",
769 URL);
770 ctxt->nbErrors++;
771 return(NULL);
772 }
773 }
774
775 /*
776 * Lookup in the hash table
777 */
778 if (ctxt->documents == NULL) {
779 ctxt->documents = xmlHashCreate(10);
780 if (ctxt->documents == NULL) {
781 if (ctxt->error != NULL)
782 ctxt->error(ctxt->userData,
783 "Failed to allocate hash table for document\n");
784 ctxt->nbErrors++;
785 return(NULL);
786 }
787 } else {
788 if (ns == NULL)
789 ret = xmlHashLookup2(ctxt->documents, BAD_CAST "", URL);
790 else
791 ret = xmlHashLookup2(ctxt->documents, ns, URL);
792 if (ret != NULL)
793 return(ret);
794 }
795
796
797 /*
798 * load the document
799 */
800 doc = xmlParseFile((const char *) URL);
801 if (doc == NULL) {
802 if (ctxt->error != NULL)
803 ctxt->error(ctxt->userData,
804 "xmlRelaxNG: could not load %s\n", URL);
805 ctxt->nbErrors++;
806 return (NULL);
807 }
808
809 /*
810 * Allocate the document structures and register it first.
811 */
812 ret = (xmlRelaxNGDocumentPtr) xmlMalloc(sizeof(xmlRelaxNGDocument));
813 if (ret == NULL) {
814 if (ctxt->error != NULL)
815 ctxt->error(ctxt->userData,
816 "xmlRelaxNG: allocate memory for doc %s\n", URL);
817 ctxt->nbErrors++;
818 xmlFreeDoc(doc);
819 return (NULL);
820 }
821 memset(ret, 0, sizeof(xmlRelaxNGDocument));
822 ret->doc = doc;
823 ret->href = xmlStrdup(URL);
824
825 /*
826 * transmit the ns if needed
827 */
828 if (ns != NULL) {
829 root = xmlDocGetRootElement(doc);
830 if (root != NULL) {
831 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
832 xmlSetProp(root, BAD_CAST"ns", ns);
833 }
834 }
835 }
836
837 /*
838 * push it on the stack and register it in the hash table
839 */
840 if (ns == NULL)
841 xmlHashAddEntry2(ctxt->documents, BAD_CAST "", URL, ret);
842 else
843 xmlHashAddEntry2(ctxt->documents, ns, URL, ret);
844 xmlRelaxNGDocumentPush(ctxt, ret);
845
846 /*
847 * Some preprocessing of the document content
848 */
849 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
850 if (doc == NULL) {
851 xmlFreeDoc(ctxt->document);
852 ctxt->doc = NULL;
853 return(NULL);
854 }
855
856 xmlRelaxNGDocumentPop(ctxt);
857
858 return(ret);
859}
860
861/************************************************************************
862 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +0000863 * Error functions *
864 * *
865 ************************************************************************/
866
867#define VALID_CTXT() \
868 if (ctxt->flags == 0) xmlGenericError(xmlGenericErrorContext, \
869 "error detected at %s:%d\n", \
870 __FILE__, __LINE__);
871#define VALID_ERROR if (ctxt->flags == 0) printf
872
873#if 0
874/**
875 * xmlRelaxNGErrorContext:
876 * @ctxt: the parsing context
877 * @schema: the schema being built
878 * @node: the node being processed
879 * @child: the child being processed
880 *
881 * Dump a RelaxNGType structure
882 */
883static void
884xmlRelaxNGErrorContext(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGPtr schema,
885 xmlNodePtr node, xmlNodePtr child)
886{
887 int line = 0;
888 const xmlChar *file = NULL;
889 const xmlChar *name = NULL;
890 const char *type = "error";
891
892 if ((ctxt == NULL) || (ctxt->error == NULL))
893 return;
894
895 if (child != NULL)
896 node = child;
897
898 if (node != NULL) {
899 if ((node->type == XML_DOCUMENT_NODE) ||
900 (node->type == XML_HTML_DOCUMENT_NODE)) {
901 xmlDocPtr doc = (xmlDocPtr) node;
902
903 file = doc->URL;
904 } else {
905 /*
906 * Try to find contextual informations to report
907 */
908 if (node->type == XML_ELEMENT_NODE) {
909 line = (int) node->content;
910 } else if ((node->prev != NULL) &&
911 (node->prev->type == XML_ELEMENT_NODE)) {
912 line = (int) node->prev->content;
913 } else if ((node->parent != NULL) &&
914 (node->parent->type == XML_ELEMENT_NODE)) {
915 line = (int) node->parent->content;
916 }
917 if ((node->doc != NULL) && (node->doc->URL != NULL))
918 file = node->doc->URL;
919 if (node->name != NULL)
920 name = node->name;
921 }
922 }
923
924 if (ctxt != NULL)
925 type = "compilation error";
926 else if (schema != NULL)
927 type = "runtime error";
928
929 if ((file != NULL) && (line != 0) && (name != NULL))
930 ctxt->error(ctxt->userData, "%s: file %s line %d element %s\n",
931 type, file, line, name);
932 else if ((file != NULL) && (name != NULL))
933 ctxt->error(ctxt->userData, "%s: file %s element %s\n",
934 type, file, name);
935 else if ((file != NULL) && (line != 0))
936 ctxt->error(ctxt->userData, "%s: file %s line %d\n", type, file, line);
937 else if (file != NULL)
938 ctxt->error(ctxt->userData, "%s: file %s\n", type, file);
939 else if (name != NULL)
940 ctxt->error(ctxt->userData, "%s: element %s\n", type, name);
941 else
942 ctxt->error(ctxt->userData, "%s\n", type);
943}
944#endif
945
946/************************************************************************
947 * *
948 * Type library hooks *
949 * *
950 ************************************************************************/
Daniel Veillardea3f3982003-01-26 19:45:18 +0000951static xmlChar *xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt,
952 const xmlChar *str);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000953
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000954/**
955 * xmlRelaxNGSchemaTypeHave:
956 * @data: data needed for the library
957 * @type: the type name
958 *
959 * Check if the given type is provided by
960 * the W3C XMLSchema Datatype library.
961 *
962 * Returns 1 if yes, 0 if no and -1 in case of error.
963 */
964static int
965xmlRelaxNGSchemaTypeHave(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000966 const xmlChar *type) {
967 xmlSchemaTypePtr typ;
968
969 if (type == NULL)
970 return(-1);
971 typ = xmlSchemaGetPredefinedType(type,
972 BAD_CAST "http://www.w3.org/2001/XMLSchema");
973 if (typ == NULL)
974 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000975 return(1);
976}
977
978/**
979 * xmlRelaxNGSchemaTypeCheck:
980 * @data: data needed for the library
981 * @type: the type name
982 * @value: the value to check
983 *
984 * Check if the given type and value are validated by
985 * the W3C XMLSchema Datatype library.
986 *
987 * Returns 1 if yes, 0 if no and -1 in case of error.
988 */
989static int
990xmlRelaxNGSchemaTypeCheck(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000991 const xmlChar *type,
992 const xmlChar *value) {
993 xmlSchemaTypePtr typ;
994 int ret;
995
996 /*
997 * TODO: the type should be cached ab provided back, interface subject
998 * to changes.
999 * TODO: handle facets, may require an additional interface and keep
1000 * the value returned from the validation.
1001 */
1002 if ((type == NULL) || (value == NULL))
1003 return(-1);
1004 typ = xmlSchemaGetPredefinedType(type,
1005 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1006 if (typ == NULL)
1007 return(-1);
1008 ret = xmlSchemaValidatePredefinedType(typ, value, NULL);
1009 if (ret == 0)
1010 return(1);
1011 if (ret > 0)
1012 return(0);
1013 return(-1);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001014}
1015
1016/**
1017 * xmlRelaxNGSchemaTypeCompare:
1018 * @data: data needed for the library
1019 * @type: the type name
1020 * @value1: the first value
1021 * @value2: the second value
1022 *
1023 * Compare two values accordingly a type from the W3C XMLSchema
1024 * Datatype library.
1025 *
1026 * Returns 1 if yes, 0 if no and -1 in case of error.
1027 */
1028static int
1029xmlRelaxNGSchemaTypeCompare(void *data ATTRIBUTE_UNUSED,
1030 const xmlChar *type ATTRIBUTE_UNUSED,
1031 const xmlChar *value1 ATTRIBUTE_UNUSED,
1032 const xmlChar *value2 ATTRIBUTE_UNUSED) {
1033 TODO
1034 return(1);
1035}
1036
1037/**
1038 * xmlRelaxNGDefaultTypeHave:
1039 * @data: data needed for the library
1040 * @type: the type name
1041 *
1042 * Check if the given type is provided by
1043 * the default datatype library.
1044 *
1045 * Returns 1 if yes, 0 if no and -1 in case of error.
1046 */
1047static int
1048xmlRelaxNGDefaultTypeHave(void *data ATTRIBUTE_UNUSED, const xmlChar *type) {
1049 if (type == NULL)
1050 return(-1);
1051 if (xmlStrEqual(type, BAD_CAST "string"))
1052 return(1);
1053 if (xmlStrEqual(type, BAD_CAST "token"))
1054 return(1);
1055 return(0);
1056}
1057
1058/**
1059 * xmlRelaxNGDefaultTypeCheck:
1060 * @data: data needed for the library
1061 * @type: the type name
1062 * @value: the value to check
1063 *
1064 * Check if the given type and value are validated by
1065 * the default datatype library.
1066 *
1067 * Returns 1 if yes, 0 if no and -1 in case of error.
1068 */
1069static int
1070xmlRelaxNGDefaultTypeCheck(void *data ATTRIBUTE_UNUSED,
1071 const xmlChar *type ATTRIBUTE_UNUSED,
1072 const xmlChar *value ATTRIBUTE_UNUSED) {
1073 return(1);
1074}
1075
1076/**
1077 * xmlRelaxNGDefaultTypeCompare:
1078 * @data: data needed for the library
1079 * @type: the type name
1080 * @value1: the first value
1081 * @value2: the second value
1082 *
1083 * Compare two values accordingly a type from the default
1084 * datatype library.
1085 *
1086 * Returns 1 if yes, 0 if no and -1 in case of error.
1087 */
1088static int
1089xmlRelaxNGDefaultTypeCompare(void *data ATTRIBUTE_UNUSED,
1090 const xmlChar *type ATTRIBUTE_UNUSED,
1091 const xmlChar *value1 ATTRIBUTE_UNUSED,
1092 const xmlChar *value2 ATTRIBUTE_UNUSED) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00001093 int ret = -1;
1094
1095 if (xmlStrEqual(type, BAD_CAST "string")) {
1096 ret = xmlStrEqual(value1, value2);
1097 } else if (xmlStrEqual(type, BAD_CAST "token")) {
1098 if (!xmlStrEqual(value1, value2)) {
1099 xmlChar *nval, *nvalue;
1100
1101 /*
1102 * TODO: trivial optimizations are possible by
1103 * computing at compile-time
1104 */
1105 nval = xmlRelaxNGNormalize(NULL, value1);
1106 nvalue = xmlRelaxNGNormalize(NULL, value2);
1107
1108 if ((nval == NULL) || (nvalue == NULL) ||
1109 (!xmlStrEqual(nval, nvalue)))
1110 ret = -1;
1111 if (nval != NULL)
1112 xmlFree(nval);
1113 if (nvalue != NULL)
1114 xmlFree(nvalue);
1115 }
1116 }
1117 return(ret);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001118}
1119
1120static int xmlRelaxNGTypeInitialized = 0;
1121static xmlHashTablePtr xmlRelaxNGRegisteredTypes = NULL;
1122
1123/**
1124 * xmlRelaxNGFreeTypeLibrary:
1125 * @lib: the type library structure
1126 * @namespace: the URI bound to the library
1127 *
1128 * Free the structure associated to the type library
1129 */
Daniel Veillard6eadf632003-01-23 18:29:16 +00001130static void
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001131xmlRelaxNGFreeTypeLibrary(xmlRelaxNGTypeLibraryPtr lib,
1132 const xmlChar *namespace ATTRIBUTE_UNUSED) {
1133 if (lib == NULL)
1134 return;
1135 if (lib->namespace != NULL)
1136 xmlFree((xmlChar *)lib->namespace);
1137 xmlFree(lib);
1138}
1139
1140/**
1141 * xmlRelaxNGRegisterTypeLibrary:
1142 * @namespace: the URI bound to the library
1143 * @data: data associated to the library
1144 * @have: the provide function
1145 * @check: the checking function
1146 * @comp: the comparison function
1147 *
1148 * Register a new type library
1149 *
1150 * Returns 0 in case of success and -1 in case of error.
1151 */
1152static int
1153xmlRelaxNGRegisterTypeLibrary(const xmlChar *namespace, void *data,
1154 xmlRelaxNGTypeHave have, xmlRelaxNGTypeCheck check,
1155 xmlRelaxNGTypeCompare comp) {
1156 xmlRelaxNGTypeLibraryPtr lib;
1157 int ret;
1158
1159 if ((xmlRelaxNGRegisteredTypes == NULL) || (namespace == NULL) ||
1160 (check == NULL) || (comp == NULL))
1161 return(-1);
1162 if (xmlHashLookup(xmlRelaxNGRegisteredTypes, namespace) != NULL) {
1163 xmlGenericError(xmlGenericErrorContext,
1164 "Relax-NG types library '%s' already registered\n",
1165 namespace);
1166 return(-1);
1167 }
1168 lib = (xmlRelaxNGTypeLibraryPtr) xmlMalloc(sizeof(xmlRelaxNGTypeLibrary));
1169 if (lib == NULL) {
1170 xmlGenericError(xmlGenericErrorContext,
1171 "Relax-NG types library '%s' malloc() failed\n",
1172 namespace);
1173 return (-1);
1174 }
1175 memset(lib, 0, sizeof(xmlRelaxNGTypeLibrary));
1176 lib->namespace = xmlStrdup(namespace);
1177 lib->data = data;
1178 lib->have = have;
1179 lib->comp = comp;
1180 lib->check = check;
1181 ret = xmlHashAddEntry(xmlRelaxNGRegisteredTypes, namespace, lib);
1182 if (ret < 0) {
1183 xmlGenericError(xmlGenericErrorContext,
1184 "Relax-NG types library failed to register '%s'\n",
1185 namespace);
1186 xmlRelaxNGFreeTypeLibrary(lib, namespace);
1187 return(-1);
1188 }
1189 return(0);
1190}
1191
1192/**
1193 * xmlRelaxNGInitTypes:
1194 *
1195 * Initilize the default type libraries.
1196 *
1197 * Returns 0 in case of success and -1 in case of error.
1198 */
1199static int
Daniel Veillard6eadf632003-01-23 18:29:16 +00001200xmlRelaxNGInitTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001201 if (xmlRelaxNGTypeInitialized != 0)
1202 return(0);
1203 xmlRelaxNGRegisteredTypes = xmlHashCreate(10);
1204 if (xmlRelaxNGRegisteredTypes == NULL) {
1205 xmlGenericError(xmlGenericErrorContext,
1206 "Failed to allocate sh table for Relax-NG types\n");
1207 return(-1);
1208 }
1209 xmlRelaxNGRegisterTypeLibrary(
1210 BAD_CAST "http://www.w3.org/2001/XMLSchema-datatypes",
1211 NULL,
1212 xmlRelaxNGSchemaTypeHave,
1213 xmlRelaxNGSchemaTypeCheck,
1214 xmlRelaxNGSchemaTypeCompare);
1215 xmlRelaxNGRegisterTypeLibrary(
1216 xmlRelaxNGNs,
1217 NULL,
1218 xmlRelaxNGDefaultTypeHave,
1219 xmlRelaxNGDefaultTypeCheck,
1220 xmlRelaxNGDefaultTypeCompare);
1221 xmlRelaxNGTypeInitialized = 1;
1222 return(0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001223}
1224
1225/**
1226 * xmlRelaxNGCleanupTypes:
1227 *
1228 * Cleanup the default Schemas type library associated to RelaxNG
1229 */
1230void
1231xmlRelaxNGCleanupTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001232 if (xmlRelaxNGTypeInitialized == 0)
1233 return;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001234 xmlSchemaCleanupTypes();
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001235 xmlHashFree(xmlRelaxNGRegisteredTypes, (xmlHashDeallocator)
1236 xmlRelaxNGFreeTypeLibrary);
1237 xmlRelaxNGTypeInitialized = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001238}
1239
1240/************************************************************************
1241 * *
1242 * Parsing functions *
1243 * *
1244 ************************************************************************/
1245
1246static xmlRelaxNGDefinePtr xmlRelaxNGParseAttribute(
1247 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1248static xmlRelaxNGDefinePtr xmlRelaxNGParseElement(
1249 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1250static xmlRelaxNGDefinePtr xmlRelaxNGParsePatterns(
Daniel Veillard154877e2003-01-30 12:17:05 +00001251 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes, int group);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001252static xmlRelaxNGDefinePtr xmlRelaxNGParsePattern(
1253 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001254static xmlRelaxNGPtr xmlRelaxNGParseDocument(
1255 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001256
1257
1258#define IS_BLANK_NODE(n) \
1259 (((n)->type == XML_TEXT_NODE) && (xmlRelaxNGIsBlank((n)->content)))
1260
1261/**
1262 * xmlRelaxNGIsBlank:
1263 * @str: a string
1264 *
1265 * Check if a string is ignorable c.f. 4.2. Whitespace
1266 *
1267 * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
1268 */
1269static int
1270xmlRelaxNGIsBlank(xmlChar *str) {
1271 if (str == NULL)
1272 return(1);
1273 while (*str != 0) {
1274 if (!(IS_BLANK(*str))) return(0);
1275 str++;
1276 }
1277 return(1);
1278}
1279
Daniel Veillard6eadf632003-01-23 18:29:16 +00001280/**
1281 * xmlRelaxNGGetDataTypeLibrary:
1282 * @ctxt: a Relax-NG parser context
1283 * @node: the current data or value element
1284 *
1285 * Applies algorithm from 4.3. datatypeLibrary attribute
1286 *
1287 * Returns the datatypeLibary value or NULL if not found
1288 */
1289static xmlChar *
1290xmlRelaxNGGetDataTypeLibrary(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1291 xmlNodePtr node) {
1292 xmlChar *ret, *escape;
1293
Daniel Veillard6eadf632003-01-23 18:29:16 +00001294 if ((IS_RELAXNG(node, "data")) || (IS_RELAXNG(node, "value"))) {
1295 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1296 if (ret != NULL) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001297 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +00001298 if (escape == NULL) {
1299 return(ret);
1300 }
1301 xmlFree(ret);
1302 return(escape);
1303 }
1304 }
1305 node = node->parent;
1306 while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) {
1307 if (IS_RELAXNG(node, "element")) {
1308 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1309 if (ret != NULL) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001310 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +00001311 if (escape == NULL) {
1312 return(ret);
1313 }
1314 xmlFree(ret);
1315 return(escape);
1316 }
1317 }
1318 node = node->parent;
1319 }
1320 return(NULL);
1321}
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001322
1323/**
Daniel Veillardedc91922003-01-26 00:52:04 +00001324 * xmlRelaxNGParseValue:
1325 * @ctxt: a Relax-NG parser context
1326 * @node: the data node.
1327 *
1328 * parse the content of a RelaxNG value node.
1329 *
1330 * Returns the definition pointer or NULL in case of error
1331 */
1332static xmlRelaxNGDefinePtr
1333xmlRelaxNGParseValue(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1334 xmlRelaxNGDefinePtr def = NULL;
1335 xmlRelaxNGTypeLibraryPtr lib;
1336 xmlChar *type;
1337 xmlChar *library;
1338 int tmp;
1339
1340 def = xmlRelaxNGNewDefine(ctxt, node);
1341 if (def == NULL)
1342 return(NULL);
1343 def->type = XML_RELAXNG_VALUE;
1344
1345 type = xmlGetProp(node, BAD_CAST "type");
1346 if (type != NULL) {
1347 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1348 if (library == NULL)
1349 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1350
1351 def->name = type;
1352 def->ns = library;
1353
1354 lib = (xmlRelaxNGTypeLibraryPtr)
1355 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1356 if (lib == NULL) {
1357 if (ctxt->error != NULL)
1358 ctxt->error(ctxt->userData,
1359 "Use of unregistered type library '%s'\n",
1360 library);
1361 ctxt->nbErrors++;
1362 def->data = NULL;
1363 } else {
1364 def->data = lib;
1365 if (lib->have == NULL) {
1366 ctxt->error(ctxt->userData,
1367 "Internal error with type library '%s': no 'have'\n",
1368 library);
1369 ctxt->nbErrors++;
1370 } else {
1371 tmp = lib->have(lib->data, def->name);
1372 if (tmp != 1) {
1373 ctxt->error(ctxt->userData,
1374 "Error type '%s' is not exported by type library '%s'\n",
1375 def->name, library);
1376 ctxt->nbErrors++;
1377 }
1378 }
1379 }
1380 }
1381 if (node->children == NULL) {
1382 if (ctxt->error != NULL)
1383 ctxt->error(ctxt->userData,
1384 "Element <value> has no content\n");
1385 ctxt->nbErrors++;
1386 } else if ((node->children->type != XML_TEXT_NODE) ||
1387 (node->children->next != NULL)) {
1388 if (ctxt->error != NULL)
1389 ctxt->error(ctxt->userData,
1390 "Expecting a single text value for <value>content\n");
1391 ctxt->nbErrors++;
1392 } else {
1393 def->value = xmlNodeGetContent(node);
1394 if (def->value == NULL) {
1395 if (ctxt->error != NULL)
1396 ctxt->error(ctxt->userData,
1397 "Element <value> has no content\n");
1398 ctxt->nbErrors++;
1399 }
1400 }
1401 /* TODO check ahead of time that the value is okay per the type */
1402 return(def);
1403}
1404
1405/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001406 * xmlRelaxNGParseData:
1407 * @ctxt: a Relax-NG parser context
1408 * @node: the data node.
1409 *
1410 * parse the content of a RelaxNG data node.
1411 *
1412 * Returns the definition pointer or NULL in case of error
1413 */
1414static xmlRelaxNGDefinePtr
1415xmlRelaxNGParseData(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1416 xmlRelaxNGDefinePtr def = NULL;
1417 xmlRelaxNGTypeLibraryPtr lib;
1418 xmlChar *type;
1419 xmlChar *library;
1420 xmlNodePtr content;
1421 int tmp;
1422
1423 type = xmlGetProp(node, BAD_CAST "type");
1424 if (type == NULL) {
1425 if (ctxt->error != NULL)
1426 ctxt->error(ctxt->userData,
1427 "data has no type\n");
1428 ctxt->nbErrors++;
1429 return(NULL);
1430 }
1431 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1432 if (library == NULL)
1433 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1434
1435 def = xmlRelaxNGNewDefine(ctxt, node);
1436 if (def == NULL) {
1437 xmlFree(type);
1438 return(NULL);
1439 }
1440 def->type = XML_RELAXNG_DATATYPE;
1441 def->name = type;
1442 def->ns = library;
1443
1444 lib = (xmlRelaxNGTypeLibraryPtr)
1445 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1446 if (lib == NULL) {
1447 if (ctxt->error != NULL)
1448 ctxt->error(ctxt->userData,
1449 "Use of unregistered type library '%s'\n",
1450 library);
1451 ctxt->nbErrors++;
1452 def->data = NULL;
1453 } else {
1454 def->data = lib;
1455 if (lib->have == NULL) {
1456 ctxt->error(ctxt->userData,
1457 "Internal error with type library '%s': no 'have'\n",
1458 library);
1459 ctxt->nbErrors++;
1460 } else {
1461 tmp = lib->have(lib->data, def->name);
1462 if (tmp != 1) {
1463 ctxt->error(ctxt->userData,
1464 "Error type '%s' is not exported by type library '%s'\n",
1465 def->name, library);
1466 ctxt->nbErrors++;
1467 }
1468 }
1469 }
1470 content = node->children;
1471 while (content != NULL) {
1472 TODO
1473 content = content->next;
1474 }
1475
1476 return(def);
1477}
1478
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001479/**
1480 * xmlRelaxNGCompareElemDefLists:
1481 * @ctxt: a Relax-NG parser context
1482 * @defs1: the first list of element defs
1483 * @defs2: the second list of element defs
1484 *
1485 * Compare the 2 lists of element definitions. The comparison is
1486 * that if both lists do not accept the same QNames, it returns 1
1487 * If the 2 lists can accept the same QName the comparison returns 0
1488 *
1489 * Returns 1 disttinct, 0 if equal
1490 */
1491static int
1492xmlRelaxNGCompareElemDefLists(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1493 xmlRelaxNGDefinePtr *def1,
1494 xmlRelaxNGDefinePtr *def2) {
1495 xmlRelaxNGDefinePtr *basedef2 = def2;
1496
Daniel Veillard154877e2003-01-30 12:17:05 +00001497 if ((def1 == NULL) || (def2 == NULL))
1498 return(1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001499 if ((*def1 == NULL) || (*def2 == NULL))
1500 return(1);
1501 while (*def1 != NULL) {
1502 while ((*def2) != NULL) {
1503 if ((*def1)->name == NULL) {
1504 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1505 return(0);
1506 } else if ((*def2)->name == NULL) {
1507 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1508 return(0);
1509 } else if (xmlStrEqual((*def1)->name, (*def2)->name)) {
1510 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1511 return(0);
1512 }
1513 def2++;
1514 }
1515 def2 = basedef2;
1516 def1++;
1517 }
1518 return(1);
1519}
1520
1521/**
1522 * xmlRelaxNGGetElements:
1523 * @ctxt: a Relax-NG parser context
1524 * @def: the interleave definition
1525 *
1526 * Compute the list of top elements a definition can generate
1527 *
1528 * Returns a list of elements or NULL if none was found.
1529 */
1530static xmlRelaxNGDefinePtr *
1531xmlRelaxNGGetElements(xmlRelaxNGParserCtxtPtr ctxt,
1532 xmlRelaxNGDefinePtr def) {
1533 xmlRelaxNGDefinePtr *ret = NULL, parent, cur, tmp;
1534 int len = 0;
1535 int max = 0;
1536
1537 parent = NULL;
1538 cur = def;
1539 while (cur != NULL) {
Daniel Veillardb08c9812003-01-28 23:09:49 +00001540 if ((cur->type == XML_RELAXNG_ELEMENT) ||
1541 (cur->type == XML_RELAXNG_TEXT)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001542 if (ret == NULL) {
1543 max = 10;
1544 ret = (xmlRelaxNGDefinePtr *)
1545 xmlMalloc((max + 1) * sizeof(xmlRelaxNGDefinePtr));
1546 if (ret == NULL) {
1547 if (ctxt->error != NULL)
1548 ctxt->error(ctxt->userData,
1549 "Out of memory in element search\n");
1550 ctxt->nbErrors++;
1551 return(NULL);
1552 }
1553 } else if (max <= len) {
1554 max *= 2;
1555 ret = xmlRealloc(ret, (max + 1) * sizeof(xmlRelaxNGDefinePtr));
1556 if (ret == NULL) {
1557 if (ctxt->error != NULL)
1558 ctxt->error(ctxt->userData,
1559 "Out of memory in element search\n");
1560 ctxt->nbErrors++;
1561 return(NULL);
1562 }
1563 }
Daniel Veillardb08c9812003-01-28 23:09:49 +00001564 ret[len++] = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001565 ret[len] = NULL;
1566 } else if ((cur->type == XML_RELAXNG_CHOICE) ||
1567 (cur->type == XML_RELAXNG_INTERLEAVE) ||
1568 (cur->type == XML_RELAXNG_GROUP) ||
1569 (cur->type == XML_RELAXNG_ONEORMORE) ||
Daniel Veillardb08c9812003-01-28 23:09:49 +00001570 (cur->type == XML_RELAXNG_ZEROORMORE) ||
1571 (cur->type == XML_RELAXNG_OPTIONAL) ||
1572 (cur->type == XML_RELAXNG_REF) ||
1573 (cur->type == XML_RELAXNG_DEF)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001574 /*
1575 * Don't go within elements or attributes or string values.
1576 * Just gather the element top list
1577 */
1578 if (cur->content != NULL) {
1579 parent = cur;
1580 cur = cur->content;
1581 tmp = cur;
1582 while (tmp != NULL) {
1583 tmp->parent = parent;
1584 tmp = tmp->next;
1585 }
1586 continue;
1587 }
1588 }
Daniel Veillard154877e2003-01-30 12:17:05 +00001589 if (cur == def)
1590 return(ret);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001591 if (cur->next != NULL) {
1592 cur = cur->next;
1593 continue;
1594 }
1595 do {
1596 cur = cur->parent;
1597 if (cur == NULL) break;
1598 if (cur == def) return(ret);
1599 if (cur->next != NULL) {
1600 cur = cur->next;
1601 break;
1602 }
1603 } while (cur != NULL);
1604 }
1605 return(ret);
1606}
1607
1608/**
1609 * xmlRelaxNGComputeInterleaves:
1610 * @def: the interleave definition
1611 * @ctxt: a Relax-NG parser context
1612 * @node: the data node.
1613 *
1614 * A lot of work for preprocessing interleave definitions
1615 * is potentially needed to get a decent execution speed at runtime
1616 * - trying to get a total order on the element nodes generated
1617 * by the interleaves, order the list of interleave definitions
1618 * following that order.
1619 * - if <text/> is used to handle mixed content, it is better to
1620 * flag this in the define and simplify the runtime checking
1621 * algorithm
1622 */
1623static void
1624xmlRelaxNGComputeInterleaves(xmlRelaxNGDefinePtr def,
1625 xmlRelaxNGParserCtxtPtr ctxt,
1626 xmlChar *name ATTRIBUTE_UNUSED) {
1627 xmlRelaxNGDefinePtr cur;
1628
1629 xmlRelaxNGDefinePtr *list = NULL;
1630 xmlRelaxNGPartitionPtr partitions = NULL;
1631 xmlRelaxNGInterleaveGroupPtr *groups = NULL;
1632 xmlRelaxNGInterleaveGroupPtr group;
1633 int i,j,ret;
1634 int nbgroups = 0;
1635 int nbchild = 0;
1636
1637#ifdef DEBUG_INTERLEAVE
1638 xmlGenericError(xmlGenericErrorContext,
1639 "xmlRelaxNGComputeInterleaves(%s)\n",
1640 name);
1641#endif
1642 cur = def->content;
1643 while (cur != NULL) {
1644 nbchild++;
1645 cur = cur->next;
1646 }
1647
1648#ifdef DEBUG_INTERLEAVE
1649 xmlGenericError(xmlGenericErrorContext, " %d child\n", nbchild);
1650#endif
1651 groups = (xmlRelaxNGInterleaveGroupPtr *)
1652 xmlMalloc(nbchild * sizeof(xmlRelaxNGInterleaveGroupPtr));
1653 if (groups == NULL)
1654 goto error;
1655 cur = def->content;
1656 while (cur != NULL) {
Daniel Veillard154877e2003-01-30 12:17:05 +00001657 groups[nbgroups] = (xmlRelaxNGInterleaveGroupPtr)
1658 xmlMalloc(sizeof(xmlRelaxNGInterleaveGroup));
1659 if (groups[nbgroups] == NULL)
1660 goto error;
1661 groups[nbgroups]->rule = cur;
1662 groups[nbgroups]->defs = xmlRelaxNGGetElements(ctxt, cur);
1663 nbgroups++;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001664 cur = cur->next;
1665 }
1666 list = NULL;
1667#ifdef DEBUG_INTERLEAVE
1668 xmlGenericError(xmlGenericErrorContext, " %d groups\n", nbgroups);
1669#endif
1670
1671 /*
1672 * Let's check that all rules makes a partitions according to 7.4
1673 */
1674 partitions = (xmlRelaxNGPartitionPtr)
1675 xmlMalloc(sizeof(xmlRelaxNGPartition));
1676 if (partitions == NULL)
1677 goto error;
1678 partitions->nbgroups = nbgroups;
1679 for (i = 0;i < nbgroups;i++) {
1680 group = groups[i];
1681 for (j = i+1;j < nbgroups;j++) {
1682 if (groups[j] == NULL)
1683 continue;
1684 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->defs,
1685 groups[j]->defs);
1686 if (ret == 0) {
1687 if (ctxt->error != NULL)
1688 ctxt->error(ctxt->userData,
1689 "Element or text conflicts in interleave\n");
1690 ctxt->nbErrors++;
1691 }
1692 }
1693 }
1694 partitions->groups = groups;
1695
1696 /*
1697 * Free Up the child list, and save the partition list back in the def
1698 */
1699 def->data = partitions;
1700 return;
1701
1702error:
1703 if (ctxt->error != NULL)
1704 ctxt->error(ctxt->userData,
1705 "Out of memory in interleave computation\n");
1706 ctxt->nbErrors++;
1707 if (list == NULL)
1708 xmlFree(list);
1709 if (groups != NULL) {
1710 for (i = 0;i < nbgroups;i++)
1711 if (groups[i] != NULL) {
1712 if (groups[i]->defs != NULL)
1713 xmlFree(groups[i]->defs);
1714 xmlFree(groups[i]);
1715 }
1716 xmlFree(groups);
1717 }
1718 xmlRelaxNGFreePartition(partitions);
1719}
1720
1721/**
1722 * xmlRelaxNGParseInterleave:
1723 * @ctxt: a Relax-NG parser context
1724 * @node: the data node.
1725 *
1726 * parse the content of a RelaxNG interleave node.
1727 *
1728 * Returns the definition pointer or NULL in case of error
1729 */
1730static xmlRelaxNGDefinePtr
1731xmlRelaxNGParseInterleave(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1732 xmlRelaxNGDefinePtr def = NULL;
1733 xmlRelaxNGDefinePtr last = NULL, cur;
1734 xmlNodePtr child;
1735
1736 def = xmlRelaxNGNewDefine(ctxt, node);
1737 if (def == NULL) {
1738 return(NULL);
1739 }
1740 def->type = XML_RELAXNG_INTERLEAVE;
1741
1742 if (ctxt->interleaves == NULL)
1743 ctxt->interleaves = xmlHashCreate(10);
1744 if (ctxt->interleaves == NULL) {
1745 if (ctxt->error != NULL)
1746 ctxt->error(ctxt->userData,
1747 "Failed to create interleaves hash table\n");
1748 ctxt->nbErrors++;
1749 } else {
1750 char name[32];
1751
1752 snprintf(name, 32, "interleave%d", ctxt->nbInterleaves++);
1753 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST name, def) < 0) {
1754 if (ctxt->error != NULL)
1755 ctxt->error(ctxt->userData,
1756 "Failed to add %s to hash table\n", name);
1757 ctxt->nbErrors++;
1758 }
1759 }
1760 child = node->children;
1761 while (child != NULL) {
1762 if (IS_RELAXNG(child, "element")) {
1763 cur = xmlRelaxNGParseElement(ctxt, child);
1764 } else {
1765 cur = xmlRelaxNGParsePattern(ctxt, child);
1766 }
1767 if (cur != NULL) {
1768 cur->parent = def;
1769 if (last == NULL) {
1770 def->content = last = cur;
1771 } else {
1772 last->next = cur;
1773 last = cur;
1774 }
1775 }
1776 child = child->next;
1777 }
1778
1779 return(def);
1780}
Daniel Veillard6eadf632003-01-23 18:29:16 +00001781
1782/**
Daniel Veillard276be4a2003-01-24 01:03:34 +00001783 * xmlRelaxNGParseDefine:
1784 * @ctxt: a Relax-NG parser context
1785 * @node: the define node
1786 *
1787 * parse the content of a RelaxNG define element node.
1788 *
1789 * Returns the definition pointer or NULL in case of error.
1790 */
1791static int
1792xmlRelaxNGParseDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1793 xmlChar *name;
1794 int ret = 0, tmp;
1795 xmlRelaxNGDefinePtr def;
1796 const xmlChar *olddefine;
1797
1798 name = xmlGetProp(node, BAD_CAST "name");
1799 if (name == NULL) {
1800 if (ctxt->error != NULL)
1801 ctxt->error(ctxt->userData,
1802 "define has no name\n");
1803 ctxt->nbErrors++;
1804 } else {
1805 def = xmlRelaxNGNewDefine(ctxt, node);
1806 if (def == NULL) {
1807 xmlFree(name);
1808 return(-1);
1809 }
1810 def->type = XML_RELAXNG_DEF;
1811 def->name = name;
1812 if (node->children == NULL) {
1813 if (ctxt->error != NULL)
1814 ctxt->error(ctxt->userData,
1815 "define has no children\n");
1816 ctxt->nbErrors++;
1817 } else {
1818 olddefine = ctxt->define;
1819 ctxt->define = name;
Daniel Veillard154877e2003-01-30 12:17:05 +00001820 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard276be4a2003-01-24 01:03:34 +00001821 ctxt->define = olddefine;
1822 }
1823 if (ctxt->grammar->defs == NULL)
1824 ctxt->grammar->defs = xmlHashCreate(10);
1825 if (ctxt->grammar->defs == NULL) {
1826 if (ctxt->error != NULL)
1827 ctxt->error(ctxt->userData,
1828 "Could not create definition hash\n");
1829 ctxt->nbErrors++;
1830 ret = -1;
1831 xmlRelaxNGFreeDefine(def);
1832 } else {
1833 tmp = xmlHashAddEntry(ctxt->grammar->defs, name, def);
1834 if (tmp < 0) {
Daniel Veillard154877e2003-01-30 12:17:05 +00001835 xmlRelaxNGDefinePtr prev;
1836
1837 prev = xmlHashLookup(ctxt->grammar->defs, name);
1838 if (prev == NULL) {
1839 if (ctxt->error != NULL)
1840 ctxt->error(ctxt->userData,
1841 "Internal error on define aggregation of %s\n",
1842 name);
1843 ctxt->nbErrors++;
1844 ret = -1;
1845 xmlRelaxNGFreeDefine(def);
1846 } else {
1847 while (prev->nextHash != NULL)
1848 prev = prev->nextHash;
1849 prev->nextHash = def;
1850 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00001851 }
1852 }
1853 }
1854 return(ret);
1855}
1856
1857/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00001858 * xmlRelaxNGParsePattern:
1859 * @ctxt: a Relax-NG parser context
1860 * @node: the pattern node.
1861 *
1862 * parse the content of a RelaxNG pattern node.
1863 *
Daniel Veillard276be4a2003-01-24 01:03:34 +00001864 * Returns the definition pointer or NULL in case of error or if no
1865 * pattern is generated.
Daniel Veillard6eadf632003-01-23 18:29:16 +00001866 */
1867static xmlRelaxNGDefinePtr
1868xmlRelaxNGParsePattern(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1869 xmlRelaxNGDefinePtr def = NULL;
1870
1871 if (IS_RELAXNG(node, "element")) {
1872 def = xmlRelaxNGParseElement(ctxt, node);
1873 } else if (IS_RELAXNG(node, "attribute")) {
1874 def = xmlRelaxNGParseAttribute(ctxt, node);
1875 } else if (IS_RELAXNG(node, "empty")) {
1876 def = xmlRelaxNGNewDefine(ctxt, node);
1877 if (def == NULL)
1878 return(NULL);
1879 def->type = XML_RELAXNG_EMPTY;
1880 } else if (IS_RELAXNG(node, "text")) {
1881 def = xmlRelaxNGNewDefine(ctxt, node);
1882 if (def == NULL)
1883 return(NULL);
1884 def->type = XML_RELAXNG_TEXT;
1885 if (node->children != NULL) {
1886 if (ctxt->error != NULL)
1887 ctxt->error(ctxt->userData, "text: had a child node\n");
1888 ctxt->nbErrors++;
1889 }
1890 } else if (IS_RELAXNG(node, "zeroOrMore")) {
1891 def = xmlRelaxNGNewDefine(ctxt, node);
1892 if (def == NULL)
1893 return(NULL);
1894 def->type = XML_RELAXNG_ZEROORMORE;
Daniel Veillard154877e2003-01-30 12:17:05 +00001895 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001896 } else if (IS_RELAXNG(node, "oneOrMore")) {
1897 def = xmlRelaxNGNewDefine(ctxt, node);
1898 if (def == NULL)
1899 return(NULL);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001900 def->type = XML_RELAXNG_ONEORMORE;
Daniel Veillard154877e2003-01-30 12:17:05 +00001901 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001902 } else if (IS_RELAXNG(node, "optional")) {
1903 def = xmlRelaxNGNewDefine(ctxt, node);
1904 if (def == NULL)
1905 return(NULL);
1906 def->type = XML_RELAXNG_OPTIONAL;
Daniel Veillard154877e2003-01-30 12:17:05 +00001907 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001908 } else if (IS_RELAXNG(node, "choice")) {
1909 def = xmlRelaxNGNewDefine(ctxt, node);
1910 if (def == NULL)
1911 return(NULL);
1912 def->type = XML_RELAXNG_CHOICE;
Daniel Veillard154877e2003-01-30 12:17:05 +00001913 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001914 } else if (IS_RELAXNG(node, "group")) {
1915 def = xmlRelaxNGNewDefine(ctxt, node);
1916 if (def == NULL)
1917 return(NULL);
1918 def->type = XML_RELAXNG_GROUP;
Daniel Veillard154877e2003-01-30 12:17:05 +00001919 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001920 } else if (IS_RELAXNG(node, "ref")) {
1921 def = xmlRelaxNGNewDefine(ctxt, node);
1922 if (def == NULL)
1923 return(NULL);
1924 def->type = XML_RELAXNG_REF;
1925 def->name = xmlGetProp(node, BAD_CAST "name");
1926 if (def->name == NULL) {
1927 if (ctxt->error != NULL)
1928 ctxt->error(ctxt->userData,
1929 "ref has no name\n");
1930 ctxt->nbErrors++;
Daniel Veillard276be4a2003-01-24 01:03:34 +00001931 } else {
1932 if ((ctxt->define != NULL) &&
1933 (xmlStrEqual(ctxt->define, def->name))) {
1934 if (ctxt->error != NULL)
1935 ctxt->error(ctxt->userData,
1936 "Recursive reference to %s not in an element\n",
1937 def->name);
1938 ctxt->nbErrors++;
1939 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00001940 }
1941 if (node->children != NULL) {
1942 if (ctxt->error != NULL)
1943 ctxt->error(ctxt->userData,
1944 "ref is not empty\n");
1945 ctxt->nbErrors++;
1946 }
1947 if (ctxt->grammar->refs == NULL)
1948 ctxt->grammar->refs = xmlHashCreate(10);
1949 if (ctxt->grammar->refs == NULL) {
1950 if (ctxt->error != NULL)
1951 ctxt->error(ctxt->userData,
1952 "Could not create references hash\n");
1953 ctxt->nbErrors++;
1954 xmlRelaxNGFreeDefine(def);
1955 def = NULL;
1956 } else {
1957 int tmp;
1958
1959 tmp = xmlHashAddEntry(ctxt->grammar->refs, def->name, def);
1960 if (tmp < 0) {
1961 xmlRelaxNGDefinePtr prev;
1962
1963 prev = (xmlRelaxNGDefinePtr)
1964 xmlHashLookup(ctxt->grammar->refs, def->name);
1965 if (prev == NULL) {
1966 if (ctxt->error != NULL)
1967 ctxt->error(ctxt->userData,
1968 "Internal error refs definitions '%s'\n",
1969 def->name);
1970 ctxt->nbErrors++;
1971 xmlRelaxNGFreeDefine(def);
1972 def = NULL;
1973 } else {
1974 def->nextHash = prev->nextHash;
1975 prev->nextHash = def;
1976 }
1977 }
1978 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001979 } else if (IS_RELAXNG(node, "data")) {
1980 def = xmlRelaxNGParseData(ctxt, node);
Daniel Veillard276be4a2003-01-24 01:03:34 +00001981 } else if (IS_RELAXNG(node, "define")) {
1982 xmlRelaxNGParseDefine(ctxt, node);
1983 def = NULL;
Daniel Veillardedc91922003-01-26 00:52:04 +00001984 } else if (IS_RELAXNG(node, "value")) {
1985 def = xmlRelaxNGParseValue(ctxt, node);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001986 } else if (IS_RELAXNG(node, "list")) {
1987 def = xmlRelaxNGNewDefine(ctxt, node);
1988 if (def == NULL)
1989 return(NULL);
1990 def->type = XML_RELAXNG_LIST;
Daniel Veillard154877e2003-01-30 12:17:05 +00001991 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001992 } else if (IS_RELAXNG(node, "interleave")) {
1993 def = xmlRelaxNGParseInterleave(ctxt, node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001994 } else if (IS_RELAXNG(node, "externalRef")) {
1995 xmlRelaxNGDocumentPtr docu;
1996 xmlNodePtr root;
1997
1998 docu = node->_private;
1999 if (docu != NULL) {
2000 def = xmlRelaxNGNewDefine(ctxt, node);
2001 if (def == NULL)
2002 return(NULL);
2003 def->type = XML_RELAXNG_EXTERNALREF;
2004
2005 if (docu->content == NULL) {
2006 /*
2007 * Then do the parsing for good
2008 */
2009 root = xmlDocGetRootElement(docu->doc);
2010 if (root == NULL) {
2011 if (ctxt->error != NULL)
2012 ctxt->error(ctxt->userData,
2013 "xmlRelaxNGParse: %s is empty\n",
2014 ctxt->URL);
2015 ctxt->nbErrors++;
2016 return (NULL);
2017 }
2018 docu->schema = xmlRelaxNGParseDocument(ctxt, root);
2019 if ((docu->schema != NULL) &&
2020 (docu->schema->topgrammar != NULL)) {
2021 docu->content = docu->schema->topgrammar->start;
2022 }
2023 }
2024 def->content = docu->content;
2025 } else {
2026 def = NULL;
2027 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002028 } else {
2029 TODO
2030 }
2031 return(def);
2032}
2033
2034/**
2035 * xmlRelaxNGParseAttribute:
2036 * @ctxt: a Relax-NG parser context
2037 * @node: the element node
2038 *
2039 * parse the content of a RelaxNG attribute node.
2040 *
2041 * Returns the definition pointer or NULL in case of error.
2042 */
2043static xmlRelaxNGDefinePtr
2044xmlRelaxNGParseAttribute(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2045 xmlRelaxNGDefinePtr ret, cur, last;
2046 xmlNodePtr child;
2047 xmlChar *val;
2048 int old_flags;
2049
2050 ret = xmlRelaxNGNewDefine(ctxt, node);
2051 if (ret == NULL)
2052 return(NULL);
2053 ret->type = XML_RELAXNG_ATTRIBUTE;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002054 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002055 child = node->children;
2056 if (child == NULL) {
2057 if (ctxt->error != NULL)
2058 ctxt->error(ctxt->userData,
2059 "xmlRelaxNGParseattribute: attribute has no children\n");
2060 ctxt->nbErrors++;
2061 return(ret);
2062 }
2063 old_flags = ctxt->flags;
2064 ctxt->flags |= XML_RELAXNG_IN_ATTRIBUTE;
2065 if (IS_RELAXNG(child, "name")) {
2066 val = xmlNodeGetContent(child);
2067 ret->name = val;
2068 val = xmlGetProp(child, BAD_CAST "ns");
2069 ret->ns = val;
2070 } else if (IS_RELAXNG(child, "anyName")) {
2071 TODO
2072 } else if (IS_RELAXNG(child, "nsName")) {
2073 TODO
2074 } else if (IS_RELAXNG(child, "choice")) {
2075 TODO
2076 } else {
2077 if (ctxt->error != NULL)
2078 ctxt->error(ctxt->userData,
2079 "element: expecting name, anyName, nsName or choice : got %s\n",
2080 child->name);
2081 ctxt->nbErrors++;
2082 ctxt->flags = old_flags;
2083 return(ret);
2084 }
2085 child = child->next;
2086 last = NULL;
2087 while (child != NULL) {
2088 cur = xmlRelaxNGParsePattern(ctxt, child);
2089 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002090 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002091 switch (cur->type) {
2092 case XML_RELAXNG_EMPTY:
2093 case XML_RELAXNG_NOT_ALLOWED:
2094 case XML_RELAXNG_TEXT:
2095 case XML_RELAXNG_ELEMENT:
2096 case XML_RELAXNG_DATATYPE:
2097 case XML_RELAXNG_VALUE:
2098 case XML_RELAXNG_LIST:
2099 case XML_RELAXNG_REF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002100 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00002101 case XML_RELAXNG_DEF:
2102 case XML_RELAXNG_ONEORMORE:
2103 case XML_RELAXNG_ZEROORMORE:
2104 case XML_RELAXNG_OPTIONAL:
2105 case XML_RELAXNG_CHOICE:
2106 case XML_RELAXNG_GROUP:
2107 case XML_RELAXNG_INTERLEAVE:
2108 if (last == NULL) {
2109 ret->content = last = cur;
2110 } else {
2111 if ((last->type == XML_RELAXNG_ELEMENT) &&
2112 (ret->content == last)) {
2113 ret->content = xmlRelaxNGNewDefine(ctxt, node);
2114 if (ret->content != NULL) {
2115 ret->content->type = XML_RELAXNG_GROUP;
2116 ret->content->content = last;
2117 } else {
2118 ret->content = last;
2119 }
2120 }
2121 last->next = cur;
2122 last = cur;
2123 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002124 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002125 break;
2126 case XML_RELAXNG_ATTRIBUTE:
2127 cur->next = ret->attrs;
2128 ret->attrs = cur;
2129 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002130 case XML_RELAXNG_START:
2131 TODO
2132 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002133 }
2134 }
2135 child = child->next;
2136 }
2137 ctxt->flags = old_flags;
2138 return(ret);
2139}
2140
2141/**
2142 * xmlRelaxNGParseElement:
2143 * @ctxt: a Relax-NG parser context
2144 * @node: the element node
2145 *
2146 * parse the content of a RelaxNG element node.
2147 *
2148 * Returns the definition pointer or NULL in case of error.
2149 */
2150static xmlRelaxNGDefinePtr
2151xmlRelaxNGParseElement(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2152 xmlRelaxNGDefinePtr ret, cur, last;
2153 xmlNodePtr child;
2154 xmlChar *val;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002155 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002156
2157 ret = xmlRelaxNGNewDefine(ctxt, node);
2158 if (ret == NULL)
2159 return(NULL);
2160 ret->type = XML_RELAXNG_ELEMENT;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002161 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002162 child = node->children;
2163 if (child == NULL) {
2164 if (ctxt->error != NULL)
2165 ctxt->error(ctxt->userData,
2166 "xmlRelaxNGParseElement: element has no children\n");
2167 ctxt->nbErrors++;
2168 return(ret);
2169 }
2170 if (IS_RELAXNG(child, "name")) {
2171 val = xmlNodeGetContent(child);
2172 ret->name = val;
2173 val = xmlGetProp(child, BAD_CAST "ns");
2174 ret->ns = val;
2175 } else if (IS_RELAXNG(child, "anyName")) {
2176 TODO
2177 } else if (IS_RELAXNG(child, "nsName")) {
2178 TODO
2179 } else if (IS_RELAXNG(child, "choice")) {
2180 TODO
2181 } else {
2182 if (ctxt->error != NULL)
2183 ctxt->error(ctxt->userData,
2184 "element: expecting name, anyName, nsName or choice : got %s\n",
2185 child->name);
2186 ctxt->nbErrors++;
2187 return(ret);
2188 }
2189 child = child->next;
2190 if (child == NULL) {
2191 if (ctxt->error != NULL)
2192 ctxt->error(ctxt->userData,
2193 "xmlRelaxNGParseElement: element has no content\n");
2194 ctxt->nbErrors++;
2195 return(ret);
2196 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002197 olddefine = ctxt->define;
2198 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002199 last = NULL;
2200 while (child != NULL) {
2201 cur = xmlRelaxNGParsePattern(ctxt, child);
2202 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002203 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002204 switch (cur->type) {
2205 case XML_RELAXNG_EMPTY:
2206 case XML_RELAXNG_NOT_ALLOWED:
2207 case XML_RELAXNG_TEXT:
2208 case XML_RELAXNG_ELEMENT:
2209 case XML_RELAXNG_DATATYPE:
2210 case XML_RELAXNG_VALUE:
2211 case XML_RELAXNG_LIST:
2212 case XML_RELAXNG_REF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002213 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00002214 case XML_RELAXNG_DEF:
2215 case XML_RELAXNG_ZEROORMORE:
2216 case XML_RELAXNG_ONEORMORE:
2217 case XML_RELAXNG_OPTIONAL:
2218 case XML_RELAXNG_CHOICE:
2219 case XML_RELAXNG_GROUP:
2220 case XML_RELAXNG_INTERLEAVE:
2221 if (last == NULL) {
2222 ret->content = last = cur;
2223 } else {
2224 if ((last->type == XML_RELAXNG_ELEMENT) &&
2225 (ret->content == last)) {
2226 ret->content = xmlRelaxNGNewDefine(ctxt, node);
2227 if (ret->content != NULL) {
2228 ret->content->type = XML_RELAXNG_GROUP;
2229 ret->content->content = last;
2230 } else {
2231 ret->content = last;
2232 }
2233 }
2234 last->next = cur;
2235 last = cur;
2236 }
2237 break;
2238 case XML_RELAXNG_ATTRIBUTE:
2239 cur->next = ret->attrs;
2240 ret->attrs = cur;
2241 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002242 case XML_RELAXNG_START:
2243 TODO
2244 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002245 }
2246 }
2247 child = child->next;
2248 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002249 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002250 return(ret);
2251}
2252
2253/**
2254 * xmlRelaxNGParsePatterns:
2255 * @ctxt: a Relax-NG parser context
2256 * @nodes: list of nodes
Daniel Veillard154877e2003-01-30 12:17:05 +00002257 * @group: use an implicit <group> for elements
Daniel Veillard6eadf632003-01-23 18:29:16 +00002258 *
2259 * parse the content of a RelaxNG start node.
2260 *
2261 * Returns the definition pointer or NULL in case of error.
2262 */
2263static xmlRelaxNGDefinePtr
Daniel Veillard154877e2003-01-30 12:17:05 +00002264xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes,
2265 int group) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002266 xmlRelaxNGDefinePtr def = NULL, last = NULL, cur, parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002267
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002268 parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002269 while (nodes != NULL) {
2270 if (IS_RELAXNG(nodes, "element")) {
2271 cur = xmlRelaxNGParseElement(ctxt, nodes);
2272 if (def == NULL) {
2273 def = last = cur;
2274 } else {
Daniel Veillard154877e2003-01-30 12:17:05 +00002275 if ((group == 1) && (def->type == XML_RELAXNG_ELEMENT) &&
2276 (def == last)) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00002277 def = xmlRelaxNGNewDefine(ctxt, nodes);
2278 def->type = XML_RELAXNG_GROUP;
2279 def->content = last;
2280 }
2281 last->next = cur;
2282 last = cur;
2283 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002284 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002285 } else {
2286 cur = xmlRelaxNGParsePattern(ctxt, nodes);
2287 if (def == NULL) {
2288 def = last = cur;
2289 } else {
2290 last->next = cur;
2291 last = cur;
2292 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002293 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002294 }
2295 nodes = nodes->next;
2296 }
2297 return(def);
2298}
2299
2300/**
2301 * xmlRelaxNGParseStart:
2302 * @ctxt: a Relax-NG parser context
2303 * @nodes: start children nodes
2304 *
2305 * parse the content of a RelaxNG start node.
2306 *
2307 * Returns 0 in case of success, -1 in case of error
2308 */
2309static int
2310xmlRelaxNGParseStart(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
2311 int ret = 0;
2312 xmlRelaxNGDefinePtr def = NULL;
2313
2314 while (nodes != NULL) {
2315 if (IS_RELAXNG(nodes, "empty")) {
2316 TODO
2317 xmlElemDump(stdout, nodes->doc, nodes);
2318 } else if (IS_RELAXNG(nodes, "notAllowed")) {
2319 TODO
2320 xmlElemDump(stdout, nodes->doc, nodes);
2321 } else {
Daniel Veillard154877e2003-01-30 12:17:05 +00002322 def = xmlRelaxNGParsePatterns(ctxt, nodes, 1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002323 ctxt->grammar->start = def;
2324 }
2325 nodes = nodes->next;
2326 }
2327 return(ret);
2328}
2329
2330/**
2331 * xmlRelaxNGParseGrammarContent:
2332 * @ctxt: a Relax-NG parser context
2333 * @nodes: grammar children nodes
2334 *
2335 * parse the content of a RelaxNG grammar node.
2336 *
2337 * Returns 0 in case of success, -1 in case of error
2338 */
2339static int
2340xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt
2341 ATTRIBUTE_UNUSED, xmlNodePtr nodes)
2342{
Daniel Veillard276be4a2003-01-24 01:03:34 +00002343 int ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002344
2345 if (nodes == NULL) {
2346 if (ctxt->error != NULL)
2347 ctxt->error(ctxt->userData,
2348 "grammar has no children\n");
2349 ctxt->nbErrors++;
2350 return(-1);
2351 }
2352 if (IS_RELAXNG(nodes, "start")) {
2353 if (nodes->children == NULL) {
2354 if (ctxt->error != NULL)
2355 ctxt->error(ctxt->userData,
2356 "grammar has no children\n");
2357 ctxt->nbErrors++;
2358 } else {
2359 xmlRelaxNGParseStart(ctxt, nodes->children);
2360 }
2361 nodes = nodes->next;
2362 } else {
2363 if (ctxt->error != NULL)
2364 ctxt->error(ctxt->userData,
2365 "grammar first child must be a <start>\n");
2366 ctxt->nbErrors++;
2367 return(-1);
2368 }
2369 while (nodes != NULL) {
2370 if (IS_RELAXNG(nodes, "define")) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00002371 ret = xmlRelaxNGParseDefine(ctxt, nodes);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002372 } else {
2373 if (ctxt->error != NULL)
2374 ctxt->error(ctxt->userData,
2375 "grammar allows onlys <define> child after <start>\n");
2376 ctxt->nbErrors++;
2377 ret = -1;
2378 }
2379 nodes = nodes->next;
2380 }
2381 return (ret);
2382}
2383
2384/**
2385 * xmlRelaxNGCheckReference:
2386 * @ref: the ref
2387 * @ctxt: a Relax-NG parser context
2388 * @name: the name associated to the defines
2389 *
2390 * Applies the 4.17. combine attribute rule for all the define
2391 * element of a given grammar using the same name.
2392 */
2393static void
2394xmlRelaxNGCheckReference(xmlRelaxNGDefinePtr ref,
2395 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
2396 xmlRelaxNGGrammarPtr grammar;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002397 xmlRelaxNGDefinePtr def, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002398
2399 grammar = ctxt->grammar;
2400 if (grammar == NULL) {
2401 if (ctxt->error != NULL)
2402 ctxt->error(ctxt->userData,
2403 "Internal error: no grammar in CheckReference %s\n",
2404 name);
2405 ctxt->nbErrors++;
2406 return;
2407 }
2408 if (ref->content != NULL) {
2409 if (ctxt->error != NULL)
2410 ctxt->error(ctxt->userData,
2411 "Internal error: reference has content in CheckReference %s\n",
2412 name);
2413 ctxt->nbErrors++;
2414 return;
2415 }
2416 if (grammar->defs != NULL) {
2417 def = xmlHashLookup(grammar->defs, name);
2418 if (def != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00002419 cur = ref;
2420 while (cur != NULL) {
2421 cur->content = def;
2422 cur = cur->nextHash;
2423 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002424 } else {
2425 TODO
2426 }
2427 }
2428 /*
2429 * TODO: make a closure and verify there is no loop !
2430 */
2431}
2432
2433/**
2434 * xmlRelaxNGCheckCombine:
2435 * @define: the define(s) list
2436 * @ctxt: a Relax-NG parser context
2437 * @name: the name associated to the defines
2438 *
2439 * Applies the 4.17. combine attribute rule for all the define
2440 * element of a given grammar using the same name.
2441 */
2442static void
2443xmlRelaxNGCheckCombine(xmlRelaxNGDefinePtr define,
2444 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
2445 xmlChar *combine;
2446 int choiceOrInterleave = -1;
2447 int missing = 0;
2448 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
2449
2450 if (define->nextHash == NULL)
2451 return;
2452 cur = define;
2453 while (cur != NULL) {
2454 combine = xmlGetProp(cur->node, BAD_CAST "combine");
2455 if (combine != NULL) {
2456 if (xmlStrEqual(combine, BAD_CAST "choice")) {
2457 if (choiceOrInterleave == -1)
2458 choiceOrInterleave = 1;
2459 else if (choiceOrInterleave == 0) {
2460 if (ctxt->error != NULL)
2461 ctxt->error(ctxt->userData,
2462 "Defines for %s use both 'choice' and 'interleave'\n",
2463 name);
2464 ctxt->nbErrors++;
2465 }
Daniel Veillard154877e2003-01-30 12:17:05 +00002466 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00002467 if (choiceOrInterleave == -1)
2468 choiceOrInterleave = 0;
2469 else if (choiceOrInterleave == 1) {
2470 if (ctxt->error != NULL)
2471 ctxt->error(ctxt->userData,
2472 "Defines for %s use both 'choice' and 'interleave'\n",
2473 name);
2474 ctxt->nbErrors++;
2475 }
2476 } else {
2477 if (ctxt->error != NULL)
2478 ctxt->error(ctxt->userData,
2479 "Defines for %s use unknown combine value '%s''\n",
2480 name, combine);
2481 ctxt->nbErrors++;
2482 }
2483 xmlFree(combine);
2484 } else {
2485 if (missing == 0)
2486 missing = 1;
2487 else {
2488 if (ctxt->error != NULL)
2489 ctxt->error(ctxt->userData,
2490 "Some defines for %s lacks the combine attribute\n",
2491 name);
2492 ctxt->nbErrors++;
2493 }
2494 }
2495
2496 cur = cur->nextHash;
2497 }
2498#ifdef DEBUG
2499 xmlGenericError(xmlGenericErrorContext,
2500 "xmlRelaxNGCheckCombine(): merging %s defines: %d\n",
2501 name, choiceOrInterleave);
2502#endif
2503 if (choiceOrInterleave == -1)
2504 choiceOrInterleave = 0;
2505 cur = xmlRelaxNGNewDefine(ctxt, define->node);
2506 if (cur == NULL)
2507 return;
2508 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00002509 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard154877e2003-01-30 12:17:05 +00002510 else
2511 cur->type = XML_RELAXNG_CHOICE;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002512 tmp = define;
2513 last = NULL;
2514 while (tmp != NULL) {
2515 if (tmp->content != NULL) {
2516 if (tmp->content->next != NULL) {
2517 /*
2518 * we need first to create a wrapper.
2519 */
2520 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
2521 if (tmp2 == NULL)
2522 break;
2523 tmp2->type = XML_RELAXNG_GROUP;
2524 tmp2->content = tmp->content;
2525 } else {
2526 tmp2 = tmp->content;
2527 }
2528 if (last == NULL) {
2529 cur->content = tmp2;
2530 } else {
2531 last->next = tmp2;
2532 }
2533 last = tmp2;
2534 tmp->content = NULL;
2535 }
2536 tmp = tmp->nextHash;
2537 }
2538 define->content = cur;
Daniel Veillard154877e2003-01-30 12:17:05 +00002539 if (choiceOrInterleave == 0) {
2540 if (ctxt->interleaves == NULL)
2541 ctxt->interleaves = xmlHashCreate(10);
2542 if (ctxt->interleaves == NULL) {
2543 if (ctxt->error != NULL)
2544 ctxt->error(ctxt->userData,
2545 "Failed to create interleaves hash table\n");
2546 ctxt->nbErrors++;
2547 } else {
2548 char tmpname[32];
2549
2550 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
2551 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
2552 if (ctxt->error != NULL)
2553 ctxt->error(ctxt->userData,
2554 "Failed to add %s to hash table\n", tmpname);
2555 ctxt->nbErrors++;
2556 }
2557 }
2558 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002559}
2560
2561/**
2562 * xmlRelaxNGCombineStart:
2563 * @ctxt: a Relax-NG parser context
2564 * @grammar: the grammar
2565 *
2566 * Applies the 4.17. combine rule for all the start
2567 * element of a given grammar.
2568 */
2569static void
2570xmlRelaxNGCombineStart(xmlRelaxNGParserCtxtPtr ctxt,
2571 xmlRelaxNGGrammarPtr grammar) {
2572 xmlRelaxNGDefinePtr starts;
2573 xmlChar *combine;
2574 int choiceOrInterleave = -1;
2575 int missing = 0;
2576 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
2577
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002578 starts = grammar->startList;
2579 if ((starts == NULL) || (starts->nextHash == NULL))
Daniel Veillard6eadf632003-01-23 18:29:16 +00002580 return;
2581 cur = starts;
2582 while (cur != NULL) {
2583 combine = xmlGetProp(cur->node, BAD_CAST "combine");
2584 if (combine != NULL) {
2585 if (xmlStrEqual(combine, BAD_CAST "choice")) {
2586 if (choiceOrInterleave == -1)
2587 choiceOrInterleave = 1;
2588 else if (choiceOrInterleave == 0) {
2589 if (ctxt->error != NULL)
2590 ctxt->error(ctxt->userData,
2591 "<start> use both 'choice' and 'interleave'\n");
2592 ctxt->nbErrors++;
2593 }
2594 } else if (xmlStrEqual(combine, BAD_CAST "choice")) {
2595 if (choiceOrInterleave == -1)
2596 choiceOrInterleave = 0;
2597 else if (choiceOrInterleave == 1) {
2598 if (ctxt->error != NULL)
2599 ctxt->error(ctxt->userData,
2600 "<start> use both 'choice' and 'interleave'\n");
2601 ctxt->nbErrors++;
2602 }
2603 } else {
2604 if (ctxt->error != NULL)
2605 ctxt->error(ctxt->userData,
2606 "<start> uses unknown combine value '%s''\n", combine);
2607 ctxt->nbErrors++;
2608 }
2609 xmlFree(combine);
2610 } else {
2611 if (missing == 0)
2612 missing = 1;
2613 else {
2614 if (ctxt->error != NULL)
2615 ctxt->error(ctxt->userData,
2616 "Some <start> elements lacks the combine attribute\n");
2617 ctxt->nbErrors++;
2618 }
2619 }
2620
2621 cur = cur->nextHash;
2622 }
2623#ifdef DEBUG
2624 xmlGenericError(xmlGenericErrorContext,
2625 "xmlRelaxNGCombineStart(): merging <start>: %d\n",
2626 choiceOrInterleave);
2627#endif
2628 if (choiceOrInterleave == -1)
2629 choiceOrInterleave = 0;
2630 cur = xmlRelaxNGNewDefine(ctxt, starts->node);
2631 if (cur == NULL)
2632 return;
2633 if (choiceOrInterleave == 0)
2634 cur->type = XML_RELAXNG_CHOICE;
2635 else
2636 cur->type = XML_RELAXNG_INTERLEAVE;
2637 tmp = starts;
2638 last = NULL;
2639 while (tmp != NULL) {
2640 if (tmp->content != NULL) {
2641 if (tmp->content->next != NULL) {
2642 /*
2643 * we need first to create a wrapper.
2644 */
2645 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
2646 if (tmp2 == NULL)
2647 break;
2648 tmp2->type = XML_RELAXNG_GROUP;
2649 tmp2->content = tmp->content;
2650 } else {
2651 tmp2 = tmp->content;
2652 }
2653 if (last == NULL) {
2654 cur->content = tmp2;
2655 } else {
2656 last->next = tmp2;
2657 }
2658 last = tmp2;
2659 tmp->content = NULL;
2660 }
2661 tmp = tmp->nextHash;
2662 }
2663 starts->content = cur;
2664}
2665
2666/**
2667 * xmlRelaxNGParseGrammar:
2668 * @ctxt: a Relax-NG parser context
2669 * @nodes: grammar children nodes
2670 *
2671 * parse a Relax-NG <grammar> node
2672 *
2673 * Returns the internal xmlRelaxNGGrammarPtr built or
2674 * NULL in case of error
2675 */
2676static xmlRelaxNGGrammarPtr
2677xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
2678 xmlRelaxNGGrammarPtr ret, tmp, old;
2679
Daniel Veillard6eadf632003-01-23 18:29:16 +00002680 ret = xmlRelaxNGNewGrammar(ctxt);
2681 if (ret == NULL)
2682 return(NULL);
2683
2684 /*
2685 * Link the new grammar in the tree
2686 */
2687 ret->parent = ctxt->grammar;
2688 if (ctxt->grammar != NULL) {
2689 tmp = ctxt->grammar->children;
2690 if (tmp == NULL) {
2691 ctxt->grammar->children = ret;
2692 } else {
2693 while (tmp->next != NULL)
2694 tmp = tmp->next;
2695 tmp->next = ret;
2696 }
2697 }
2698
2699 old = ctxt->grammar;
2700 ctxt->grammar = ret;
2701 xmlRelaxNGParseGrammarContent(ctxt, nodes);
2702 ctxt->grammar = ret;
2703
2704 /*
2705 * Apply 4.17 mergingd rules to defines and starts
2706 */
2707 xmlRelaxNGCombineStart(ctxt, ret);
2708 if (ret->defs != NULL) {
2709 xmlHashScan(ret->defs, (xmlHashScanner) xmlRelaxNGCheckCombine,
2710 ctxt);
2711 }
2712
2713 /*
2714 * link together defines and refs in this grammar
2715 */
2716 if (ret->refs != NULL) {
2717 xmlHashScan(ret->refs, (xmlHashScanner) xmlRelaxNGCheckReference,
2718 ctxt);
2719 }
2720 ctxt->grammar = old;
2721 return(ret);
2722}
2723
2724/**
2725 * xmlRelaxNGParseDocument:
2726 * @ctxt: a Relax-NG parser context
2727 * @node: the root node of the RelaxNG schema
2728 *
2729 * parse a Relax-NG definition resource and build an internal
2730 * xmlRelaxNG struture which can be used to validate instances.
2731 *
2732 * Returns the internal XML RelaxNG structure built or
2733 * NULL in case of error
2734 */
2735static xmlRelaxNGPtr
2736xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2737 xmlRelaxNGPtr schema = NULL;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002738 const xmlChar *olddefine;
Daniel Veillarde431a272003-01-29 23:02:33 +00002739 xmlRelaxNGGrammarPtr old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002740
2741 if ((ctxt == NULL) || (node == NULL))
2742 return (NULL);
2743
2744 schema = xmlRelaxNGNewRelaxNG(ctxt);
2745 if (schema == NULL)
2746 return(NULL);
2747
Daniel Veillard276be4a2003-01-24 01:03:34 +00002748 olddefine = ctxt->define;
2749 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002750 if (IS_RELAXNG(node, "grammar")) {
2751 schema->topgrammar = xmlRelaxNGParseGrammar(ctxt, node->children);
2752 } else {
2753 schema->topgrammar = xmlRelaxNGNewGrammar(ctxt);
2754 if (schema->topgrammar == NULL) {
2755 return(schema);
2756 }
2757 schema->topgrammar->parent = NULL;
Daniel Veillarde431a272003-01-29 23:02:33 +00002758 old = ctxt->grammar;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002759 ctxt->grammar = schema->topgrammar;
2760 xmlRelaxNGParseStart(ctxt, node);
Daniel Veillarde431a272003-01-29 23:02:33 +00002761 if (old != NULL)
2762 ctxt->grammar = old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002763 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002764 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002765
2766#ifdef DEBUG
2767 if (schema == NULL)
2768 xmlGenericError(xmlGenericErrorContext,
2769 "xmlRelaxNGParseDocument() failed\n");
2770#endif
2771
2772 return (schema);
2773}
2774
2775/************************************************************************
2776 * *
2777 * Reading RelaxNGs *
2778 * *
2779 ************************************************************************/
2780
2781/**
2782 * xmlRelaxNGNewParserCtxt:
2783 * @URL: the location of the schema
2784 *
2785 * Create an XML RelaxNGs parse context for that file/resource expected
2786 * to contain an XML RelaxNGs file.
2787 *
2788 * Returns the parser context or NULL in case of error
2789 */
2790xmlRelaxNGParserCtxtPtr
2791xmlRelaxNGNewParserCtxt(const char *URL) {
2792 xmlRelaxNGParserCtxtPtr ret;
2793
2794 if (URL == NULL)
2795 return(NULL);
2796
2797 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
2798 if (ret == NULL) {
2799 xmlGenericError(xmlGenericErrorContext,
2800 "Failed to allocate new schama parser context for %s\n", URL);
2801 return (NULL);
2802 }
2803 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
2804 ret->URL = xmlStrdup((const xmlChar *)URL);
2805 return (ret);
2806}
2807
2808/**
2809 * xmlRelaxNGNewMemParserCtxt:
2810 * @buffer: a pointer to a char array containing the schemas
2811 * @size: the size of the array
2812 *
2813 * Create an XML RelaxNGs parse context for that memory buffer expected
2814 * to contain an XML RelaxNGs file.
2815 *
2816 * Returns the parser context or NULL in case of error
2817 */
2818xmlRelaxNGParserCtxtPtr
2819xmlRelaxNGNewMemParserCtxt(const char *buffer, int size) {
2820 xmlRelaxNGParserCtxtPtr ret;
2821
2822 if ((buffer == NULL) || (size <= 0))
2823 return(NULL);
2824
2825 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
2826 if (ret == NULL) {
2827 xmlGenericError(xmlGenericErrorContext,
2828 "Failed to allocate new schama parser context\n");
2829 return (NULL);
2830 }
2831 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
2832 ret->buffer = buffer;
2833 ret->size = size;
2834 return (ret);
2835}
2836
2837/**
2838 * xmlRelaxNGFreeParserCtxt:
2839 * @ctxt: the schema parser context
2840 *
2841 * Free the resources associated to the schema parser context
2842 */
2843void
2844xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxtPtr ctxt) {
2845 if (ctxt == NULL)
2846 return;
2847 if (ctxt->URL != NULL)
2848 xmlFree(ctxt->URL);
2849 if (ctxt->doc != NULL)
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002850 xmlFreeDoc(ctxt->document);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002851 if (ctxt->interleaves != NULL)
2852 xmlHashFree(ctxt->interleaves, NULL);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002853 if (ctxt->documents != NULL)
2854 xmlHashFree(ctxt->documents, (xmlHashDeallocator)
2855 xmlRelaxNGFreeDocument);
2856 if (ctxt->docTab != NULL)
2857 xmlFree(ctxt->docTab);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002858 xmlFree(ctxt);
2859}
2860
Daniel Veillard6eadf632003-01-23 18:29:16 +00002861/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002862 * xmlRelaxNGCleanupDoc:
2863 * @ctxt: a Relax-NG parser context
2864 * @doc: an xmldocPtr document pointer
Daniel Veillard6eadf632003-01-23 18:29:16 +00002865 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002866 * Cleanup the document from unwanted nodes for parsing, resolve
2867 * Include and externalRef lookups.
Daniel Veillard6eadf632003-01-23 18:29:16 +00002868 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002869 * Returns the cleaned up document or NULL in case of error
Daniel Veillard6eadf632003-01-23 18:29:16 +00002870 */
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002871static xmlDocPtr
2872xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt, xmlDocPtr doc) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00002873 xmlNodePtr root, cur, delete;
2874
Daniel Veillard6eadf632003-01-23 18:29:16 +00002875 /*
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002876 * Extract the root
Daniel Veillard6eadf632003-01-23 18:29:16 +00002877 */
2878 root = xmlDocGetRootElement(doc);
2879 if (root == NULL) {
2880 if (ctxt->error != NULL)
2881 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
2882 ctxt->URL);
2883 ctxt->nbErrors++;
2884 return (NULL);
2885 }
2886
2887 /*
2888 * Remove all the blank text nodes
2889 */
2890 delete = NULL;
2891 cur = root;
2892 while (cur != NULL) {
2893 if (delete != NULL) {
2894 xmlUnlinkNode(delete);
2895 xmlFreeNode(delete);
2896 delete = NULL;
2897 }
2898 if (cur->type == XML_ELEMENT_NODE) {
2899 /*
2900 * Simplification 4.1. Annotations
2901 */
2902 if ((cur->ns == NULL) ||
2903 (!xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
2904 delete = cur;
2905 goto skip_children;
2906 } else {
2907 if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002908 xmlChar *href, *ns, *base, *URL;
2909 xmlRelaxNGDocumentPtr docu;
2910
2911 ns = xmlGetProp(cur, BAD_CAST "ns");
2912 href = xmlGetProp(cur, BAD_CAST "href");
2913 if (href == NULL) {
2914 if (ctxt->error != NULL)
2915 ctxt->error(ctxt->userData,
2916 "xmlRelaxNGParse: externalRef has no href attribute\n");
2917 ctxt->nbErrors++;
2918 delete = cur;
2919 goto skip_children;
2920 }
2921 base = xmlNodeGetBase(cur->doc, cur);
2922 URL = xmlBuildURI(href, base);
2923 if (URL == NULL) {
2924 if (ctxt->error != NULL)
2925 ctxt->error(ctxt->userData,
2926 "Failed to compute URL for externalRef %s\n", href);
2927 ctxt->nbErrors++;
2928 if (href != NULL)
2929 xmlFree(href);
2930 if (base != NULL)
2931 xmlFree(base);
2932 delete = cur;
2933 goto skip_children;
2934 }
2935 if (href != NULL)
2936 xmlFree(href);
2937 if (base != NULL)
2938 xmlFree(base);
2939 docu = xmlRelaxNGLoaddocument(ctxt, URL, ns);
2940 if (docu == NULL) {
2941 if (ctxt->error != NULL)
2942 ctxt->error(ctxt->userData,
2943 "Failed to load externalRef %s\n", URL);
2944 ctxt->nbErrors++;
2945 xmlFree(URL);
2946 delete = cur;
2947 goto skip_children;
2948 }
2949 xmlFree(URL);
2950 cur->_private = docu;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002951 } else if (xmlStrEqual(cur->name, BAD_CAST "include")) {
2952 TODO
2953 } else if ((xmlStrEqual(cur->name, BAD_CAST "element")) ||
2954 (xmlStrEqual(cur->name, BAD_CAST "attribute"))) {
2955 xmlChar *name;
2956 xmlNodePtr text = NULL;
2957
2958 /*
2959 * Simplification 4.8. name attribute of element
2960 * and attribute elements
2961 */
2962 name = xmlGetProp(cur, BAD_CAST "name");
2963 if (name != NULL) {
2964 if (cur->children == NULL) {
2965 text = xmlNewChild(cur, cur->ns, BAD_CAST "name",
2966 name);
2967 } else {
2968 xmlNodePtr node;
2969 node = xmlNewNode(cur->ns, BAD_CAST "name");
2970 if (node != NULL) {
2971 xmlAddPrevSibling(cur->children, node);
2972 text = xmlNewText(name);
2973 xmlAddChild(node, text);
2974 text = node;
2975 }
2976 }
2977 xmlUnsetProp(cur, BAD_CAST "name");
2978 xmlFree(name);
2979 }
2980 if (xmlStrEqual(cur->name, BAD_CAST "attribute")) {
2981 if (text == NULL) {
2982 text = cur->children;
2983 while (text != NULL) {
2984 if ((text->type == XML_ELEMENT_NODE) &&
2985 (xmlStrEqual(text->name, BAD_CAST "name")))
2986 break;
2987 text = text->next;
2988 }
2989 }
2990 if (text == NULL) {
2991 if (ctxt->error != NULL)
2992 ctxt->error(ctxt->userData,
2993 "xmlRelaxNGParse: attribute without name\n");
2994 ctxt->nbErrors++;
2995 } else {
2996 xmlSetProp(text, BAD_CAST "ns", BAD_CAST "");
2997 }
2998 }
2999 } else if ((xmlStrEqual(cur->name, BAD_CAST "name")) ||
3000 (xmlStrEqual(cur->name, BAD_CAST "nsName")) ||
3001 (xmlStrEqual(cur->name, BAD_CAST "value"))) {
3002 /*
3003 * Simplification 4.8. name attribute of element
3004 * and attribute elements
3005 */
3006 if (xmlHasProp(cur, BAD_CAST "ns") == NULL) {
3007 xmlNodePtr node;
3008 xmlChar *ns = NULL;
3009
3010 node = cur->parent;
3011 while ((node != NULL) &&
3012 (node->type == XML_ELEMENT_NODE)) {
3013 ns = xmlGetProp(node, BAD_CAST "ns");
3014 if (ns != NULL) {
3015 break;
3016 }
3017 node = node->parent;
3018 }
3019 if (ns == NULL) {
3020 xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
3021 } else {
3022 xmlSetProp(cur, BAD_CAST "ns", ns);
3023 xmlFree(ns);
3024 }
3025 }
3026 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
3027 xmlChar *name, *local, *prefix;
3028
3029 /*
3030 * Simplification: 4.10. QNames
3031 */
3032 name = xmlNodeGetContent(cur);
3033 if (name != NULL) {
3034 local = xmlSplitQName2(name, &prefix);
3035 if (local != NULL) {
3036 xmlNsPtr ns;
3037
3038 ns = xmlSearchNs(cur->doc, cur, prefix);
3039 if (ns == NULL) {
3040 if (ctxt->error != NULL)
3041 ctxt->error(ctxt->userData,
3042 "xmlRelaxNGParse: no namespace for prefix %s\n", prefix);
3043 ctxt->nbErrors++;
3044 } else {
3045 xmlSetProp(cur, BAD_CAST "ns", ns->href);
3046 xmlNodeSetContent(cur, local);
3047 }
3048 xmlFree(local);
3049 xmlFree(prefix);
3050 }
3051 xmlFree(name);
3052 }
3053 }
3054 }
3055 }
3056 }
3057 /*
3058 * Simplification 4.2 whitespaces
3059 */
3060 else if (cur->type == XML_TEXT_NODE) {
3061 if (IS_BLANK_NODE(cur)) {
3062 if (cur->parent->type == XML_ELEMENT_NODE) {
3063 if ((!xmlStrEqual(cur->parent->name, BAD_CAST "value")) &&
3064 (!xmlStrEqual(cur->parent->name, BAD_CAST "param")))
3065 delete = cur;
3066 } else {
3067 delete = cur;
3068 goto skip_children;
3069 }
3070 }
3071 } else if (cur->type != XML_CDATA_SECTION_NODE) {
3072 delete = cur;
3073 goto skip_children;
3074 }
3075
3076 /*
3077 * Skip to next node
3078 */
3079 if (cur->children != NULL) {
3080 if ((cur->children->type != XML_ENTITY_DECL) &&
3081 (cur->children->type != XML_ENTITY_REF_NODE) &&
3082 (cur->children->type != XML_ENTITY_NODE)) {
3083 cur = cur->children;
3084 continue;
3085 }
3086 }
3087skip_children:
3088 if (cur->next != NULL) {
3089 cur = cur->next;
3090 continue;
3091 }
3092
3093 do {
3094 cur = cur->parent;
3095 if (cur == NULL)
3096 break;
3097 if (cur == root) {
3098 cur = NULL;
3099 break;
3100 }
3101 if (cur->next != NULL) {
3102 cur = cur->next;
3103 break;
3104 }
3105 } while (cur != NULL);
3106 }
3107 if (delete != NULL) {
3108 xmlUnlinkNode(delete);
3109 xmlFreeNode(delete);
3110 delete = NULL;
3111 }
3112
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003113 return(doc);
3114}
3115
3116/**
3117 * xmlRelaxNGParse:
3118 * @ctxt: a Relax-NG parser context
3119 *
3120 * parse a schema definition resource and build an internal
3121 * XML Shema struture which can be used to validate instances.
3122 * *WARNING* this interface is highly subject to change
3123 *
3124 * Returns the internal XML RelaxNG structure built from the resource or
3125 * NULL in case of error
3126 */
3127xmlRelaxNGPtr
3128xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt)
3129{
3130 xmlRelaxNGPtr ret = NULL;
3131 xmlDocPtr doc;
3132 xmlNodePtr root;
3133
3134 xmlRelaxNGInitTypes();
3135
3136 if (ctxt == NULL)
3137 return (NULL);
3138
3139 /*
3140 * First step is to parse the input document into an DOM/Infoset
3141 */
3142 if (ctxt->URL != NULL) {
3143 doc = xmlParseFile((const char *) ctxt->URL);
3144 if (doc == NULL) {
3145 if (ctxt->error != NULL)
3146 ctxt->error(ctxt->userData,
3147 "xmlRelaxNGParse: could not load %s\n", ctxt->URL);
3148 ctxt->nbErrors++;
3149 return (NULL);
3150 }
3151 } else if (ctxt->buffer != NULL) {
3152 doc = xmlParseMemory(ctxt->buffer, ctxt->size);
3153 if (doc == NULL) {
3154 if (ctxt->error != NULL)
3155 ctxt->error(ctxt->userData,
3156 "xmlRelaxNGParse: could not parse schemas\n");
3157 ctxt->nbErrors++;
3158 return (NULL);
3159 }
3160 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
3161 ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
3162 } else {
3163 if (ctxt->error != NULL)
3164 ctxt->error(ctxt->userData,
3165 "xmlRelaxNGParse: nothing to parse\n");
3166 ctxt->nbErrors++;
3167 return (NULL);
3168 }
3169 ctxt->document = doc;
3170
3171 /*
3172 * Some preprocessing of the document content
3173 */
3174 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
3175 if (doc == NULL) {
3176 xmlFreeDoc(ctxt->document);
3177 ctxt->document = NULL;
3178 return(NULL);
3179 }
3180
Daniel Veillard6eadf632003-01-23 18:29:16 +00003181 /*
3182 * Then do the parsing for good
3183 */
3184 root = xmlDocGetRootElement(doc);
3185 if (root == NULL) {
3186 if (ctxt->error != NULL)
3187 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
3188 ctxt->URL);
3189 ctxt->nbErrors++;
3190 return (NULL);
3191 }
3192 ret = xmlRelaxNGParseDocument(ctxt, root);
3193 if (ret == NULL)
3194 return(NULL);
3195
3196 /*
3197 * Check the ref/defines links
3198 */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003199 /*
3200 * try to preprocess interleaves
3201 */
3202 if (ctxt->interleaves != NULL) {
3203 xmlHashScan(ctxt->interleaves,
3204 (xmlHashScanner)xmlRelaxNGComputeInterleaves, ctxt);
3205 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003206
3207 /*
3208 * if there was a parsing error return NULL
3209 */
3210 if (ctxt->nbErrors > 0) {
3211 xmlRelaxNGFree(ret);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003212 ctxt->document = NULL;
3213 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003214 return(NULL);
3215 }
3216
3217 /*
3218 * Transfer the pointer for cleanup at the schema level.
3219 */
3220 ret->doc = doc;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003221 ctxt->document = NULL;
3222 ret->documents = ctxt->documents;
3223 ctxt->documents = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003224
3225 return (ret);
3226}
3227
3228/**
3229 * xmlRelaxNGSetParserErrors:
3230 * @ctxt: a Relax-NG validation context
3231 * @err: the error callback
3232 * @warn: the warning callback
3233 * @ctx: contextual data for the callbacks
3234 *
3235 * Set the callback functions used to handle errors for a validation context
3236 */
3237void
3238xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
3239 xmlRelaxNGValidityErrorFunc err,
3240 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
3241 if (ctxt == NULL)
3242 return;
3243 ctxt->error = err;
3244 ctxt->warning = warn;
3245 ctxt->userData = ctx;
3246}
3247/************************************************************************
3248 * *
3249 * Dump back a compiled form *
3250 * *
3251 ************************************************************************/
3252static void xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define);
3253
3254/**
3255 * xmlRelaxNGDumpDefines:
3256 * @output: the file output
3257 * @defines: a list of define structures
3258 *
3259 * Dump a RelaxNG structure back
3260 */
3261static void
3262xmlRelaxNGDumpDefines(FILE * output, xmlRelaxNGDefinePtr defines) {
3263 while (defines != NULL) {
3264 xmlRelaxNGDumpDefine(output, defines);
3265 defines = defines->next;
3266 }
3267}
3268
3269/**
3270 * xmlRelaxNGDumpDefine:
3271 * @output: the file output
3272 * @define: a define structure
3273 *
3274 * Dump a RelaxNG structure back
3275 */
3276static void
3277xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define) {
3278 if (define == NULL)
3279 return;
3280 switch(define->type) {
3281 case XML_RELAXNG_EMPTY:
3282 fprintf(output, "<empty/>\n");
3283 break;
3284 case XML_RELAXNG_NOT_ALLOWED:
3285 fprintf(output, "<notAllowed/>\n");
3286 break;
3287 case XML_RELAXNG_TEXT:
3288 fprintf(output, "<text/>\n");
3289 break;
3290 case XML_RELAXNG_ELEMENT:
3291 fprintf(output, "<element>\n");
3292 if (define->name != NULL) {
3293 fprintf(output, "<name");
3294 if (define->ns != NULL)
3295 fprintf(output, " ns=\"%s\"", define->ns);
3296 fprintf(output, ">%s</name>\n", define->name);
3297 }
3298 xmlRelaxNGDumpDefines(output, define->attrs);
3299 xmlRelaxNGDumpDefines(output, define->content);
3300 fprintf(output, "</element>\n");
3301 break;
3302 case XML_RELAXNG_LIST:
3303 fprintf(output, "<list>\n");
3304 xmlRelaxNGDumpDefines(output, define->content);
3305 fprintf(output, "</list>\n");
3306 break;
3307 case XML_RELAXNG_ONEORMORE:
3308 fprintf(output, "<oneOrMore>\n");
3309 xmlRelaxNGDumpDefines(output, define->content);
3310 fprintf(output, "</oneOrMore>\n");
3311 break;
3312 case XML_RELAXNG_ZEROORMORE:
3313 fprintf(output, "<zeroOrMore>\n");
3314 xmlRelaxNGDumpDefines(output, define->content);
3315 fprintf(output, "</zeroOrMore>\n");
3316 break;
3317 case XML_RELAXNG_CHOICE:
3318 fprintf(output, "<choice>\n");
3319 xmlRelaxNGDumpDefines(output, define->content);
3320 fprintf(output, "</choice>\n");
3321 break;
3322 case XML_RELAXNG_GROUP:
3323 fprintf(output, "<group>\n");
3324 xmlRelaxNGDumpDefines(output, define->content);
3325 fprintf(output, "</group>\n");
3326 break;
3327 case XML_RELAXNG_INTERLEAVE:
3328 fprintf(output, "<interleave>\n");
3329 xmlRelaxNGDumpDefines(output, define->content);
3330 fprintf(output, "</interleave>\n");
3331 break;
3332 case XML_RELAXNG_OPTIONAL:
3333 fprintf(output, "<optional>\n");
3334 xmlRelaxNGDumpDefines(output, define->content);
3335 fprintf(output, "</optional>\n");
3336 break;
3337 case XML_RELAXNG_ATTRIBUTE:
3338 fprintf(output, "<attribute>\n");
3339 xmlRelaxNGDumpDefines(output, define->content);
3340 fprintf(output, "</attribute>\n");
3341 break;
3342 case XML_RELAXNG_DEF:
3343 fprintf(output, "<define");
3344 if (define->name != NULL)
3345 fprintf(output, " name=\"%s\"", define->name);
3346 fprintf(output, ">\n");
3347 xmlRelaxNGDumpDefines(output, define->content);
3348 fprintf(output, "</define>\n");
3349 break;
3350 case XML_RELAXNG_REF:
3351 fprintf(output, "<ref");
3352 if (define->name != NULL)
3353 fprintf(output, " name=\"%s\"", define->name);
3354 fprintf(output, ">\n");
3355 xmlRelaxNGDumpDefines(output, define->content);
3356 fprintf(output, "</ref>\n");
3357 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003358 case XML_RELAXNG_EXTERNALREF:
Daniel Veillarde431a272003-01-29 23:02:33 +00003359 fprintf(output, "<externalRef");
3360 xmlRelaxNGDumpDefines(output, define->content);
3361 fprintf(output, "</externalRef>\n");
3362 break;
3363 case XML_RELAXNG_DATATYPE:
Daniel Veillard6eadf632003-01-23 18:29:16 +00003364 case XML_RELAXNG_VALUE:
3365 TODO
3366 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003367 case XML_RELAXNG_START:
3368 TODO
3369 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003370 }
3371}
3372
3373/**
3374 * xmlRelaxNGDumpGrammar:
3375 * @output: the file output
3376 * @grammar: a grammar structure
3377 * @top: is this a top grammar
3378 *
3379 * Dump a RelaxNG structure back
3380 */
3381static void
3382xmlRelaxNGDumpGrammar(FILE * output, xmlRelaxNGGrammarPtr grammar, int top)
3383{
3384 if (grammar == NULL)
3385 return;
3386
3387 fprintf(output, "<grammar");
3388 if (top)
3389 fprintf(output,
3390 " xmlns=\"http://relaxng.org/ns/structure/1.0\"");
3391 switch(grammar->combine) {
3392 case XML_RELAXNG_COMBINE_UNDEFINED:
3393 break;
3394 case XML_RELAXNG_COMBINE_CHOICE:
3395 fprintf(output, " combine=\"choice\"");
3396 break;
3397 case XML_RELAXNG_COMBINE_INTERLEAVE:
3398 fprintf(output, " combine=\"interleave\"");
3399 break;
3400 default:
3401 fprintf(output, " <!-- invalid combine value -->");
3402 }
3403 fprintf(output, ">\n");
3404 if (grammar->start == NULL) {
3405 fprintf(output, " <!-- grammar had no start -->");
3406 } else {
3407 fprintf(output, "<start>\n");
3408 xmlRelaxNGDumpDefine(output, grammar->start);
3409 fprintf(output, "</start>\n");
3410 }
3411 /* TODO ? Dump the defines ? */
3412 fprintf(output, "</grammar>\n");
3413}
3414
3415/**
3416 * xmlRelaxNGDump:
3417 * @output: the file output
3418 * @schema: a schema structure
3419 *
3420 * Dump a RelaxNG structure back
3421 */
3422void
3423xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema)
3424{
3425 if (schema == NULL) {
3426 fprintf(output, "RelaxNG empty or failed to compile\n");
3427 return;
3428 }
3429 fprintf(output, "RelaxNG: ");
3430 if (schema->doc == NULL) {
3431 fprintf(output, "no document\n");
3432 } else if (schema->doc->URL != NULL) {
3433 fprintf(output, "%s\n", schema->doc->URL);
3434 } else {
3435 fprintf(output, "\n");
3436 }
3437 if (schema->topgrammar == NULL) {
3438 fprintf(output, "RelaxNG has no top grammar\n");
3439 return;
3440 }
3441 xmlRelaxNGDumpGrammar(output, schema->topgrammar, 1);
3442}
3443
3444/************************************************************************
3445 * *
3446 * Validation implementation *
3447 * *
3448 ************************************************************************/
3449static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
3450 xmlRelaxNGDefinePtr define);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00003451static int xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
3452 xmlRelaxNGDefinePtr define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003453
3454/**
3455 * xmlRelaxNGSkipIgnored:
3456 * @ctxt: a schema validation context
3457 * @node: the top node.
3458 *
3459 * Skip ignorable nodes in that context
3460 *
3461 * Returns the new sibling or NULL in case of error.
3462 */
3463static xmlNodePtr
3464xmlRelaxNGSkipIgnored(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
3465 xmlNodePtr node) {
3466 /*
3467 * TODO complete and handle entities
3468 */
3469 while ((node != NULL) &&
3470 ((node->type == XML_COMMENT_NODE) ||
3471 ((node->type == XML_TEXT_NODE) &&
3472 (IS_BLANK_NODE(node))))) {
3473 node = node->next;
3474 }
3475 return(node);
3476}
3477
3478/**
Daniel Veillardedc91922003-01-26 00:52:04 +00003479 * xmlRelaxNGNormalize:
3480 * @ctxt: a schema validation context
3481 * @str: the string to normalize
3482 *
3483 * Implements the normalizeWhiteSpace( s ) function from
3484 * section 6.2.9 of the spec
3485 *
3486 * Returns the new string or NULL in case of error.
3487 */
3488static xmlChar *
3489xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *str) {
3490 xmlChar *ret, *p;
3491 const xmlChar *tmp;
3492 int len;
3493
3494 if (str == NULL)
3495 return(NULL);
3496 tmp = str;
3497 while (*tmp != 0) tmp++;
3498 len = tmp - str;
3499
3500 ret = (xmlChar *) xmlMalloc((len + 1) * sizeof(xmlChar));
3501 if (ret == NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00003502 if (ctxt != NULL) {
3503 VALID_CTXT();
3504 VALID_ERROR("xmlRelaxNGNormalize: out of memory\n");
3505 } else {
3506 xmlGenericError(xmlGenericErrorContext,
3507 "xmlRelaxNGNormalize: out of memory\n");
3508 }
Daniel Veillardedc91922003-01-26 00:52:04 +00003509 return(NULL);
3510 }
3511 p = ret;
3512 while (IS_BLANK(*str)) str++;
3513 while (*str != 0) {
3514 if (IS_BLANK(*str)) {
3515 while (IS_BLANK(*str)) str++;
3516 if (*str == 0)
3517 break;
3518 *p++ = ' ';
3519 } else
3520 *p++ = *str++;
3521 }
3522 *p = 0;
3523 return(ret);
3524}
3525
3526/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00003527 * xmlRelaxNGValidateDatatype:
3528 * @ctxt: a Relax-NG validation context
3529 * @value: the string value
3530 * @type: the datatype definition
3531 *
3532 * Validate the given value against the dataype
3533 *
3534 * Returns 0 if the validation succeeded or an error code.
3535 */
3536static int
3537xmlRelaxNGValidateDatatype(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *value,
3538 xmlRelaxNGDefinePtr define) {
3539 int ret;
3540 xmlRelaxNGTypeLibraryPtr lib;
3541
3542 if ((define == NULL) || (define->data == NULL)) {
3543 return(-1);
3544 }
3545 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
3546 if (lib->check != NULL)
3547 ret = lib->check(lib->data, define->name, value);
3548 else
3549 ret = -1;
3550 if (ret < 0) {
3551 VALID_CTXT();
3552 VALID_ERROR("Internal: failed to validate type %s\n", define->name);
3553 return(-1);
3554 } else if (ret == 1) {
3555 ret = 0;
3556 } else {
3557 VALID_CTXT();
3558 VALID_ERROR("Type %s doesn't allow value %s\n", define->name, value);
3559 return(-1);
3560 ret = -1;
3561 }
3562 return(ret);
3563}
3564
3565/**
Daniel Veillardc6e997c2003-01-27 12:35:42 +00003566 * xmlRelaxNGNextValue:
3567 * @ctxt: a Relax-NG validation context
3568 *
3569 * Skip to the next value when validating within a list
3570 *
3571 * Returns 0 if the operation succeeded or an error code.
3572 */
3573static int
3574xmlRelaxNGNextValue(xmlRelaxNGValidCtxtPtr ctxt) {
3575 xmlChar *cur;
3576
3577 cur = ctxt->state->value;
3578 if ((cur == NULL) || (ctxt->state->endvalue == NULL)) {
3579 ctxt->state->value = NULL;
3580 return(0);
3581 }
3582 while (*cur != 0) cur++;
3583 while ((cur != ctxt->state->endvalue) && (*cur == 0)) cur++;
3584 if (cur == ctxt->state->endvalue)
3585 ctxt->state->value = NULL;
3586 else
3587 ctxt->state->value = cur;
3588 return(0);
3589}
3590
3591/**
3592 * xmlRelaxNGValidateValueList:
3593 * @ctxt: a Relax-NG validation context
3594 * @defines: the list of definitions to verify
3595 *
3596 * Validate the given set of definitions for the current value
3597 *
3598 * Returns 0 if the validation succeeded or an error code.
3599 */
3600static int
3601xmlRelaxNGValidateValueList(xmlRelaxNGValidCtxtPtr ctxt,
3602 xmlRelaxNGDefinePtr defines) {
3603 int ret = 0;
3604
3605 while (defines != NULL) {
3606 ret = xmlRelaxNGValidateValue(ctxt, defines);
3607 if (ret != 0)
3608 break;
3609 defines = defines->next;
3610 }
3611 return(ret);
3612}
3613
3614/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00003615 * xmlRelaxNGValidateValue:
3616 * @ctxt: a Relax-NG validation context
3617 * @define: the definition to verify
3618 *
3619 * Validate the given definition for the current value
3620 *
3621 * Returns 0 if the validation succeeded or an error code.
3622 */
3623static int
3624xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
3625 xmlRelaxNGDefinePtr define) {
Daniel Veillardedc91922003-01-26 00:52:04 +00003626 int ret = 0, oldflags;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003627 xmlChar *value;
3628
3629 value = ctxt->state->value;
3630 switch (define->type) {
3631 case XML_RELAXNG_EMPTY:
3632 if ((value != NULL) && (value[0] != '0'))
3633 ret = -1;
3634 break;
3635 case XML_RELAXNG_TEXT:
3636 break;
Daniel Veillardedc91922003-01-26 00:52:04 +00003637 case XML_RELAXNG_VALUE: {
3638 if (!xmlStrEqual(value, define->value)) {
3639 if (define->name != NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00003640 xmlRelaxNGTypeLibraryPtr lib;
3641
3642 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
3643 if ((lib != NULL) && (lib->comp != NULL))
3644 ret = lib->comp(lib->data, define->name, value,
3645 define->value);
3646 else
3647 ret = -1;
3648 if (ret < 0) {
3649 VALID_CTXT();
3650 VALID_ERROR("Internal: failed to compare type %s\n",
3651 define->name);
3652 return(-1);
3653 } else if (ret == 1) {
3654 ret = 0;
3655 } else {
3656 ret = -1;
3657 }
Daniel Veillardedc91922003-01-26 00:52:04 +00003658 } else {
3659 xmlChar *nval, *nvalue;
3660
3661 /*
3662 * TODO: trivial optimizations are possible by
3663 * computing at compile-time
3664 */
3665 nval = xmlRelaxNGNormalize(ctxt, define->value);
3666 nvalue = xmlRelaxNGNormalize(ctxt, value);
3667
Daniel Veillardea3f3982003-01-26 19:45:18 +00003668 if ((nval == NULL) || (nvalue == NULL) ||
3669 (!xmlStrEqual(nval, nvalue)))
Daniel Veillardedc91922003-01-26 00:52:04 +00003670 ret = -1;
3671 if (nval != NULL)
3672 xmlFree(nval);
3673 if (nvalue != NULL)
3674 xmlFree(nvalue);
3675 }
3676 }
3677 break;
3678 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00003679 case XML_RELAXNG_DATATYPE: {
3680 ret = xmlRelaxNGValidateDatatype(ctxt, value, define);
3681 if (ret == 0)
3682 xmlRelaxNGNextValue(ctxt);
3683
3684 break;
3685 }
3686 case XML_RELAXNG_CHOICE: {
3687 xmlRelaxNGDefinePtr list = define->content;
3688 xmlChar *oldvalue;
3689
3690 oldflags = ctxt->flags;
3691 ctxt->flags |= FLAGS_IGNORABLE;
3692
3693 oldvalue = ctxt->state->value;
3694 while (list != NULL) {
3695 ret = xmlRelaxNGValidateValue(ctxt, list);
3696 if (ret == 0) {
3697 break;
3698 }
3699 ctxt->state->value = oldvalue;
3700 list = list->next;
3701 }
3702 ctxt->flags = oldflags;
3703 break;
3704 }
3705 case XML_RELAXNG_LIST: {
3706 xmlRelaxNGDefinePtr list = define->content;
3707 xmlChar *oldvalue, *oldend, *val, *cur;
3708
3709 oldvalue = ctxt->state->value;
3710 oldend = ctxt->state->endvalue;
3711
3712 val = xmlStrdup(oldvalue);
3713 if (val == NULL) {
3714 VALID_CTXT();
3715 VALID_ERROR("Internal: no state\n");
3716 return(-1);
3717 }
3718 cur = val;
3719 while (*cur != 0) {
3720 if (IS_BLANK(*cur))
3721 *cur = 0;
3722 cur++;
3723 }
3724 ctxt->state->endvalue = cur;
3725 cur = val;
3726 while ((*cur == 0) && (cur != ctxt->state->endvalue)) cur++;
3727
3728 ctxt->state->value = cur;
3729
3730 while (list != NULL) {
3731 ret = xmlRelaxNGValidateValue(ctxt, list);
3732 if (ret != 0) {
3733 break;
3734 }
3735 list = list->next;
3736 }
3737 if ((ret == 0) && (ctxt->state->value != NULL) &&
3738 (ctxt->state->value != ctxt->state->endvalue)) {
3739 VALID_CTXT();
3740 VALID_ERROR("Extra data in list: %s\n", ctxt->state->value);
3741 ret = -1;
3742 }
3743 xmlFree(val);
3744 ctxt->state->value = oldvalue;
3745 ctxt->state->endvalue = oldend;
3746 break;
3747 }
3748 case XML_RELAXNG_ONEORMORE:
3749 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
3750 if (ret != 0) {
3751 break;
3752 }
3753 /* no break on purpose */
3754 case XML_RELAXNG_ZEROORMORE: {
3755 xmlChar *cur, *temp;
3756
3757 oldflags = ctxt->flags;
3758 ctxt->flags |= FLAGS_IGNORABLE;
3759 cur = ctxt->state->value;
3760 temp = NULL;
3761 while ((cur != NULL) && (cur != ctxt->state->endvalue) &&
3762 (temp != cur)) {
3763 temp = cur;
3764 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
3765 if (ret != 0) {
3766 ctxt->state->value = temp;
3767 ret = 0;
3768 break;
3769 }
3770 cur = ctxt->state->value;
3771 }
3772 ctxt->flags = oldflags;
3773 break;
3774 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003775 default:
3776 TODO
3777 ret = -1;
3778 }
3779 return(ret);
3780}
3781
3782/**
3783 * xmlRelaxNGValidateValueContent:
3784 * @ctxt: a Relax-NG validation context
3785 * @defines: the list of definitions to verify
3786 *
3787 * Validate the given definitions for the current value
3788 *
3789 * Returns 0 if the validation succeeded or an error code.
3790 */
3791static int
3792xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt,
3793 xmlRelaxNGDefinePtr defines) {
3794 int ret = 0;
3795
3796 while (defines != NULL) {
3797 ret = xmlRelaxNGValidateValue(ctxt, defines);
3798 if (ret != 0)
3799 break;
3800 defines = defines->next;
3801 }
3802 return(ret);
3803}
3804
3805/**
3806 * xmlRelaxNGValidateAttribute:
3807 * @ctxt: a Relax-NG validation context
3808 * @define: the definition to verify
3809 *
3810 * Validate the given attribute definition for that node
3811 *
3812 * Returns 0 if the validation succeeded or an error code.
3813 */
3814static int
3815xmlRelaxNGValidateAttribute(xmlRelaxNGValidCtxtPtr ctxt,
3816 xmlRelaxNGDefinePtr define) {
3817 int ret = 0, i;
3818 xmlChar *value, *oldvalue;
3819 xmlAttrPtr prop = NULL, tmp;
3820
3821 if (define->name != NULL) {
3822 for (i = 0;i < ctxt->state->nbAttrs;i++) {
3823 tmp = ctxt->state->attrs[i];
3824 if ((tmp != NULL) && (xmlStrEqual(define->name, tmp->name))) {
3825 if ((((define->ns == NULL) || (define->ns[0] == 0)) &&
3826 (tmp->ns == NULL)) ||
3827 ((tmp->ns != NULL) &&
3828 (xmlStrEqual(define->ns, tmp->ns->href)))) {
3829 prop = tmp;
3830 break;
3831 }
3832 }
3833 }
3834 if (prop != NULL) {
3835 value = xmlNodeListGetString(prop->doc, prop->children, 1);
3836 oldvalue = ctxt->state->value;
3837 ctxt->state->value = value;
3838 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
3839 value = ctxt->state->value;
3840 ctxt->state->value = oldvalue;
3841 if (value != NULL)
3842 xmlFree(value);
3843 if (ret == 0) {
3844 /*
3845 * flag the attribute as processed
3846 */
3847 ctxt->state->attrs[i] = NULL;
3848 }
3849 } else {
3850 ret = -1;
3851 }
3852#ifdef DEBUG
3853 xmlGenericError(xmlGenericErrorContext,
3854 "xmlRelaxNGValidateAttribute(%s): %d\n", define->name, ret);
3855#endif
3856 } else {
3857 TODO
3858 }
3859
3860 return(ret);
3861}
3862
3863/**
3864 * xmlRelaxNGValidateAttributeList:
3865 * @ctxt: a Relax-NG validation context
3866 * @define: the list of definition to verify
3867 *
3868 * Validate the given node against the list of attribute definitions
3869 *
3870 * Returns 0 if the validation succeeded or an error code.
3871 */
3872static int
3873xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt,
3874 xmlRelaxNGDefinePtr defines) {
3875 int ret = 0;
3876 while (defines != NULL) {
3877 if (xmlRelaxNGValidateAttribute(ctxt, defines) != 0)
3878 ret = -1;
3879 defines = defines->next;
3880 }
3881 return(ret);
3882}
3883
3884/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003885 * xmlRelaxNGValidateTryPermutation:
3886 * @ctxt: a Relax-NG validation context
3887 * @groups: the array of groups
3888 * @nbgroups: the number of groups in the array
3889 * @array: the permutation to try
3890 * @len: the size of the set
3891 *
3892 * Try to validate a permutation for the group of definitions.
3893 *
3894 * Returns 0 if the validation succeeded or an error code.
3895 */
3896static int
3897xmlRelaxNGValidateTryPermutation(xmlRelaxNGValidCtxtPtr ctxt,
3898 xmlRelaxNGDefinePtr rule,
3899 xmlNodePtr *array, int len) {
3900 int i, ret;
3901
3902 if (len > 0) {
3903 /*
3904 * One only need the next pointer set-up to do the validation
3905 */
3906 for (i = 0;i < (len - 1);i++)
3907 array[i]->next = array[i + 1];
3908 array[i]->next = NULL;
3909
3910 /*
3911 * Now try to validate the sequence
3912 */
3913 ctxt->state->seq = array[0];
3914 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
3915 } else {
3916 ctxt->state->seq = NULL;
3917 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
3918 }
3919
3920 /*
3921 * the sequence must be fully consumed
3922 */
3923 if (ctxt->state->seq != NULL)
3924 return(-1);
3925
3926 return(ret);
3927}
3928
3929/**
3930 * xmlRelaxNGValidateWalkPermutations:
3931 * @ctxt: a Relax-NG validation context
3932 * @groups: the array of groups
3933 * @nbgroups: the number of groups in the array
3934 * @nodes: the set of nodes
3935 * @array: the current state of the parmutation
3936 * @len: the size of the set
3937 * @level: a pointer to the level variable
3938 * @k: the index in the array to fill
3939 *
3940 * Validate a set of nodes for a groups of definitions, will try the
3941 * full set of permutations
3942 *
3943 * Returns 0 if the validation succeeded or an error code.
3944 */
3945static int
3946xmlRelaxNGValidateWalkPermutations(xmlRelaxNGValidCtxtPtr ctxt,
3947 xmlRelaxNGDefinePtr rule, xmlNodePtr *nodes,
3948 xmlNodePtr *array, int len,
3949 int *level, int k) {
3950 int i, ret;
3951
3952 if ((k >= 0) && (k < len))
3953 array[k] = nodes[*level];
3954 *level = *level + 1;
3955 if (*level == len) {
3956 ret = xmlRelaxNGValidateTryPermutation(ctxt, rule, array, len);
3957 if (ret == 0)
3958 return(0);
3959 } else {
3960 for (i = 0;i < len;i++) {
3961 if (array[i] == NULL) {
3962 ret = xmlRelaxNGValidateWalkPermutations(ctxt, rule,
3963 nodes, array, len, level, i);
3964 if (ret == 0)
3965 return(0);
3966 }
3967 }
3968 }
3969 *level = *level - 1;
3970 array[k] = NULL;
3971 return(-1);
3972}
3973
3974/**
3975 * xmlRelaxNGNodeMatchesList:
3976 * @node: the node
3977 * @list: a NULL terminated array of definitions
3978 *
3979 * Check if a node can be matched by one of the definitions
3980 *
3981 * Returns 1 if matches 0 otherwise
3982 */
3983static int
3984xmlRelaxNGNodeMatchesList(xmlNodePtr node, xmlRelaxNGDefinePtr *list) {
3985 xmlRelaxNGDefinePtr cur;
3986 int i = 0;
3987
3988 if ((node == NULL) || (list == NULL))
3989 return(0);
3990
3991 cur = list[i++];
3992 while (cur != NULL) {
3993 if ((node->type == XML_ELEMENT_NODE) &&
3994 (cur->type == XML_RELAXNG_ELEMENT)) {
3995 if (cur->name == NULL) {
3996 if ((node->ns != NULL) &&
3997 (xmlStrEqual(node->ns->href, cur->ns)))
3998 return(1);
3999 } else if (xmlStrEqual(cur->name, node->name)) {
4000 if ((cur->ns == NULL) || (cur->ns[0] == 0)) {
4001 if (node->ns == NULL)
4002 return(1);
4003 } else {
4004 if ((node->ns != NULL) &&
4005 (xmlStrEqual(node->ns->href, cur->ns)))
4006 return(1);
4007 }
4008 }
4009 } else if ((node->type == XML_TEXT_NODE) &&
4010 (cur->type == XML_RELAXNG_TEXT)) {
4011 return(1);
4012 }
4013 cur = list[i++];
4014 }
4015 return(0);
4016}
4017
4018/**
4019 * xmlRelaxNGValidatePartGroup:
4020 * @ctxt: a Relax-NG validation context
4021 * @groups: the array of groups
4022 * @nbgroups: the number of groups in the array
4023 * @nodes: the set of nodes
4024 * @len: the size of the set of nodes
4025 *
4026 * Validate a set of nodes for a groups of definitions
4027 *
4028 * Returns 0 if the validation succeeded or an error code.
4029 */
4030static int
4031xmlRelaxNGValidatePartGroup(xmlRelaxNGValidCtxtPtr ctxt,
4032 xmlRelaxNGInterleaveGroupPtr *groups,
4033 int nbgroups, xmlNodePtr *nodes, int len) {
Daniel Veillardb08c9812003-01-28 23:09:49 +00004034 int level, ret = -1, i, j, k;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004035 xmlNodePtr *array = NULL, *list, oldseq;
4036 xmlRelaxNGInterleaveGroupPtr group;
4037
4038 list = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
4039 if (list == NULL) {
4040 return(-1);
4041 }
4042 array = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
4043 if (array == NULL) {
4044 xmlFree(list);
4045 return(-1);
4046 }
4047 memset(array, 0, len * sizeof(xmlNodePtr));
4048
4049 /*
4050 * Partition the elements and validate the subsets.
4051 */
4052 oldseq = ctxt->state->seq;
4053 for (i = 0;i < nbgroups;i++) {
4054 group = groups[i];
4055 if (group == NULL)
4056 continue;
4057 k = 0;
4058 for (j = 0;j < len;j++) {
4059 if (nodes[j] == NULL)
4060 continue;
4061 if (xmlRelaxNGNodeMatchesList(nodes[j], group->defs)) {
4062 list[k++] = nodes[j];
4063 nodes[j] = NULL;
4064 }
4065 }
4066 ctxt->state->seq = oldseq;
4067 if (k > 1) {
4068 memset(array, 0, k * sizeof(xmlNodePtr));
Daniel Veillardb08c9812003-01-28 23:09:49 +00004069 level = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004070 ret = xmlRelaxNGValidateWalkPermutations(ctxt, group->rule,
4071 list, array, k, &level, -1);
4072 } else {
4073 ret = xmlRelaxNGValidateTryPermutation(ctxt, group->rule, list, k);
4074 }
4075 if (ret != 0) {
4076 ctxt->state->seq = oldseq;
4077 break;
4078 }
4079 }
4080
4081 xmlFree(list);
4082 xmlFree(array);
4083 return(ret);
4084}
4085
4086/**
4087 * xmlRelaxNGValidateInterleave:
4088 * @ctxt: a Relax-NG validation context
4089 * @define: the definition to verify
4090 *
4091 * Validate an interleave definition for a node.
4092 *
4093 * Returns 0 if the validation succeeded or an error code.
4094 */
4095static int
4096xmlRelaxNGValidateInterleave(xmlRelaxNGValidCtxtPtr ctxt,
4097 xmlRelaxNGDefinePtr define) {
4098 int ret = 0, nbchildren, nbtot, i, j;
4099 xmlRelaxNGPartitionPtr partitions;
4100 xmlNodePtr *children = NULL;
4101 xmlNodePtr *order = NULL;
4102 xmlNodePtr cur;
4103
4104 if (define->data != NULL) {
4105 partitions = (xmlRelaxNGPartitionPtr) define->data;
4106 } else {
4107 VALID_CTXT();
4108 VALID_ERROR("Internal: interleave block has no data\n");
4109 return(-1);
4110 }
4111
4112 /*
4113 * Build the sequence of child and an array preserving the children
4114 * initial order.
4115 */
4116 cur = ctxt->state->seq;
4117 nbchildren = 0;
4118 nbtot = 0;
4119 while (cur != NULL) {
4120 if ((cur->type == XML_COMMENT_NODE) ||
4121 (cur->type == XML_PI_NODE) ||
4122 ((cur->type == XML_TEXT_NODE) &&
4123 (IS_BLANK_NODE(cur)))) {
4124 nbtot++;
4125 } else {
4126 nbchildren++;
4127 nbtot++;
4128 }
4129 cur = cur->next;
4130 }
4131 children = (xmlNodePtr *) xmlMalloc(nbchildren * sizeof(xmlNodePtr));
4132 if (children == NULL)
4133 goto error;
4134 order = (xmlNodePtr *) xmlMalloc(nbtot * sizeof(xmlNodePtr));
4135 if (order == NULL)
4136 goto error;
4137 cur = ctxt->state->seq;
4138 i = 0;
4139 j = 0;
4140 while (cur != NULL) {
4141 if ((cur->type == XML_COMMENT_NODE) ||
4142 (cur->type == XML_PI_NODE) ||
4143 ((cur->type == XML_TEXT_NODE) &&
4144 (IS_BLANK_NODE(cur)))) {
4145 order[j++] = cur;
4146 } else {
4147 order[j++] = cur;
4148 children[i++] = cur;
4149 }
4150 cur = cur->next;
4151 }
4152
4153 /* TODO: retry with a maller set of child if there is a next... */
4154 ret = xmlRelaxNGValidatePartGroup(ctxt, partitions->groups,
4155 partitions->nbgroups, children, nbchildren);
4156 if (ret == 0) {
4157 ctxt->state->seq = NULL;
4158 }
4159
4160 /*
4161 * Cleanup: rebuid the child sequence and free the structure
4162 */
4163 if (order != NULL) {
4164 for (i = 0;i < nbtot;i++) {
4165 if (i == 0)
4166 order[i]->prev = NULL;
4167 else
4168 order[i]->prev = order[i - 1];
4169 if (i == nbtot - 1)
4170 order[i]->next = NULL;
4171 else
4172 order[i]->next = order[i + 1];
4173 }
4174 xmlFree(order);
4175 }
4176 if (children != NULL)
4177 xmlFree(children);
4178
4179 return(ret);
4180
4181error:
4182 if (order != NULL) {
4183 for (i = 0;i < nbtot;i++) {
4184 if (i == 0)
4185 order[i]->prev = NULL;
4186 else
4187 order[i]->prev = order[i - 1];
4188 if (i == nbtot - 1)
4189 order[i]->next = NULL;
4190 else
4191 order[i]->next = order[i + 1];
4192 }
4193 xmlFree(order);
4194 }
4195 if (children != NULL)
4196 xmlFree(children);
4197 return(-1);
4198}
4199
4200/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004201 * xmlRelaxNGValidateElementContent:
4202 * @ctxt: a Relax-NG validation context
4203 * @define: the list of definition to verify
4204 *
4205 * Validate the given node content against the (list) of definitions
4206 *
4207 * Returns 0 if the validation succeeded or an error code.
4208 */
4209static int
4210xmlRelaxNGValidateElementContent(xmlRelaxNGValidCtxtPtr ctxt,
4211 xmlRelaxNGDefinePtr defines) {
4212 int ret = 0, res;
4213
4214 if (ctxt->state == NULL) {
4215 VALID_CTXT();
4216 VALID_ERROR("Internal: no state\n");
4217 return(-1);
4218 }
4219 while (defines != NULL) {
4220 res = xmlRelaxNGValidateDefinition(ctxt, defines);
4221 if (res < 0)
4222 ret = -1;
4223 defines = defines->next;
4224 }
4225
4226 return(ret);
4227}
4228
4229/**
4230 * xmlRelaxNGValidateDefinition:
4231 * @ctxt: a Relax-NG validation context
4232 * @define: the definition to verify
4233 *
4234 * Validate the current node against the definition
4235 *
4236 * Returns 0 if the validation succeeded or an error code.
4237 */
4238static int
4239xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
4240 xmlRelaxNGDefinePtr define) {
4241 xmlNodePtr node;
4242 int ret = 0, i, tmp, oldflags;
4243 xmlRelaxNGValidStatePtr oldstate, state;
4244
4245 if (define == NULL) {
4246 VALID_CTXT();
4247 VALID_ERROR("internal error: define == NULL\n");
4248 return(-1);
4249 }
4250 if (ctxt->state != NULL) {
4251 node = ctxt->state->seq;
4252 } else {
4253 node = NULL;
4254 }
4255 switch (define->type) {
4256 case XML_RELAXNG_EMPTY:
4257 if (node != NULL) {
4258 VALID_CTXT();
4259 VALID_ERROR("Expecting an empty element\n");
4260 return(-1);
4261 }
4262#ifdef DEBUG
4263 xmlGenericError(xmlGenericErrorContext,
4264 "xmlRelaxNGValidateDefinition(): validated empty\n");
4265#endif
4266 return(0);
4267 case XML_RELAXNG_NOT_ALLOWED:
4268 TODO
4269 break;
4270 case XML_RELAXNG_TEXT:
4271 if (node == NULL)
4272 return(0);
4273 while ((node != NULL) &&
4274 ((node->type == XML_TEXT_NODE) ||
4275 (node->type == XML_CDATA_SECTION_NODE)))
4276 node = node->next;
Daniel Veillard276be4a2003-01-24 01:03:34 +00004277 if (node == ctxt->state->seq) {
4278 VALID_CTXT();
4279 VALID_ERROR("Expecting text content\n");
4280 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004281 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00004282 ctxt->state->seq = node;
4283 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004284 case XML_RELAXNG_ELEMENT:
4285 node = xmlRelaxNGSkipIgnored(ctxt, node);
4286 if ((node == NULL) || (node->type != XML_ELEMENT_NODE)) {
4287 VALID_CTXT();
4288 VALID_ERROR("Expecting an element\n");
4289 return(-1);
4290 }
4291 if (define->name != NULL) {
4292 if (!xmlStrEqual(node->name, define->name)) {
4293 VALID_CTXT();
4294 VALID_ERROR("Expecting element %s, got %s\n",
4295 define->name, node->name);
4296 return(-1);
4297 }
4298 }
4299 if ((define->ns != NULL) && (define->ns[0] != 0)) {
4300 if (node->ns == NULL) {
4301 VALID_CTXT();
4302 VALID_ERROR("Expecting a namespace for element %s\n",
4303 node->name);
4304 return(-1);
4305 } else if (!xmlStrEqual(node->ns->href, define->ns)) {
4306 VALID_CTXT();
4307 VALID_ERROR("Expecting element %s has wrong namespace: expecting %s\n",
4308 node->name, define->ns);
4309 return(-1);
4310 }
4311 } else {
4312 if (node->ns != NULL) {
4313 VALID_CTXT();
4314 VALID_ERROR("Expecting no namespace for element %s\n",
4315 node->name);
4316 return(-1);
4317 }
4318 }
4319
4320 state = xmlRelaxNGNewValidState(ctxt, node);
4321 if (state == NULL) {
4322 return(-1);
4323 }
4324
4325 oldstate = ctxt->state;
4326 ctxt->state = state;
4327 if (define->attrs != NULL) {
4328 tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
4329 if (tmp != 0)
4330 ret = -1;
4331 }
4332 if (define->content != NULL) {
4333 tmp = xmlRelaxNGValidateElementContent(ctxt, define->content);
4334 if (tmp != 0)
4335 ret = -1;
4336 }
4337 state = ctxt->state;
4338 if (state->seq != NULL) {
4339 state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq);
4340 if (state->seq != NULL) {
4341 VALID_CTXT();
4342 VALID_ERROR("Extra content for element %s\n",
4343 node->name);
4344 ret = -1;
4345 }
4346 }
4347 for (i = 0;i < state->nbAttrs;i++) {
4348 if (state->attrs[i] != NULL) {
4349 VALID_CTXT();
Daniel Veillardea3f3982003-01-26 19:45:18 +00004350 VALID_ERROR("Invalid attribute %s for element %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00004351 state->attrs[i]->name, node->name);
4352 ret = -1;
4353 }
4354 }
4355 ctxt->state = oldstate;
4356 xmlRelaxNGFreeValidState(state);
4357 if (oldstate != NULL)
4358 oldstate->seq = node->next;
4359
4360
4361#ifdef DEBUG
4362 xmlGenericError(xmlGenericErrorContext,
4363 "xmlRelaxNGValidateDefinition(): validated %s : %d\n",
4364 node->name, ret);
4365#endif
4366 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004367 case XML_RELAXNG_OPTIONAL:
4368 oldflags = ctxt->flags;
4369 ctxt->flags |= FLAGS_IGNORABLE;
4370 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
4371 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
4372 if (ret != 0) {
4373 xmlRelaxNGFreeValidState(ctxt->state);
4374 ctxt->state = oldstate;
4375 ret = 0;
4376 break;
4377 }
4378 xmlRelaxNGFreeValidState(oldstate);
4379 ctxt->flags = oldflags;
4380 ret = 0;
4381 break;
4382 case XML_RELAXNG_ONEORMORE:
4383 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
4384 if (ret != 0) {
4385 break;
4386 }
4387 /* no break on purpose */
Daniel Veillard276be4a2003-01-24 01:03:34 +00004388 case XML_RELAXNG_ZEROORMORE: {
4389 xmlNodePtr cur, temp;
4390
Daniel Veillard6eadf632003-01-23 18:29:16 +00004391 oldflags = ctxt->flags;
4392 ctxt->flags |= FLAGS_IGNORABLE;
Daniel Veillard276be4a2003-01-24 01:03:34 +00004393 cur = ctxt->state->seq;
4394 temp = NULL;
4395 while ((cur != NULL) && (temp != cur)) {
4396 temp = cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004397 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
4398 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
4399 if (ret != 0) {
4400 xmlRelaxNGFreeValidState(ctxt->state);
4401 ctxt->state = oldstate;
4402 ret = 0;
4403 break;
4404 }
4405 xmlRelaxNGFreeValidState(oldstate);
Daniel Veillard276be4a2003-01-24 01:03:34 +00004406 cur = ctxt->state->seq;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004407 }
4408 ctxt->flags = oldflags;
4409 break;
Daniel Veillard276be4a2003-01-24 01:03:34 +00004410 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004411 case XML_RELAXNG_CHOICE: {
4412 xmlRelaxNGDefinePtr list = define->content;
4413
4414 oldflags = ctxt->flags;
4415 ctxt->flags |= FLAGS_IGNORABLE;
4416
4417 while (list != NULL) {
4418 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
4419 ret = xmlRelaxNGValidateDefinition(ctxt, list);
4420 if (ret == 0) {
4421 xmlRelaxNGFreeValidState(oldstate);
4422 break;
4423 }
4424 xmlRelaxNGFreeValidState(ctxt->state);
4425 ctxt->state = oldstate;
4426 list = list->next;
4427 }
4428 ctxt->flags = oldflags;
4429 break;
4430 }
4431 case XML_RELAXNG_GROUP: {
4432 xmlRelaxNGDefinePtr list = define->content;
4433
4434 while (list != NULL) {
4435 ret = xmlRelaxNGValidateDefinition(ctxt, list);
4436 if (ret != 0)
4437 break;
4438 list = list->next;
4439 }
4440 break;
4441 }
4442 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004443 ret = xmlRelaxNGValidateInterleave(ctxt, define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004444 break;
4445 case XML_RELAXNG_ATTRIBUTE:
4446 ret = xmlRelaxNGValidateAttribute(ctxt, define);
4447 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004448 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00004449 case XML_RELAXNG_REF:
4450 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
4451 break;
4452 case XML_RELAXNG_DEF:
4453 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
4454 break;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004455 case XML_RELAXNG_DATATYPE: {
4456 xmlChar *content;
4457
4458 content = xmlNodeGetContent(node);
4459 ret = xmlRelaxNGValidateDatatype(ctxt, content, define);
4460 if (ret == -1) {
4461 VALID_CTXT();
4462 VALID_ERROR("internal error validating %s\n", define->name);
4463 } else if (ret == 0) {
4464 ctxt->state->seq = node->next;
4465 }
4466 /*
4467 * TODO cover the problems with
4468 * <p>12<!-- comment -->34</p>
4469 * TODO detect full element coverage at compilation time.
4470 */
4471 if ((node != NULL) && (node->next != NULL)) {
4472 VALID_CTXT();
4473 VALID_ERROR("The data does not cover the full element %s\n",
4474 node->parent->name);
4475 ret = -1;
4476 }
4477 if (content != NULL)
4478 xmlFree(content);
4479 break;
4480 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00004481 case XML_RELAXNG_VALUE: {
4482 xmlChar *content;
4483 xmlChar *oldvalue;
4484
4485 content = xmlNodeGetContent(node);
4486 oldvalue = ctxt->state->value;
4487 ctxt->state->value = content;
4488 ret = xmlRelaxNGValidateValue(ctxt, define);
4489 ctxt->state->value = oldvalue;
4490 if (ret == -1) {
4491 VALID_CTXT();
4492 VALID_ERROR("internal error validating %s\n", define->name);
4493 } else if (ret == 0) {
4494 ctxt->state->seq = node->next;
4495 }
4496 /*
4497 * TODO cover the problems with
4498 * <p>12<!-- comment -->34</p>
4499 * TODO detect full element coverage at compilation time.
4500 */
4501 if ((node != NULL) && (node->next != NULL)) {
4502 VALID_CTXT();
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004503 VALID_ERROR("The value does not cover the full element %s\n",
Daniel Veillardea3f3982003-01-26 19:45:18 +00004504 node->parent->name);
4505 ret = -1;
4506 }
4507 if (content != NULL)
4508 xmlFree(content);
4509 break;
4510 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004511 case XML_RELAXNG_LIST: {
4512 xmlChar *content;
4513 xmlChar *oldvalue, *oldendvalue;
4514 int len;
Daniel Veillardea3f3982003-01-26 19:45:18 +00004515
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004516 content = xmlNodeGetContent(node);
4517 len = xmlStrlen(content);
4518 oldvalue = ctxt->state->value;
4519 oldendvalue = ctxt->state->endvalue;
4520 ctxt->state->value = content;
4521 ctxt->state->endvalue = content + len;
4522 ret = xmlRelaxNGValidateValue(ctxt, define);
4523 ctxt->state->value = oldvalue;
4524 ctxt->state->endvalue = oldendvalue;
4525 if (ret == -1) {
4526 VALID_CTXT();
4527 VALID_ERROR("internal error validating list\n");
4528 } else if (ret == 0) {
4529 ctxt->state->seq = node->next;
4530 }
4531 /*
4532 * TODO cover the problems with
4533 * <p>12<!-- comment -->34</p>
4534 * TODO detect full element coverage at compilation time.
4535 */
4536 if ((node != NULL) && (node->next != NULL)) {
4537 VALID_CTXT();
4538 VALID_ERROR("The list does not cover the full element %s\n",
4539 node->parent->name);
4540 ret = -1;
4541 }
4542 if (content != NULL)
4543 xmlFree(content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004544 break;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004545 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004546 case XML_RELAXNG_START:
4547 TODO
4548 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004549 }
4550 return(ret);
4551}
4552
4553/**
4554 * xmlRelaxNGValidateDocument:
4555 * @ctxt: a Relax-NG validation context
4556 * @doc: the document
4557 *
4558 * Validate the given document
4559 *
4560 * Returns 0 if the validation succeeded or an error code.
4561 */
4562static int
4563xmlRelaxNGValidateDocument(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
4564 int ret;
4565 xmlRelaxNGPtr schema;
4566 xmlRelaxNGGrammarPtr grammar;
4567 xmlRelaxNGValidStatePtr state;
4568
4569 if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL))
4570 return(-1);
4571
4572 schema = ctxt->schema;
4573 grammar = schema->topgrammar;
4574 if (grammar == NULL) {
4575 VALID_CTXT();
4576 VALID_ERROR("No top grammar defined\n");
4577 return(-1);
4578 }
4579 state = xmlRelaxNGNewValidState(ctxt, NULL);
4580 ctxt->state = state;
4581 ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start);
4582 state = ctxt->state;
4583 if ((state != NULL) && (state->seq != NULL)) {
4584 xmlNodePtr node;
4585
4586 node = state->seq;
4587 node = xmlRelaxNGSkipIgnored(ctxt, node);
4588 if (node != NULL) {
4589 VALID_CTXT();
4590 VALID_ERROR("extra data on the document\n");
4591 ret = -1;
4592 }
4593 }
4594 xmlRelaxNGFreeValidState(state);
4595
4596 return(ret);
4597}
4598
4599/************************************************************************
4600 * *
4601 * Validation interfaces *
4602 * *
4603 ************************************************************************/
4604/**
4605 * xmlRelaxNGNewValidCtxt:
4606 * @schema: a precompiled XML RelaxNGs
4607 *
4608 * Create an XML RelaxNGs validation context based on the given schema
4609 *
4610 * Returns the validation context or NULL in case of error
4611 */
4612xmlRelaxNGValidCtxtPtr
4613xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema) {
4614 xmlRelaxNGValidCtxtPtr ret;
4615
4616 ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt));
4617 if (ret == NULL) {
4618 xmlGenericError(xmlGenericErrorContext,
4619 "Failed to allocate new schama validation context\n");
4620 return (NULL);
4621 }
4622 memset(ret, 0, sizeof(xmlRelaxNGValidCtxt));
4623 ret->schema = schema;
4624 return (ret);
4625}
4626
4627/**
4628 * xmlRelaxNGFreeValidCtxt:
4629 * @ctxt: the schema validation context
4630 *
4631 * Free the resources associated to the schema validation context
4632 */
4633void
4634xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt) {
4635 if (ctxt == NULL)
4636 return;
4637 xmlFree(ctxt);
4638}
4639
4640/**
4641 * xmlRelaxNGSetValidErrors:
4642 * @ctxt: a Relax-NG validation context
4643 * @err: the error function
4644 * @warn: the warning function
4645 * @ctx: the functions context
4646 *
4647 * Set the error and warning callback informations
4648 */
4649void
4650xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
4651 xmlRelaxNGValidityErrorFunc err,
4652 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
4653 if (ctxt == NULL)
4654 return;
4655 ctxt->error = err;
4656 ctxt->warning = warn;
4657 ctxt->userData = ctx;
4658}
4659
4660/**
4661 * xmlRelaxNGValidateDoc:
4662 * @ctxt: a Relax-NG validation context
4663 * @doc: a parsed document tree
4664 *
4665 * Validate a document tree in memory.
4666 *
4667 * Returns 0 if the document is valid, a positive error code
4668 * number otherwise and -1 in case of internal or API error.
4669 */
4670int
4671xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
4672 int ret;
4673
4674 if ((ctxt == NULL) || (doc == NULL))
4675 return(-1);
4676
4677 ctxt->doc = doc;
4678
4679 ret = xmlRelaxNGValidateDocument(ctxt, doc);
4680 return(ret);
4681}
4682
4683#endif /* LIBXML_SCHEMAS_ENABLED */
4684