blob: 0776147e4c0464e42a514a64486ca633ba5ee2b9 [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
9#define IN_LIBXML
10#include "libxml.h"
11
12#ifdef LIBXML_SCHEMAS_ENABLED
13
14#include <string.h>
15#include <stdio.h>
16#include <libxml/xmlmemory.h>
17#include <libxml/parser.h>
18#include <libxml/parserInternals.h>
19#include <libxml/hash.h>
20#include <libxml/uri.h>
21
22#include <libxml/relaxng.h>
23
24#include <libxml/xmlschemastypes.h>
25#include <libxml/xmlautomata.h>
26#include <libxml/xmlregexp.h>
Daniel Veillardc6e997c2003-01-27 12:35:42 +000027#include <libxml/xmlschemastypes.h>
Daniel Veillard6eadf632003-01-23 18:29:16 +000028
29/*
30 * The Relax-NG namespace
31 */
32static const xmlChar *xmlRelaxNGNs = (const xmlChar *)
33 "http://relaxng.org/ns/structure/1.0";
34
35#define IS_RELAXNG(node, type) \
36 ((node != NULL) && (node->ns != NULL) && \
37 (xmlStrEqual(node->name, (const xmlChar *) type)) && \
38 (xmlStrEqual(node->ns->href, xmlRelaxNGNs)))
39
40
41#define DEBUG 1 /* very verbose output */
42#define DEBUG_CONTENT 1
43#define DEBUG_TYPE 1
Daniel Veillard276be4a2003-01-24 01:03:34 +000044#define DEBUG_VALID 1
Daniel Veillardb08c9812003-01-28 23:09:49 +000045#define DEBUG_INTERLEAVE 1 */
Daniel Veillard6eadf632003-01-23 18:29:16 +000046
47#define UNBOUNDED (1 << 30)
48#define TODO \
49 xmlGenericError(xmlGenericErrorContext, \
50 "Unimplemented block at %s:%d\n", \
51 __FILE__, __LINE__);
52
53typedef struct _xmlRelaxNGSchema xmlRelaxNGSchema;
54typedef xmlRelaxNGSchema *xmlRelaxNGSchemaPtr;
55
56typedef struct _xmlRelaxNGDefine xmlRelaxNGDefine;
57typedef xmlRelaxNGDefine *xmlRelaxNGDefinePtr;
58
59typedef enum {
60 XML_RELAXNG_COMBINE_UNDEFINED = 0, /* undefined */
61 XML_RELAXNG_COMBINE_CHOICE, /* choice */
62 XML_RELAXNG_COMBINE_INTERLEAVE /* interleave */
63} xmlRelaxNGCombine;
64
65typedef struct _xmlRelaxNGGrammar xmlRelaxNGGrammar;
66typedef xmlRelaxNGGrammar *xmlRelaxNGGrammarPtr;
67
68struct _xmlRelaxNGGrammar {
69 xmlRelaxNGGrammarPtr parent;/* the parent grammar if any */
70 xmlRelaxNGGrammarPtr children;/* the children grammar if any */
71 xmlRelaxNGGrammarPtr next; /* the next grammar if any */
72 xmlRelaxNGDefinePtr start; /* <start> content */
73 xmlRelaxNGCombine combine; /* the default combine value */
74 xmlHashTablePtr defs; /* define* */
75 xmlHashTablePtr refs; /* references */
76};
77
78
Daniel Veillard6eadf632003-01-23 18:29:16 +000079typedef enum {
80 XML_RELAXNG_EMPTY = 0, /* an empty pattern */
81 XML_RELAXNG_NOT_ALLOWED, /* not allowed top */
82 XML_RELAXNG_TEXT, /* textual content */
83 XML_RELAXNG_ELEMENT, /* an element */
84 XML_RELAXNG_DATATYPE, /* extenal data type definition */
85 XML_RELAXNG_VALUE, /* value from an extenal data type definition */
86 XML_RELAXNG_LIST, /* a list of patterns */
87 XML_RELAXNG_ATTRIBUTE, /* an attrbute following a pattern */
88 XML_RELAXNG_DEF, /* a definition */
89 XML_RELAXNG_REF, /* reference to a definition */
90 XML_RELAXNG_OPTIONAL, /* optional patterns */
91 XML_RELAXNG_ZEROORMORE, /* zero or more non empty patterns */
92 XML_RELAXNG_ONEORMORE, /* one or more non empty patterns */
93 XML_RELAXNG_CHOICE, /* a choice between non empty patterns */
94 XML_RELAXNG_GROUP, /* a pair/group of non empty patterns */
95 XML_RELAXNG_INTERLEAVE /* interleaving choice of non-empty patterns */
96} xmlRelaxNGType;
97
98struct _xmlRelaxNGDefine {
99 xmlRelaxNGType type; /* the type of definition */
100 xmlNodePtr node; /* the node in the source */
101 xmlChar *name; /* the element local name if present */
102 xmlChar *ns; /* the namespace local name if present */
Daniel Veillardedc91922003-01-26 00:52:04 +0000103 xmlChar *value; /* value when available */
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000104 void *data; /* data lib or specific pointer */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000105 xmlRelaxNGDefinePtr content;/* the expected content */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000106 xmlRelaxNGDefinePtr parent; /* the parent definition, if any */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000107 xmlRelaxNGDefinePtr next; /* list within grouping sequences */
108 xmlRelaxNGDefinePtr attrs; /* list of attributes for elements */
109 xmlRelaxNGDefinePtr nextHash;/* next define in defs/refs hash tables */
110};
111
112/**
113 * _xmlRelaxNG:
114 *
115 * A RelaxNGs definition
116 */
117struct _xmlRelaxNG {
118 xmlRelaxNGGrammarPtr topgrammar;
119 xmlDocPtr doc;
120
121 xmlHashTablePtr defs; /* define */
122 xmlHashTablePtr refs; /* references */
123 void *_private; /* unused by the library for users or bindings */
124};
125
126typedef enum {
127 XML_RELAXNG_ERR_OK = 0,
128 XML_RELAXNG_ERR_NOROOT = 1,
129 XML_RELAXNG_ERR_
130} xmlRelaxNGValidError;
131
132#define XML_RELAXNG_IN_ATTRIBUTE 1
133
134struct _xmlRelaxNGParserCtxt {
135 void *userData; /* user specific data block */
136 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
137 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
138 xmlRelaxNGValidError err;
139
140 xmlRelaxNGPtr schema; /* The schema in use */
141 xmlRelaxNGGrammarPtr grammar; /* the current grammar */
142 int flags; /* parser flags */
143 int nbErrors; /* number of errors at parse time */
144 int nbWarnings; /* number of warnings at parse time */
Daniel Veillard276be4a2003-01-24 01:03:34 +0000145 const xmlChar *define; /* the current define scope */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000146 xmlRelaxNGDefinePtr def; /* the current define */
147
148 int nbInterleaves;
149 xmlHashTablePtr interleaves; /* keep track of all the interleaves */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000150
151 xmlChar *URL;
152 xmlDocPtr doc;
153
154 const char *buffer;
155 int size;
156
157 /*
158 * Used to build complex element content models
159 */
160 xmlAutomataPtr am;
161 xmlAutomataStatePtr start;
162 xmlAutomataStatePtr end;
163 xmlAutomataStatePtr state;
164};
165
166#define FLAGS_IGNORABLE 1
167#define FLAGS_NEGATIVE 2
168
169/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000170 * xmlRelaxNGInterleaveGroup:
171 *
172 * A RelaxNGs partition set associated to lists of definitions
173 */
174typedef struct _xmlRelaxNGInterleaveGroup xmlRelaxNGInterleaveGroup;
175typedef xmlRelaxNGInterleaveGroup *xmlRelaxNGInterleaveGroupPtr;
176struct _xmlRelaxNGInterleaveGroup {
177 xmlRelaxNGDefinePtr rule; /* the rule to satisfy */
178 xmlRelaxNGDefinePtr *defs; /* the array of element definitions */
179};
180
181/**
182 * xmlRelaxNGPartitions:
183 *
184 * A RelaxNGs partition associated to an interleave group
185 */
186typedef struct _xmlRelaxNGPartition xmlRelaxNGPartition;
187typedef xmlRelaxNGPartition *xmlRelaxNGPartitionPtr;
188struct _xmlRelaxNGPartition {
189 int nbgroups; /* number of groups in the partitions */
190 xmlRelaxNGInterleaveGroupPtr *groups;
191};
192
193/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000194 * xmlRelaxNGValidState:
195 *
196 * A RelaxNGs validation state
197 */
198#define MAX_ATTR 20
199typedef struct _xmlRelaxNGValidState xmlRelaxNGValidState;
200typedef xmlRelaxNGValidState *xmlRelaxNGValidStatePtr;
201struct _xmlRelaxNGValidState {
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000202 xmlNodePtr node; /* the current node */
203 xmlNodePtr seq; /* the sequence of children left to validate */
204 int nbAttrs; /* the number of attributes */
205 xmlChar *value; /* the value when operating on string */
206 xmlChar *endvalue; /* the end value when operating on string */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000207 xmlAttrPtr attrs[1]; /* the array of attributes */
208};
209
210/**
211 * xmlRelaxNGValidCtxt:
212 *
213 * A RelaxNGs validation context
214 */
215
216struct _xmlRelaxNGValidCtxt {
217 void *userData; /* user specific data block */
218 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
219 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
220
221 xmlRelaxNGPtr schema; /* The schema in use */
222 xmlDocPtr doc; /* the document being validated */
223 xmlRelaxNGValidStatePtr state; /* the current validation state */
224 int flags; /* validation flags */
225};
226
227/************************************************************************
228 * *
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000229 * Preliminary type checking interfaces *
230 * *
231 ************************************************************************/
232/**
233 * xmlRelaxNGTypeHave:
234 * @data: data needed for the library
235 * @type: the type name
236 * @value: the value to check
237 *
238 * Function provided by a type library to check if a type is exported
239 *
240 * Returns 1 if yes, 0 if no and -1 in case of error.
241 */
242typedef int (*xmlRelaxNGTypeHave) (void *data, const xmlChar *type);
243
244/**
245 * xmlRelaxNGTypeCheck:
246 * @data: data needed for the library
247 * @type: the type name
248 * @value: the value to check
249 *
250 * Function provided by a type library to check if a value match a type
251 *
252 * Returns 1 if yes, 0 if no and -1 in case of error.
253 */
254typedef int (*xmlRelaxNGTypeCheck) (void *data, const xmlChar *type,
255 const xmlChar *value);
256
257/**
258 * xmlRelaxNGTypeCompare:
259 * @data: data needed for the library
260 * @type: the type name
261 * @value1: the first value
262 * @value2: the second value
263 *
264 * Function provided by a type library to compare two values accordingly
265 * to a type.
266 *
267 * Returns 1 if yes, 0 if no and -1 in case of error.
268 */
269typedef int (*xmlRelaxNGTypeCompare) (void *data, const xmlChar *type,
270 const xmlChar *value1,
271 const xmlChar *value2);
272typedef struct _xmlRelaxNGTypeLibrary xmlRelaxNGTypeLibrary;
273typedef xmlRelaxNGTypeLibrary *xmlRelaxNGTypeLibraryPtr;
274struct _xmlRelaxNGTypeLibrary {
275 const xmlChar *namespace; /* the datatypeLibrary value */
276 void *data; /* data needed for the library */
277 xmlRelaxNGTypeHave have; /* the export function */
278 xmlRelaxNGTypeCheck check; /* the checking function */
279 xmlRelaxNGTypeCompare comp; /* the compare function */
280};
281
282/************************************************************************
283 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +0000284 * Allocation functions *
285 * *
286 ************************************************************************/
287static void xmlRelaxNGFreeDefineList(xmlRelaxNGDefinePtr defines);
288static void xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar);
289static void xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define);
290
291/**
292 * xmlRelaxNGNewRelaxNG:
293 * @ctxt: a Relax-NG validation context (optional)
294 *
295 * Allocate a new RelaxNG structure.
296 *
297 * Returns the newly allocated structure or NULL in case or error
298 */
299static xmlRelaxNGPtr
300xmlRelaxNGNewRelaxNG(xmlRelaxNGParserCtxtPtr ctxt)
301{
302 xmlRelaxNGPtr ret;
303
304 ret = (xmlRelaxNGPtr) xmlMalloc(sizeof(xmlRelaxNG));
305 if (ret == NULL) {
306 if ((ctxt != NULL) && (ctxt->error != NULL))
307 ctxt->error(ctxt->userData, "Out of memory\n");
308 ctxt->nbErrors++;
309 return (NULL);
310 }
311 memset(ret, 0, sizeof(xmlRelaxNG));
312
313 return (ret);
314}
315
316/**
317 * xmlRelaxNGFree:
318 * @schema: a schema structure
319 *
320 * Deallocate a RelaxNG structure.
321 */
322void
323xmlRelaxNGFree(xmlRelaxNGPtr schema)
324{
325 if (schema == NULL)
326 return;
327
328#if 0
329 if (schema->elemDecl != NULL)
330 xmlHashFree(schema->elemDecl,
331 (xmlHashDeallocator) xmlRelaxNGFreeElement);
332 if (schema->typeDecl != NULL)
333 xmlHashFree(schema->typeDecl,
334 (xmlHashDeallocator) xmlRelaxNGFreeType);
335#endif
336
337 if (schema->topgrammar != NULL)
338 xmlRelaxNGFreeGrammar(schema->topgrammar);
339 if (schema->doc != NULL)
340 xmlFreeDoc(schema->doc);
341
342 xmlFree(schema);
343}
344
345/**
346 * xmlRelaxNGNewGrammar:
347 * @ctxt: a Relax-NG validation context (optional)
348 *
349 * Allocate a new RelaxNG grammar.
350 *
351 * Returns the newly allocated structure or NULL in case or error
352 */
353static xmlRelaxNGGrammarPtr
354xmlRelaxNGNewGrammar(xmlRelaxNGParserCtxtPtr ctxt)
355{
356 xmlRelaxNGGrammarPtr ret;
357
358 ret = (xmlRelaxNGGrammarPtr) xmlMalloc(sizeof(xmlRelaxNGGrammar));
359 if (ret == NULL) {
360 if ((ctxt != NULL) && (ctxt->error != NULL))
361 ctxt->error(ctxt->userData, "Out of memory\n");
362 ctxt->nbErrors++;
363 return (NULL);
364 }
365 memset(ret, 0, sizeof(xmlRelaxNGGrammar));
366
367 return (ret);
368}
369
370/**
Daniel Veillard276be4a2003-01-24 01:03:34 +0000371 * xmlRelaxNGFreeDefineHash:
372 * @defines: a list of define structures
373 *
374 * Deallocate a RelaxNG definition in the hash table
375 */
376static void
377xmlRelaxNGFreeDefineHash(xmlRelaxNGDefinePtr defines)
378{
379 xmlRelaxNGDefinePtr next;
380
381 while (defines != NULL) {
382 next = defines->nextHash;
383 xmlRelaxNGFreeDefine(defines);
384 defines = next;
385 }
386}
387
388/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000389 * xmlRelaxNGFreeGrammar:
390 * @grammar: a grammar structure
391 *
392 * Deallocate a RelaxNG grammar structure.
393 */
394static void
395xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar)
396{
397 if (grammar == NULL)
398 return;
399
400 if (grammar->start != NULL)
401 xmlRelaxNGFreeDefine(grammar->start);
402 if (grammar->refs != NULL) {
403 xmlHashFree(grammar->refs, NULL);
404 }
405 if (grammar->defs != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +0000406 xmlHashFree(grammar->defs, (xmlHashDeallocator)
407 xmlRelaxNGFreeDefineHash);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000408 }
409
410 xmlFree(grammar);
411}
412
413/**
414 * xmlRelaxNGNewDefine:
415 * @ctxt: a Relax-NG validation context
416 * @node: the node in the input document.
417 *
418 * Allocate a new RelaxNG define.
419 *
420 * Returns the newly allocated structure or NULL in case or error
421 */
422static xmlRelaxNGDefinePtr
423xmlRelaxNGNewDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
424{
425 xmlRelaxNGDefinePtr ret;
426
427 ret = (xmlRelaxNGDefinePtr) xmlMalloc(sizeof(xmlRelaxNGDefine));
428 if (ret == NULL) {
429 if ((ctxt != NULL) && (ctxt->error != NULL))
430 ctxt->error(ctxt->userData, "Out of memory\n");
431 ctxt->nbErrors++;
432 return (NULL);
433 }
434 memset(ret, 0, sizeof(xmlRelaxNGDefine));
435 ret->node = node;
436
437 return (ret);
438}
439
440/**
441 * xmlRelaxNGFreeDefineList:
442 * @defines: a list of define structures
443 *
444 * Deallocate a RelaxNG define structures.
445 */
446static void
447xmlRelaxNGFreeDefineList(xmlRelaxNGDefinePtr defines)
448{
449 xmlRelaxNGDefinePtr next;
450
451 while (defines != NULL) {
452 next = defines->next;
453 xmlRelaxNGFreeDefine(defines);
454 defines = next;
455 }
456}
457
458/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000459 * xmlRelaxNGFreePartition:
460 * @partitions: a partition set structure
461 *
462 * Deallocate RelaxNG partition set structures.
463 */
464static void
465xmlRelaxNGFreePartition(xmlRelaxNGPartitionPtr partitions) {
466 xmlRelaxNGInterleaveGroupPtr group;
467 int j;
468
469 if (partitions != NULL) {
470 if (partitions->groups != NULL) {
471 for (j = 0;j < partitions->nbgroups;j++) {
472 group = partitions->groups[j];
473 if (group != NULL) {
474 if (group->defs != NULL)
475 xmlFree(group->defs);
476 xmlFree(group);
477 }
478 }
479 xmlFree(partitions->groups);
480 }
481 xmlFree(partitions);
482 }
483}
484/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000485 * xmlRelaxNGFreeDefine:
486 * @define: a define structure
487 *
488 * Deallocate a RelaxNG define structure.
489 */
490static void
491xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define)
492{
493 if (define == NULL)
494 return;
495
496 if (define->name != NULL)
497 xmlFree(define->name);
498 if (define->ns != NULL)
499 xmlFree(define->ns);
Daniel Veillardedc91922003-01-26 00:52:04 +0000500 if (define->value != NULL)
501 xmlFree(define->value);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000502 if (define->attrs != NULL)
503 xmlRelaxNGFreeDefineList(define->attrs);
Daniel Veillard276be4a2003-01-24 01:03:34 +0000504 if ((define->content != NULL) &&
505 (define->type != XML_RELAXNG_REF))
Daniel Veillard6eadf632003-01-23 18:29:16 +0000506 xmlRelaxNGFreeDefineList(define->content);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000507 if ((define->data != NULL) &&
508 (define->type == XML_RELAXNG_INTERLEAVE))
509 xmlRelaxNGFreePartition((xmlRelaxNGPartitionPtr) define->data);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000510 xmlFree(define);
511}
512
513/**
514 * xmlRelaxNGNewValidState:
515 * @ctxt: a Relax-NG validation context
516 * @node: the current node or NULL for the document
517 *
518 * Allocate a new RelaxNG validation state
519 *
520 * Returns the newly allocated structure or NULL in case or error
521 */
522static xmlRelaxNGValidStatePtr
523xmlRelaxNGNewValidState(xmlRelaxNGValidCtxtPtr ctxt, xmlNodePtr node)
524{
525 xmlRelaxNGValidStatePtr ret;
526 xmlAttrPtr attr;
527 xmlAttrPtr attrs[MAX_ATTR];
528 int nbAttrs = 0;
529 xmlNodePtr root = NULL;
530
531 if (node == NULL) {
532 root = xmlDocGetRootElement(ctxt->doc);
533 if (root == NULL)
534 return(NULL);
535 } else {
536 attr = node->properties;
537 while (attr != NULL) {
538 if (nbAttrs < MAX_ATTR)
539 attrs[nbAttrs++] = attr;
540 else
541 nbAttrs++;
542 attr = attr->next;
543 }
544 }
545
546 if (nbAttrs < MAX_ATTR)
547 attrs[nbAttrs] = NULL;
548 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(sizeof(xmlRelaxNGValidState) +
549 nbAttrs * sizeof(xmlAttrPtr));
550 if (ret == NULL) {
551 if ((ctxt != NULL) && (ctxt->error != NULL))
552 ctxt->error(ctxt->userData, "Out of memory\n");
553 return (NULL);
554 }
555 if (node == NULL) {
556 ret->node = (xmlNodePtr) ctxt->doc;
557 ret->seq = root;
558 ret->nbAttrs = 0;
559 } else {
560 ret->node = node;
561 ret->seq = node->children;
562 ret->nbAttrs = nbAttrs;
563 if (nbAttrs > 0) {
564 if (nbAttrs < MAX_ATTR) {
565 memcpy(&(ret->attrs[0]), attrs,
566 sizeof(xmlAttrPtr) * (nbAttrs + 1));
567 } else {
568 attr = node->properties;
569 nbAttrs = 0;
570 while (attr != NULL) {
571 ret->attrs[nbAttrs++] = attr;
572 attr = attr->next;
573 }
574 ret->attrs[nbAttrs] = NULL;
575 }
576 }
577 }
578 return (ret);
579}
580
581/**
582 * xmlRelaxNGCopyValidState:
583 * @ctxt: a Relax-NG validation context
584 * @state: a validation state
585 *
586 * Copy the validation state
587 *
588 * Returns the newly allocated structure or NULL in case or error
589 */
590static xmlRelaxNGValidStatePtr
591xmlRelaxNGCopyValidState(xmlRelaxNGValidCtxtPtr ctxt,
592 xmlRelaxNGValidStatePtr state)
593{
594 xmlRelaxNGValidStatePtr ret;
595 unsigned int size;
596
597 if (state == NULL)
598 return(NULL);
599
600 size = sizeof(xmlRelaxNGValidState) +
601 state->nbAttrs * sizeof(xmlAttrPtr);
602 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(size);
603 if (ret == NULL) {
604 if ((ctxt != NULL) && (ctxt->error != NULL))
605 ctxt->error(ctxt->userData, "Out of memory\n");
606 return (NULL);
607 }
608 memcpy(ret, state, size);
609 return(ret);
610}
611
612/**
613 * xmlRelaxNGFreeValidState:
614 * @state: a validation state structure
615 *
616 * Deallocate a RelaxNG validation state structure.
617 */
618static void
619xmlRelaxNGFreeValidState(xmlRelaxNGValidStatePtr state)
620{
621 if (state == NULL)
622 return;
623
624 xmlFree(state);
625}
626
627/************************************************************************
628 * *
629 * Error functions *
630 * *
631 ************************************************************************/
632
633#define VALID_CTXT() \
634 if (ctxt->flags == 0) xmlGenericError(xmlGenericErrorContext, \
635 "error detected at %s:%d\n", \
636 __FILE__, __LINE__);
637#define VALID_ERROR if (ctxt->flags == 0) printf
638
639#if 0
640/**
641 * xmlRelaxNGErrorContext:
642 * @ctxt: the parsing context
643 * @schema: the schema being built
644 * @node: the node being processed
645 * @child: the child being processed
646 *
647 * Dump a RelaxNGType structure
648 */
649static void
650xmlRelaxNGErrorContext(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGPtr schema,
651 xmlNodePtr node, xmlNodePtr child)
652{
653 int line = 0;
654 const xmlChar *file = NULL;
655 const xmlChar *name = NULL;
656 const char *type = "error";
657
658 if ((ctxt == NULL) || (ctxt->error == NULL))
659 return;
660
661 if (child != NULL)
662 node = child;
663
664 if (node != NULL) {
665 if ((node->type == XML_DOCUMENT_NODE) ||
666 (node->type == XML_HTML_DOCUMENT_NODE)) {
667 xmlDocPtr doc = (xmlDocPtr) node;
668
669 file = doc->URL;
670 } else {
671 /*
672 * Try to find contextual informations to report
673 */
674 if (node->type == XML_ELEMENT_NODE) {
675 line = (int) node->content;
676 } else if ((node->prev != NULL) &&
677 (node->prev->type == XML_ELEMENT_NODE)) {
678 line = (int) node->prev->content;
679 } else if ((node->parent != NULL) &&
680 (node->parent->type == XML_ELEMENT_NODE)) {
681 line = (int) node->parent->content;
682 }
683 if ((node->doc != NULL) && (node->doc->URL != NULL))
684 file = node->doc->URL;
685 if (node->name != NULL)
686 name = node->name;
687 }
688 }
689
690 if (ctxt != NULL)
691 type = "compilation error";
692 else if (schema != NULL)
693 type = "runtime error";
694
695 if ((file != NULL) && (line != 0) && (name != NULL))
696 ctxt->error(ctxt->userData, "%s: file %s line %d element %s\n",
697 type, file, line, name);
698 else if ((file != NULL) && (name != NULL))
699 ctxt->error(ctxt->userData, "%s: file %s element %s\n",
700 type, file, name);
701 else if ((file != NULL) && (line != 0))
702 ctxt->error(ctxt->userData, "%s: file %s line %d\n", type, file, line);
703 else if (file != NULL)
704 ctxt->error(ctxt->userData, "%s: file %s\n", type, file);
705 else if (name != NULL)
706 ctxt->error(ctxt->userData, "%s: element %s\n", type, name);
707 else
708 ctxt->error(ctxt->userData, "%s\n", type);
709}
710#endif
711
712/************************************************************************
713 * *
714 * Type library hooks *
715 * *
716 ************************************************************************/
Daniel Veillardea3f3982003-01-26 19:45:18 +0000717static xmlChar *xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt,
718 const xmlChar *str);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000719
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000720/**
721 * xmlRelaxNGSchemaTypeHave:
722 * @data: data needed for the library
723 * @type: the type name
724 *
725 * Check if the given type is provided by
726 * the W3C XMLSchema Datatype library.
727 *
728 * Returns 1 if yes, 0 if no and -1 in case of error.
729 */
730static int
731xmlRelaxNGSchemaTypeHave(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000732 const xmlChar *type) {
733 xmlSchemaTypePtr typ;
734
735 if (type == NULL)
736 return(-1);
737 typ = xmlSchemaGetPredefinedType(type,
738 BAD_CAST "http://www.w3.org/2001/XMLSchema");
739 if (typ == NULL)
740 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000741 return(1);
742}
743
744/**
745 * xmlRelaxNGSchemaTypeCheck:
746 * @data: data needed for the library
747 * @type: the type name
748 * @value: the value to check
749 *
750 * Check if the given type and value are validated by
751 * the W3C XMLSchema Datatype library.
752 *
753 * Returns 1 if yes, 0 if no and -1 in case of error.
754 */
755static int
756xmlRelaxNGSchemaTypeCheck(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000757 const xmlChar *type,
758 const xmlChar *value) {
759 xmlSchemaTypePtr typ;
760 int ret;
761
762 /*
763 * TODO: the type should be cached ab provided back, interface subject
764 * to changes.
765 * TODO: handle facets, may require an additional interface and keep
766 * the value returned from the validation.
767 */
768 if ((type == NULL) || (value == NULL))
769 return(-1);
770 typ = xmlSchemaGetPredefinedType(type,
771 BAD_CAST "http://www.w3.org/2001/XMLSchema");
772 if (typ == NULL)
773 return(-1);
774 ret = xmlSchemaValidatePredefinedType(typ, value, NULL);
775 if (ret == 0)
776 return(1);
777 if (ret > 0)
778 return(0);
779 return(-1);
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000780}
781
782/**
783 * xmlRelaxNGSchemaTypeCompare:
784 * @data: data needed for the library
785 * @type: the type name
786 * @value1: the first value
787 * @value2: the second value
788 *
789 * Compare two values accordingly a type from the W3C XMLSchema
790 * Datatype library.
791 *
792 * Returns 1 if yes, 0 if no and -1 in case of error.
793 */
794static int
795xmlRelaxNGSchemaTypeCompare(void *data ATTRIBUTE_UNUSED,
796 const xmlChar *type ATTRIBUTE_UNUSED,
797 const xmlChar *value1 ATTRIBUTE_UNUSED,
798 const xmlChar *value2 ATTRIBUTE_UNUSED) {
799 TODO
800 return(1);
801}
802
803/**
804 * xmlRelaxNGDefaultTypeHave:
805 * @data: data needed for the library
806 * @type: the type name
807 *
808 * Check if the given type is provided by
809 * the default datatype library.
810 *
811 * Returns 1 if yes, 0 if no and -1 in case of error.
812 */
813static int
814xmlRelaxNGDefaultTypeHave(void *data ATTRIBUTE_UNUSED, const xmlChar *type) {
815 if (type == NULL)
816 return(-1);
817 if (xmlStrEqual(type, BAD_CAST "string"))
818 return(1);
819 if (xmlStrEqual(type, BAD_CAST "token"))
820 return(1);
821 return(0);
822}
823
824/**
825 * xmlRelaxNGDefaultTypeCheck:
826 * @data: data needed for the library
827 * @type: the type name
828 * @value: the value to check
829 *
830 * Check if the given type and value are validated by
831 * the default datatype library.
832 *
833 * Returns 1 if yes, 0 if no and -1 in case of error.
834 */
835static int
836xmlRelaxNGDefaultTypeCheck(void *data ATTRIBUTE_UNUSED,
837 const xmlChar *type ATTRIBUTE_UNUSED,
838 const xmlChar *value ATTRIBUTE_UNUSED) {
839 return(1);
840}
841
842/**
843 * xmlRelaxNGDefaultTypeCompare:
844 * @data: data needed for the library
845 * @type: the type name
846 * @value1: the first value
847 * @value2: the second value
848 *
849 * Compare two values accordingly a type from the default
850 * datatype library.
851 *
852 * Returns 1 if yes, 0 if no and -1 in case of error.
853 */
854static int
855xmlRelaxNGDefaultTypeCompare(void *data ATTRIBUTE_UNUSED,
856 const xmlChar *type ATTRIBUTE_UNUSED,
857 const xmlChar *value1 ATTRIBUTE_UNUSED,
858 const xmlChar *value2 ATTRIBUTE_UNUSED) {
Daniel Veillardea3f3982003-01-26 19:45:18 +0000859 int ret = -1;
860
861 if (xmlStrEqual(type, BAD_CAST "string")) {
862 ret = xmlStrEqual(value1, value2);
863 } else if (xmlStrEqual(type, BAD_CAST "token")) {
864 if (!xmlStrEqual(value1, value2)) {
865 xmlChar *nval, *nvalue;
866
867 /*
868 * TODO: trivial optimizations are possible by
869 * computing at compile-time
870 */
871 nval = xmlRelaxNGNormalize(NULL, value1);
872 nvalue = xmlRelaxNGNormalize(NULL, value2);
873
874 if ((nval == NULL) || (nvalue == NULL) ||
875 (!xmlStrEqual(nval, nvalue)))
876 ret = -1;
877 if (nval != NULL)
878 xmlFree(nval);
879 if (nvalue != NULL)
880 xmlFree(nvalue);
881 }
882 }
883 return(ret);
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000884}
885
886static int xmlRelaxNGTypeInitialized = 0;
887static xmlHashTablePtr xmlRelaxNGRegisteredTypes = NULL;
888
889/**
890 * xmlRelaxNGFreeTypeLibrary:
891 * @lib: the type library structure
892 * @namespace: the URI bound to the library
893 *
894 * Free the structure associated to the type library
895 */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000896static void
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000897xmlRelaxNGFreeTypeLibrary(xmlRelaxNGTypeLibraryPtr lib,
898 const xmlChar *namespace ATTRIBUTE_UNUSED) {
899 if (lib == NULL)
900 return;
901 if (lib->namespace != NULL)
902 xmlFree((xmlChar *)lib->namespace);
903 xmlFree(lib);
904}
905
906/**
907 * xmlRelaxNGRegisterTypeLibrary:
908 * @namespace: the URI bound to the library
909 * @data: data associated to the library
910 * @have: the provide function
911 * @check: the checking function
912 * @comp: the comparison function
913 *
914 * Register a new type library
915 *
916 * Returns 0 in case of success and -1 in case of error.
917 */
918static int
919xmlRelaxNGRegisterTypeLibrary(const xmlChar *namespace, void *data,
920 xmlRelaxNGTypeHave have, xmlRelaxNGTypeCheck check,
921 xmlRelaxNGTypeCompare comp) {
922 xmlRelaxNGTypeLibraryPtr lib;
923 int ret;
924
925 if ((xmlRelaxNGRegisteredTypes == NULL) || (namespace == NULL) ||
926 (check == NULL) || (comp == NULL))
927 return(-1);
928 if (xmlHashLookup(xmlRelaxNGRegisteredTypes, namespace) != NULL) {
929 xmlGenericError(xmlGenericErrorContext,
930 "Relax-NG types library '%s' already registered\n",
931 namespace);
932 return(-1);
933 }
934 lib = (xmlRelaxNGTypeLibraryPtr) xmlMalloc(sizeof(xmlRelaxNGTypeLibrary));
935 if (lib == NULL) {
936 xmlGenericError(xmlGenericErrorContext,
937 "Relax-NG types library '%s' malloc() failed\n",
938 namespace);
939 return (-1);
940 }
941 memset(lib, 0, sizeof(xmlRelaxNGTypeLibrary));
942 lib->namespace = xmlStrdup(namespace);
943 lib->data = data;
944 lib->have = have;
945 lib->comp = comp;
946 lib->check = check;
947 ret = xmlHashAddEntry(xmlRelaxNGRegisteredTypes, namespace, lib);
948 if (ret < 0) {
949 xmlGenericError(xmlGenericErrorContext,
950 "Relax-NG types library failed to register '%s'\n",
951 namespace);
952 xmlRelaxNGFreeTypeLibrary(lib, namespace);
953 return(-1);
954 }
955 return(0);
956}
957
958/**
959 * xmlRelaxNGInitTypes:
960 *
961 * Initilize the default type libraries.
962 *
963 * Returns 0 in case of success and -1 in case of error.
964 */
965static int
Daniel Veillard6eadf632003-01-23 18:29:16 +0000966xmlRelaxNGInitTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000967 if (xmlRelaxNGTypeInitialized != 0)
968 return(0);
969 xmlRelaxNGRegisteredTypes = xmlHashCreate(10);
970 if (xmlRelaxNGRegisteredTypes == NULL) {
971 xmlGenericError(xmlGenericErrorContext,
972 "Failed to allocate sh table for Relax-NG types\n");
973 return(-1);
974 }
975 xmlRelaxNGRegisterTypeLibrary(
976 BAD_CAST "http://www.w3.org/2001/XMLSchema-datatypes",
977 NULL,
978 xmlRelaxNGSchemaTypeHave,
979 xmlRelaxNGSchemaTypeCheck,
980 xmlRelaxNGSchemaTypeCompare);
981 xmlRelaxNGRegisterTypeLibrary(
982 xmlRelaxNGNs,
983 NULL,
984 xmlRelaxNGDefaultTypeHave,
985 xmlRelaxNGDefaultTypeCheck,
986 xmlRelaxNGDefaultTypeCompare);
987 xmlRelaxNGTypeInitialized = 1;
988 return(0);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000989}
990
991/**
992 * xmlRelaxNGCleanupTypes:
993 *
994 * Cleanup the default Schemas type library associated to RelaxNG
995 */
996void
997xmlRelaxNGCleanupTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000998 if (xmlRelaxNGTypeInitialized == 0)
999 return;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001000 xmlSchemaCleanupTypes();
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001001 xmlHashFree(xmlRelaxNGRegisteredTypes, (xmlHashDeallocator)
1002 xmlRelaxNGFreeTypeLibrary);
1003 xmlRelaxNGTypeInitialized = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001004}
1005
1006/************************************************************************
1007 * *
1008 * Parsing functions *
1009 * *
1010 ************************************************************************/
1011
1012static xmlRelaxNGDefinePtr xmlRelaxNGParseAttribute(
1013 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1014static xmlRelaxNGDefinePtr xmlRelaxNGParseElement(
1015 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1016static xmlRelaxNGDefinePtr xmlRelaxNGParsePatterns(
1017 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001018static xmlRelaxNGDefinePtr xmlRelaxNGParsePattern(
1019 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001020
1021
1022#define IS_BLANK_NODE(n) \
1023 (((n)->type == XML_TEXT_NODE) && (xmlRelaxNGIsBlank((n)->content)))
1024
1025/**
1026 * xmlRelaxNGIsBlank:
1027 * @str: a string
1028 *
1029 * Check if a string is ignorable c.f. 4.2. Whitespace
1030 *
1031 * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
1032 */
1033static int
1034xmlRelaxNGIsBlank(xmlChar *str) {
1035 if (str == NULL)
1036 return(1);
1037 while (*str != 0) {
1038 if (!(IS_BLANK(*str))) return(0);
1039 str++;
1040 }
1041 return(1);
1042}
1043
Daniel Veillard6eadf632003-01-23 18:29:16 +00001044/**
1045 * xmlRelaxNGGetDataTypeLibrary:
1046 * @ctxt: a Relax-NG parser context
1047 * @node: the current data or value element
1048 *
1049 * Applies algorithm from 4.3. datatypeLibrary attribute
1050 *
1051 * Returns the datatypeLibary value or NULL if not found
1052 */
1053static xmlChar *
1054xmlRelaxNGGetDataTypeLibrary(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1055 xmlNodePtr node) {
1056 xmlChar *ret, *escape;
1057
Daniel Veillard6eadf632003-01-23 18:29:16 +00001058 if ((IS_RELAXNG(node, "data")) || (IS_RELAXNG(node, "value"))) {
1059 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1060 if (ret != NULL) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001061 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +00001062 if (escape == NULL) {
1063 return(ret);
1064 }
1065 xmlFree(ret);
1066 return(escape);
1067 }
1068 }
1069 node = node->parent;
1070 while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) {
1071 if (IS_RELAXNG(node, "element")) {
1072 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1073 if (ret != NULL) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001074 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +00001075 if (escape == NULL) {
1076 return(ret);
1077 }
1078 xmlFree(ret);
1079 return(escape);
1080 }
1081 }
1082 node = node->parent;
1083 }
1084 return(NULL);
1085}
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001086
1087/**
Daniel Veillardedc91922003-01-26 00:52:04 +00001088 * xmlRelaxNGParseValue:
1089 * @ctxt: a Relax-NG parser context
1090 * @node: the data node.
1091 *
1092 * parse the content of a RelaxNG value node.
1093 *
1094 * Returns the definition pointer or NULL in case of error
1095 */
1096static xmlRelaxNGDefinePtr
1097xmlRelaxNGParseValue(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1098 xmlRelaxNGDefinePtr def = NULL;
1099 xmlRelaxNGTypeLibraryPtr lib;
1100 xmlChar *type;
1101 xmlChar *library;
1102 int tmp;
1103
1104 def = xmlRelaxNGNewDefine(ctxt, node);
1105 if (def == NULL)
1106 return(NULL);
1107 def->type = XML_RELAXNG_VALUE;
1108
1109 type = xmlGetProp(node, BAD_CAST "type");
1110 if (type != NULL) {
1111 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1112 if (library == NULL)
1113 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1114
1115 def->name = type;
1116 def->ns = library;
1117
1118 lib = (xmlRelaxNGTypeLibraryPtr)
1119 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1120 if (lib == NULL) {
1121 if (ctxt->error != NULL)
1122 ctxt->error(ctxt->userData,
1123 "Use of unregistered type library '%s'\n",
1124 library);
1125 ctxt->nbErrors++;
1126 def->data = NULL;
1127 } else {
1128 def->data = lib;
1129 if (lib->have == NULL) {
1130 ctxt->error(ctxt->userData,
1131 "Internal error with type library '%s': no 'have'\n",
1132 library);
1133 ctxt->nbErrors++;
1134 } else {
1135 tmp = lib->have(lib->data, def->name);
1136 if (tmp != 1) {
1137 ctxt->error(ctxt->userData,
1138 "Error type '%s' is not exported by type library '%s'\n",
1139 def->name, library);
1140 ctxt->nbErrors++;
1141 }
1142 }
1143 }
1144 }
1145 if (node->children == NULL) {
1146 if (ctxt->error != NULL)
1147 ctxt->error(ctxt->userData,
1148 "Element <value> has no content\n");
1149 ctxt->nbErrors++;
1150 } else if ((node->children->type != XML_TEXT_NODE) ||
1151 (node->children->next != NULL)) {
1152 if (ctxt->error != NULL)
1153 ctxt->error(ctxt->userData,
1154 "Expecting a single text value for <value>content\n");
1155 ctxt->nbErrors++;
1156 } else {
1157 def->value = xmlNodeGetContent(node);
1158 if (def->value == NULL) {
1159 if (ctxt->error != NULL)
1160 ctxt->error(ctxt->userData,
1161 "Element <value> has no content\n");
1162 ctxt->nbErrors++;
1163 }
1164 }
1165 /* TODO check ahead of time that the value is okay per the type */
1166 return(def);
1167}
1168
1169/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001170 * xmlRelaxNGParseData:
1171 * @ctxt: a Relax-NG parser context
1172 * @node: the data node.
1173 *
1174 * parse the content of a RelaxNG data node.
1175 *
1176 * Returns the definition pointer or NULL in case of error
1177 */
1178static xmlRelaxNGDefinePtr
1179xmlRelaxNGParseData(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1180 xmlRelaxNGDefinePtr def = NULL;
1181 xmlRelaxNGTypeLibraryPtr lib;
1182 xmlChar *type;
1183 xmlChar *library;
1184 xmlNodePtr content;
1185 int tmp;
1186
1187 type = xmlGetProp(node, BAD_CAST "type");
1188 if (type == NULL) {
1189 if (ctxt->error != NULL)
1190 ctxt->error(ctxt->userData,
1191 "data has no type\n");
1192 ctxt->nbErrors++;
1193 return(NULL);
1194 }
1195 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1196 if (library == NULL)
1197 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1198
1199 def = xmlRelaxNGNewDefine(ctxt, node);
1200 if (def == NULL) {
1201 xmlFree(type);
1202 return(NULL);
1203 }
1204 def->type = XML_RELAXNG_DATATYPE;
1205 def->name = type;
1206 def->ns = library;
1207
1208 lib = (xmlRelaxNGTypeLibraryPtr)
1209 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1210 if (lib == NULL) {
1211 if (ctxt->error != NULL)
1212 ctxt->error(ctxt->userData,
1213 "Use of unregistered type library '%s'\n",
1214 library);
1215 ctxt->nbErrors++;
1216 def->data = NULL;
1217 } else {
1218 def->data = lib;
1219 if (lib->have == NULL) {
1220 ctxt->error(ctxt->userData,
1221 "Internal error with type library '%s': no 'have'\n",
1222 library);
1223 ctxt->nbErrors++;
1224 } else {
1225 tmp = lib->have(lib->data, def->name);
1226 if (tmp != 1) {
1227 ctxt->error(ctxt->userData,
1228 "Error type '%s' is not exported by type library '%s'\n",
1229 def->name, library);
1230 ctxt->nbErrors++;
1231 }
1232 }
1233 }
1234 content = node->children;
1235 while (content != NULL) {
1236 TODO
1237 content = content->next;
1238 }
1239
1240 return(def);
1241}
1242
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001243/**
1244 * xmlRelaxNGCompareElemDefLists:
1245 * @ctxt: a Relax-NG parser context
1246 * @defs1: the first list of element defs
1247 * @defs2: the second list of element defs
1248 *
1249 * Compare the 2 lists of element definitions. The comparison is
1250 * that if both lists do not accept the same QNames, it returns 1
1251 * If the 2 lists can accept the same QName the comparison returns 0
1252 *
1253 * Returns 1 disttinct, 0 if equal
1254 */
1255static int
1256xmlRelaxNGCompareElemDefLists(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1257 xmlRelaxNGDefinePtr *def1,
1258 xmlRelaxNGDefinePtr *def2) {
1259 xmlRelaxNGDefinePtr *basedef2 = def2;
1260
1261 if ((*def1 == NULL) || (*def2 == NULL))
1262 return(1);
1263 while (*def1 != NULL) {
1264 while ((*def2) != NULL) {
1265 if ((*def1)->name == NULL) {
1266 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1267 return(0);
1268 } else if ((*def2)->name == NULL) {
1269 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1270 return(0);
1271 } else if (xmlStrEqual((*def1)->name, (*def2)->name)) {
1272 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1273 return(0);
1274 }
1275 def2++;
1276 }
1277 def2 = basedef2;
1278 def1++;
1279 }
1280 return(1);
1281}
1282
1283/**
1284 * xmlRelaxNGGetElements:
1285 * @ctxt: a Relax-NG parser context
1286 * @def: the interleave definition
1287 *
1288 * Compute the list of top elements a definition can generate
1289 *
1290 * Returns a list of elements or NULL if none was found.
1291 */
1292static xmlRelaxNGDefinePtr *
1293xmlRelaxNGGetElements(xmlRelaxNGParserCtxtPtr ctxt,
1294 xmlRelaxNGDefinePtr def) {
1295 xmlRelaxNGDefinePtr *ret = NULL, parent, cur, tmp;
1296 int len = 0;
1297 int max = 0;
1298
1299 parent = NULL;
1300 cur = def;
1301 while (cur != NULL) {
Daniel Veillardb08c9812003-01-28 23:09:49 +00001302 if ((cur->type == XML_RELAXNG_ELEMENT) ||
1303 (cur->type == XML_RELAXNG_TEXT)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001304 if (ret == NULL) {
1305 max = 10;
1306 ret = (xmlRelaxNGDefinePtr *)
1307 xmlMalloc((max + 1) * sizeof(xmlRelaxNGDefinePtr));
1308 if (ret == NULL) {
1309 if (ctxt->error != NULL)
1310 ctxt->error(ctxt->userData,
1311 "Out of memory in element search\n");
1312 ctxt->nbErrors++;
1313 return(NULL);
1314 }
1315 } else if (max <= len) {
1316 max *= 2;
1317 ret = xmlRealloc(ret, (max + 1) * sizeof(xmlRelaxNGDefinePtr));
1318 if (ret == NULL) {
1319 if (ctxt->error != NULL)
1320 ctxt->error(ctxt->userData,
1321 "Out of memory in element search\n");
1322 ctxt->nbErrors++;
1323 return(NULL);
1324 }
1325 }
Daniel Veillardb08c9812003-01-28 23:09:49 +00001326 ret[len++] = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001327 ret[len] = NULL;
1328 } else if ((cur->type == XML_RELAXNG_CHOICE) ||
1329 (cur->type == XML_RELAXNG_INTERLEAVE) ||
1330 (cur->type == XML_RELAXNG_GROUP) ||
1331 (cur->type == XML_RELAXNG_ONEORMORE) ||
Daniel Veillardb08c9812003-01-28 23:09:49 +00001332 (cur->type == XML_RELAXNG_ZEROORMORE) ||
1333 (cur->type == XML_RELAXNG_OPTIONAL) ||
1334 (cur->type == XML_RELAXNG_REF) ||
1335 (cur->type == XML_RELAXNG_DEF)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001336 /*
1337 * Don't go within elements or attributes or string values.
1338 * Just gather the element top list
1339 */
1340 if (cur->content != NULL) {
1341 parent = cur;
1342 cur = cur->content;
1343 tmp = cur;
1344 while (tmp != NULL) {
1345 tmp->parent = parent;
1346 tmp = tmp->next;
1347 }
1348 continue;
1349 }
1350 }
1351 if (cur == def) return(ret);
1352 if (cur->next != NULL) {
1353 cur = cur->next;
1354 continue;
1355 }
1356 do {
1357 cur = cur->parent;
1358 if (cur == NULL) break;
1359 if (cur == def) return(ret);
1360 if (cur->next != NULL) {
1361 cur = cur->next;
1362 break;
1363 }
1364 } while (cur != NULL);
1365 }
1366 return(ret);
1367}
1368
1369/**
1370 * xmlRelaxNGComputeInterleaves:
1371 * @def: the interleave definition
1372 * @ctxt: a Relax-NG parser context
1373 * @node: the data node.
1374 *
1375 * A lot of work for preprocessing interleave definitions
1376 * is potentially needed to get a decent execution speed at runtime
1377 * - trying to get a total order on the element nodes generated
1378 * by the interleaves, order the list of interleave definitions
1379 * following that order.
1380 * - if <text/> is used to handle mixed content, it is better to
1381 * flag this in the define and simplify the runtime checking
1382 * algorithm
1383 */
1384static void
1385xmlRelaxNGComputeInterleaves(xmlRelaxNGDefinePtr def,
1386 xmlRelaxNGParserCtxtPtr ctxt,
1387 xmlChar *name ATTRIBUTE_UNUSED) {
1388 xmlRelaxNGDefinePtr cur;
1389
1390 xmlRelaxNGDefinePtr *list = NULL;
1391 xmlRelaxNGPartitionPtr partitions = NULL;
1392 xmlRelaxNGInterleaveGroupPtr *groups = NULL;
1393 xmlRelaxNGInterleaveGroupPtr group;
1394 int i,j,ret;
1395 int nbgroups = 0;
1396 int nbchild = 0;
1397
1398#ifdef DEBUG_INTERLEAVE
1399 xmlGenericError(xmlGenericErrorContext,
1400 "xmlRelaxNGComputeInterleaves(%s)\n",
1401 name);
1402#endif
1403 cur = def->content;
1404 while (cur != NULL) {
1405 nbchild++;
1406 cur = cur->next;
1407 }
1408
1409#ifdef DEBUG_INTERLEAVE
1410 xmlGenericError(xmlGenericErrorContext, " %d child\n", nbchild);
1411#endif
1412 groups = (xmlRelaxNGInterleaveGroupPtr *)
1413 xmlMalloc(nbchild * sizeof(xmlRelaxNGInterleaveGroupPtr));
1414 if (groups == NULL)
1415 goto error;
1416 cur = def->content;
1417 while (cur != NULL) {
1418 list = xmlRelaxNGGetElements(ctxt, cur);
1419 if (list != NULL) {
1420 groups[nbgroups] = (xmlRelaxNGInterleaveGroupPtr)
1421 xmlMalloc(sizeof(xmlRelaxNGInterleaveGroup));
1422 if (groups[nbgroups] == NULL)
1423 goto error;
1424 groups[nbgroups]->rule = cur;
1425 groups[nbgroups]->defs = list;
1426 nbgroups++;
1427 }
1428 cur = cur->next;
1429 }
1430 list = NULL;
1431#ifdef DEBUG_INTERLEAVE
1432 xmlGenericError(xmlGenericErrorContext, " %d groups\n", nbgroups);
1433#endif
1434
1435 /*
1436 * Let's check that all rules makes a partitions according to 7.4
1437 */
1438 partitions = (xmlRelaxNGPartitionPtr)
1439 xmlMalloc(sizeof(xmlRelaxNGPartition));
1440 if (partitions == NULL)
1441 goto error;
1442 partitions->nbgroups = nbgroups;
1443 for (i = 0;i < nbgroups;i++) {
1444 group = groups[i];
1445 for (j = i+1;j < nbgroups;j++) {
1446 if (groups[j] == NULL)
1447 continue;
1448 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->defs,
1449 groups[j]->defs);
1450 if (ret == 0) {
1451 if (ctxt->error != NULL)
1452 ctxt->error(ctxt->userData,
1453 "Element or text conflicts in interleave\n");
1454 ctxt->nbErrors++;
1455 }
1456 }
1457 }
1458 partitions->groups = groups;
1459
1460 /*
1461 * Free Up the child list, and save the partition list back in the def
1462 */
1463 def->data = partitions;
1464 return;
1465
1466error:
1467 if (ctxt->error != NULL)
1468 ctxt->error(ctxt->userData,
1469 "Out of memory in interleave computation\n");
1470 ctxt->nbErrors++;
1471 if (list == NULL)
1472 xmlFree(list);
1473 if (groups != NULL) {
1474 for (i = 0;i < nbgroups;i++)
1475 if (groups[i] != NULL) {
1476 if (groups[i]->defs != NULL)
1477 xmlFree(groups[i]->defs);
1478 xmlFree(groups[i]);
1479 }
1480 xmlFree(groups);
1481 }
1482 xmlRelaxNGFreePartition(partitions);
1483}
1484
1485/**
1486 * xmlRelaxNGParseInterleave:
1487 * @ctxt: a Relax-NG parser context
1488 * @node: the data node.
1489 *
1490 * parse the content of a RelaxNG interleave node.
1491 *
1492 * Returns the definition pointer or NULL in case of error
1493 */
1494static xmlRelaxNGDefinePtr
1495xmlRelaxNGParseInterleave(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1496 xmlRelaxNGDefinePtr def = NULL;
1497 xmlRelaxNGDefinePtr last = NULL, cur;
1498 xmlNodePtr child;
1499
1500 def = xmlRelaxNGNewDefine(ctxt, node);
1501 if (def == NULL) {
1502 return(NULL);
1503 }
1504 def->type = XML_RELAXNG_INTERLEAVE;
1505
1506 if (ctxt->interleaves == NULL)
1507 ctxt->interleaves = xmlHashCreate(10);
1508 if (ctxt->interleaves == NULL) {
1509 if (ctxt->error != NULL)
1510 ctxt->error(ctxt->userData,
1511 "Failed to create interleaves hash table\n");
1512 ctxt->nbErrors++;
1513 } else {
1514 char name[32];
1515
1516 snprintf(name, 32, "interleave%d", ctxt->nbInterleaves++);
1517 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST name, def) < 0) {
1518 if (ctxt->error != NULL)
1519 ctxt->error(ctxt->userData,
1520 "Failed to add %s to hash table\n", name);
1521 ctxt->nbErrors++;
1522 }
1523 }
1524 child = node->children;
1525 while (child != NULL) {
1526 if (IS_RELAXNG(child, "element")) {
1527 cur = xmlRelaxNGParseElement(ctxt, child);
1528 } else {
1529 cur = xmlRelaxNGParsePattern(ctxt, child);
1530 }
1531 if (cur != NULL) {
1532 cur->parent = def;
1533 if (last == NULL) {
1534 def->content = last = cur;
1535 } else {
1536 last->next = cur;
1537 last = cur;
1538 }
1539 }
1540 child = child->next;
1541 }
1542
1543 return(def);
1544}
Daniel Veillard6eadf632003-01-23 18:29:16 +00001545
1546/**
Daniel Veillard276be4a2003-01-24 01:03:34 +00001547 * xmlRelaxNGParseDefine:
1548 * @ctxt: a Relax-NG parser context
1549 * @node: the define node
1550 *
1551 * parse the content of a RelaxNG define element node.
1552 *
1553 * Returns the definition pointer or NULL in case of error.
1554 */
1555static int
1556xmlRelaxNGParseDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1557 xmlChar *name;
1558 int ret = 0, tmp;
1559 xmlRelaxNGDefinePtr def;
1560 const xmlChar *olddefine;
1561
1562 name = xmlGetProp(node, BAD_CAST "name");
1563 if (name == NULL) {
1564 if (ctxt->error != NULL)
1565 ctxt->error(ctxt->userData,
1566 "define has no name\n");
1567 ctxt->nbErrors++;
1568 } else {
1569 def = xmlRelaxNGNewDefine(ctxt, node);
1570 if (def == NULL) {
1571 xmlFree(name);
1572 return(-1);
1573 }
1574 def->type = XML_RELAXNG_DEF;
1575 def->name = name;
1576 if (node->children == NULL) {
1577 if (ctxt->error != NULL)
1578 ctxt->error(ctxt->userData,
1579 "define has no children\n");
1580 ctxt->nbErrors++;
1581 } else {
1582 olddefine = ctxt->define;
1583 ctxt->define = name;
1584 def->content = xmlRelaxNGParsePatterns(ctxt,
1585 node->children);
1586 ctxt->define = olddefine;
1587 }
1588 if (ctxt->grammar->defs == NULL)
1589 ctxt->grammar->defs = xmlHashCreate(10);
1590 if (ctxt->grammar->defs == NULL) {
1591 if (ctxt->error != NULL)
1592 ctxt->error(ctxt->userData,
1593 "Could not create definition hash\n");
1594 ctxt->nbErrors++;
1595 ret = -1;
1596 xmlRelaxNGFreeDefine(def);
1597 } else {
1598 tmp = xmlHashAddEntry(ctxt->grammar->defs, name, def);
1599 if (tmp < 0) {
1600 TODO
1601 /* store and implement 4.17 on combining */
1602 ctxt->nbErrors++;
1603 ret = -1;
1604 xmlRelaxNGFreeDefine(def);
1605 }
1606 }
1607 }
1608 return(ret);
1609}
1610
1611/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00001612 * xmlRelaxNGParsePattern:
1613 * @ctxt: a Relax-NG parser context
1614 * @node: the pattern node.
1615 *
1616 * parse the content of a RelaxNG pattern node.
1617 *
Daniel Veillard276be4a2003-01-24 01:03:34 +00001618 * Returns the definition pointer or NULL in case of error or if no
1619 * pattern is generated.
Daniel Veillard6eadf632003-01-23 18:29:16 +00001620 */
1621static xmlRelaxNGDefinePtr
1622xmlRelaxNGParsePattern(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1623 xmlRelaxNGDefinePtr def = NULL;
1624
1625 if (IS_RELAXNG(node, "element")) {
1626 def = xmlRelaxNGParseElement(ctxt, node);
1627 } else if (IS_RELAXNG(node, "attribute")) {
1628 def = xmlRelaxNGParseAttribute(ctxt, node);
1629 } else if (IS_RELAXNG(node, "empty")) {
1630 def = xmlRelaxNGNewDefine(ctxt, node);
1631 if (def == NULL)
1632 return(NULL);
1633 def->type = XML_RELAXNG_EMPTY;
1634 } else if (IS_RELAXNG(node, "text")) {
1635 def = xmlRelaxNGNewDefine(ctxt, node);
1636 if (def == NULL)
1637 return(NULL);
1638 def->type = XML_RELAXNG_TEXT;
1639 if (node->children != NULL) {
1640 if (ctxt->error != NULL)
1641 ctxt->error(ctxt->userData, "text: had a child node\n");
1642 ctxt->nbErrors++;
1643 }
1644 } else if (IS_RELAXNG(node, "zeroOrMore")) {
1645 def = xmlRelaxNGNewDefine(ctxt, node);
1646 if (def == NULL)
1647 return(NULL);
1648 def->type = XML_RELAXNG_ZEROORMORE;
1649 def->content = xmlRelaxNGParsePatterns(ctxt, node->children);
1650 } else if (IS_RELAXNG(node, "oneOrMore")) {
1651 def = xmlRelaxNGNewDefine(ctxt, node);
1652 if (def == NULL)
1653 return(NULL);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001654 def->type = XML_RELAXNG_ONEORMORE;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001655 def->content = xmlRelaxNGParsePatterns(ctxt, node->children);
1656 } else if (IS_RELAXNG(node, "optional")) {
1657 def = xmlRelaxNGNewDefine(ctxt, node);
1658 if (def == NULL)
1659 return(NULL);
1660 def->type = XML_RELAXNG_OPTIONAL;
1661 def->content = xmlRelaxNGParsePatterns(ctxt, node->children);
1662 } else if (IS_RELAXNG(node, "choice")) {
1663 def = xmlRelaxNGNewDefine(ctxt, node);
1664 if (def == NULL)
1665 return(NULL);
1666 def->type = XML_RELAXNG_CHOICE;
1667 def->content = xmlRelaxNGParsePatterns(ctxt, node->children);
1668 } else if (IS_RELAXNG(node, "group")) {
1669 def = xmlRelaxNGNewDefine(ctxt, node);
1670 if (def == NULL)
1671 return(NULL);
1672 def->type = XML_RELAXNG_GROUP;
1673 def->content = xmlRelaxNGParsePatterns(ctxt, node->children);
1674 } else if (IS_RELAXNG(node, "ref")) {
1675 def = xmlRelaxNGNewDefine(ctxt, node);
1676 if (def == NULL)
1677 return(NULL);
1678 def->type = XML_RELAXNG_REF;
1679 def->name = xmlGetProp(node, BAD_CAST "name");
1680 if (def->name == NULL) {
1681 if (ctxt->error != NULL)
1682 ctxt->error(ctxt->userData,
1683 "ref has no name\n");
1684 ctxt->nbErrors++;
Daniel Veillard276be4a2003-01-24 01:03:34 +00001685 } else {
1686 if ((ctxt->define != NULL) &&
1687 (xmlStrEqual(ctxt->define, def->name))) {
1688 if (ctxt->error != NULL)
1689 ctxt->error(ctxt->userData,
1690 "Recursive reference to %s not in an element\n",
1691 def->name);
1692 ctxt->nbErrors++;
1693 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00001694 }
1695 if (node->children != NULL) {
1696 if (ctxt->error != NULL)
1697 ctxt->error(ctxt->userData,
1698 "ref is not empty\n");
1699 ctxt->nbErrors++;
1700 }
1701 if (ctxt->grammar->refs == NULL)
1702 ctxt->grammar->refs = xmlHashCreate(10);
1703 if (ctxt->grammar->refs == NULL) {
1704 if (ctxt->error != NULL)
1705 ctxt->error(ctxt->userData,
1706 "Could not create references hash\n");
1707 ctxt->nbErrors++;
1708 xmlRelaxNGFreeDefine(def);
1709 def = NULL;
1710 } else {
1711 int tmp;
1712
1713 tmp = xmlHashAddEntry(ctxt->grammar->refs, def->name, def);
1714 if (tmp < 0) {
1715 xmlRelaxNGDefinePtr prev;
1716
1717 prev = (xmlRelaxNGDefinePtr)
1718 xmlHashLookup(ctxt->grammar->refs, def->name);
1719 if (prev == NULL) {
1720 if (ctxt->error != NULL)
1721 ctxt->error(ctxt->userData,
1722 "Internal error refs definitions '%s'\n",
1723 def->name);
1724 ctxt->nbErrors++;
1725 xmlRelaxNGFreeDefine(def);
1726 def = NULL;
1727 } else {
1728 def->nextHash = prev->nextHash;
1729 prev->nextHash = def;
1730 }
1731 }
1732 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001733 } else if (IS_RELAXNG(node, "data")) {
1734 def = xmlRelaxNGParseData(ctxt, node);
Daniel Veillard276be4a2003-01-24 01:03:34 +00001735 } else if (IS_RELAXNG(node, "define")) {
1736 xmlRelaxNGParseDefine(ctxt, node);
1737 def = NULL;
Daniel Veillardedc91922003-01-26 00:52:04 +00001738 } else if (IS_RELAXNG(node, "value")) {
1739 def = xmlRelaxNGParseValue(ctxt, node);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001740 } else if (IS_RELAXNG(node, "list")) {
1741 def = xmlRelaxNGNewDefine(ctxt, node);
1742 if (def == NULL)
1743 return(NULL);
1744 def->type = XML_RELAXNG_LIST;
1745 def->content = xmlRelaxNGParsePatterns(ctxt, node->children);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001746 } else if (IS_RELAXNG(node, "interleave")) {
1747 def = xmlRelaxNGParseInterleave(ctxt, node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001748 } else {
1749 TODO
1750 }
1751 return(def);
1752}
1753
1754/**
1755 * xmlRelaxNGParseAttribute:
1756 * @ctxt: a Relax-NG parser context
1757 * @node: the element node
1758 *
1759 * parse the content of a RelaxNG attribute node.
1760 *
1761 * Returns the definition pointer or NULL in case of error.
1762 */
1763static xmlRelaxNGDefinePtr
1764xmlRelaxNGParseAttribute(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1765 xmlRelaxNGDefinePtr ret, cur, last;
1766 xmlNodePtr child;
1767 xmlChar *val;
1768 int old_flags;
1769
1770 ret = xmlRelaxNGNewDefine(ctxt, node);
1771 if (ret == NULL)
1772 return(NULL);
1773 ret->type = XML_RELAXNG_ATTRIBUTE;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001774 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001775 child = node->children;
1776 if (child == NULL) {
1777 if (ctxt->error != NULL)
1778 ctxt->error(ctxt->userData,
1779 "xmlRelaxNGParseattribute: attribute has no children\n");
1780 ctxt->nbErrors++;
1781 return(ret);
1782 }
1783 old_flags = ctxt->flags;
1784 ctxt->flags |= XML_RELAXNG_IN_ATTRIBUTE;
1785 if (IS_RELAXNG(child, "name")) {
1786 val = xmlNodeGetContent(child);
1787 ret->name = val;
1788 val = xmlGetProp(child, BAD_CAST "ns");
1789 ret->ns = val;
1790 } else if (IS_RELAXNG(child, "anyName")) {
1791 TODO
1792 } else if (IS_RELAXNG(child, "nsName")) {
1793 TODO
1794 } else if (IS_RELAXNG(child, "choice")) {
1795 TODO
1796 } else {
1797 if (ctxt->error != NULL)
1798 ctxt->error(ctxt->userData,
1799 "element: expecting name, anyName, nsName or choice : got %s\n",
1800 child->name);
1801 ctxt->nbErrors++;
1802 ctxt->flags = old_flags;
1803 return(ret);
1804 }
1805 child = child->next;
1806 last = NULL;
1807 while (child != NULL) {
1808 cur = xmlRelaxNGParsePattern(ctxt, child);
1809 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001810 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001811 switch (cur->type) {
1812 case XML_RELAXNG_EMPTY:
1813 case XML_RELAXNG_NOT_ALLOWED:
1814 case XML_RELAXNG_TEXT:
1815 case XML_RELAXNG_ELEMENT:
1816 case XML_RELAXNG_DATATYPE:
1817 case XML_RELAXNG_VALUE:
1818 case XML_RELAXNG_LIST:
1819 case XML_RELAXNG_REF:
1820 case XML_RELAXNG_DEF:
1821 case XML_RELAXNG_ONEORMORE:
1822 case XML_RELAXNG_ZEROORMORE:
1823 case XML_RELAXNG_OPTIONAL:
1824 case XML_RELAXNG_CHOICE:
1825 case XML_RELAXNG_GROUP:
1826 case XML_RELAXNG_INTERLEAVE:
1827 if (last == NULL) {
1828 ret->content = last = cur;
1829 } else {
1830 if ((last->type == XML_RELAXNG_ELEMENT) &&
1831 (ret->content == last)) {
1832 ret->content = xmlRelaxNGNewDefine(ctxt, node);
1833 if (ret->content != NULL) {
1834 ret->content->type = XML_RELAXNG_GROUP;
1835 ret->content->content = last;
1836 } else {
1837 ret->content = last;
1838 }
1839 }
1840 last->next = cur;
1841 last = cur;
1842 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001843 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001844 break;
1845 case XML_RELAXNG_ATTRIBUTE:
1846 cur->next = ret->attrs;
1847 ret->attrs = cur;
1848 break;
1849 }
1850 }
1851 child = child->next;
1852 }
1853 ctxt->flags = old_flags;
1854 return(ret);
1855}
1856
1857/**
1858 * xmlRelaxNGParseElement:
1859 * @ctxt: a Relax-NG parser context
1860 * @node: the element node
1861 *
1862 * parse the content of a RelaxNG element node.
1863 *
1864 * Returns the definition pointer or NULL in case of error.
1865 */
1866static xmlRelaxNGDefinePtr
1867xmlRelaxNGParseElement(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1868 xmlRelaxNGDefinePtr ret, cur, last;
1869 xmlNodePtr child;
1870 xmlChar *val;
Daniel Veillard276be4a2003-01-24 01:03:34 +00001871 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001872
1873 ret = xmlRelaxNGNewDefine(ctxt, node);
1874 if (ret == NULL)
1875 return(NULL);
1876 ret->type = XML_RELAXNG_ELEMENT;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001877 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001878 child = node->children;
1879 if (child == NULL) {
1880 if (ctxt->error != NULL)
1881 ctxt->error(ctxt->userData,
1882 "xmlRelaxNGParseElement: element has no children\n");
1883 ctxt->nbErrors++;
1884 return(ret);
1885 }
1886 if (IS_RELAXNG(child, "name")) {
1887 val = xmlNodeGetContent(child);
1888 ret->name = val;
1889 val = xmlGetProp(child, BAD_CAST "ns");
1890 ret->ns = val;
1891 } else if (IS_RELAXNG(child, "anyName")) {
1892 TODO
1893 } else if (IS_RELAXNG(child, "nsName")) {
1894 TODO
1895 } else if (IS_RELAXNG(child, "choice")) {
1896 TODO
1897 } else {
1898 if (ctxt->error != NULL)
1899 ctxt->error(ctxt->userData,
1900 "element: expecting name, anyName, nsName or choice : got %s\n",
1901 child->name);
1902 ctxt->nbErrors++;
1903 return(ret);
1904 }
1905 child = child->next;
1906 if (child == NULL) {
1907 if (ctxt->error != NULL)
1908 ctxt->error(ctxt->userData,
1909 "xmlRelaxNGParseElement: element has no content\n");
1910 ctxt->nbErrors++;
1911 return(ret);
1912 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00001913 olddefine = ctxt->define;
1914 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001915 last = NULL;
1916 while (child != NULL) {
1917 cur = xmlRelaxNGParsePattern(ctxt, child);
1918 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001919 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001920 switch (cur->type) {
1921 case XML_RELAXNG_EMPTY:
1922 case XML_RELAXNG_NOT_ALLOWED:
1923 case XML_RELAXNG_TEXT:
1924 case XML_RELAXNG_ELEMENT:
1925 case XML_RELAXNG_DATATYPE:
1926 case XML_RELAXNG_VALUE:
1927 case XML_RELAXNG_LIST:
1928 case XML_RELAXNG_REF:
1929 case XML_RELAXNG_DEF:
1930 case XML_RELAXNG_ZEROORMORE:
1931 case XML_RELAXNG_ONEORMORE:
1932 case XML_RELAXNG_OPTIONAL:
1933 case XML_RELAXNG_CHOICE:
1934 case XML_RELAXNG_GROUP:
1935 case XML_RELAXNG_INTERLEAVE:
1936 if (last == NULL) {
1937 ret->content = last = cur;
1938 } else {
1939 if ((last->type == XML_RELAXNG_ELEMENT) &&
1940 (ret->content == last)) {
1941 ret->content = xmlRelaxNGNewDefine(ctxt, node);
1942 if (ret->content != NULL) {
1943 ret->content->type = XML_RELAXNG_GROUP;
1944 ret->content->content = last;
1945 } else {
1946 ret->content = last;
1947 }
1948 }
1949 last->next = cur;
1950 last = cur;
1951 }
1952 break;
1953 case XML_RELAXNG_ATTRIBUTE:
1954 cur->next = ret->attrs;
1955 ret->attrs = cur;
1956 break;
1957 }
1958 }
1959 child = child->next;
1960 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00001961 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001962 return(ret);
1963}
1964
1965/**
1966 * xmlRelaxNGParsePatterns:
1967 * @ctxt: a Relax-NG parser context
1968 * @nodes: list of nodes
1969 *
1970 * parse the content of a RelaxNG start node.
1971 *
1972 * Returns the definition pointer or NULL in case of error.
1973 */
1974static xmlRelaxNGDefinePtr
1975xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001976 xmlRelaxNGDefinePtr def = NULL, last = NULL, cur, parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001977
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001978 parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001979 while (nodes != NULL) {
1980 if (IS_RELAXNG(nodes, "element")) {
1981 cur = xmlRelaxNGParseElement(ctxt, nodes);
1982 if (def == NULL) {
1983 def = last = cur;
1984 } else {
1985 if ((def->type == XML_RELAXNG_ELEMENT) && (def == last)) {
1986 def = xmlRelaxNGNewDefine(ctxt, nodes);
1987 def->type = XML_RELAXNG_GROUP;
1988 def->content = last;
1989 }
1990 last->next = cur;
1991 last = cur;
1992 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001993 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001994 } else {
1995 cur = xmlRelaxNGParsePattern(ctxt, nodes);
1996 if (def == NULL) {
1997 def = last = cur;
1998 } else {
1999 last->next = cur;
2000 last = cur;
2001 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002002 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002003 }
2004 nodes = nodes->next;
2005 }
2006 return(def);
2007}
2008
2009/**
2010 * xmlRelaxNGParseStart:
2011 * @ctxt: a Relax-NG parser context
2012 * @nodes: start children nodes
2013 *
2014 * parse the content of a RelaxNG start node.
2015 *
2016 * Returns 0 in case of success, -1 in case of error
2017 */
2018static int
2019xmlRelaxNGParseStart(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
2020 int ret = 0;
2021 xmlRelaxNGDefinePtr def = NULL;
2022
2023 while (nodes != NULL) {
2024 if (IS_RELAXNG(nodes, "empty")) {
2025 TODO
2026 xmlElemDump(stdout, nodes->doc, nodes);
2027 } else if (IS_RELAXNG(nodes, "notAllowed")) {
2028 TODO
2029 xmlElemDump(stdout, nodes->doc, nodes);
2030 } else {
2031 def = xmlRelaxNGParsePatterns(ctxt, nodes);
2032 ctxt->grammar->start = def;
2033 }
2034 nodes = nodes->next;
2035 }
2036 return(ret);
2037}
2038
2039/**
2040 * xmlRelaxNGParseGrammarContent:
2041 * @ctxt: a Relax-NG parser context
2042 * @nodes: grammar children nodes
2043 *
2044 * parse the content of a RelaxNG grammar node.
2045 *
2046 * Returns 0 in case of success, -1 in case of error
2047 */
2048static int
2049xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt
2050 ATTRIBUTE_UNUSED, xmlNodePtr nodes)
2051{
Daniel Veillard276be4a2003-01-24 01:03:34 +00002052 int ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002053
2054 if (nodes == NULL) {
2055 if (ctxt->error != NULL)
2056 ctxt->error(ctxt->userData,
2057 "grammar has no children\n");
2058 ctxt->nbErrors++;
2059 return(-1);
2060 }
2061 if (IS_RELAXNG(nodes, "start")) {
2062 if (nodes->children == NULL) {
2063 if (ctxt->error != NULL)
2064 ctxt->error(ctxt->userData,
2065 "grammar has no children\n");
2066 ctxt->nbErrors++;
2067 } else {
2068 xmlRelaxNGParseStart(ctxt, nodes->children);
2069 }
2070 nodes = nodes->next;
2071 } else {
2072 if (ctxt->error != NULL)
2073 ctxt->error(ctxt->userData,
2074 "grammar first child must be a <start>\n");
2075 ctxt->nbErrors++;
2076 return(-1);
2077 }
2078 while (nodes != NULL) {
2079 if (IS_RELAXNG(nodes, "define")) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00002080 ret = xmlRelaxNGParseDefine(ctxt, nodes);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002081 } else {
2082 if (ctxt->error != NULL)
2083 ctxt->error(ctxt->userData,
2084 "grammar allows onlys <define> child after <start>\n");
2085 ctxt->nbErrors++;
2086 ret = -1;
2087 }
2088 nodes = nodes->next;
2089 }
2090 return (ret);
2091}
2092
2093/**
2094 * xmlRelaxNGCheckReference:
2095 * @ref: the ref
2096 * @ctxt: a Relax-NG parser context
2097 * @name: the name associated to the defines
2098 *
2099 * Applies the 4.17. combine attribute rule for all the define
2100 * element of a given grammar using the same name.
2101 */
2102static void
2103xmlRelaxNGCheckReference(xmlRelaxNGDefinePtr ref,
2104 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
2105 xmlRelaxNGGrammarPtr grammar;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002106 xmlRelaxNGDefinePtr def, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002107
2108 grammar = ctxt->grammar;
2109 if (grammar == NULL) {
2110 if (ctxt->error != NULL)
2111 ctxt->error(ctxt->userData,
2112 "Internal error: no grammar in CheckReference %s\n",
2113 name);
2114 ctxt->nbErrors++;
2115 return;
2116 }
2117 if (ref->content != NULL) {
2118 if (ctxt->error != NULL)
2119 ctxt->error(ctxt->userData,
2120 "Internal error: reference has content in CheckReference %s\n",
2121 name);
2122 ctxt->nbErrors++;
2123 return;
2124 }
2125 if (grammar->defs != NULL) {
2126 def = xmlHashLookup(grammar->defs, name);
2127 if (def != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00002128 cur = ref;
2129 while (cur != NULL) {
2130 cur->content = def;
2131 cur = cur->nextHash;
2132 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002133 } else {
2134 TODO
2135 }
2136 }
2137 /*
2138 * TODO: make a closure and verify there is no loop !
2139 */
2140}
2141
2142/**
2143 * xmlRelaxNGCheckCombine:
2144 * @define: the define(s) list
2145 * @ctxt: a Relax-NG parser context
2146 * @name: the name associated to the defines
2147 *
2148 * Applies the 4.17. combine attribute rule for all the define
2149 * element of a given grammar using the same name.
2150 */
2151static void
2152xmlRelaxNGCheckCombine(xmlRelaxNGDefinePtr define,
2153 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
2154 xmlChar *combine;
2155 int choiceOrInterleave = -1;
2156 int missing = 0;
2157 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
2158
2159 if (define->nextHash == NULL)
2160 return;
2161 cur = define;
2162 while (cur != NULL) {
2163 combine = xmlGetProp(cur->node, BAD_CAST "combine");
2164 if (combine != NULL) {
2165 if (xmlStrEqual(combine, BAD_CAST "choice")) {
2166 if (choiceOrInterleave == -1)
2167 choiceOrInterleave = 1;
2168 else if (choiceOrInterleave == 0) {
2169 if (ctxt->error != NULL)
2170 ctxt->error(ctxt->userData,
2171 "Defines for %s use both 'choice' and 'interleave'\n",
2172 name);
2173 ctxt->nbErrors++;
2174 }
2175 } else if (xmlStrEqual(combine, BAD_CAST "choice")) {
2176 if (choiceOrInterleave == -1)
2177 choiceOrInterleave = 0;
2178 else if (choiceOrInterleave == 1) {
2179 if (ctxt->error != NULL)
2180 ctxt->error(ctxt->userData,
2181 "Defines for %s use both 'choice' and 'interleave'\n",
2182 name);
2183 ctxt->nbErrors++;
2184 }
2185 } else {
2186 if (ctxt->error != NULL)
2187 ctxt->error(ctxt->userData,
2188 "Defines for %s use unknown combine value '%s''\n",
2189 name, combine);
2190 ctxt->nbErrors++;
2191 }
2192 xmlFree(combine);
2193 } else {
2194 if (missing == 0)
2195 missing = 1;
2196 else {
2197 if (ctxt->error != NULL)
2198 ctxt->error(ctxt->userData,
2199 "Some defines for %s lacks the combine attribute\n",
2200 name);
2201 ctxt->nbErrors++;
2202 }
2203 }
2204
2205 cur = cur->nextHash;
2206 }
2207#ifdef DEBUG
2208 xmlGenericError(xmlGenericErrorContext,
2209 "xmlRelaxNGCheckCombine(): merging %s defines: %d\n",
2210 name, choiceOrInterleave);
2211#endif
2212 if (choiceOrInterleave == -1)
2213 choiceOrInterleave = 0;
2214 cur = xmlRelaxNGNewDefine(ctxt, define->node);
2215 if (cur == NULL)
2216 return;
2217 if (choiceOrInterleave == 0)
2218 cur->type = XML_RELAXNG_CHOICE;
2219 else
2220 cur->type = XML_RELAXNG_INTERLEAVE;
2221 tmp = define;
2222 last = NULL;
2223 while (tmp != NULL) {
2224 if (tmp->content != NULL) {
2225 if (tmp->content->next != NULL) {
2226 /*
2227 * we need first to create a wrapper.
2228 */
2229 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
2230 if (tmp2 == NULL)
2231 break;
2232 tmp2->type = XML_RELAXNG_GROUP;
2233 tmp2->content = tmp->content;
2234 } else {
2235 tmp2 = tmp->content;
2236 }
2237 if (last == NULL) {
2238 cur->content = tmp2;
2239 } else {
2240 last->next = tmp2;
2241 }
2242 last = tmp2;
2243 tmp->content = NULL;
2244 }
2245 tmp = tmp->nextHash;
2246 }
2247 define->content = cur;
2248}
2249
2250/**
2251 * xmlRelaxNGCombineStart:
2252 * @ctxt: a Relax-NG parser context
2253 * @grammar: the grammar
2254 *
2255 * Applies the 4.17. combine rule for all the start
2256 * element of a given grammar.
2257 */
2258static void
2259xmlRelaxNGCombineStart(xmlRelaxNGParserCtxtPtr ctxt,
2260 xmlRelaxNGGrammarPtr grammar) {
2261 xmlRelaxNGDefinePtr starts;
2262 xmlChar *combine;
2263 int choiceOrInterleave = -1;
2264 int missing = 0;
2265 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
2266
2267 starts = grammar->start;
2268 if (starts->nextHash == NULL)
2269 return;
2270 cur = starts;
2271 while (cur != NULL) {
2272 combine = xmlGetProp(cur->node, BAD_CAST "combine");
2273 if (combine != NULL) {
2274 if (xmlStrEqual(combine, BAD_CAST "choice")) {
2275 if (choiceOrInterleave == -1)
2276 choiceOrInterleave = 1;
2277 else if (choiceOrInterleave == 0) {
2278 if (ctxt->error != NULL)
2279 ctxt->error(ctxt->userData,
2280 "<start> use both 'choice' and 'interleave'\n");
2281 ctxt->nbErrors++;
2282 }
2283 } else if (xmlStrEqual(combine, BAD_CAST "choice")) {
2284 if (choiceOrInterleave == -1)
2285 choiceOrInterleave = 0;
2286 else if (choiceOrInterleave == 1) {
2287 if (ctxt->error != NULL)
2288 ctxt->error(ctxt->userData,
2289 "<start> use both 'choice' and 'interleave'\n");
2290 ctxt->nbErrors++;
2291 }
2292 } else {
2293 if (ctxt->error != NULL)
2294 ctxt->error(ctxt->userData,
2295 "<start> uses unknown combine value '%s''\n", combine);
2296 ctxt->nbErrors++;
2297 }
2298 xmlFree(combine);
2299 } else {
2300 if (missing == 0)
2301 missing = 1;
2302 else {
2303 if (ctxt->error != NULL)
2304 ctxt->error(ctxt->userData,
2305 "Some <start> elements lacks the combine attribute\n");
2306 ctxt->nbErrors++;
2307 }
2308 }
2309
2310 cur = cur->nextHash;
2311 }
2312#ifdef DEBUG
2313 xmlGenericError(xmlGenericErrorContext,
2314 "xmlRelaxNGCombineStart(): merging <start>: %d\n",
2315 choiceOrInterleave);
2316#endif
2317 if (choiceOrInterleave == -1)
2318 choiceOrInterleave = 0;
2319 cur = xmlRelaxNGNewDefine(ctxt, starts->node);
2320 if (cur == NULL)
2321 return;
2322 if (choiceOrInterleave == 0)
2323 cur->type = XML_RELAXNG_CHOICE;
2324 else
2325 cur->type = XML_RELAXNG_INTERLEAVE;
2326 tmp = starts;
2327 last = NULL;
2328 while (tmp != NULL) {
2329 if (tmp->content != NULL) {
2330 if (tmp->content->next != NULL) {
2331 /*
2332 * we need first to create a wrapper.
2333 */
2334 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
2335 if (tmp2 == NULL)
2336 break;
2337 tmp2->type = XML_RELAXNG_GROUP;
2338 tmp2->content = tmp->content;
2339 } else {
2340 tmp2 = tmp->content;
2341 }
2342 if (last == NULL) {
2343 cur->content = tmp2;
2344 } else {
2345 last->next = tmp2;
2346 }
2347 last = tmp2;
2348 tmp->content = NULL;
2349 }
2350 tmp = tmp->nextHash;
2351 }
2352 starts->content = cur;
2353}
2354
2355/**
2356 * xmlRelaxNGParseGrammar:
2357 * @ctxt: a Relax-NG parser context
2358 * @nodes: grammar children nodes
2359 *
2360 * parse a Relax-NG <grammar> node
2361 *
2362 * Returns the internal xmlRelaxNGGrammarPtr built or
2363 * NULL in case of error
2364 */
2365static xmlRelaxNGGrammarPtr
2366xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
2367 xmlRelaxNGGrammarPtr ret, tmp, old;
2368
Daniel Veillard6eadf632003-01-23 18:29:16 +00002369 ret = xmlRelaxNGNewGrammar(ctxt);
2370 if (ret == NULL)
2371 return(NULL);
2372
2373 /*
2374 * Link the new grammar in the tree
2375 */
2376 ret->parent = ctxt->grammar;
2377 if (ctxt->grammar != NULL) {
2378 tmp = ctxt->grammar->children;
2379 if (tmp == NULL) {
2380 ctxt->grammar->children = ret;
2381 } else {
2382 while (tmp->next != NULL)
2383 tmp = tmp->next;
2384 tmp->next = ret;
2385 }
2386 }
2387
2388 old = ctxt->grammar;
2389 ctxt->grammar = ret;
2390 xmlRelaxNGParseGrammarContent(ctxt, nodes);
2391 ctxt->grammar = ret;
2392
2393 /*
2394 * Apply 4.17 mergingd rules to defines and starts
2395 */
2396 xmlRelaxNGCombineStart(ctxt, ret);
2397 if (ret->defs != NULL) {
2398 xmlHashScan(ret->defs, (xmlHashScanner) xmlRelaxNGCheckCombine,
2399 ctxt);
2400 }
2401
2402 /*
2403 * link together defines and refs in this grammar
2404 */
2405 if (ret->refs != NULL) {
2406 xmlHashScan(ret->refs, (xmlHashScanner) xmlRelaxNGCheckReference,
2407 ctxt);
2408 }
2409 ctxt->grammar = old;
2410 return(ret);
2411}
2412
2413/**
2414 * xmlRelaxNGParseDocument:
2415 * @ctxt: a Relax-NG parser context
2416 * @node: the root node of the RelaxNG schema
2417 *
2418 * parse a Relax-NG definition resource and build an internal
2419 * xmlRelaxNG struture which can be used to validate instances.
2420 *
2421 * Returns the internal XML RelaxNG structure built or
2422 * NULL in case of error
2423 */
2424static xmlRelaxNGPtr
2425xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2426 xmlRelaxNGPtr schema = NULL;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002427 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002428
2429 if ((ctxt == NULL) || (node == NULL))
2430 return (NULL);
2431
2432 schema = xmlRelaxNGNewRelaxNG(ctxt);
2433 if (schema == NULL)
2434 return(NULL);
2435
Daniel Veillard276be4a2003-01-24 01:03:34 +00002436 olddefine = ctxt->define;
2437 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002438 if (IS_RELAXNG(node, "grammar")) {
2439 schema->topgrammar = xmlRelaxNGParseGrammar(ctxt, node->children);
2440 } else {
2441 schema->topgrammar = xmlRelaxNGNewGrammar(ctxt);
2442 if (schema->topgrammar == NULL) {
2443 return(schema);
2444 }
2445 schema->topgrammar->parent = NULL;
2446 ctxt->grammar = schema->topgrammar;
2447 xmlRelaxNGParseStart(ctxt, node);
2448 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002449 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002450
2451#ifdef DEBUG
2452 if (schema == NULL)
2453 xmlGenericError(xmlGenericErrorContext,
2454 "xmlRelaxNGParseDocument() failed\n");
2455#endif
2456
2457 return (schema);
2458}
2459
2460/************************************************************************
2461 * *
2462 * Reading RelaxNGs *
2463 * *
2464 ************************************************************************/
2465
2466/**
2467 * xmlRelaxNGNewParserCtxt:
2468 * @URL: the location of the schema
2469 *
2470 * Create an XML RelaxNGs parse context for that file/resource expected
2471 * to contain an XML RelaxNGs file.
2472 *
2473 * Returns the parser context or NULL in case of error
2474 */
2475xmlRelaxNGParserCtxtPtr
2476xmlRelaxNGNewParserCtxt(const char *URL) {
2477 xmlRelaxNGParserCtxtPtr ret;
2478
2479 if (URL == NULL)
2480 return(NULL);
2481
2482 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
2483 if (ret == NULL) {
2484 xmlGenericError(xmlGenericErrorContext,
2485 "Failed to allocate new schama parser context for %s\n", URL);
2486 return (NULL);
2487 }
2488 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
2489 ret->URL = xmlStrdup((const xmlChar *)URL);
2490 return (ret);
2491}
2492
2493/**
2494 * xmlRelaxNGNewMemParserCtxt:
2495 * @buffer: a pointer to a char array containing the schemas
2496 * @size: the size of the array
2497 *
2498 * Create an XML RelaxNGs parse context for that memory buffer expected
2499 * to contain an XML RelaxNGs file.
2500 *
2501 * Returns the parser context or NULL in case of error
2502 */
2503xmlRelaxNGParserCtxtPtr
2504xmlRelaxNGNewMemParserCtxt(const char *buffer, int size) {
2505 xmlRelaxNGParserCtxtPtr ret;
2506
2507 if ((buffer == NULL) || (size <= 0))
2508 return(NULL);
2509
2510 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
2511 if (ret == NULL) {
2512 xmlGenericError(xmlGenericErrorContext,
2513 "Failed to allocate new schama parser context\n");
2514 return (NULL);
2515 }
2516 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
2517 ret->buffer = buffer;
2518 ret->size = size;
2519 return (ret);
2520}
2521
2522/**
2523 * xmlRelaxNGFreeParserCtxt:
2524 * @ctxt: the schema parser context
2525 *
2526 * Free the resources associated to the schema parser context
2527 */
2528void
2529xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxtPtr ctxt) {
2530 if (ctxt == NULL)
2531 return;
2532 if (ctxt->URL != NULL)
2533 xmlFree(ctxt->URL);
2534 if (ctxt->doc != NULL)
2535 xmlFreeDoc(ctxt->doc);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002536 if (ctxt->interleaves != NULL)
2537 xmlHashFree(ctxt->interleaves, NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002538 xmlFree(ctxt);
2539}
2540
2541
2542/**
2543 * xmlRelaxNGParse:
2544 * @ctxt: a Relax-NG validation context
2545 *
2546 * parse a schema definition resource and build an internal
2547 * XML Shema struture which can be used to validate instances.
2548 * *WARNING* this interface is highly subject to change
2549 *
2550 * Returns the internal XML RelaxNG structure built from the resource or
2551 * NULL in case of error
2552 */
2553xmlRelaxNGPtr
2554xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt)
2555{
2556 xmlRelaxNGPtr ret = NULL;
2557 xmlDocPtr doc;
2558 xmlNodePtr root, cur, delete;
2559
2560 xmlRelaxNGInitTypes();
2561
2562 if (ctxt == NULL)
2563 return (NULL);
2564
2565 /*
2566 * First step is to parse the input document into an DOM/Infoset
2567 */
2568 if (ctxt->URL != NULL) {
2569 doc = xmlParseFile((const char *) ctxt->URL);
2570 if (doc == NULL) {
2571 if (ctxt->error != NULL)
2572 ctxt->error(ctxt->userData,
2573 "xmlRelaxNGParse: could not load %s\n", ctxt->URL);
2574 ctxt->nbErrors++;
2575 return (NULL);
2576 }
2577 } else if (ctxt->buffer != NULL) {
2578 doc = xmlParseMemory(ctxt->buffer, ctxt->size);
2579 if (doc == NULL) {
2580 if (ctxt->error != NULL)
2581 ctxt->error(ctxt->userData,
2582 "xmlRelaxNGParse: could not parse schemas\n");
2583 ctxt->nbErrors++;
2584 return (NULL);
2585 }
2586 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
2587 ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
2588 } else {
2589 if (ctxt->error != NULL)
2590 ctxt->error(ctxt->userData,
2591 "xmlRelaxNGParse: nothing to parse\n");
2592 ctxt->nbErrors++;
2593 return (NULL);
2594 }
2595 ctxt->doc = doc;
2596
2597 /*
2598 * Then extract the root and RelaxNG parse it
2599 */
2600 root = xmlDocGetRootElement(doc);
2601 if (root == NULL) {
2602 if (ctxt->error != NULL)
2603 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
2604 ctxt->URL);
2605 ctxt->nbErrors++;
2606 return (NULL);
2607 }
2608
2609 /*
2610 * Remove all the blank text nodes
2611 */
2612 delete = NULL;
2613 cur = root;
2614 while (cur != NULL) {
2615 if (delete != NULL) {
2616 xmlUnlinkNode(delete);
2617 xmlFreeNode(delete);
2618 delete = NULL;
2619 }
2620 if (cur->type == XML_ELEMENT_NODE) {
2621 /*
2622 * Simplification 4.1. Annotations
2623 */
2624 if ((cur->ns == NULL) ||
2625 (!xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
2626 delete = cur;
2627 goto skip_children;
2628 } else {
2629 if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
2630 TODO
2631 } else if (xmlStrEqual(cur->name, BAD_CAST "include")) {
2632 TODO
2633 } else if ((xmlStrEqual(cur->name, BAD_CAST "element")) ||
2634 (xmlStrEqual(cur->name, BAD_CAST "attribute"))) {
2635 xmlChar *name;
2636 xmlNodePtr text = NULL;
2637
2638 /*
2639 * Simplification 4.8. name attribute of element
2640 * and attribute elements
2641 */
2642 name = xmlGetProp(cur, BAD_CAST "name");
2643 if (name != NULL) {
2644 if (cur->children == NULL) {
2645 text = xmlNewChild(cur, cur->ns, BAD_CAST "name",
2646 name);
2647 } else {
2648 xmlNodePtr node;
2649 node = xmlNewNode(cur->ns, BAD_CAST "name");
2650 if (node != NULL) {
2651 xmlAddPrevSibling(cur->children, node);
2652 text = xmlNewText(name);
2653 xmlAddChild(node, text);
2654 text = node;
2655 }
2656 }
2657 xmlUnsetProp(cur, BAD_CAST "name");
2658 xmlFree(name);
2659 }
2660 if (xmlStrEqual(cur->name, BAD_CAST "attribute")) {
2661 if (text == NULL) {
2662 text = cur->children;
2663 while (text != NULL) {
2664 if ((text->type == XML_ELEMENT_NODE) &&
2665 (xmlStrEqual(text->name, BAD_CAST "name")))
2666 break;
2667 text = text->next;
2668 }
2669 }
2670 if (text == NULL) {
2671 if (ctxt->error != NULL)
2672 ctxt->error(ctxt->userData,
2673 "xmlRelaxNGParse: attribute without name\n");
2674 ctxt->nbErrors++;
2675 } else {
2676 xmlSetProp(text, BAD_CAST "ns", BAD_CAST "");
2677 }
2678 }
2679 } else if ((xmlStrEqual(cur->name, BAD_CAST "name")) ||
2680 (xmlStrEqual(cur->name, BAD_CAST "nsName")) ||
2681 (xmlStrEqual(cur->name, BAD_CAST "value"))) {
2682 /*
2683 * Simplification 4.8. name attribute of element
2684 * and attribute elements
2685 */
2686 if (xmlHasProp(cur, BAD_CAST "ns") == NULL) {
2687 xmlNodePtr node;
2688 xmlChar *ns = NULL;
2689
2690 node = cur->parent;
2691 while ((node != NULL) &&
2692 (node->type == XML_ELEMENT_NODE)) {
2693 ns = xmlGetProp(node, BAD_CAST "ns");
2694 if (ns != NULL) {
2695 break;
2696 }
2697 node = node->parent;
2698 }
2699 if (ns == NULL) {
2700 xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
2701 } else {
2702 xmlSetProp(cur, BAD_CAST "ns", ns);
2703 xmlFree(ns);
2704 }
2705 }
2706 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
2707 xmlChar *name, *local, *prefix;
2708
2709 /*
2710 * Simplification: 4.10. QNames
2711 */
2712 name = xmlNodeGetContent(cur);
2713 if (name != NULL) {
2714 local = xmlSplitQName2(name, &prefix);
2715 if (local != NULL) {
2716 xmlNsPtr ns;
2717
2718 ns = xmlSearchNs(cur->doc, cur, prefix);
2719 if (ns == NULL) {
2720 if (ctxt->error != NULL)
2721 ctxt->error(ctxt->userData,
2722 "xmlRelaxNGParse: no namespace for prefix %s\n", prefix);
2723 ctxt->nbErrors++;
2724 } else {
2725 xmlSetProp(cur, BAD_CAST "ns", ns->href);
2726 xmlNodeSetContent(cur, local);
2727 }
2728 xmlFree(local);
2729 xmlFree(prefix);
2730 }
2731 xmlFree(name);
2732 }
2733 }
2734 }
2735 }
2736 }
2737 /*
2738 * Simplification 4.2 whitespaces
2739 */
2740 else if (cur->type == XML_TEXT_NODE) {
2741 if (IS_BLANK_NODE(cur)) {
2742 if (cur->parent->type == XML_ELEMENT_NODE) {
2743 if ((!xmlStrEqual(cur->parent->name, BAD_CAST "value")) &&
2744 (!xmlStrEqual(cur->parent->name, BAD_CAST "param")))
2745 delete = cur;
2746 } else {
2747 delete = cur;
2748 goto skip_children;
2749 }
2750 }
2751 } else if (cur->type != XML_CDATA_SECTION_NODE) {
2752 delete = cur;
2753 goto skip_children;
2754 }
2755
2756 /*
2757 * Skip to next node
2758 */
2759 if (cur->children != NULL) {
2760 if ((cur->children->type != XML_ENTITY_DECL) &&
2761 (cur->children->type != XML_ENTITY_REF_NODE) &&
2762 (cur->children->type != XML_ENTITY_NODE)) {
2763 cur = cur->children;
2764 continue;
2765 }
2766 }
2767skip_children:
2768 if (cur->next != NULL) {
2769 cur = cur->next;
2770 continue;
2771 }
2772
2773 do {
2774 cur = cur->parent;
2775 if (cur == NULL)
2776 break;
2777 if (cur == root) {
2778 cur = NULL;
2779 break;
2780 }
2781 if (cur->next != NULL) {
2782 cur = cur->next;
2783 break;
2784 }
2785 } while (cur != NULL);
2786 }
2787 if (delete != NULL) {
2788 xmlUnlinkNode(delete);
2789 xmlFreeNode(delete);
2790 delete = NULL;
2791 }
2792
2793 /*
2794 * Then do the parsing for good
2795 */
2796 root = xmlDocGetRootElement(doc);
2797 if (root == NULL) {
2798 if (ctxt->error != NULL)
2799 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
2800 ctxt->URL);
2801 ctxt->nbErrors++;
2802 return (NULL);
2803 }
2804 ret = xmlRelaxNGParseDocument(ctxt, root);
2805 if (ret == NULL)
2806 return(NULL);
2807
2808 /*
2809 * Check the ref/defines links
2810 */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002811 /*
2812 * try to preprocess interleaves
2813 */
2814 if (ctxt->interleaves != NULL) {
2815 xmlHashScan(ctxt->interleaves,
2816 (xmlHashScanner)xmlRelaxNGComputeInterleaves, ctxt);
2817 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002818
2819 /*
2820 * if there was a parsing error return NULL
2821 */
2822 if (ctxt->nbErrors > 0) {
2823 xmlRelaxNGFree(ret);
2824 return(NULL);
2825 }
2826
2827 /*
2828 * Transfer the pointer for cleanup at the schema level.
2829 */
2830 ret->doc = doc;
2831 ctxt->doc = NULL;
2832
2833 return (ret);
2834}
2835
2836/**
2837 * xmlRelaxNGSetParserErrors:
2838 * @ctxt: a Relax-NG validation context
2839 * @err: the error callback
2840 * @warn: the warning callback
2841 * @ctx: contextual data for the callbacks
2842 *
2843 * Set the callback functions used to handle errors for a validation context
2844 */
2845void
2846xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
2847 xmlRelaxNGValidityErrorFunc err,
2848 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
2849 if (ctxt == NULL)
2850 return;
2851 ctxt->error = err;
2852 ctxt->warning = warn;
2853 ctxt->userData = ctx;
2854}
2855/************************************************************************
2856 * *
2857 * Dump back a compiled form *
2858 * *
2859 ************************************************************************/
2860static void xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define);
2861
2862/**
2863 * xmlRelaxNGDumpDefines:
2864 * @output: the file output
2865 * @defines: a list of define structures
2866 *
2867 * Dump a RelaxNG structure back
2868 */
2869static void
2870xmlRelaxNGDumpDefines(FILE * output, xmlRelaxNGDefinePtr defines) {
2871 while (defines != NULL) {
2872 xmlRelaxNGDumpDefine(output, defines);
2873 defines = defines->next;
2874 }
2875}
2876
2877/**
2878 * xmlRelaxNGDumpDefine:
2879 * @output: the file output
2880 * @define: a define structure
2881 *
2882 * Dump a RelaxNG structure back
2883 */
2884static void
2885xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define) {
2886 if (define == NULL)
2887 return;
2888 switch(define->type) {
2889 case XML_RELAXNG_EMPTY:
2890 fprintf(output, "<empty/>\n");
2891 break;
2892 case XML_RELAXNG_NOT_ALLOWED:
2893 fprintf(output, "<notAllowed/>\n");
2894 break;
2895 case XML_RELAXNG_TEXT:
2896 fprintf(output, "<text/>\n");
2897 break;
2898 case XML_RELAXNG_ELEMENT:
2899 fprintf(output, "<element>\n");
2900 if (define->name != NULL) {
2901 fprintf(output, "<name");
2902 if (define->ns != NULL)
2903 fprintf(output, " ns=\"%s\"", define->ns);
2904 fprintf(output, ">%s</name>\n", define->name);
2905 }
2906 xmlRelaxNGDumpDefines(output, define->attrs);
2907 xmlRelaxNGDumpDefines(output, define->content);
2908 fprintf(output, "</element>\n");
2909 break;
2910 case XML_RELAXNG_LIST:
2911 fprintf(output, "<list>\n");
2912 xmlRelaxNGDumpDefines(output, define->content);
2913 fprintf(output, "</list>\n");
2914 break;
2915 case XML_RELAXNG_ONEORMORE:
2916 fprintf(output, "<oneOrMore>\n");
2917 xmlRelaxNGDumpDefines(output, define->content);
2918 fprintf(output, "</oneOrMore>\n");
2919 break;
2920 case XML_RELAXNG_ZEROORMORE:
2921 fprintf(output, "<zeroOrMore>\n");
2922 xmlRelaxNGDumpDefines(output, define->content);
2923 fprintf(output, "</zeroOrMore>\n");
2924 break;
2925 case XML_RELAXNG_CHOICE:
2926 fprintf(output, "<choice>\n");
2927 xmlRelaxNGDumpDefines(output, define->content);
2928 fprintf(output, "</choice>\n");
2929 break;
2930 case XML_RELAXNG_GROUP:
2931 fprintf(output, "<group>\n");
2932 xmlRelaxNGDumpDefines(output, define->content);
2933 fprintf(output, "</group>\n");
2934 break;
2935 case XML_RELAXNG_INTERLEAVE:
2936 fprintf(output, "<interleave>\n");
2937 xmlRelaxNGDumpDefines(output, define->content);
2938 fprintf(output, "</interleave>\n");
2939 break;
2940 case XML_RELAXNG_OPTIONAL:
2941 fprintf(output, "<optional>\n");
2942 xmlRelaxNGDumpDefines(output, define->content);
2943 fprintf(output, "</optional>\n");
2944 break;
2945 case XML_RELAXNG_ATTRIBUTE:
2946 fprintf(output, "<attribute>\n");
2947 xmlRelaxNGDumpDefines(output, define->content);
2948 fprintf(output, "</attribute>\n");
2949 break;
2950 case XML_RELAXNG_DEF:
2951 fprintf(output, "<define");
2952 if (define->name != NULL)
2953 fprintf(output, " name=\"%s\"", define->name);
2954 fprintf(output, ">\n");
2955 xmlRelaxNGDumpDefines(output, define->content);
2956 fprintf(output, "</define>\n");
2957 break;
2958 case XML_RELAXNG_REF:
2959 fprintf(output, "<ref");
2960 if (define->name != NULL)
2961 fprintf(output, " name=\"%s\"", define->name);
2962 fprintf(output, ">\n");
2963 xmlRelaxNGDumpDefines(output, define->content);
2964 fprintf(output, "</ref>\n");
2965 break;
2966 case XML_RELAXNG_DATATYPE:
2967 case XML_RELAXNG_VALUE:
2968 TODO
2969 break;
2970 }
2971}
2972
2973/**
2974 * xmlRelaxNGDumpGrammar:
2975 * @output: the file output
2976 * @grammar: a grammar structure
2977 * @top: is this a top grammar
2978 *
2979 * Dump a RelaxNG structure back
2980 */
2981static void
2982xmlRelaxNGDumpGrammar(FILE * output, xmlRelaxNGGrammarPtr grammar, int top)
2983{
2984 if (grammar == NULL)
2985 return;
2986
2987 fprintf(output, "<grammar");
2988 if (top)
2989 fprintf(output,
2990 " xmlns=\"http://relaxng.org/ns/structure/1.0\"");
2991 switch(grammar->combine) {
2992 case XML_RELAXNG_COMBINE_UNDEFINED:
2993 break;
2994 case XML_RELAXNG_COMBINE_CHOICE:
2995 fprintf(output, " combine=\"choice\"");
2996 break;
2997 case XML_RELAXNG_COMBINE_INTERLEAVE:
2998 fprintf(output, " combine=\"interleave\"");
2999 break;
3000 default:
3001 fprintf(output, " <!-- invalid combine value -->");
3002 }
3003 fprintf(output, ">\n");
3004 if (grammar->start == NULL) {
3005 fprintf(output, " <!-- grammar had no start -->");
3006 } else {
3007 fprintf(output, "<start>\n");
3008 xmlRelaxNGDumpDefine(output, grammar->start);
3009 fprintf(output, "</start>\n");
3010 }
3011 /* TODO ? Dump the defines ? */
3012 fprintf(output, "</grammar>\n");
3013}
3014
3015/**
3016 * xmlRelaxNGDump:
3017 * @output: the file output
3018 * @schema: a schema structure
3019 *
3020 * Dump a RelaxNG structure back
3021 */
3022void
3023xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema)
3024{
3025 if (schema == NULL) {
3026 fprintf(output, "RelaxNG empty or failed to compile\n");
3027 return;
3028 }
3029 fprintf(output, "RelaxNG: ");
3030 if (schema->doc == NULL) {
3031 fprintf(output, "no document\n");
3032 } else if (schema->doc->URL != NULL) {
3033 fprintf(output, "%s\n", schema->doc->URL);
3034 } else {
3035 fprintf(output, "\n");
3036 }
3037 if (schema->topgrammar == NULL) {
3038 fprintf(output, "RelaxNG has no top grammar\n");
3039 return;
3040 }
3041 xmlRelaxNGDumpGrammar(output, schema->topgrammar, 1);
3042}
3043
3044/************************************************************************
3045 * *
3046 * Validation implementation *
3047 * *
3048 ************************************************************************/
3049static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
3050 xmlRelaxNGDefinePtr define);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00003051static int xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
3052 xmlRelaxNGDefinePtr define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003053
3054/**
3055 * xmlRelaxNGSkipIgnored:
3056 * @ctxt: a schema validation context
3057 * @node: the top node.
3058 *
3059 * Skip ignorable nodes in that context
3060 *
3061 * Returns the new sibling or NULL in case of error.
3062 */
3063static xmlNodePtr
3064xmlRelaxNGSkipIgnored(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
3065 xmlNodePtr node) {
3066 /*
3067 * TODO complete and handle entities
3068 */
3069 while ((node != NULL) &&
3070 ((node->type == XML_COMMENT_NODE) ||
3071 ((node->type == XML_TEXT_NODE) &&
3072 (IS_BLANK_NODE(node))))) {
3073 node = node->next;
3074 }
3075 return(node);
3076}
3077
3078/**
Daniel Veillardedc91922003-01-26 00:52:04 +00003079 * xmlRelaxNGNormalize:
3080 * @ctxt: a schema validation context
3081 * @str: the string to normalize
3082 *
3083 * Implements the normalizeWhiteSpace( s ) function from
3084 * section 6.2.9 of the spec
3085 *
3086 * Returns the new string or NULL in case of error.
3087 */
3088static xmlChar *
3089xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *str) {
3090 xmlChar *ret, *p;
3091 const xmlChar *tmp;
3092 int len;
3093
3094 if (str == NULL)
3095 return(NULL);
3096 tmp = str;
3097 while (*tmp != 0) tmp++;
3098 len = tmp - str;
3099
3100 ret = (xmlChar *) xmlMalloc((len + 1) * sizeof(xmlChar));
3101 if (ret == NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00003102 if (ctxt != NULL) {
3103 VALID_CTXT();
3104 VALID_ERROR("xmlRelaxNGNormalize: out of memory\n");
3105 } else {
3106 xmlGenericError(xmlGenericErrorContext,
3107 "xmlRelaxNGNormalize: out of memory\n");
3108 }
Daniel Veillardedc91922003-01-26 00:52:04 +00003109 return(NULL);
3110 }
3111 p = ret;
3112 while (IS_BLANK(*str)) str++;
3113 while (*str != 0) {
3114 if (IS_BLANK(*str)) {
3115 while (IS_BLANK(*str)) str++;
3116 if (*str == 0)
3117 break;
3118 *p++ = ' ';
3119 } else
3120 *p++ = *str++;
3121 }
3122 *p = 0;
3123 return(ret);
3124}
3125
3126/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00003127 * xmlRelaxNGValidateDatatype:
3128 * @ctxt: a Relax-NG validation context
3129 * @value: the string value
3130 * @type: the datatype definition
3131 *
3132 * Validate the given value against the dataype
3133 *
3134 * Returns 0 if the validation succeeded or an error code.
3135 */
3136static int
3137xmlRelaxNGValidateDatatype(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *value,
3138 xmlRelaxNGDefinePtr define) {
3139 int ret;
3140 xmlRelaxNGTypeLibraryPtr lib;
3141
3142 if ((define == NULL) || (define->data == NULL)) {
3143 return(-1);
3144 }
3145 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
3146 if (lib->check != NULL)
3147 ret = lib->check(lib->data, define->name, value);
3148 else
3149 ret = -1;
3150 if (ret < 0) {
3151 VALID_CTXT();
3152 VALID_ERROR("Internal: failed to validate type %s\n", define->name);
3153 return(-1);
3154 } else if (ret == 1) {
3155 ret = 0;
3156 } else {
3157 VALID_CTXT();
3158 VALID_ERROR("Type %s doesn't allow value %s\n", define->name, value);
3159 return(-1);
3160 ret = -1;
3161 }
3162 return(ret);
3163}
3164
3165/**
Daniel Veillardc6e997c2003-01-27 12:35:42 +00003166 * xmlRelaxNGNextValue:
3167 * @ctxt: a Relax-NG validation context
3168 *
3169 * Skip to the next value when validating within a list
3170 *
3171 * Returns 0 if the operation succeeded or an error code.
3172 */
3173static int
3174xmlRelaxNGNextValue(xmlRelaxNGValidCtxtPtr ctxt) {
3175 xmlChar *cur;
3176
3177 cur = ctxt->state->value;
3178 if ((cur == NULL) || (ctxt->state->endvalue == NULL)) {
3179 ctxt->state->value = NULL;
3180 return(0);
3181 }
3182 while (*cur != 0) cur++;
3183 while ((cur != ctxt->state->endvalue) && (*cur == 0)) cur++;
3184 if (cur == ctxt->state->endvalue)
3185 ctxt->state->value = NULL;
3186 else
3187 ctxt->state->value = cur;
3188 return(0);
3189}
3190
3191/**
3192 * xmlRelaxNGValidateValueList:
3193 * @ctxt: a Relax-NG validation context
3194 * @defines: the list of definitions to verify
3195 *
3196 * Validate the given set of definitions for the current value
3197 *
3198 * Returns 0 if the validation succeeded or an error code.
3199 */
3200static int
3201xmlRelaxNGValidateValueList(xmlRelaxNGValidCtxtPtr ctxt,
3202 xmlRelaxNGDefinePtr defines) {
3203 int ret = 0;
3204
3205 while (defines != NULL) {
3206 ret = xmlRelaxNGValidateValue(ctxt, defines);
3207 if (ret != 0)
3208 break;
3209 defines = defines->next;
3210 }
3211 return(ret);
3212}
3213
3214/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00003215 * xmlRelaxNGValidateValue:
3216 * @ctxt: a Relax-NG validation context
3217 * @define: the definition to verify
3218 *
3219 * Validate the given definition for the current value
3220 *
3221 * Returns 0 if the validation succeeded or an error code.
3222 */
3223static int
3224xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
3225 xmlRelaxNGDefinePtr define) {
Daniel Veillardedc91922003-01-26 00:52:04 +00003226 int ret = 0, oldflags;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003227 xmlChar *value;
3228
3229 value = ctxt->state->value;
3230 switch (define->type) {
3231 case XML_RELAXNG_EMPTY:
3232 if ((value != NULL) && (value[0] != '0'))
3233 ret = -1;
3234 break;
3235 case XML_RELAXNG_TEXT:
3236 break;
Daniel Veillardedc91922003-01-26 00:52:04 +00003237 case XML_RELAXNG_VALUE: {
3238 if (!xmlStrEqual(value, define->value)) {
3239 if (define->name != NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00003240 xmlRelaxNGTypeLibraryPtr lib;
3241
3242 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
3243 if ((lib != NULL) && (lib->comp != NULL))
3244 ret = lib->comp(lib->data, define->name, value,
3245 define->value);
3246 else
3247 ret = -1;
3248 if (ret < 0) {
3249 VALID_CTXT();
3250 VALID_ERROR("Internal: failed to compare type %s\n",
3251 define->name);
3252 return(-1);
3253 } else if (ret == 1) {
3254 ret = 0;
3255 } else {
3256 ret = -1;
3257 }
Daniel Veillardedc91922003-01-26 00:52:04 +00003258 } else {
3259 xmlChar *nval, *nvalue;
3260
3261 /*
3262 * TODO: trivial optimizations are possible by
3263 * computing at compile-time
3264 */
3265 nval = xmlRelaxNGNormalize(ctxt, define->value);
3266 nvalue = xmlRelaxNGNormalize(ctxt, value);
3267
Daniel Veillardea3f3982003-01-26 19:45:18 +00003268 if ((nval == NULL) || (nvalue == NULL) ||
3269 (!xmlStrEqual(nval, nvalue)))
Daniel Veillardedc91922003-01-26 00:52:04 +00003270 ret = -1;
3271 if (nval != NULL)
3272 xmlFree(nval);
3273 if (nvalue != NULL)
3274 xmlFree(nvalue);
3275 }
3276 }
3277 break;
3278 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00003279 case XML_RELAXNG_DATATYPE: {
3280 ret = xmlRelaxNGValidateDatatype(ctxt, value, define);
3281 if (ret == 0)
3282 xmlRelaxNGNextValue(ctxt);
3283
3284 break;
3285 }
3286 case XML_RELAXNG_CHOICE: {
3287 xmlRelaxNGDefinePtr list = define->content;
3288 xmlChar *oldvalue;
3289
3290 oldflags = ctxt->flags;
3291 ctxt->flags |= FLAGS_IGNORABLE;
3292
3293 oldvalue = ctxt->state->value;
3294 while (list != NULL) {
3295 ret = xmlRelaxNGValidateValue(ctxt, list);
3296 if (ret == 0) {
3297 break;
3298 }
3299 ctxt->state->value = oldvalue;
3300 list = list->next;
3301 }
3302 ctxt->flags = oldflags;
3303 break;
3304 }
3305 case XML_RELAXNG_LIST: {
3306 xmlRelaxNGDefinePtr list = define->content;
3307 xmlChar *oldvalue, *oldend, *val, *cur;
3308
3309 oldvalue = ctxt->state->value;
3310 oldend = ctxt->state->endvalue;
3311
3312 val = xmlStrdup(oldvalue);
3313 if (val == NULL) {
3314 VALID_CTXT();
3315 VALID_ERROR("Internal: no state\n");
3316 return(-1);
3317 }
3318 cur = val;
3319 while (*cur != 0) {
3320 if (IS_BLANK(*cur))
3321 *cur = 0;
3322 cur++;
3323 }
3324 ctxt->state->endvalue = cur;
3325 cur = val;
3326 while ((*cur == 0) && (cur != ctxt->state->endvalue)) cur++;
3327
3328 ctxt->state->value = cur;
3329
3330 while (list != NULL) {
3331 ret = xmlRelaxNGValidateValue(ctxt, list);
3332 if (ret != 0) {
3333 break;
3334 }
3335 list = list->next;
3336 }
3337 if ((ret == 0) && (ctxt->state->value != NULL) &&
3338 (ctxt->state->value != ctxt->state->endvalue)) {
3339 VALID_CTXT();
3340 VALID_ERROR("Extra data in list: %s\n", ctxt->state->value);
3341 ret = -1;
3342 }
3343 xmlFree(val);
3344 ctxt->state->value = oldvalue;
3345 ctxt->state->endvalue = oldend;
3346 break;
3347 }
3348 case XML_RELAXNG_ONEORMORE:
3349 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
3350 if (ret != 0) {
3351 break;
3352 }
3353 /* no break on purpose */
3354 case XML_RELAXNG_ZEROORMORE: {
3355 xmlChar *cur, *temp;
3356
3357 oldflags = ctxt->flags;
3358 ctxt->flags |= FLAGS_IGNORABLE;
3359 cur = ctxt->state->value;
3360 temp = NULL;
3361 while ((cur != NULL) && (cur != ctxt->state->endvalue) &&
3362 (temp != cur)) {
3363 temp = cur;
3364 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
3365 if (ret != 0) {
3366 ctxt->state->value = temp;
3367 ret = 0;
3368 break;
3369 }
3370 cur = ctxt->state->value;
3371 }
3372 ctxt->flags = oldflags;
3373 break;
3374 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003375 default:
3376 TODO
3377 ret = -1;
3378 }
3379 return(ret);
3380}
3381
3382/**
3383 * xmlRelaxNGValidateValueContent:
3384 * @ctxt: a Relax-NG validation context
3385 * @defines: the list of definitions to verify
3386 *
3387 * Validate the given definitions for the current value
3388 *
3389 * Returns 0 if the validation succeeded or an error code.
3390 */
3391static int
3392xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt,
3393 xmlRelaxNGDefinePtr defines) {
3394 int ret = 0;
3395
3396 while (defines != NULL) {
3397 ret = xmlRelaxNGValidateValue(ctxt, defines);
3398 if (ret != 0)
3399 break;
3400 defines = defines->next;
3401 }
3402 return(ret);
3403}
3404
3405/**
3406 * xmlRelaxNGValidateAttribute:
3407 * @ctxt: a Relax-NG validation context
3408 * @define: the definition to verify
3409 *
3410 * Validate the given attribute definition for that node
3411 *
3412 * Returns 0 if the validation succeeded or an error code.
3413 */
3414static int
3415xmlRelaxNGValidateAttribute(xmlRelaxNGValidCtxtPtr ctxt,
3416 xmlRelaxNGDefinePtr define) {
3417 int ret = 0, i;
3418 xmlChar *value, *oldvalue;
3419 xmlAttrPtr prop = NULL, tmp;
3420
3421 if (define->name != NULL) {
3422 for (i = 0;i < ctxt->state->nbAttrs;i++) {
3423 tmp = ctxt->state->attrs[i];
3424 if ((tmp != NULL) && (xmlStrEqual(define->name, tmp->name))) {
3425 if ((((define->ns == NULL) || (define->ns[0] == 0)) &&
3426 (tmp->ns == NULL)) ||
3427 ((tmp->ns != NULL) &&
3428 (xmlStrEqual(define->ns, tmp->ns->href)))) {
3429 prop = tmp;
3430 break;
3431 }
3432 }
3433 }
3434 if (prop != NULL) {
3435 value = xmlNodeListGetString(prop->doc, prop->children, 1);
3436 oldvalue = ctxt->state->value;
3437 ctxt->state->value = value;
3438 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
3439 value = ctxt->state->value;
3440 ctxt->state->value = oldvalue;
3441 if (value != NULL)
3442 xmlFree(value);
3443 if (ret == 0) {
3444 /*
3445 * flag the attribute as processed
3446 */
3447 ctxt->state->attrs[i] = NULL;
3448 }
3449 } else {
3450 ret = -1;
3451 }
3452#ifdef DEBUG
3453 xmlGenericError(xmlGenericErrorContext,
3454 "xmlRelaxNGValidateAttribute(%s): %d\n", define->name, ret);
3455#endif
3456 } else {
3457 TODO
3458 }
3459
3460 return(ret);
3461}
3462
3463/**
3464 * xmlRelaxNGValidateAttributeList:
3465 * @ctxt: a Relax-NG validation context
3466 * @define: the list of definition to verify
3467 *
3468 * Validate the given node against the list of attribute definitions
3469 *
3470 * Returns 0 if the validation succeeded or an error code.
3471 */
3472static int
3473xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt,
3474 xmlRelaxNGDefinePtr defines) {
3475 int ret = 0;
3476 while (defines != NULL) {
3477 if (xmlRelaxNGValidateAttribute(ctxt, defines) != 0)
3478 ret = -1;
3479 defines = defines->next;
3480 }
3481 return(ret);
3482}
3483
3484/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003485 * xmlRelaxNGValidateTryPermutation:
3486 * @ctxt: a Relax-NG validation context
3487 * @groups: the array of groups
3488 * @nbgroups: the number of groups in the array
3489 * @array: the permutation to try
3490 * @len: the size of the set
3491 *
3492 * Try to validate a permutation for the group of definitions.
3493 *
3494 * Returns 0 if the validation succeeded or an error code.
3495 */
3496static int
3497xmlRelaxNGValidateTryPermutation(xmlRelaxNGValidCtxtPtr ctxt,
3498 xmlRelaxNGDefinePtr rule,
3499 xmlNodePtr *array, int len) {
3500 int i, ret;
3501
3502 if (len > 0) {
3503 /*
3504 * One only need the next pointer set-up to do the validation
3505 */
3506 for (i = 0;i < (len - 1);i++)
3507 array[i]->next = array[i + 1];
3508 array[i]->next = NULL;
3509
3510 /*
3511 * Now try to validate the sequence
3512 */
3513 ctxt->state->seq = array[0];
3514 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
3515 } else {
3516 ctxt->state->seq = NULL;
3517 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
3518 }
3519
3520 /*
3521 * the sequence must be fully consumed
3522 */
3523 if (ctxt->state->seq != NULL)
3524 return(-1);
3525
3526 return(ret);
3527}
3528
3529/**
3530 * xmlRelaxNGValidateWalkPermutations:
3531 * @ctxt: a Relax-NG validation context
3532 * @groups: the array of groups
3533 * @nbgroups: the number of groups in the array
3534 * @nodes: the set of nodes
3535 * @array: the current state of the parmutation
3536 * @len: the size of the set
3537 * @level: a pointer to the level variable
3538 * @k: the index in the array to fill
3539 *
3540 * Validate a set of nodes for a groups of definitions, will try the
3541 * full set of permutations
3542 *
3543 * Returns 0 if the validation succeeded or an error code.
3544 */
3545static int
3546xmlRelaxNGValidateWalkPermutations(xmlRelaxNGValidCtxtPtr ctxt,
3547 xmlRelaxNGDefinePtr rule, xmlNodePtr *nodes,
3548 xmlNodePtr *array, int len,
3549 int *level, int k) {
3550 int i, ret;
3551
3552 if ((k >= 0) && (k < len))
3553 array[k] = nodes[*level];
3554 *level = *level + 1;
3555 if (*level == len) {
3556 ret = xmlRelaxNGValidateTryPermutation(ctxt, rule, array, len);
3557 if (ret == 0)
3558 return(0);
3559 } else {
3560 for (i = 0;i < len;i++) {
3561 if (array[i] == NULL) {
3562 ret = xmlRelaxNGValidateWalkPermutations(ctxt, rule,
3563 nodes, array, len, level, i);
3564 if (ret == 0)
3565 return(0);
3566 }
3567 }
3568 }
3569 *level = *level - 1;
3570 array[k] = NULL;
3571 return(-1);
3572}
3573
3574/**
3575 * xmlRelaxNGNodeMatchesList:
3576 * @node: the node
3577 * @list: a NULL terminated array of definitions
3578 *
3579 * Check if a node can be matched by one of the definitions
3580 *
3581 * Returns 1 if matches 0 otherwise
3582 */
3583static int
3584xmlRelaxNGNodeMatchesList(xmlNodePtr node, xmlRelaxNGDefinePtr *list) {
3585 xmlRelaxNGDefinePtr cur;
3586 int i = 0;
3587
3588 if ((node == NULL) || (list == NULL))
3589 return(0);
3590
3591 cur = list[i++];
3592 while (cur != NULL) {
3593 if ((node->type == XML_ELEMENT_NODE) &&
3594 (cur->type == XML_RELAXNG_ELEMENT)) {
3595 if (cur->name == NULL) {
3596 if ((node->ns != NULL) &&
3597 (xmlStrEqual(node->ns->href, cur->ns)))
3598 return(1);
3599 } else if (xmlStrEqual(cur->name, node->name)) {
3600 if ((cur->ns == NULL) || (cur->ns[0] == 0)) {
3601 if (node->ns == NULL)
3602 return(1);
3603 } else {
3604 if ((node->ns != NULL) &&
3605 (xmlStrEqual(node->ns->href, cur->ns)))
3606 return(1);
3607 }
3608 }
3609 } else if ((node->type == XML_TEXT_NODE) &&
3610 (cur->type == XML_RELAXNG_TEXT)) {
3611 return(1);
3612 }
3613 cur = list[i++];
3614 }
3615 return(0);
3616}
3617
3618/**
3619 * xmlRelaxNGValidatePartGroup:
3620 * @ctxt: a Relax-NG validation context
3621 * @groups: the array of groups
3622 * @nbgroups: the number of groups in the array
3623 * @nodes: the set of nodes
3624 * @len: the size of the set of nodes
3625 *
3626 * Validate a set of nodes for a groups of definitions
3627 *
3628 * Returns 0 if the validation succeeded or an error code.
3629 */
3630static int
3631xmlRelaxNGValidatePartGroup(xmlRelaxNGValidCtxtPtr ctxt,
3632 xmlRelaxNGInterleaveGroupPtr *groups,
3633 int nbgroups, xmlNodePtr *nodes, int len) {
Daniel Veillardb08c9812003-01-28 23:09:49 +00003634 int level, ret = -1, i, j, k;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003635 xmlNodePtr *array = NULL, *list, oldseq;
3636 xmlRelaxNGInterleaveGroupPtr group;
3637
3638 list = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
3639 if (list == NULL) {
3640 return(-1);
3641 }
3642 array = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
3643 if (array == NULL) {
3644 xmlFree(list);
3645 return(-1);
3646 }
3647 memset(array, 0, len * sizeof(xmlNodePtr));
3648
3649 /*
3650 * Partition the elements and validate the subsets.
3651 */
3652 oldseq = ctxt->state->seq;
3653 for (i = 0;i < nbgroups;i++) {
3654 group = groups[i];
3655 if (group == NULL)
3656 continue;
3657 k = 0;
3658 for (j = 0;j < len;j++) {
3659 if (nodes[j] == NULL)
3660 continue;
3661 if (xmlRelaxNGNodeMatchesList(nodes[j], group->defs)) {
3662 list[k++] = nodes[j];
3663 nodes[j] = NULL;
3664 }
3665 }
3666 ctxt->state->seq = oldseq;
3667 if (k > 1) {
3668 memset(array, 0, k * sizeof(xmlNodePtr));
Daniel Veillardb08c9812003-01-28 23:09:49 +00003669 level = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003670 ret = xmlRelaxNGValidateWalkPermutations(ctxt, group->rule,
3671 list, array, k, &level, -1);
3672 } else {
3673 ret = xmlRelaxNGValidateTryPermutation(ctxt, group->rule, list, k);
3674 }
3675 if (ret != 0) {
3676 ctxt->state->seq = oldseq;
3677 break;
3678 }
3679 }
3680
3681 xmlFree(list);
3682 xmlFree(array);
3683 return(ret);
3684}
3685
3686/**
3687 * xmlRelaxNGValidateInterleave:
3688 * @ctxt: a Relax-NG validation context
3689 * @define: the definition to verify
3690 *
3691 * Validate an interleave definition for a node.
3692 *
3693 * Returns 0 if the validation succeeded or an error code.
3694 */
3695static int
3696xmlRelaxNGValidateInterleave(xmlRelaxNGValidCtxtPtr ctxt,
3697 xmlRelaxNGDefinePtr define) {
3698 int ret = 0, nbchildren, nbtot, i, j;
3699 xmlRelaxNGPartitionPtr partitions;
3700 xmlNodePtr *children = NULL;
3701 xmlNodePtr *order = NULL;
3702 xmlNodePtr cur;
3703
3704 if (define->data != NULL) {
3705 partitions = (xmlRelaxNGPartitionPtr) define->data;
3706 } else {
3707 VALID_CTXT();
3708 VALID_ERROR("Internal: interleave block has no data\n");
3709 return(-1);
3710 }
3711
3712 /*
3713 * Build the sequence of child and an array preserving the children
3714 * initial order.
3715 */
3716 cur = ctxt->state->seq;
3717 nbchildren = 0;
3718 nbtot = 0;
3719 while (cur != NULL) {
3720 if ((cur->type == XML_COMMENT_NODE) ||
3721 (cur->type == XML_PI_NODE) ||
3722 ((cur->type == XML_TEXT_NODE) &&
3723 (IS_BLANK_NODE(cur)))) {
3724 nbtot++;
3725 } else {
3726 nbchildren++;
3727 nbtot++;
3728 }
3729 cur = cur->next;
3730 }
3731 children = (xmlNodePtr *) xmlMalloc(nbchildren * sizeof(xmlNodePtr));
3732 if (children == NULL)
3733 goto error;
3734 order = (xmlNodePtr *) xmlMalloc(nbtot * sizeof(xmlNodePtr));
3735 if (order == NULL)
3736 goto error;
3737 cur = ctxt->state->seq;
3738 i = 0;
3739 j = 0;
3740 while (cur != NULL) {
3741 if ((cur->type == XML_COMMENT_NODE) ||
3742 (cur->type == XML_PI_NODE) ||
3743 ((cur->type == XML_TEXT_NODE) &&
3744 (IS_BLANK_NODE(cur)))) {
3745 order[j++] = cur;
3746 } else {
3747 order[j++] = cur;
3748 children[i++] = cur;
3749 }
3750 cur = cur->next;
3751 }
3752
3753 /* TODO: retry with a maller set of child if there is a next... */
3754 ret = xmlRelaxNGValidatePartGroup(ctxt, partitions->groups,
3755 partitions->nbgroups, children, nbchildren);
3756 if (ret == 0) {
3757 ctxt->state->seq = NULL;
3758 }
3759
3760 /*
3761 * Cleanup: rebuid the child sequence and free the structure
3762 */
3763 if (order != NULL) {
3764 for (i = 0;i < nbtot;i++) {
3765 if (i == 0)
3766 order[i]->prev = NULL;
3767 else
3768 order[i]->prev = order[i - 1];
3769 if (i == nbtot - 1)
3770 order[i]->next = NULL;
3771 else
3772 order[i]->next = order[i + 1];
3773 }
3774 xmlFree(order);
3775 }
3776 if (children != NULL)
3777 xmlFree(children);
3778
3779 return(ret);
3780
3781error:
3782 if (order != NULL) {
3783 for (i = 0;i < nbtot;i++) {
3784 if (i == 0)
3785 order[i]->prev = NULL;
3786 else
3787 order[i]->prev = order[i - 1];
3788 if (i == nbtot - 1)
3789 order[i]->next = NULL;
3790 else
3791 order[i]->next = order[i + 1];
3792 }
3793 xmlFree(order);
3794 }
3795 if (children != NULL)
3796 xmlFree(children);
3797 return(-1);
3798}
3799
3800/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00003801 * xmlRelaxNGValidateElementContent:
3802 * @ctxt: a Relax-NG validation context
3803 * @define: the list of definition to verify
3804 *
3805 * Validate the given node content against the (list) of definitions
3806 *
3807 * Returns 0 if the validation succeeded or an error code.
3808 */
3809static int
3810xmlRelaxNGValidateElementContent(xmlRelaxNGValidCtxtPtr ctxt,
3811 xmlRelaxNGDefinePtr defines) {
3812 int ret = 0, res;
3813
3814 if (ctxt->state == NULL) {
3815 VALID_CTXT();
3816 VALID_ERROR("Internal: no state\n");
3817 return(-1);
3818 }
3819 while (defines != NULL) {
3820 res = xmlRelaxNGValidateDefinition(ctxt, defines);
3821 if (res < 0)
3822 ret = -1;
3823 defines = defines->next;
3824 }
3825
3826 return(ret);
3827}
3828
3829/**
3830 * xmlRelaxNGValidateDefinition:
3831 * @ctxt: a Relax-NG validation context
3832 * @define: the definition to verify
3833 *
3834 * Validate the current node against the definition
3835 *
3836 * Returns 0 if the validation succeeded or an error code.
3837 */
3838static int
3839xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
3840 xmlRelaxNGDefinePtr define) {
3841 xmlNodePtr node;
3842 int ret = 0, i, tmp, oldflags;
3843 xmlRelaxNGValidStatePtr oldstate, state;
3844
3845 if (define == NULL) {
3846 VALID_CTXT();
3847 VALID_ERROR("internal error: define == NULL\n");
3848 return(-1);
3849 }
3850 if (ctxt->state != NULL) {
3851 node = ctxt->state->seq;
3852 } else {
3853 node = NULL;
3854 }
3855 switch (define->type) {
3856 case XML_RELAXNG_EMPTY:
3857 if (node != NULL) {
3858 VALID_CTXT();
3859 VALID_ERROR("Expecting an empty element\n");
3860 return(-1);
3861 }
3862#ifdef DEBUG
3863 xmlGenericError(xmlGenericErrorContext,
3864 "xmlRelaxNGValidateDefinition(): validated empty\n");
3865#endif
3866 return(0);
3867 case XML_RELAXNG_NOT_ALLOWED:
3868 TODO
3869 break;
3870 case XML_RELAXNG_TEXT:
3871 if (node == NULL)
3872 return(0);
3873 while ((node != NULL) &&
3874 ((node->type == XML_TEXT_NODE) ||
3875 (node->type == XML_CDATA_SECTION_NODE)))
3876 node = node->next;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003877 if (node == ctxt->state->seq) {
3878 VALID_CTXT();
3879 VALID_ERROR("Expecting text content\n");
3880 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003881 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003882 ctxt->state->seq = node;
3883 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003884 case XML_RELAXNG_ELEMENT:
3885 node = xmlRelaxNGSkipIgnored(ctxt, node);
3886 if ((node == NULL) || (node->type != XML_ELEMENT_NODE)) {
3887 VALID_CTXT();
3888 VALID_ERROR("Expecting an element\n");
3889 return(-1);
3890 }
3891 if (define->name != NULL) {
3892 if (!xmlStrEqual(node->name, define->name)) {
3893 VALID_CTXT();
3894 VALID_ERROR("Expecting element %s, got %s\n",
3895 define->name, node->name);
3896 return(-1);
3897 }
3898 }
3899 if ((define->ns != NULL) && (define->ns[0] != 0)) {
3900 if (node->ns == NULL) {
3901 VALID_CTXT();
3902 VALID_ERROR("Expecting a namespace for element %s\n",
3903 node->name);
3904 return(-1);
3905 } else if (!xmlStrEqual(node->ns->href, define->ns)) {
3906 VALID_CTXT();
3907 VALID_ERROR("Expecting element %s has wrong namespace: expecting %s\n",
3908 node->name, define->ns);
3909 return(-1);
3910 }
3911 } else {
3912 if (node->ns != NULL) {
3913 VALID_CTXT();
3914 VALID_ERROR("Expecting no namespace for element %s\n",
3915 node->name);
3916 return(-1);
3917 }
3918 }
3919
3920 state = xmlRelaxNGNewValidState(ctxt, node);
3921 if (state == NULL) {
3922 return(-1);
3923 }
3924
3925 oldstate = ctxt->state;
3926 ctxt->state = state;
3927 if (define->attrs != NULL) {
3928 tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
3929 if (tmp != 0)
3930 ret = -1;
3931 }
3932 if (define->content != NULL) {
3933 tmp = xmlRelaxNGValidateElementContent(ctxt, define->content);
3934 if (tmp != 0)
3935 ret = -1;
3936 }
3937 state = ctxt->state;
3938 if (state->seq != NULL) {
3939 state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq);
3940 if (state->seq != NULL) {
3941 VALID_CTXT();
3942 VALID_ERROR("Extra content for element %s\n",
3943 node->name);
3944 ret = -1;
3945 }
3946 }
3947 for (i = 0;i < state->nbAttrs;i++) {
3948 if (state->attrs[i] != NULL) {
3949 VALID_CTXT();
Daniel Veillardea3f3982003-01-26 19:45:18 +00003950 VALID_ERROR("Invalid attribute %s for element %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00003951 state->attrs[i]->name, node->name);
3952 ret = -1;
3953 }
3954 }
3955 ctxt->state = oldstate;
3956 xmlRelaxNGFreeValidState(state);
3957 if (oldstate != NULL)
3958 oldstate->seq = node->next;
3959
3960
3961#ifdef DEBUG
3962 xmlGenericError(xmlGenericErrorContext,
3963 "xmlRelaxNGValidateDefinition(): validated %s : %d\n",
3964 node->name, ret);
3965#endif
3966 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003967 case XML_RELAXNG_OPTIONAL:
3968 oldflags = ctxt->flags;
3969 ctxt->flags |= FLAGS_IGNORABLE;
3970 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
3971 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
3972 if (ret != 0) {
3973 xmlRelaxNGFreeValidState(ctxt->state);
3974 ctxt->state = oldstate;
3975 ret = 0;
3976 break;
3977 }
3978 xmlRelaxNGFreeValidState(oldstate);
3979 ctxt->flags = oldflags;
3980 ret = 0;
3981 break;
3982 case XML_RELAXNG_ONEORMORE:
3983 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
3984 if (ret != 0) {
3985 break;
3986 }
3987 /* no break on purpose */
Daniel Veillard276be4a2003-01-24 01:03:34 +00003988 case XML_RELAXNG_ZEROORMORE: {
3989 xmlNodePtr cur, temp;
3990
Daniel Veillard6eadf632003-01-23 18:29:16 +00003991 oldflags = ctxt->flags;
3992 ctxt->flags |= FLAGS_IGNORABLE;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003993 cur = ctxt->state->seq;
3994 temp = NULL;
3995 while ((cur != NULL) && (temp != cur)) {
3996 temp = cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003997 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
3998 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
3999 if (ret != 0) {
4000 xmlRelaxNGFreeValidState(ctxt->state);
4001 ctxt->state = oldstate;
4002 ret = 0;
4003 break;
4004 }
4005 xmlRelaxNGFreeValidState(oldstate);
Daniel Veillard276be4a2003-01-24 01:03:34 +00004006 cur = ctxt->state->seq;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004007 }
4008 ctxt->flags = oldflags;
4009 break;
Daniel Veillard276be4a2003-01-24 01:03:34 +00004010 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004011 case XML_RELAXNG_CHOICE: {
4012 xmlRelaxNGDefinePtr list = define->content;
4013
4014 oldflags = ctxt->flags;
4015 ctxt->flags |= FLAGS_IGNORABLE;
4016
4017 while (list != NULL) {
4018 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
4019 ret = xmlRelaxNGValidateDefinition(ctxt, list);
4020 if (ret == 0) {
4021 xmlRelaxNGFreeValidState(oldstate);
4022 break;
4023 }
4024 xmlRelaxNGFreeValidState(ctxt->state);
4025 ctxt->state = oldstate;
4026 list = list->next;
4027 }
4028 ctxt->flags = oldflags;
4029 break;
4030 }
4031 case XML_RELAXNG_GROUP: {
4032 xmlRelaxNGDefinePtr list = define->content;
4033
4034 while (list != NULL) {
4035 ret = xmlRelaxNGValidateDefinition(ctxt, list);
4036 if (ret != 0)
4037 break;
4038 list = list->next;
4039 }
4040 break;
4041 }
4042 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004043 ret = xmlRelaxNGValidateInterleave(ctxt, define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004044 break;
4045 case XML_RELAXNG_ATTRIBUTE:
4046 ret = xmlRelaxNGValidateAttribute(ctxt, define);
4047 break;
4048 case XML_RELAXNG_REF:
4049 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
4050 break;
4051 case XML_RELAXNG_DEF:
4052 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
4053 break;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004054 case XML_RELAXNG_DATATYPE: {
4055 xmlChar *content;
4056
4057 content = xmlNodeGetContent(node);
4058 ret = xmlRelaxNGValidateDatatype(ctxt, content, define);
4059 if (ret == -1) {
4060 VALID_CTXT();
4061 VALID_ERROR("internal error validating %s\n", define->name);
4062 } else if (ret == 0) {
4063 ctxt->state->seq = node->next;
4064 }
4065 /*
4066 * TODO cover the problems with
4067 * <p>12<!-- comment -->34</p>
4068 * TODO detect full element coverage at compilation time.
4069 */
4070 if ((node != NULL) && (node->next != NULL)) {
4071 VALID_CTXT();
4072 VALID_ERROR("The data does not cover the full element %s\n",
4073 node->parent->name);
4074 ret = -1;
4075 }
4076 if (content != NULL)
4077 xmlFree(content);
4078 break;
4079 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00004080 case XML_RELAXNG_VALUE: {
4081 xmlChar *content;
4082 xmlChar *oldvalue;
4083
4084 content = xmlNodeGetContent(node);
4085 oldvalue = ctxt->state->value;
4086 ctxt->state->value = content;
4087 ret = xmlRelaxNGValidateValue(ctxt, define);
4088 ctxt->state->value = oldvalue;
4089 if (ret == -1) {
4090 VALID_CTXT();
4091 VALID_ERROR("internal error validating %s\n", define->name);
4092 } else if (ret == 0) {
4093 ctxt->state->seq = node->next;
4094 }
4095 /*
4096 * TODO cover the problems with
4097 * <p>12<!-- comment -->34</p>
4098 * TODO detect full element coverage at compilation time.
4099 */
4100 if ((node != NULL) && (node->next != NULL)) {
4101 VALID_CTXT();
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004102 VALID_ERROR("The value does not cover the full element %s\n",
Daniel Veillardea3f3982003-01-26 19:45:18 +00004103 node->parent->name);
4104 ret = -1;
4105 }
4106 if (content != NULL)
4107 xmlFree(content);
4108 break;
4109 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004110 case XML_RELAXNG_LIST: {
4111 xmlChar *content;
4112 xmlChar *oldvalue, *oldendvalue;
4113 int len;
Daniel Veillardea3f3982003-01-26 19:45:18 +00004114
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004115 content = xmlNodeGetContent(node);
4116 len = xmlStrlen(content);
4117 oldvalue = ctxt->state->value;
4118 oldendvalue = ctxt->state->endvalue;
4119 ctxt->state->value = content;
4120 ctxt->state->endvalue = content + len;
4121 ret = xmlRelaxNGValidateValue(ctxt, define);
4122 ctxt->state->value = oldvalue;
4123 ctxt->state->endvalue = oldendvalue;
4124 if (ret == -1) {
4125 VALID_CTXT();
4126 VALID_ERROR("internal error validating list\n");
4127 } else if (ret == 0) {
4128 ctxt->state->seq = node->next;
4129 }
4130 /*
4131 * TODO cover the problems with
4132 * <p>12<!-- comment -->34</p>
4133 * TODO detect full element coverage at compilation time.
4134 */
4135 if ((node != NULL) && (node->next != NULL)) {
4136 VALID_CTXT();
4137 VALID_ERROR("The list does not cover the full element %s\n",
4138 node->parent->name);
4139 ret = -1;
4140 }
4141 if (content != NULL)
4142 xmlFree(content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004143 break;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004144 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004145 }
4146 return(ret);
4147}
4148
4149/**
4150 * xmlRelaxNGValidateDocument:
4151 * @ctxt: a Relax-NG validation context
4152 * @doc: the document
4153 *
4154 * Validate the given document
4155 *
4156 * Returns 0 if the validation succeeded or an error code.
4157 */
4158static int
4159xmlRelaxNGValidateDocument(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
4160 int ret;
4161 xmlRelaxNGPtr schema;
4162 xmlRelaxNGGrammarPtr grammar;
4163 xmlRelaxNGValidStatePtr state;
4164
4165 if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL))
4166 return(-1);
4167
4168 schema = ctxt->schema;
4169 grammar = schema->topgrammar;
4170 if (grammar == NULL) {
4171 VALID_CTXT();
4172 VALID_ERROR("No top grammar defined\n");
4173 return(-1);
4174 }
4175 state = xmlRelaxNGNewValidState(ctxt, NULL);
4176 ctxt->state = state;
4177 ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start);
4178 state = ctxt->state;
4179 if ((state != NULL) && (state->seq != NULL)) {
4180 xmlNodePtr node;
4181
4182 node = state->seq;
4183 node = xmlRelaxNGSkipIgnored(ctxt, node);
4184 if (node != NULL) {
4185 VALID_CTXT();
4186 VALID_ERROR("extra data on the document\n");
4187 ret = -1;
4188 }
4189 }
4190 xmlRelaxNGFreeValidState(state);
4191
4192 return(ret);
4193}
4194
4195/************************************************************************
4196 * *
4197 * Validation interfaces *
4198 * *
4199 ************************************************************************/
4200/**
4201 * xmlRelaxNGNewValidCtxt:
4202 * @schema: a precompiled XML RelaxNGs
4203 *
4204 * Create an XML RelaxNGs validation context based on the given schema
4205 *
4206 * Returns the validation context or NULL in case of error
4207 */
4208xmlRelaxNGValidCtxtPtr
4209xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema) {
4210 xmlRelaxNGValidCtxtPtr ret;
4211
4212 ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt));
4213 if (ret == NULL) {
4214 xmlGenericError(xmlGenericErrorContext,
4215 "Failed to allocate new schama validation context\n");
4216 return (NULL);
4217 }
4218 memset(ret, 0, sizeof(xmlRelaxNGValidCtxt));
4219 ret->schema = schema;
4220 return (ret);
4221}
4222
4223/**
4224 * xmlRelaxNGFreeValidCtxt:
4225 * @ctxt: the schema validation context
4226 *
4227 * Free the resources associated to the schema validation context
4228 */
4229void
4230xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt) {
4231 if (ctxt == NULL)
4232 return;
4233 xmlFree(ctxt);
4234}
4235
4236/**
4237 * xmlRelaxNGSetValidErrors:
4238 * @ctxt: a Relax-NG validation context
4239 * @err: the error function
4240 * @warn: the warning function
4241 * @ctx: the functions context
4242 *
4243 * Set the error and warning callback informations
4244 */
4245void
4246xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
4247 xmlRelaxNGValidityErrorFunc err,
4248 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
4249 if (ctxt == NULL)
4250 return;
4251 ctxt->error = err;
4252 ctxt->warning = warn;
4253 ctxt->userData = ctx;
4254}
4255
4256/**
4257 * xmlRelaxNGValidateDoc:
4258 * @ctxt: a Relax-NG validation context
4259 * @doc: a parsed document tree
4260 *
4261 * Validate a document tree in memory.
4262 *
4263 * Returns 0 if the document is valid, a positive error code
4264 * number otherwise and -1 in case of internal or API error.
4265 */
4266int
4267xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
4268 int ret;
4269
4270 if ((ctxt == NULL) || (doc == NULL))
4271 return(-1);
4272
4273 ctxt->doc = doc;
4274
4275 ret = xmlRelaxNGValidateDocument(ctxt, doc);
4276 return(ret);
4277}
4278
4279#endif /* LIBXML_SCHEMAS_ENABLED */
4280