blob: 49b089eb6bcd934402809410f0b822039c5706c0 [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 Veillard76fc5ed2003-01-28 20:58:15 +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) {
1302 if (cur->type == XML_RELAXNG_ELEMENT) {
1303 if (ret == NULL) {
1304 max = 10;
1305 ret = (xmlRelaxNGDefinePtr *)
1306 xmlMalloc((max + 1) * sizeof(xmlRelaxNGDefinePtr));
1307 if (ret == NULL) {
1308 if (ctxt->error != NULL)
1309 ctxt->error(ctxt->userData,
1310 "Out of memory in element search\n");
1311 ctxt->nbErrors++;
1312 return(NULL);
1313 }
1314 } else if (max <= len) {
1315 max *= 2;
1316 ret = xmlRealloc(ret, (max + 1) * sizeof(xmlRelaxNGDefinePtr));
1317 if (ret == NULL) {
1318 if (ctxt->error != NULL)
1319 ctxt->error(ctxt->userData,
1320 "Out of memory in element search\n");
1321 ctxt->nbErrors++;
1322 return(NULL);
1323 }
1324 }
1325 ret[len++] = def;
1326 ret[len] = NULL;
1327 } else if ((cur->type == XML_RELAXNG_CHOICE) ||
1328 (cur->type == XML_RELAXNG_INTERLEAVE) ||
1329 (cur->type == XML_RELAXNG_GROUP) ||
1330 (cur->type == XML_RELAXNG_ONEORMORE) ||
1331 (cur->type == XML_RELAXNG_ZEROORMORE)) {
1332 /*
1333 * Don't go within elements or attributes or string values.
1334 * Just gather the element top list
1335 */
1336 if (cur->content != NULL) {
1337 parent = cur;
1338 cur = cur->content;
1339 tmp = cur;
1340 while (tmp != NULL) {
1341 tmp->parent = parent;
1342 tmp = tmp->next;
1343 }
1344 continue;
1345 }
1346 }
1347 if (cur == def) return(ret);
1348 if (cur->next != NULL) {
1349 cur = cur->next;
1350 continue;
1351 }
1352 do {
1353 cur = cur->parent;
1354 if (cur == NULL) break;
1355 if (cur == def) return(ret);
1356 if (cur->next != NULL) {
1357 cur = cur->next;
1358 break;
1359 }
1360 } while (cur != NULL);
1361 }
1362 return(ret);
1363}
1364
1365/**
1366 * xmlRelaxNGComputeInterleaves:
1367 * @def: the interleave definition
1368 * @ctxt: a Relax-NG parser context
1369 * @node: the data node.
1370 *
1371 * A lot of work for preprocessing interleave definitions
1372 * is potentially needed to get a decent execution speed at runtime
1373 * - trying to get a total order on the element nodes generated
1374 * by the interleaves, order the list of interleave definitions
1375 * following that order.
1376 * - if <text/> is used to handle mixed content, it is better to
1377 * flag this in the define and simplify the runtime checking
1378 * algorithm
1379 */
1380static void
1381xmlRelaxNGComputeInterleaves(xmlRelaxNGDefinePtr def,
1382 xmlRelaxNGParserCtxtPtr ctxt,
1383 xmlChar *name ATTRIBUTE_UNUSED) {
1384 xmlRelaxNGDefinePtr cur;
1385
1386 xmlRelaxNGDefinePtr *list = NULL;
1387 xmlRelaxNGPartitionPtr partitions = NULL;
1388 xmlRelaxNGInterleaveGroupPtr *groups = NULL;
1389 xmlRelaxNGInterleaveGroupPtr group;
1390 int i,j,ret;
1391 int nbgroups = 0;
1392 int nbchild = 0;
1393
1394#ifdef DEBUG_INTERLEAVE
1395 xmlGenericError(xmlGenericErrorContext,
1396 "xmlRelaxNGComputeInterleaves(%s)\n",
1397 name);
1398#endif
1399 cur = def->content;
1400 while (cur != NULL) {
1401 nbchild++;
1402 cur = cur->next;
1403 }
1404
1405#ifdef DEBUG_INTERLEAVE
1406 xmlGenericError(xmlGenericErrorContext, " %d child\n", nbchild);
1407#endif
1408 groups = (xmlRelaxNGInterleaveGroupPtr *)
1409 xmlMalloc(nbchild * sizeof(xmlRelaxNGInterleaveGroupPtr));
1410 if (groups == NULL)
1411 goto error;
1412 cur = def->content;
1413 while (cur != NULL) {
1414 list = xmlRelaxNGGetElements(ctxt, cur);
1415 if (list != NULL) {
1416 groups[nbgroups] = (xmlRelaxNGInterleaveGroupPtr)
1417 xmlMalloc(sizeof(xmlRelaxNGInterleaveGroup));
1418 if (groups[nbgroups] == NULL)
1419 goto error;
1420 groups[nbgroups]->rule = cur;
1421 groups[nbgroups]->defs = list;
1422 nbgroups++;
1423 }
1424 cur = cur->next;
1425 }
1426 list = NULL;
1427#ifdef DEBUG_INTERLEAVE
1428 xmlGenericError(xmlGenericErrorContext, " %d groups\n", nbgroups);
1429#endif
1430
1431 /*
1432 * Let's check that all rules makes a partitions according to 7.4
1433 */
1434 partitions = (xmlRelaxNGPartitionPtr)
1435 xmlMalloc(sizeof(xmlRelaxNGPartition));
1436 if (partitions == NULL)
1437 goto error;
1438 partitions->nbgroups = nbgroups;
1439 for (i = 0;i < nbgroups;i++) {
1440 group = groups[i];
1441 for (j = i+1;j < nbgroups;j++) {
1442 if (groups[j] == NULL)
1443 continue;
1444 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->defs,
1445 groups[j]->defs);
1446 if (ret == 0) {
1447 if (ctxt->error != NULL)
1448 ctxt->error(ctxt->userData,
1449 "Element or text conflicts in interleave\n");
1450 ctxt->nbErrors++;
1451 }
1452 }
1453 }
1454 partitions->groups = groups;
1455
1456 /*
1457 * Free Up the child list, and save the partition list back in the def
1458 */
1459 def->data = partitions;
1460 return;
1461
1462error:
1463 if (ctxt->error != NULL)
1464 ctxt->error(ctxt->userData,
1465 "Out of memory in interleave computation\n");
1466 ctxt->nbErrors++;
1467 if (list == NULL)
1468 xmlFree(list);
1469 if (groups != NULL) {
1470 for (i = 0;i < nbgroups;i++)
1471 if (groups[i] != NULL) {
1472 if (groups[i]->defs != NULL)
1473 xmlFree(groups[i]->defs);
1474 xmlFree(groups[i]);
1475 }
1476 xmlFree(groups);
1477 }
1478 xmlRelaxNGFreePartition(partitions);
1479}
1480
1481/**
1482 * xmlRelaxNGParseInterleave:
1483 * @ctxt: a Relax-NG parser context
1484 * @node: the data node.
1485 *
1486 * parse the content of a RelaxNG interleave node.
1487 *
1488 * Returns the definition pointer or NULL in case of error
1489 */
1490static xmlRelaxNGDefinePtr
1491xmlRelaxNGParseInterleave(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1492 xmlRelaxNGDefinePtr def = NULL;
1493 xmlRelaxNGDefinePtr last = NULL, cur;
1494 xmlNodePtr child;
1495
1496 def = xmlRelaxNGNewDefine(ctxt, node);
1497 if (def == NULL) {
1498 return(NULL);
1499 }
1500 def->type = XML_RELAXNG_INTERLEAVE;
1501
1502 if (ctxt->interleaves == NULL)
1503 ctxt->interleaves = xmlHashCreate(10);
1504 if (ctxt->interleaves == NULL) {
1505 if (ctxt->error != NULL)
1506 ctxt->error(ctxt->userData,
1507 "Failed to create interleaves hash table\n");
1508 ctxt->nbErrors++;
1509 } else {
1510 char name[32];
1511
1512 snprintf(name, 32, "interleave%d", ctxt->nbInterleaves++);
1513 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST name, def) < 0) {
1514 if (ctxt->error != NULL)
1515 ctxt->error(ctxt->userData,
1516 "Failed to add %s to hash table\n", name);
1517 ctxt->nbErrors++;
1518 }
1519 }
1520 child = node->children;
1521 while (child != NULL) {
1522 if (IS_RELAXNG(child, "element")) {
1523 cur = xmlRelaxNGParseElement(ctxt, child);
1524 } else {
1525 cur = xmlRelaxNGParsePattern(ctxt, child);
1526 }
1527 if (cur != NULL) {
1528 cur->parent = def;
1529 if (last == NULL) {
1530 def->content = last = cur;
1531 } else {
1532 last->next = cur;
1533 last = cur;
1534 }
1535 }
1536 child = child->next;
1537 }
1538
1539 return(def);
1540}
Daniel Veillard6eadf632003-01-23 18:29:16 +00001541
1542/**
Daniel Veillard276be4a2003-01-24 01:03:34 +00001543 * xmlRelaxNGParseDefine:
1544 * @ctxt: a Relax-NG parser context
1545 * @node: the define node
1546 *
1547 * parse the content of a RelaxNG define element node.
1548 *
1549 * Returns the definition pointer or NULL in case of error.
1550 */
1551static int
1552xmlRelaxNGParseDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1553 xmlChar *name;
1554 int ret = 0, tmp;
1555 xmlRelaxNGDefinePtr def;
1556 const xmlChar *olddefine;
1557
1558 name = xmlGetProp(node, BAD_CAST "name");
1559 if (name == NULL) {
1560 if (ctxt->error != NULL)
1561 ctxt->error(ctxt->userData,
1562 "define has no name\n");
1563 ctxt->nbErrors++;
1564 } else {
1565 def = xmlRelaxNGNewDefine(ctxt, node);
1566 if (def == NULL) {
1567 xmlFree(name);
1568 return(-1);
1569 }
1570 def->type = XML_RELAXNG_DEF;
1571 def->name = name;
1572 if (node->children == NULL) {
1573 if (ctxt->error != NULL)
1574 ctxt->error(ctxt->userData,
1575 "define has no children\n");
1576 ctxt->nbErrors++;
1577 } else {
1578 olddefine = ctxt->define;
1579 ctxt->define = name;
1580 def->content = xmlRelaxNGParsePatterns(ctxt,
1581 node->children);
1582 ctxt->define = olddefine;
1583 }
1584 if (ctxt->grammar->defs == NULL)
1585 ctxt->grammar->defs = xmlHashCreate(10);
1586 if (ctxt->grammar->defs == NULL) {
1587 if (ctxt->error != NULL)
1588 ctxt->error(ctxt->userData,
1589 "Could not create definition hash\n");
1590 ctxt->nbErrors++;
1591 ret = -1;
1592 xmlRelaxNGFreeDefine(def);
1593 } else {
1594 tmp = xmlHashAddEntry(ctxt->grammar->defs, name, def);
1595 if (tmp < 0) {
1596 TODO
1597 /* store and implement 4.17 on combining */
1598 ctxt->nbErrors++;
1599 ret = -1;
1600 xmlRelaxNGFreeDefine(def);
1601 }
1602 }
1603 }
1604 return(ret);
1605}
1606
1607/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00001608 * xmlRelaxNGParsePattern:
1609 * @ctxt: a Relax-NG parser context
1610 * @node: the pattern node.
1611 *
1612 * parse the content of a RelaxNG pattern node.
1613 *
Daniel Veillard276be4a2003-01-24 01:03:34 +00001614 * Returns the definition pointer or NULL in case of error or if no
1615 * pattern is generated.
Daniel Veillard6eadf632003-01-23 18:29:16 +00001616 */
1617static xmlRelaxNGDefinePtr
1618xmlRelaxNGParsePattern(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1619 xmlRelaxNGDefinePtr def = NULL;
1620
1621 if (IS_RELAXNG(node, "element")) {
1622 def = xmlRelaxNGParseElement(ctxt, node);
1623 } else if (IS_RELAXNG(node, "attribute")) {
1624 def = xmlRelaxNGParseAttribute(ctxt, node);
1625 } else if (IS_RELAXNG(node, "empty")) {
1626 def = xmlRelaxNGNewDefine(ctxt, node);
1627 if (def == NULL)
1628 return(NULL);
1629 def->type = XML_RELAXNG_EMPTY;
1630 } else if (IS_RELAXNG(node, "text")) {
1631 def = xmlRelaxNGNewDefine(ctxt, node);
1632 if (def == NULL)
1633 return(NULL);
1634 def->type = XML_RELAXNG_TEXT;
1635 if (node->children != NULL) {
1636 if (ctxt->error != NULL)
1637 ctxt->error(ctxt->userData, "text: had a child node\n");
1638 ctxt->nbErrors++;
1639 }
1640 } else if (IS_RELAXNG(node, "zeroOrMore")) {
1641 def = xmlRelaxNGNewDefine(ctxt, node);
1642 if (def == NULL)
1643 return(NULL);
1644 def->type = XML_RELAXNG_ZEROORMORE;
1645 def->content = xmlRelaxNGParsePatterns(ctxt, node->children);
1646 } else if (IS_RELAXNG(node, "oneOrMore")) {
1647 def = xmlRelaxNGNewDefine(ctxt, node);
1648 if (def == NULL)
1649 return(NULL);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001650 def->type = XML_RELAXNG_ONEORMORE;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001651 def->content = xmlRelaxNGParsePatterns(ctxt, node->children);
1652 } else if (IS_RELAXNG(node, "optional")) {
1653 def = xmlRelaxNGNewDefine(ctxt, node);
1654 if (def == NULL)
1655 return(NULL);
1656 def->type = XML_RELAXNG_OPTIONAL;
1657 def->content = xmlRelaxNGParsePatterns(ctxt, node->children);
1658 } else if (IS_RELAXNG(node, "choice")) {
1659 def = xmlRelaxNGNewDefine(ctxt, node);
1660 if (def == NULL)
1661 return(NULL);
1662 def->type = XML_RELAXNG_CHOICE;
1663 def->content = xmlRelaxNGParsePatterns(ctxt, node->children);
1664 } else if (IS_RELAXNG(node, "group")) {
1665 def = xmlRelaxNGNewDefine(ctxt, node);
1666 if (def == NULL)
1667 return(NULL);
1668 def->type = XML_RELAXNG_GROUP;
1669 def->content = xmlRelaxNGParsePatterns(ctxt, node->children);
1670 } else if (IS_RELAXNG(node, "ref")) {
1671 def = xmlRelaxNGNewDefine(ctxt, node);
1672 if (def == NULL)
1673 return(NULL);
1674 def->type = XML_RELAXNG_REF;
1675 def->name = xmlGetProp(node, BAD_CAST "name");
1676 if (def->name == NULL) {
1677 if (ctxt->error != NULL)
1678 ctxt->error(ctxt->userData,
1679 "ref has no name\n");
1680 ctxt->nbErrors++;
Daniel Veillard276be4a2003-01-24 01:03:34 +00001681 } else {
1682 if ((ctxt->define != NULL) &&
1683 (xmlStrEqual(ctxt->define, def->name))) {
1684 if (ctxt->error != NULL)
1685 ctxt->error(ctxt->userData,
1686 "Recursive reference to %s not in an element\n",
1687 def->name);
1688 ctxt->nbErrors++;
1689 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00001690 }
1691 if (node->children != NULL) {
1692 if (ctxt->error != NULL)
1693 ctxt->error(ctxt->userData,
1694 "ref is not empty\n");
1695 ctxt->nbErrors++;
1696 }
1697 if (ctxt->grammar->refs == NULL)
1698 ctxt->grammar->refs = xmlHashCreate(10);
1699 if (ctxt->grammar->refs == NULL) {
1700 if (ctxt->error != NULL)
1701 ctxt->error(ctxt->userData,
1702 "Could not create references hash\n");
1703 ctxt->nbErrors++;
1704 xmlRelaxNGFreeDefine(def);
1705 def = NULL;
1706 } else {
1707 int tmp;
1708
1709 tmp = xmlHashAddEntry(ctxt->grammar->refs, def->name, def);
1710 if (tmp < 0) {
1711 xmlRelaxNGDefinePtr prev;
1712
1713 prev = (xmlRelaxNGDefinePtr)
1714 xmlHashLookup(ctxt->grammar->refs, def->name);
1715 if (prev == NULL) {
1716 if (ctxt->error != NULL)
1717 ctxt->error(ctxt->userData,
1718 "Internal error refs definitions '%s'\n",
1719 def->name);
1720 ctxt->nbErrors++;
1721 xmlRelaxNGFreeDefine(def);
1722 def = NULL;
1723 } else {
1724 def->nextHash = prev->nextHash;
1725 prev->nextHash = def;
1726 }
1727 }
1728 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001729 } else if (IS_RELAXNG(node, "data")) {
1730 def = xmlRelaxNGParseData(ctxt, node);
Daniel Veillard276be4a2003-01-24 01:03:34 +00001731 } else if (IS_RELAXNG(node, "define")) {
1732 xmlRelaxNGParseDefine(ctxt, node);
1733 def = NULL;
Daniel Veillardedc91922003-01-26 00:52:04 +00001734 } else if (IS_RELAXNG(node, "value")) {
1735 def = xmlRelaxNGParseValue(ctxt, node);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001736 } else if (IS_RELAXNG(node, "list")) {
1737 def = xmlRelaxNGNewDefine(ctxt, node);
1738 if (def == NULL)
1739 return(NULL);
1740 def->type = XML_RELAXNG_LIST;
1741 def->content = xmlRelaxNGParsePatterns(ctxt, node->children);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001742 } else if (IS_RELAXNG(node, "interleave")) {
1743 def = xmlRelaxNGParseInterleave(ctxt, node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001744 } else {
1745 TODO
1746 }
1747 return(def);
1748}
1749
1750/**
1751 * xmlRelaxNGParseAttribute:
1752 * @ctxt: a Relax-NG parser context
1753 * @node: the element node
1754 *
1755 * parse the content of a RelaxNG attribute node.
1756 *
1757 * Returns the definition pointer or NULL in case of error.
1758 */
1759static xmlRelaxNGDefinePtr
1760xmlRelaxNGParseAttribute(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1761 xmlRelaxNGDefinePtr ret, cur, last;
1762 xmlNodePtr child;
1763 xmlChar *val;
1764 int old_flags;
1765
1766 ret = xmlRelaxNGNewDefine(ctxt, node);
1767 if (ret == NULL)
1768 return(NULL);
1769 ret->type = XML_RELAXNG_ATTRIBUTE;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001770 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001771 child = node->children;
1772 if (child == NULL) {
1773 if (ctxt->error != NULL)
1774 ctxt->error(ctxt->userData,
1775 "xmlRelaxNGParseattribute: attribute has no children\n");
1776 ctxt->nbErrors++;
1777 return(ret);
1778 }
1779 old_flags = ctxt->flags;
1780 ctxt->flags |= XML_RELAXNG_IN_ATTRIBUTE;
1781 if (IS_RELAXNG(child, "name")) {
1782 val = xmlNodeGetContent(child);
1783 ret->name = val;
1784 val = xmlGetProp(child, BAD_CAST "ns");
1785 ret->ns = val;
1786 } else if (IS_RELAXNG(child, "anyName")) {
1787 TODO
1788 } else if (IS_RELAXNG(child, "nsName")) {
1789 TODO
1790 } else if (IS_RELAXNG(child, "choice")) {
1791 TODO
1792 } else {
1793 if (ctxt->error != NULL)
1794 ctxt->error(ctxt->userData,
1795 "element: expecting name, anyName, nsName or choice : got %s\n",
1796 child->name);
1797 ctxt->nbErrors++;
1798 ctxt->flags = old_flags;
1799 return(ret);
1800 }
1801 child = child->next;
1802 last = NULL;
1803 while (child != NULL) {
1804 cur = xmlRelaxNGParsePattern(ctxt, child);
1805 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001806 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001807 switch (cur->type) {
1808 case XML_RELAXNG_EMPTY:
1809 case XML_RELAXNG_NOT_ALLOWED:
1810 case XML_RELAXNG_TEXT:
1811 case XML_RELAXNG_ELEMENT:
1812 case XML_RELAXNG_DATATYPE:
1813 case XML_RELAXNG_VALUE:
1814 case XML_RELAXNG_LIST:
1815 case XML_RELAXNG_REF:
1816 case XML_RELAXNG_DEF:
1817 case XML_RELAXNG_ONEORMORE:
1818 case XML_RELAXNG_ZEROORMORE:
1819 case XML_RELAXNG_OPTIONAL:
1820 case XML_RELAXNG_CHOICE:
1821 case XML_RELAXNG_GROUP:
1822 case XML_RELAXNG_INTERLEAVE:
1823 if (last == NULL) {
1824 ret->content = last = cur;
1825 } else {
1826 if ((last->type == XML_RELAXNG_ELEMENT) &&
1827 (ret->content == last)) {
1828 ret->content = xmlRelaxNGNewDefine(ctxt, node);
1829 if (ret->content != NULL) {
1830 ret->content->type = XML_RELAXNG_GROUP;
1831 ret->content->content = last;
1832 } else {
1833 ret->content = last;
1834 }
1835 }
1836 last->next = cur;
1837 last = cur;
1838 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001839 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001840 break;
1841 case XML_RELAXNG_ATTRIBUTE:
1842 cur->next = ret->attrs;
1843 ret->attrs = cur;
1844 break;
1845 }
1846 }
1847 child = child->next;
1848 }
1849 ctxt->flags = old_flags;
1850 return(ret);
1851}
1852
1853/**
1854 * xmlRelaxNGParseElement:
1855 * @ctxt: a Relax-NG parser context
1856 * @node: the element node
1857 *
1858 * parse the content of a RelaxNG element node.
1859 *
1860 * Returns the definition pointer or NULL in case of error.
1861 */
1862static xmlRelaxNGDefinePtr
1863xmlRelaxNGParseElement(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1864 xmlRelaxNGDefinePtr ret, cur, last;
1865 xmlNodePtr child;
1866 xmlChar *val;
Daniel Veillard276be4a2003-01-24 01:03:34 +00001867 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001868
1869 ret = xmlRelaxNGNewDefine(ctxt, node);
1870 if (ret == NULL)
1871 return(NULL);
1872 ret->type = XML_RELAXNG_ELEMENT;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001873 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001874 child = node->children;
1875 if (child == NULL) {
1876 if (ctxt->error != NULL)
1877 ctxt->error(ctxt->userData,
1878 "xmlRelaxNGParseElement: element has no children\n");
1879 ctxt->nbErrors++;
1880 return(ret);
1881 }
1882 if (IS_RELAXNG(child, "name")) {
1883 val = xmlNodeGetContent(child);
1884 ret->name = val;
1885 val = xmlGetProp(child, BAD_CAST "ns");
1886 ret->ns = val;
1887 } else if (IS_RELAXNG(child, "anyName")) {
1888 TODO
1889 } else if (IS_RELAXNG(child, "nsName")) {
1890 TODO
1891 } else if (IS_RELAXNG(child, "choice")) {
1892 TODO
1893 } else {
1894 if (ctxt->error != NULL)
1895 ctxt->error(ctxt->userData,
1896 "element: expecting name, anyName, nsName or choice : got %s\n",
1897 child->name);
1898 ctxt->nbErrors++;
1899 return(ret);
1900 }
1901 child = child->next;
1902 if (child == NULL) {
1903 if (ctxt->error != NULL)
1904 ctxt->error(ctxt->userData,
1905 "xmlRelaxNGParseElement: element has no content\n");
1906 ctxt->nbErrors++;
1907 return(ret);
1908 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00001909 olddefine = ctxt->define;
1910 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001911 last = NULL;
1912 while (child != NULL) {
1913 cur = xmlRelaxNGParsePattern(ctxt, child);
1914 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001915 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001916 switch (cur->type) {
1917 case XML_RELAXNG_EMPTY:
1918 case XML_RELAXNG_NOT_ALLOWED:
1919 case XML_RELAXNG_TEXT:
1920 case XML_RELAXNG_ELEMENT:
1921 case XML_RELAXNG_DATATYPE:
1922 case XML_RELAXNG_VALUE:
1923 case XML_RELAXNG_LIST:
1924 case XML_RELAXNG_REF:
1925 case XML_RELAXNG_DEF:
1926 case XML_RELAXNG_ZEROORMORE:
1927 case XML_RELAXNG_ONEORMORE:
1928 case XML_RELAXNG_OPTIONAL:
1929 case XML_RELAXNG_CHOICE:
1930 case XML_RELAXNG_GROUP:
1931 case XML_RELAXNG_INTERLEAVE:
1932 if (last == NULL) {
1933 ret->content = last = cur;
1934 } else {
1935 if ((last->type == XML_RELAXNG_ELEMENT) &&
1936 (ret->content == last)) {
1937 ret->content = xmlRelaxNGNewDefine(ctxt, node);
1938 if (ret->content != NULL) {
1939 ret->content->type = XML_RELAXNG_GROUP;
1940 ret->content->content = last;
1941 } else {
1942 ret->content = last;
1943 }
1944 }
1945 last->next = cur;
1946 last = cur;
1947 }
1948 break;
1949 case XML_RELAXNG_ATTRIBUTE:
1950 cur->next = ret->attrs;
1951 ret->attrs = cur;
1952 break;
1953 }
1954 }
1955 child = child->next;
1956 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00001957 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001958 return(ret);
1959}
1960
1961/**
1962 * xmlRelaxNGParsePatterns:
1963 * @ctxt: a Relax-NG parser context
1964 * @nodes: list of nodes
1965 *
1966 * parse the content of a RelaxNG start node.
1967 *
1968 * Returns the definition pointer or NULL in case of error.
1969 */
1970static xmlRelaxNGDefinePtr
1971xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001972 xmlRelaxNGDefinePtr def = NULL, last = NULL, cur, parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001973
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001974 parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001975 while (nodes != NULL) {
1976 if (IS_RELAXNG(nodes, "element")) {
1977 cur = xmlRelaxNGParseElement(ctxt, nodes);
1978 if (def == NULL) {
1979 def = last = cur;
1980 } else {
1981 if ((def->type == XML_RELAXNG_ELEMENT) && (def == last)) {
1982 def = xmlRelaxNGNewDefine(ctxt, nodes);
1983 def->type = XML_RELAXNG_GROUP;
1984 def->content = last;
1985 }
1986 last->next = cur;
1987 last = cur;
1988 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001989 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001990 } else {
1991 cur = xmlRelaxNGParsePattern(ctxt, nodes);
1992 if (def == NULL) {
1993 def = last = cur;
1994 } else {
1995 last->next = cur;
1996 last = cur;
1997 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001998 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001999 }
2000 nodes = nodes->next;
2001 }
2002 return(def);
2003}
2004
2005/**
2006 * xmlRelaxNGParseStart:
2007 * @ctxt: a Relax-NG parser context
2008 * @nodes: start children nodes
2009 *
2010 * parse the content of a RelaxNG start node.
2011 *
2012 * Returns 0 in case of success, -1 in case of error
2013 */
2014static int
2015xmlRelaxNGParseStart(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
2016 int ret = 0;
2017 xmlRelaxNGDefinePtr def = NULL;
2018
2019 while (nodes != NULL) {
2020 if (IS_RELAXNG(nodes, "empty")) {
2021 TODO
2022 xmlElemDump(stdout, nodes->doc, nodes);
2023 } else if (IS_RELAXNG(nodes, "notAllowed")) {
2024 TODO
2025 xmlElemDump(stdout, nodes->doc, nodes);
2026 } else {
2027 def = xmlRelaxNGParsePatterns(ctxt, nodes);
2028 ctxt->grammar->start = def;
2029 }
2030 nodes = nodes->next;
2031 }
2032 return(ret);
2033}
2034
2035/**
2036 * xmlRelaxNGParseGrammarContent:
2037 * @ctxt: a Relax-NG parser context
2038 * @nodes: grammar children nodes
2039 *
2040 * parse the content of a RelaxNG grammar node.
2041 *
2042 * Returns 0 in case of success, -1 in case of error
2043 */
2044static int
2045xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt
2046 ATTRIBUTE_UNUSED, xmlNodePtr nodes)
2047{
Daniel Veillard276be4a2003-01-24 01:03:34 +00002048 int ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002049
2050 if (nodes == NULL) {
2051 if (ctxt->error != NULL)
2052 ctxt->error(ctxt->userData,
2053 "grammar has no children\n");
2054 ctxt->nbErrors++;
2055 return(-1);
2056 }
2057 if (IS_RELAXNG(nodes, "start")) {
2058 if (nodes->children == NULL) {
2059 if (ctxt->error != NULL)
2060 ctxt->error(ctxt->userData,
2061 "grammar has no children\n");
2062 ctxt->nbErrors++;
2063 } else {
2064 xmlRelaxNGParseStart(ctxt, nodes->children);
2065 }
2066 nodes = nodes->next;
2067 } else {
2068 if (ctxt->error != NULL)
2069 ctxt->error(ctxt->userData,
2070 "grammar first child must be a <start>\n");
2071 ctxt->nbErrors++;
2072 return(-1);
2073 }
2074 while (nodes != NULL) {
2075 if (IS_RELAXNG(nodes, "define")) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00002076 ret = xmlRelaxNGParseDefine(ctxt, nodes);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002077 } else {
2078 if (ctxt->error != NULL)
2079 ctxt->error(ctxt->userData,
2080 "grammar allows onlys <define> child after <start>\n");
2081 ctxt->nbErrors++;
2082 ret = -1;
2083 }
2084 nodes = nodes->next;
2085 }
2086 return (ret);
2087}
2088
2089/**
2090 * xmlRelaxNGCheckReference:
2091 * @ref: the ref
2092 * @ctxt: a Relax-NG parser context
2093 * @name: the name associated to the defines
2094 *
2095 * Applies the 4.17. combine attribute rule for all the define
2096 * element of a given grammar using the same name.
2097 */
2098static void
2099xmlRelaxNGCheckReference(xmlRelaxNGDefinePtr ref,
2100 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
2101 xmlRelaxNGGrammarPtr grammar;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002102 xmlRelaxNGDefinePtr def, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002103
2104 grammar = ctxt->grammar;
2105 if (grammar == NULL) {
2106 if (ctxt->error != NULL)
2107 ctxt->error(ctxt->userData,
2108 "Internal error: no grammar in CheckReference %s\n",
2109 name);
2110 ctxt->nbErrors++;
2111 return;
2112 }
2113 if (ref->content != NULL) {
2114 if (ctxt->error != NULL)
2115 ctxt->error(ctxt->userData,
2116 "Internal error: reference has content in CheckReference %s\n",
2117 name);
2118 ctxt->nbErrors++;
2119 return;
2120 }
2121 if (grammar->defs != NULL) {
2122 def = xmlHashLookup(grammar->defs, name);
2123 if (def != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00002124 cur = ref;
2125 while (cur != NULL) {
2126 cur->content = def;
2127 cur = cur->nextHash;
2128 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002129 } else {
2130 TODO
2131 }
2132 }
2133 /*
2134 * TODO: make a closure and verify there is no loop !
2135 */
2136}
2137
2138/**
2139 * xmlRelaxNGCheckCombine:
2140 * @define: the define(s) list
2141 * @ctxt: a Relax-NG parser context
2142 * @name: the name associated to the defines
2143 *
2144 * Applies the 4.17. combine attribute rule for all the define
2145 * element of a given grammar using the same name.
2146 */
2147static void
2148xmlRelaxNGCheckCombine(xmlRelaxNGDefinePtr define,
2149 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
2150 xmlChar *combine;
2151 int choiceOrInterleave = -1;
2152 int missing = 0;
2153 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
2154
2155 if (define->nextHash == NULL)
2156 return;
2157 cur = define;
2158 while (cur != NULL) {
2159 combine = xmlGetProp(cur->node, BAD_CAST "combine");
2160 if (combine != NULL) {
2161 if (xmlStrEqual(combine, BAD_CAST "choice")) {
2162 if (choiceOrInterleave == -1)
2163 choiceOrInterleave = 1;
2164 else if (choiceOrInterleave == 0) {
2165 if (ctxt->error != NULL)
2166 ctxt->error(ctxt->userData,
2167 "Defines for %s use both 'choice' and 'interleave'\n",
2168 name);
2169 ctxt->nbErrors++;
2170 }
2171 } else if (xmlStrEqual(combine, BAD_CAST "choice")) {
2172 if (choiceOrInterleave == -1)
2173 choiceOrInterleave = 0;
2174 else if (choiceOrInterleave == 1) {
2175 if (ctxt->error != NULL)
2176 ctxt->error(ctxt->userData,
2177 "Defines for %s use both 'choice' and 'interleave'\n",
2178 name);
2179 ctxt->nbErrors++;
2180 }
2181 } else {
2182 if (ctxt->error != NULL)
2183 ctxt->error(ctxt->userData,
2184 "Defines for %s use unknown combine value '%s''\n",
2185 name, combine);
2186 ctxt->nbErrors++;
2187 }
2188 xmlFree(combine);
2189 } else {
2190 if (missing == 0)
2191 missing = 1;
2192 else {
2193 if (ctxt->error != NULL)
2194 ctxt->error(ctxt->userData,
2195 "Some defines for %s lacks the combine attribute\n",
2196 name);
2197 ctxt->nbErrors++;
2198 }
2199 }
2200
2201 cur = cur->nextHash;
2202 }
2203#ifdef DEBUG
2204 xmlGenericError(xmlGenericErrorContext,
2205 "xmlRelaxNGCheckCombine(): merging %s defines: %d\n",
2206 name, choiceOrInterleave);
2207#endif
2208 if (choiceOrInterleave == -1)
2209 choiceOrInterleave = 0;
2210 cur = xmlRelaxNGNewDefine(ctxt, define->node);
2211 if (cur == NULL)
2212 return;
2213 if (choiceOrInterleave == 0)
2214 cur->type = XML_RELAXNG_CHOICE;
2215 else
2216 cur->type = XML_RELAXNG_INTERLEAVE;
2217 tmp = define;
2218 last = NULL;
2219 while (tmp != NULL) {
2220 if (tmp->content != NULL) {
2221 if (tmp->content->next != NULL) {
2222 /*
2223 * we need first to create a wrapper.
2224 */
2225 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
2226 if (tmp2 == NULL)
2227 break;
2228 tmp2->type = XML_RELAXNG_GROUP;
2229 tmp2->content = tmp->content;
2230 } else {
2231 tmp2 = tmp->content;
2232 }
2233 if (last == NULL) {
2234 cur->content = tmp2;
2235 } else {
2236 last->next = tmp2;
2237 }
2238 last = tmp2;
2239 tmp->content = NULL;
2240 }
2241 tmp = tmp->nextHash;
2242 }
2243 define->content = cur;
2244}
2245
2246/**
2247 * xmlRelaxNGCombineStart:
2248 * @ctxt: a Relax-NG parser context
2249 * @grammar: the grammar
2250 *
2251 * Applies the 4.17. combine rule for all the start
2252 * element of a given grammar.
2253 */
2254static void
2255xmlRelaxNGCombineStart(xmlRelaxNGParserCtxtPtr ctxt,
2256 xmlRelaxNGGrammarPtr grammar) {
2257 xmlRelaxNGDefinePtr starts;
2258 xmlChar *combine;
2259 int choiceOrInterleave = -1;
2260 int missing = 0;
2261 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
2262
2263 starts = grammar->start;
2264 if (starts->nextHash == NULL)
2265 return;
2266 cur = starts;
2267 while (cur != NULL) {
2268 combine = xmlGetProp(cur->node, BAD_CAST "combine");
2269 if (combine != NULL) {
2270 if (xmlStrEqual(combine, BAD_CAST "choice")) {
2271 if (choiceOrInterleave == -1)
2272 choiceOrInterleave = 1;
2273 else if (choiceOrInterleave == 0) {
2274 if (ctxt->error != NULL)
2275 ctxt->error(ctxt->userData,
2276 "<start> use both 'choice' and 'interleave'\n");
2277 ctxt->nbErrors++;
2278 }
2279 } else if (xmlStrEqual(combine, BAD_CAST "choice")) {
2280 if (choiceOrInterleave == -1)
2281 choiceOrInterleave = 0;
2282 else if (choiceOrInterleave == 1) {
2283 if (ctxt->error != NULL)
2284 ctxt->error(ctxt->userData,
2285 "<start> use both 'choice' and 'interleave'\n");
2286 ctxt->nbErrors++;
2287 }
2288 } else {
2289 if (ctxt->error != NULL)
2290 ctxt->error(ctxt->userData,
2291 "<start> uses unknown combine value '%s''\n", combine);
2292 ctxt->nbErrors++;
2293 }
2294 xmlFree(combine);
2295 } else {
2296 if (missing == 0)
2297 missing = 1;
2298 else {
2299 if (ctxt->error != NULL)
2300 ctxt->error(ctxt->userData,
2301 "Some <start> elements lacks the combine attribute\n");
2302 ctxt->nbErrors++;
2303 }
2304 }
2305
2306 cur = cur->nextHash;
2307 }
2308#ifdef DEBUG
2309 xmlGenericError(xmlGenericErrorContext,
2310 "xmlRelaxNGCombineStart(): merging <start>: %d\n",
2311 choiceOrInterleave);
2312#endif
2313 if (choiceOrInterleave == -1)
2314 choiceOrInterleave = 0;
2315 cur = xmlRelaxNGNewDefine(ctxt, starts->node);
2316 if (cur == NULL)
2317 return;
2318 if (choiceOrInterleave == 0)
2319 cur->type = XML_RELAXNG_CHOICE;
2320 else
2321 cur->type = XML_RELAXNG_INTERLEAVE;
2322 tmp = starts;
2323 last = NULL;
2324 while (tmp != NULL) {
2325 if (tmp->content != NULL) {
2326 if (tmp->content->next != NULL) {
2327 /*
2328 * we need first to create a wrapper.
2329 */
2330 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
2331 if (tmp2 == NULL)
2332 break;
2333 tmp2->type = XML_RELAXNG_GROUP;
2334 tmp2->content = tmp->content;
2335 } else {
2336 tmp2 = tmp->content;
2337 }
2338 if (last == NULL) {
2339 cur->content = tmp2;
2340 } else {
2341 last->next = tmp2;
2342 }
2343 last = tmp2;
2344 tmp->content = NULL;
2345 }
2346 tmp = tmp->nextHash;
2347 }
2348 starts->content = cur;
2349}
2350
2351/**
2352 * xmlRelaxNGParseGrammar:
2353 * @ctxt: a Relax-NG parser context
2354 * @nodes: grammar children nodes
2355 *
2356 * parse a Relax-NG <grammar> node
2357 *
2358 * Returns the internal xmlRelaxNGGrammarPtr built or
2359 * NULL in case of error
2360 */
2361static xmlRelaxNGGrammarPtr
2362xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
2363 xmlRelaxNGGrammarPtr ret, tmp, old;
2364
Daniel Veillard6eadf632003-01-23 18:29:16 +00002365 ret = xmlRelaxNGNewGrammar(ctxt);
2366 if (ret == NULL)
2367 return(NULL);
2368
2369 /*
2370 * Link the new grammar in the tree
2371 */
2372 ret->parent = ctxt->grammar;
2373 if (ctxt->grammar != NULL) {
2374 tmp = ctxt->grammar->children;
2375 if (tmp == NULL) {
2376 ctxt->grammar->children = ret;
2377 } else {
2378 while (tmp->next != NULL)
2379 tmp = tmp->next;
2380 tmp->next = ret;
2381 }
2382 }
2383
2384 old = ctxt->grammar;
2385 ctxt->grammar = ret;
2386 xmlRelaxNGParseGrammarContent(ctxt, nodes);
2387 ctxt->grammar = ret;
2388
2389 /*
2390 * Apply 4.17 mergingd rules to defines and starts
2391 */
2392 xmlRelaxNGCombineStart(ctxt, ret);
2393 if (ret->defs != NULL) {
2394 xmlHashScan(ret->defs, (xmlHashScanner) xmlRelaxNGCheckCombine,
2395 ctxt);
2396 }
2397
2398 /*
2399 * link together defines and refs in this grammar
2400 */
2401 if (ret->refs != NULL) {
2402 xmlHashScan(ret->refs, (xmlHashScanner) xmlRelaxNGCheckReference,
2403 ctxt);
2404 }
2405 ctxt->grammar = old;
2406 return(ret);
2407}
2408
2409/**
2410 * xmlRelaxNGParseDocument:
2411 * @ctxt: a Relax-NG parser context
2412 * @node: the root node of the RelaxNG schema
2413 *
2414 * parse a Relax-NG definition resource and build an internal
2415 * xmlRelaxNG struture which can be used to validate instances.
2416 *
2417 * Returns the internal XML RelaxNG structure built or
2418 * NULL in case of error
2419 */
2420static xmlRelaxNGPtr
2421xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2422 xmlRelaxNGPtr schema = NULL;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002423 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002424
2425 if ((ctxt == NULL) || (node == NULL))
2426 return (NULL);
2427
2428 schema = xmlRelaxNGNewRelaxNG(ctxt);
2429 if (schema == NULL)
2430 return(NULL);
2431
Daniel Veillard276be4a2003-01-24 01:03:34 +00002432 olddefine = ctxt->define;
2433 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002434 if (IS_RELAXNG(node, "grammar")) {
2435 schema->topgrammar = xmlRelaxNGParseGrammar(ctxt, node->children);
2436 } else {
2437 schema->topgrammar = xmlRelaxNGNewGrammar(ctxt);
2438 if (schema->topgrammar == NULL) {
2439 return(schema);
2440 }
2441 schema->topgrammar->parent = NULL;
2442 ctxt->grammar = schema->topgrammar;
2443 xmlRelaxNGParseStart(ctxt, node);
2444 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002445 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002446
2447#ifdef DEBUG
2448 if (schema == NULL)
2449 xmlGenericError(xmlGenericErrorContext,
2450 "xmlRelaxNGParseDocument() failed\n");
2451#endif
2452
2453 return (schema);
2454}
2455
2456/************************************************************************
2457 * *
2458 * Reading RelaxNGs *
2459 * *
2460 ************************************************************************/
2461
2462/**
2463 * xmlRelaxNGNewParserCtxt:
2464 * @URL: the location of the schema
2465 *
2466 * Create an XML RelaxNGs parse context for that file/resource expected
2467 * to contain an XML RelaxNGs file.
2468 *
2469 * Returns the parser context or NULL in case of error
2470 */
2471xmlRelaxNGParserCtxtPtr
2472xmlRelaxNGNewParserCtxt(const char *URL) {
2473 xmlRelaxNGParserCtxtPtr ret;
2474
2475 if (URL == NULL)
2476 return(NULL);
2477
2478 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
2479 if (ret == NULL) {
2480 xmlGenericError(xmlGenericErrorContext,
2481 "Failed to allocate new schama parser context for %s\n", URL);
2482 return (NULL);
2483 }
2484 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
2485 ret->URL = xmlStrdup((const xmlChar *)URL);
2486 return (ret);
2487}
2488
2489/**
2490 * xmlRelaxNGNewMemParserCtxt:
2491 * @buffer: a pointer to a char array containing the schemas
2492 * @size: the size of the array
2493 *
2494 * Create an XML RelaxNGs parse context for that memory buffer expected
2495 * to contain an XML RelaxNGs file.
2496 *
2497 * Returns the parser context or NULL in case of error
2498 */
2499xmlRelaxNGParserCtxtPtr
2500xmlRelaxNGNewMemParserCtxt(const char *buffer, int size) {
2501 xmlRelaxNGParserCtxtPtr ret;
2502
2503 if ((buffer == NULL) || (size <= 0))
2504 return(NULL);
2505
2506 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
2507 if (ret == NULL) {
2508 xmlGenericError(xmlGenericErrorContext,
2509 "Failed to allocate new schama parser context\n");
2510 return (NULL);
2511 }
2512 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
2513 ret->buffer = buffer;
2514 ret->size = size;
2515 return (ret);
2516}
2517
2518/**
2519 * xmlRelaxNGFreeParserCtxt:
2520 * @ctxt: the schema parser context
2521 *
2522 * Free the resources associated to the schema parser context
2523 */
2524void
2525xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxtPtr ctxt) {
2526 if (ctxt == NULL)
2527 return;
2528 if (ctxt->URL != NULL)
2529 xmlFree(ctxt->URL);
2530 if (ctxt->doc != NULL)
2531 xmlFreeDoc(ctxt->doc);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002532 if (ctxt->interleaves != NULL)
2533 xmlHashFree(ctxt->interleaves, NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002534 xmlFree(ctxt);
2535}
2536
2537
2538/**
2539 * xmlRelaxNGParse:
2540 * @ctxt: a Relax-NG validation context
2541 *
2542 * parse a schema definition resource and build an internal
2543 * XML Shema struture which can be used to validate instances.
2544 * *WARNING* this interface is highly subject to change
2545 *
2546 * Returns the internal XML RelaxNG structure built from the resource or
2547 * NULL in case of error
2548 */
2549xmlRelaxNGPtr
2550xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt)
2551{
2552 xmlRelaxNGPtr ret = NULL;
2553 xmlDocPtr doc;
2554 xmlNodePtr root, cur, delete;
2555
2556 xmlRelaxNGInitTypes();
2557
2558 if (ctxt == NULL)
2559 return (NULL);
2560
2561 /*
2562 * First step is to parse the input document into an DOM/Infoset
2563 */
2564 if (ctxt->URL != NULL) {
2565 doc = xmlParseFile((const char *) ctxt->URL);
2566 if (doc == NULL) {
2567 if (ctxt->error != NULL)
2568 ctxt->error(ctxt->userData,
2569 "xmlRelaxNGParse: could not load %s\n", ctxt->URL);
2570 ctxt->nbErrors++;
2571 return (NULL);
2572 }
2573 } else if (ctxt->buffer != NULL) {
2574 doc = xmlParseMemory(ctxt->buffer, ctxt->size);
2575 if (doc == NULL) {
2576 if (ctxt->error != NULL)
2577 ctxt->error(ctxt->userData,
2578 "xmlRelaxNGParse: could not parse schemas\n");
2579 ctxt->nbErrors++;
2580 return (NULL);
2581 }
2582 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
2583 ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
2584 } else {
2585 if (ctxt->error != NULL)
2586 ctxt->error(ctxt->userData,
2587 "xmlRelaxNGParse: nothing to parse\n");
2588 ctxt->nbErrors++;
2589 return (NULL);
2590 }
2591 ctxt->doc = doc;
2592
2593 /*
2594 * Then extract the root and RelaxNG parse it
2595 */
2596 root = xmlDocGetRootElement(doc);
2597 if (root == NULL) {
2598 if (ctxt->error != NULL)
2599 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
2600 ctxt->URL);
2601 ctxt->nbErrors++;
2602 return (NULL);
2603 }
2604
2605 /*
2606 * Remove all the blank text nodes
2607 */
2608 delete = NULL;
2609 cur = root;
2610 while (cur != NULL) {
2611 if (delete != NULL) {
2612 xmlUnlinkNode(delete);
2613 xmlFreeNode(delete);
2614 delete = NULL;
2615 }
2616 if (cur->type == XML_ELEMENT_NODE) {
2617 /*
2618 * Simplification 4.1. Annotations
2619 */
2620 if ((cur->ns == NULL) ||
2621 (!xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
2622 delete = cur;
2623 goto skip_children;
2624 } else {
2625 if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
2626 TODO
2627 } else if (xmlStrEqual(cur->name, BAD_CAST "include")) {
2628 TODO
2629 } else if ((xmlStrEqual(cur->name, BAD_CAST "element")) ||
2630 (xmlStrEqual(cur->name, BAD_CAST "attribute"))) {
2631 xmlChar *name;
2632 xmlNodePtr text = NULL;
2633
2634 /*
2635 * Simplification 4.8. name attribute of element
2636 * and attribute elements
2637 */
2638 name = xmlGetProp(cur, BAD_CAST "name");
2639 if (name != NULL) {
2640 if (cur->children == NULL) {
2641 text = xmlNewChild(cur, cur->ns, BAD_CAST "name",
2642 name);
2643 } else {
2644 xmlNodePtr node;
2645 node = xmlNewNode(cur->ns, BAD_CAST "name");
2646 if (node != NULL) {
2647 xmlAddPrevSibling(cur->children, node);
2648 text = xmlNewText(name);
2649 xmlAddChild(node, text);
2650 text = node;
2651 }
2652 }
2653 xmlUnsetProp(cur, BAD_CAST "name");
2654 xmlFree(name);
2655 }
2656 if (xmlStrEqual(cur->name, BAD_CAST "attribute")) {
2657 if (text == NULL) {
2658 text = cur->children;
2659 while (text != NULL) {
2660 if ((text->type == XML_ELEMENT_NODE) &&
2661 (xmlStrEqual(text->name, BAD_CAST "name")))
2662 break;
2663 text = text->next;
2664 }
2665 }
2666 if (text == NULL) {
2667 if (ctxt->error != NULL)
2668 ctxt->error(ctxt->userData,
2669 "xmlRelaxNGParse: attribute without name\n");
2670 ctxt->nbErrors++;
2671 } else {
2672 xmlSetProp(text, BAD_CAST "ns", BAD_CAST "");
2673 }
2674 }
2675 } else if ((xmlStrEqual(cur->name, BAD_CAST "name")) ||
2676 (xmlStrEqual(cur->name, BAD_CAST "nsName")) ||
2677 (xmlStrEqual(cur->name, BAD_CAST "value"))) {
2678 /*
2679 * Simplification 4.8. name attribute of element
2680 * and attribute elements
2681 */
2682 if (xmlHasProp(cur, BAD_CAST "ns") == NULL) {
2683 xmlNodePtr node;
2684 xmlChar *ns = NULL;
2685
2686 node = cur->parent;
2687 while ((node != NULL) &&
2688 (node->type == XML_ELEMENT_NODE)) {
2689 ns = xmlGetProp(node, BAD_CAST "ns");
2690 if (ns != NULL) {
2691 break;
2692 }
2693 node = node->parent;
2694 }
2695 if (ns == NULL) {
2696 xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
2697 } else {
2698 xmlSetProp(cur, BAD_CAST "ns", ns);
2699 xmlFree(ns);
2700 }
2701 }
2702 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
2703 xmlChar *name, *local, *prefix;
2704
2705 /*
2706 * Simplification: 4.10. QNames
2707 */
2708 name = xmlNodeGetContent(cur);
2709 if (name != NULL) {
2710 local = xmlSplitQName2(name, &prefix);
2711 if (local != NULL) {
2712 xmlNsPtr ns;
2713
2714 ns = xmlSearchNs(cur->doc, cur, prefix);
2715 if (ns == NULL) {
2716 if (ctxt->error != NULL)
2717 ctxt->error(ctxt->userData,
2718 "xmlRelaxNGParse: no namespace for prefix %s\n", prefix);
2719 ctxt->nbErrors++;
2720 } else {
2721 xmlSetProp(cur, BAD_CAST "ns", ns->href);
2722 xmlNodeSetContent(cur, local);
2723 }
2724 xmlFree(local);
2725 xmlFree(prefix);
2726 }
2727 xmlFree(name);
2728 }
2729 }
2730 }
2731 }
2732 }
2733 /*
2734 * Simplification 4.2 whitespaces
2735 */
2736 else if (cur->type == XML_TEXT_NODE) {
2737 if (IS_BLANK_NODE(cur)) {
2738 if (cur->parent->type == XML_ELEMENT_NODE) {
2739 if ((!xmlStrEqual(cur->parent->name, BAD_CAST "value")) &&
2740 (!xmlStrEqual(cur->parent->name, BAD_CAST "param")))
2741 delete = cur;
2742 } else {
2743 delete = cur;
2744 goto skip_children;
2745 }
2746 }
2747 } else if (cur->type != XML_CDATA_SECTION_NODE) {
2748 delete = cur;
2749 goto skip_children;
2750 }
2751
2752 /*
2753 * Skip to next node
2754 */
2755 if (cur->children != NULL) {
2756 if ((cur->children->type != XML_ENTITY_DECL) &&
2757 (cur->children->type != XML_ENTITY_REF_NODE) &&
2758 (cur->children->type != XML_ENTITY_NODE)) {
2759 cur = cur->children;
2760 continue;
2761 }
2762 }
2763skip_children:
2764 if (cur->next != NULL) {
2765 cur = cur->next;
2766 continue;
2767 }
2768
2769 do {
2770 cur = cur->parent;
2771 if (cur == NULL)
2772 break;
2773 if (cur == root) {
2774 cur = NULL;
2775 break;
2776 }
2777 if (cur->next != NULL) {
2778 cur = cur->next;
2779 break;
2780 }
2781 } while (cur != NULL);
2782 }
2783 if (delete != NULL) {
2784 xmlUnlinkNode(delete);
2785 xmlFreeNode(delete);
2786 delete = NULL;
2787 }
2788
2789 /*
2790 * Then do the parsing for good
2791 */
2792 root = xmlDocGetRootElement(doc);
2793 if (root == NULL) {
2794 if (ctxt->error != NULL)
2795 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
2796 ctxt->URL);
2797 ctxt->nbErrors++;
2798 return (NULL);
2799 }
2800 ret = xmlRelaxNGParseDocument(ctxt, root);
2801 if (ret == NULL)
2802 return(NULL);
2803
2804 /*
2805 * Check the ref/defines links
2806 */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002807 /*
2808 * try to preprocess interleaves
2809 */
2810 if (ctxt->interleaves != NULL) {
2811 xmlHashScan(ctxt->interleaves,
2812 (xmlHashScanner)xmlRelaxNGComputeInterleaves, ctxt);
2813 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002814
2815 /*
2816 * if there was a parsing error return NULL
2817 */
2818 if (ctxt->nbErrors > 0) {
2819 xmlRelaxNGFree(ret);
2820 return(NULL);
2821 }
2822
2823 /*
2824 * Transfer the pointer for cleanup at the schema level.
2825 */
2826 ret->doc = doc;
2827 ctxt->doc = NULL;
2828
2829 return (ret);
2830}
2831
2832/**
2833 * xmlRelaxNGSetParserErrors:
2834 * @ctxt: a Relax-NG validation context
2835 * @err: the error callback
2836 * @warn: the warning callback
2837 * @ctx: contextual data for the callbacks
2838 *
2839 * Set the callback functions used to handle errors for a validation context
2840 */
2841void
2842xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
2843 xmlRelaxNGValidityErrorFunc err,
2844 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
2845 if (ctxt == NULL)
2846 return;
2847 ctxt->error = err;
2848 ctxt->warning = warn;
2849 ctxt->userData = ctx;
2850}
2851/************************************************************************
2852 * *
2853 * Dump back a compiled form *
2854 * *
2855 ************************************************************************/
2856static void xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define);
2857
2858/**
2859 * xmlRelaxNGDumpDefines:
2860 * @output: the file output
2861 * @defines: a list of define structures
2862 *
2863 * Dump a RelaxNG structure back
2864 */
2865static void
2866xmlRelaxNGDumpDefines(FILE * output, xmlRelaxNGDefinePtr defines) {
2867 while (defines != NULL) {
2868 xmlRelaxNGDumpDefine(output, defines);
2869 defines = defines->next;
2870 }
2871}
2872
2873/**
2874 * xmlRelaxNGDumpDefine:
2875 * @output: the file output
2876 * @define: a define structure
2877 *
2878 * Dump a RelaxNG structure back
2879 */
2880static void
2881xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define) {
2882 if (define == NULL)
2883 return;
2884 switch(define->type) {
2885 case XML_RELAXNG_EMPTY:
2886 fprintf(output, "<empty/>\n");
2887 break;
2888 case XML_RELAXNG_NOT_ALLOWED:
2889 fprintf(output, "<notAllowed/>\n");
2890 break;
2891 case XML_RELAXNG_TEXT:
2892 fprintf(output, "<text/>\n");
2893 break;
2894 case XML_RELAXNG_ELEMENT:
2895 fprintf(output, "<element>\n");
2896 if (define->name != NULL) {
2897 fprintf(output, "<name");
2898 if (define->ns != NULL)
2899 fprintf(output, " ns=\"%s\"", define->ns);
2900 fprintf(output, ">%s</name>\n", define->name);
2901 }
2902 xmlRelaxNGDumpDefines(output, define->attrs);
2903 xmlRelaxNGDumpDefines(output, define->content);
2904 fprintf(output, "</element>\n");
2905 break;
2906 case XML_RELAXNG_LIST:
2907 fprintf(output, "<list>\n");
2908 xmlRelaxNGDumpDefines(output, define->content);
2909 fprintf(output, "</list>\n");
2910 break;
2911 case XML_RELAXNG_ONEORMORE:
2912 fprintf(output, "<oneOrMore>\n");
2913 xmlRelaxNGDumpDefines(output, define->content);
2914 fprintf(output, "</oneOrMore>\n");
2915 break;
2916 case XML_RELAXNG_ZEROORMORE:
2917 fprintf(output, "<zeroOrMore>\n");
2918 xmlRelaxNGDumpDefines(output, define->content);
2919 fprintf(output, "</zeroOrMore>\n");
2920 break;
2921 case XML_RELAXNG_CHOICE:
2922 fprintf(output, "<choice>\n");
2923 xmlRelaxNGDumpDefines(output, define->content);
2924 fprintf(output, "</choice>\n");
2925 break;
2926 case XML_RELAXNG_GROUP:
2927 fprintf(output, "<group>\n");
2928 xmlRelaxNGDumpDefines(output, define->content);
2929 fprintf(output, "</group>\n");
2930 break;
2931 case XML_RELAXNG_INTERLEAVE:
2932 fprintf(output, "<interleave>\n");
2933 xmlRelaxNGDumpDefines(output, define->content);
2934 fprintf(output, "</interleave>\n");
2935 break;
2936 case XML_RELAXNG_OPTIONAL:
2937 fprintf(output, "<optional>\n");
2938 xmlRelaxNGDumpDefines(output, define->content);
2939 fprintf(output, "</optional>\n");
2940 break;
2941 case XML_RELAXNG_ATTRIBUTE:
2942 fprintf(output, "<attribute>\n");
2943 xmlRelaxNGDumpDefines(output, define->content);
2944 fprintf(output, "</attribute>\n");
2945 break;
2946 case XML_RELAXNG_DEF:
2947 fprintf(output, "<define");
2948 if (define->name != NULL)
2949 fprintf(output, " name=\"%s\"", define->name);
2950 fprintf(output, ">\n");
2951 xmlRelaxNGDumpDefines(output, define->content);
2952 fprintf(output, "</define>\n");
2953 break;
2954 case XML_RELAXNG_REF:
2955 fprintf(output, "<ref");
2956 if (define->name != NULL)
2957 fprintf(output, " name=\"%s\"", define->name);
2958 fprintf(output, ">\n");
2959 xmlRelaxNGDumpDefines(output, define->content);
2960 fprintf(output, "</ref>\n");
2961 break;
2962 case XML_RELAXNG_DATATYPE:
2963 case XML_RELAXNG_VALUE:
2964 TODO
2965 break;
2966 }
2967}
2968
2969/**
2970 * xmlRelaxNGDumpGrammar:
2971 * @output: the file output
2972 * @grammar: a grammar structure
2973 * @top: is this a top grammar
2974 *
2975 * Dump a RelaxNG structure back
2976 */
2977static void
2978xmlRelaxNGDumpGrammar(FILE * output, xmlRelaxNGGrammarPtr grammar, int top)
2979{
2980 if (grammar == NULL)
2981 return;
2982
2983 fprintf(output, "<grammar");
2984 if (top)
2985 fprintf(output,
2986 " xmlns=\"http://relaxng.org/ns/structure/1.0\"");
2987 switch(grammar->combine) {
2988 case XML_RELAXNG_COMBINE_UNDEFINED:
2989 break;
2990 case XML_RELAXNG_COMBINE_CHOICE:
2991 fprintf(output, " combine=\"choice\"");
2992 break;
2993 case XML_RELAXNG_COMBINE_INTERLEAVE:
2994 fprintf(output, " combine=\"interleave\"");
2995 break;
2996 default:
2997 fprintf(output, " <!-- invalid combine value -->");
2998 }
2999 fprintf(output, ">\n");
3000 if (grammar->start == NULL) {
3001 fprintf(output, " <!-- grammar had no start -->");
3002 } else {
3003 fprintf(output, "<start>\n");
3004 xmlRelaxNGDumpDefine(output, grammar->start);
3005 fprintf(output, "</start>\n");
3006 }
3007 /* TODO ? Dump the defines ? */
3008 fprintf(output, "</grammar>\n");
3009}
3010
3011/**
3012 * xmlRelaxNGDump:
3013 * @output: the file output
3014 * @schema: a schema structure
3015 *
3016 * Dump a RelaxNG structure back
3017 */
3018void
3019xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema)
3020{
3021 if (schema == NULL) {
3022 fprintf(output, "RelaxNG empty or failed to compile\n");
3023 return;
3024 }
3025 fprintf(output, "RelaxNG: ");
3026 if (schema->doc == NULL) {
3027 fprintf(output, "no document\n");
3028 } else if (schema->doc->URL != NULL) {
3029 fprintf(output, "%s\n", schema->doc->URL);
3030 } else {
3031 fprintf(output, "\n");
3032 }
3033 if (schema->topgrammar == NULL) {
3034 fprintf(output, "RelaxNG has no top grammar\n");
3035 return;
3036 }
3037 xmlRelaxNGDumpGrammar(output, schema->topgrammar, 1);
3038}
3039
3040/************************************************************************
3041 * *
3042 * Validation implementation *
3043 * *
3044 ************************************************************************/
3045static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
3046 xmlRelaxNGDefinePtr define);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00003047static int xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
3048 xmlRelaxNGDefinePtr define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003049
3050/**
3051 * xmlRelaxNGSkipIgnored:
3052 * @ctxt: a schema validation context
3053 * @node: the top node.
3054 *
3055 * Skip ignorable nodes in that context
3056 *
3057 * Returns the new sibling or NULL in case of error.
3058 */
3059static xmlNodePtr
3060xmlRelaxNGSkipIgnored(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
3061 xmlNodePtr node) {
3062 /*
3063 * TODO complete and handle entities
3064 */
3065 while ((node != NULL) &&
3066 ((node->type == XML_COMMENT_NODE) ||
3067 ((node->type == XML_TEXT_NODE) &&
3068 (IS_BLANK_NODE(node))))) {
3069 node = node->next;
3070 }
3071 return(node);
3072}
3073
3074/**
Daniel Veillardedc91922003-01-26 00:52:04 +00003075 * xmlRelaxNGNormalize:
3076 * @ctxt: a schema validation context
3077 * @str: the string to normalize
3078 *
3079 * Implements the normalizeWhiteSpace( s ) function from
3080 * section 6.2.9 of the spec
3081 *
3082 * Returns the new string or NULL in case of error.
3083 */
3084static xmlChar *
3085xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *str) {
3086 xmlChar *ret, *p;
3087 const xmlChar *tmp;
3088 int len;
3089
3090 if (str == NULL)
3091 return(NULL);
3092 tmp = str;
3093 while (*tmp != 0) tmp++;
3094 len = tmp - str;
3095
3096 ret = (xmlChar *) xmlMalloc((len + 1) * sizeof(xmlChar));
3097 if (ret == NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00003098 if (ctxt != NULL) {
3099 VALID_CTXT();
3100 VALID_ERROR("xmlRelaxNGNormalize: out of memory\n");
3101 } else {
3102 xmlGenericError(xmlGenericErrorContext,
3103 "xmlRelaxNGNormalize: out of memory\n");
3104 }
Daniel Veillardedc91922003-01-26 00:52:04 +00003105 return(NULL);
3106 }
3107 p = ret;
3108 while (IS_BLANK(*str)) str++;
3109 while (*str != 0) {
3110 if (IS_BLANK(*str)) {
3111 while (IS_BLANK(*str)) str++;
3112 if (*str == 0)
3113 break;
3114 *p++ = ' ';
3115 } else
3116 *p++ = *str++;
3117 }
3118 *p = 0;
3119 return(ret);
3120}
3121
3122/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00003123 * xmlRelaxNGValidateDatatype:
3124 * @ctxt: a Relax-NG validation context
3125 * @value: the string value
3126 * @type: the datatype definition
3127 *
3128 * Validate the given value against the dataype
3129 *
3130 * Returns 0 if the validation succeeded or an error code.
3131 */
3132static int
3133xmlRelaxNGValidateDatatype(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *value,
3134 xmlRelaxNGDefinePtr define) {
3135 int ret;
3136 xmlRelaxNGTypeLibraryPtr lib;
3137
3138 if ((define == NULL) || (define->data == NULL)) {
3139 return(-1);
3140 }
3141 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
3142 if (lib->check != NULL)
3143 ret = lib->check(lib->data, define->name, value);
3144 else
3145 ret = -1;
3146 if (ret < 0) {
3147 VALID_CTXT();
3148 VALID_ERROR("Internal: failed to validate type %s\n", define->name);
3149 return(-1);
3150 } else if (ret == 1) {
3151 ret = 0;
3152 } else {
3153 VALID_CTXT();
3154 VALID_ERROR("Type %s doesn't allow value %s\n", define->name, value);
3155 return(-1);
3156 ret = -1;
3157 }
3158 return(ret);
3159}
3160
3161/**
Daniel Veillardc6e997c2003-01-27 12:35:42 +00003162 * xmlRelaxNGNextValue:
3163 * @ctxt: a Relax-NG validation context
3164 *
3165 * Skip to the next value when validating within a list
3166 *
3167 * Returns 0 if the operation succeeded or an error code.
3168 */
3169static int
3170xmlRelaxNGNextValue(xmlRelaxNGValidCtxtPtr ctxt) {
3171 xmlChar *cur;
3172
3173 cur = ctxt->state->value;
3174 if ((cur == NULL) || (ctxt->state->endvalue == NULL)) {
3175 ctxt->state->value = NULL;
3176 return(0);
3177 }
3178 while (*cur != 0) cur++;
3179 while ((cur != ctxt->state->endvalue) && (*cur == 0)) cur++;
3180 if (cur == ctxt->state->endvalue)
3181 ctxt->state->value = NULL;
3182 else
3183 ctxt->state->value = cur;
3184 return(0);
3185}
3186
3187/**
3188 * xmlRelaxNGValidateValueList:
3189 * @ctxt: a Relax-NG validation context
3190 * @defines: the list of definitions to verify
3191 *
3192 * Validate the given set of definitions for the current value
3193 *
3194 * Returns 0 if the validation succeeded or an error code.
3195 */
3196static int
3197xmlRelaxNGValidateValueList(xmlRelaxNGValidCtxtPtr ctxt,
3198 xmlRelaxNGDefinePtr defines) {
3199 int ret = 0;
3200
3201 while (defines != NULL) {
3202 ret = xmlRelaxNGValidateValue(ctxt, defines);
3203 if (ret != 0)
3204 break;
3205 defines = defines->next;
3206 }
3207 return(ret);
3208}
3209
3210/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00003211 * xmlRelaxNGValidateValue:
3212 * @ctxt: a Relax-NG validation context
3213 * @define: the definition to verify
3214 *
3215 * Validate the given definition for the current value
3216 *
3217 * Returns 0 if the validation succeeded or an error code.
3218 */
3219static int
3220xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
3221 xmlRelaxNGDefinePtr define) {
Daniel Veillardedc91922003-01-26 00:52:04 +00003222 int ret = 0, oldflags;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003223 xmlChar *value;
3224
3225 value = ctxt->state->value;
3226 switch (define->type) {
3227 case XML_RELAXNG_EMPTY:
3228 if ((value != NULL) && (value[0] != '0'))
3229 ret = -1;
3230 break;
3231 case XML_RELAXNG_TEXT:
3232 break;
Daniel Veillardedc91922003-01-26 00:52:04 +00003233 case XML_RELAXNG_VALUE: {
3234 if (!xmlStrEqual(value, define->value)) {
3235 if (define->name != NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00003236 xmlRelaxNGTypeLibraryPtr lib;
3237
3238 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
3239 if ((lib != NULL) && (lib->comp != NULL))
3240 ret = lib->comp(lib->data, define->name, value,
3241 define->value);
3242 else
3243 ret = -1;
3244 if (ret < 0) {
3245 VALID_CTXT();
3246 VALID_ERROR("Internal: failed to compare type %s\n",
3247 define->name);
3248 return(-1);
3249 } else if (ret == 1) {
3250 ret = 0;
3251 } else {
3252 ret = -1;
3253 }
Daniel Veillardedc91922003-01-26 00:52:04 +00003254 } else {
3255 xmlChar *nval, *nvalue;
3256
3257 /*
3258 * TODO: trivial optimizations are possible by
3259 * computing at compile-time
3260 */
3261 nval = xmlRelaxNGNormalize(ctxt, define->value);
3262 nvalue = xmlRelaxNGNormalize(ctxt, value);
3263
Daniel Veillardea3f3982003-01-26 19:45:18 +00003264 if ((nval == NULL) || (nvalue == NULL) ||
3265 (!xmlStrEqual(nval, nvalue)))
Daniel Veillardedc91922003-01-26 00:52:04 +00003266 ret = -1;
3267 if (nval != NULL)
3268 xmlFree(nval);
3269 if (nvalue != NULL)
3270 xmlFree(nvalue);
3271 }
3272 }
3273 break;
3274 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00003275 case XML_RELAXNG_DATATYPE: {
3276 ret = xmlRelaxNGValidateDatatype(ctxt, value, define);
3277 if (ret == 0)
3278 xmlRelaxNGNextValue(ctxt);
3279
3280 break;
3281 }
3282 case XML_RELAXNG_CHOICE: {
3283 xmlRelaxNGDefinePtr list = define->content;
3284 xmlChar *oldvalue;
3285
3286 oldflags = ctxt->flags;
3287 ctxt->flags |= FLAGS_IGNORABLE;
3288
3289 oldvalue = ctxt->state->value;
3290 while (list != NULL) {
3291 ret = xmlRelaxNGValidateValue(ctxt, list);
3292 if (ret == 0) {
3293 break;
3294 }
3295 ctxt->state->value = oldvalue;
3296 list = list->next;
3297 }
3298 ctxt->flags = oldflags;
3299 break;
3300 }
3301 case XML_RELAXNG_LIST: {
3302 xmlRelaxNGDefinePtr list = define->content;
3303 xmlChar *oldvalue, *oldend, *val, *cur;
3304
3305 oldvalue = ctxt->state->value;
3306 oldend = ctxt->state->endvalue;
3307
3308 val = xmlStrdup(oldvalue);
3309 if (val == NULL) {
3310 VALID_CTXT();
3311 VALID_ERROR("Internal: no state\n");
3312 return(-1);
3313 }
3314 cur = val;
3315 while (*cur != 0) {
3316 if (IS_BLANK(*cur))
3317 *cur = 0;
3318 cur++;
3319 }
3320 ctxt->state->endvalue = cur;
3321 cur = val;
3322 while ((*cur == 0) && (cur != ctxt->state->endvalue)) cur++;
3323
3324 ctxt->state->value = cur;
3325
3326 while (list != NULL) {
3327 ret = xmlRelaxNGValidateValue(ctxt, list);
3328 if (ret != 0) {
3329 break;
3330 }
3331 list = list->next;
3332 }
3333 if ((ret == 0) && (ctxt->state->value != NULL) &&
3334 (ctxt->state->value != ctxt->state->endvalue)) {
3335 VALID_CTXT();
3336 VALID_ERROR("Extra data in list: %s\n", ctxt->state->value);
3337 ret = -1;
3338 }
3339 xmlFree(val);
3340 ctxt->state->value = oldvalue;
3341 ctxt->state->endvalue = oldend;
3342 break;
3343 }
3344 case XML_RELAXNG_ONEORMORE:
3345 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
3346 if (ret != 0) {
3347 break;
3348 }
3349 /* no break on purpose */
3350 case XML_RELAXNG_ZEROORMORE: {
3351 xmlChar *cur, *temp;
3352
3353 oldflags = ctxt->flags;
3354 ctxt->flags |= FLAGS_IGNORABLE;
3355 cur = ctxt->state->value;
3356 temp = NULL;
3357 while ((cur != NULL) && (cur != ctxt->state->endvalue) &&
3358 (temp != cur)) {
3359 temp = cur;
3360 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
3361 if (ret != 0) {
3362 ctxt->state->value = temp;
3363 ret = 0;
3364 break;
3365 }
3366 cur = ctxt->state->value;
3367 }
3368 ctxt->flags = oldflags;
3369 break;
3370 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003371 default:
3372 TODO
3373 ret = -1;
3374 }
3375 return(ret);
3376}
3377
3378/**
3379 * xmlRelaxNGValidateValueContent:
3380 * @ctxt: a Relax-NG validation context
3381 * @defines: the list of definitions to verify
3382 *
3383 * Validate the given definitions for the current value
3384 *
3385 * Returns 0 if the validation succeeded or an error code.
3386 */
3387static int
3388xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt,
3389 xmlRelaxNGDefinePtr defines) {
3390 int ret = 0;
3391
3392 while (defines != NULL) {
3393 ret = xmlRelaxNGValidateValue(ctxt, defines);
3394 if (ret != 0)
3395 break;
3396 defines = defines->next;
3397 }
3398 return(ret);
3399}
3400
3401/**
3402 * xmlRelaxNGValidateAttribute:
3403 * @ctxt: a Relax-NG validation context
3404 * @define: the definition to verify
3405 *
3406 * Validate the given attribute definition for that node
3407 *
3408 * Returns 0 if the validation succeeded or an error code.
3409 */
3410static int
3411xmlRelaxNGValidateAttribute(xmlRelaxNGValidCtxtPtr ctxt,
3412 xmlRelaxNGDefinePtr define) {
3413 int ret = 0, i;
3414 xmlChar *value, *oldvalue;
3415 xmlAttrPtr prop = NULL, tmp;
3416
3417 if (define->name != NULL) {
3418 for (i = 0;i < ctxt->state->nbAttrs;i++) {
3419 tmp = ctxt->state->attrs[i];
3420 if ((tmp != NULL) && (xmlStrEqual(define->name, tmp->name))) {
3421 if ((((define->ns == NULL) || (define->ns[0] == 0)) &&
3422 (tmp->ns == NULL)) ||
3423 ((tmp->ns != NULL) &&
3424 (xmlStrEqual(define->ns, tmp->ns->href)))) {
3425 prop = tmp;
3426 break;
3427 }
3428 }
3429 }
3430 if (prop != NULL) {
3431 value = xmlNodeListGetString(prop->doc, prop->children, 1);
3432 oldvalue = ctxt->state->value;
3433 ctxt->state->value = value;
3434 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
3435 value = ctxt->state->value;
3436 ctxt->state->value = oldvalue;
3437 if (value != NULL)
3438 xmlFree(value);
3439 if (ret == 0) {
3440 /*
3441 * flag the attribute as processed
3442 */
3443 ctxt->state->attrs[i] = NULL;
3444 }
3445 } else {
3446 ret = -1;
3447 }
3448#ifdef DEBUG
3449 xmlGenericError(xmlGenericErrorContext,
3450 "xmlRelaxNGValidateAttribute(%s): %d\n", define->name, ret);
3451#endif
3452 } else {
3453 TODO
3454 }
3455
3456 return(ret);
3457}
3458
3459/**
3460 * xmlRelaxNGValidateAttributeList:
3461 * @ctxt: a Relax-NG validation context
3462 * @define: the list of definition to verify
3463 *
3464 * Validate the given node against the list of attribute definitions
3465 *
3466 * Returns 0 if the validation succeeded or an error code.
3467 */
3468static int
3469xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt,
3470 xmlRelaxNGDefinePtr defines) {
3471 int ret = 0;
3472 while (defines != NULL) {
3473 if (xmlRelaxNGValidateAttribute(ctxt, defines) != 0)
3474 ret = -1;
3475 defines = defines->next;
3476 }
3477 return(ret);
3478}
3479
3480/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003481 * xmlRelaxNGValidateTryPermutation:
3482 * @ctxt: a Relax-NG validation context
3483 * @groups: the array of groups
3484 * @nbgroups: the number of groups in the array
3485 * @array: the permutation to try
3486 * @len: the size of the set
3487 *
3488 * Try to validate a permutation for the group of definitions.
3489 *
3490 * Returns 0 if the validation succeeded or an error code.
3491 */
3492static int
3493xmlRelaxNGValidateTryPermutation(xmlRelaxNGValidCtxtPtr ctxt,
3494 xmlRelaxNGDefinePtr rule,
3495 xmlNodePtr *array, int len) {
3496 int i, ret;
3497
3498 if (len > 0) {
3499 /*
3500 * One only need the next pointer set-up to do the validation
3501 */
3502 for (i = 0;i < (len - 1);i++)
3503 array[i]->next = array[i + 1];
3504 array[i]->next = NULL;
3505
3506 /*
3507 * Now try to validate the sequence
3508 */
3509 ctxt->state->seq = array[0];
3510 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
3511 } else {
3512 ctxt->state->seq = NULL;
3513 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
3514 }
3515
3516 /*
3517 * the sequence must be fully consumed
3518 */
3519 if (ctxt->state->seq != NULL)
3520 return(-1);
3521
3522 return(ret);
3523}
3524
3525/**
3526 * xmlRelaxNGValidateWalkPermutations:
3527 * @ctxt: a Relax-NG validation context
3528 * @groups: the array of groups
3529 * @nbgroups: the number of groups in the array
3530 * @nodes: the set of nodes
3531 * @array: the current state of the parmutation
3532 * @len: the size of the set
3533 * @level: a pointer to the level variable
3534 * @k: the index in the array to fill
3535 *
3536 * Validate a set of nodes for a groups of definitions, will try the
3537 * full set of permutations
3538 *
3539 * Returns 0 if the validation succeeded or an error code.
3540 */
3541static int
3542xmlRelaxNGValidateWalkPermutations(xmlRelaxNGValidCtxtPtr ctxt,
3543 xmlRelaxNGDefinePtr rule, xmlNodePtr *nodes,
3544 xmlNodePtr *array, int len,
3545 int *level, int k) {
3546 int i, ret;
3547
3548 if ((k >= 0) && (k < len))
3549 array[k] = nodes[*level];
3550 *level = *level + 1;
3551 if (*level == len) {
3552 ret = xmlRelaxNGValidateTryPermutation(ctxt, rule, array, len);
3553 if (ret == 0)
3554 return(0);
3555 } else {
3556 for (i = 0;i < len;i++) {
3557 if (array[i] == NULL) {
3558 ret = xmlRelaxNGValidateWalkPermutations(ctxt, rule,
3559 nodes, array, len, level, i);
3560 if (ret == 0)
3561 return(0);
3562 }
3563 }
3564 }
3565 *level = *level - 1;
3566 array[k] = NULL;
3567 return(-1);
3568}
3569
3570/**
3571 * xmlRelaxNGNodeMatchesList:
3572 * @node: the node
3573 * @list: a NULL terminated array of definitions
3574 *
3575 * Check if a node can be matched by one of the definitions
3576 *
3577 * Returns 1 if matches 0 otherwise
3578 */
3579static int
3580xmlRelaxNGNodeMatchesList(xmlNodePtr node, xmlRelaxNGDefinePtr *list) {
3581 xmlRelaxNGDefinePtr cur;
3582 int i = 0;
3583
3584 if ((node == NULL) || (list == NULL))
3585 return(0);
3586
3587 cur = list[i++];
3588 while (cur != NULL) {
3589 if ((node->type == XML_ELEMENT_NODE) &&
3590 (cur->type == XML_RELAXNG_ELEMENT)) {
3591 if (cur->name == NULL) {
3592 if ((node->ns != NULL) &&
3593 (xmlStrEqual(node->ns->href, cur->ns)))
3594 return(1);
3595 } else if (xmlStrEqual(cur->name, node->name)) {
3596 if ((cur->ns == NULL) || (cur->ns[0] == 0)) {
3597 if (node->ns == NULL)
3598 return(1);
3599 } else {
3600 if ((node->ns != NULL) &&
3601 (xmlStrEqual(node->ns->href, cur->ns)))
3602 return(1);
3603 }
3604 }
3605 } else if ((node->type == XML_TEXT_NODE) &&
3606 (cur->type == XML_RELAXNG_TEXT)) {
3607 return(1);
3608 }
3609 cur = list[i++];
3610 }
3611 return(0);
3612}
3613
3614/**
3615 * xmlRelaxNGValidatePartGroup:
3616 * @ctxt: a Relax-NG validation context
3617 * @groups: the array of groups
3618 * @nbgroups: the number of groups in the array
3619 * @nodes: the set of nodes
3620 * @len: the size of the set of nodes
3621 *
3622 * Validate a set of nodes for a groups of definitions
3623 *
3624 * Returns 0 if the validation succeeded or an error code.
3625 */
3626static int
3627xmlRelaxNGValidatePartGroup(xmlRelaxNGValidCtxtPtr ctxt,
3628 xmlRelaxNGInterleaveGroupPtr *groups,
3629 int nbgroups, xmlNodePtr *nodes, int len) {
3630 int level = -1, ret = -1, i, j, k;
3631 xmlNodePtr *array = NULL, *list, oldseq;
3632 xmlRelaxNGInterleaveGroupPtr group;
3633
3634 list = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
3635 if (list == NULL) {
3636 return(-1);
3637 }
3638 array = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
3639 if (array == NULL) {
3640 xmlFree(list);
3641 return(-1);
3642 }
3643 memset(array, 0, len * sizeof(xmlNodePtr));
3644
3645 /*
3646 * Partition the elements and validate the subsets.
3647 */
3648 oldseq = ctxt->state->seq;
3649 for (i = 0;i < nbgroups;i++) {
3650 group = groups[i];
3651 if (group == NULL)
3652 continue;
3653 k = 0;
3654 for (j = 0;j < len;j++) {
3655 if (nodes[j] == NULL)
3656 continue;
3657 if (xmlRelaxNGNodeMatchesList(nodes[j], group->defs)) {
3658 list[k++] = nodes[j];
3659 nodes[j] = NULL;
3660 }
3661 }
3662 ctxt->state->seq = oldseq;
3663 if (k > 1) {
3664 memset(array, 0, k * sizeof(xmlNodePtr));
3665 ret = xmlRelaxNGValidateWalkPermutations(ctxt, group->rule,
3666 list, array, k, &level, -1);
3667 } else {
3668 ret = xmlRelaxNGValidateTryPermutation(ctxt, group->rule, list, k);
3669 }
3670 if (ret != 0) {
3671 ctxt->state->seq = oldseq;
3672 break;
3673 }
3674 }
3675
3676 xmlFree(list);
3677 xmlFree(array);
3678 return(ret);
3679}
3680
3681/**
3682 * xmlRelaxNGValidateInterleave:
3683 * @ctxt: a Relax-NG validation context
3684 * @define: the definition to verify
3685 *
3686 * Validate an interleave definition for a node.
3687 *
3688 * Returns 0 if the validation succeeded or an error code.
3689 */
3690static int
3691xmlRelaxNGValidateInterleave(xmlRelaxNGValidCtxtPtr ctxt,
3692 xmlRelaxNGDefinePtr define) {
3693 int ret = 0, nbchildren, nbtot, i, j;
3694 xmlRelaxNGPartitionPtr partitions;
3695 xmlNodePtr *children = NULL;
3696 xmlNodePtr *order = NULL;
3697 xmlNodePtr cur;
3698
3699 if (define->data != NULL) {
3700 partitions = (xmlRelaxNGPartitionPtr) define->data;
3701 } else {
3702 VALID_CTXT();
3703 VALID_ERROR("Internal: interleave block has no data\n");
3704 return(-1);
3705 }
3706
3707 /*
3708 * Build the sequence of child and an array preserving the children
3709 * initial order.
3710 */
3711 cur = ctxt->state->seq;
3712 nbchildren = 0;
3713 nbtot = 0;
3714 while (cur != NULL) {
3715 if ((cur->type == XML_COMMENT_NODE) ||
3716 (cur->type == XML_PI_NODE) ||
3717 ((cur->type == XML_TEXT_NODE) &&
3718 (IS_BLANK_NODE(cur)))) {
3719 nbtot++;
3720 } else {
3721 nbchildren++;
3722 nbtot++;
3723 }
3724 cur = cur->next;
3725 }
3726 children = (xmlNodePtr *) xmlMalloc(nbchildren * sizeof(xmlNodePtr));
3727 if (children == NULL)
3728 goto error;
3729 order = (xmlNodePtr *) xmlMalloc(nbtot * sizeof(xmlNodePtr));
3730 if (order == NULL)
3731 goto error;
3732 cur = ctxt->state->seq;
3733 i = 0;
3734 j = 0;
3735 while (cur != NULL) {
3736 if ((cur->type == XML_COMMENT_NODE) ||
3737 (cur->type == XML_PI_NODE) ||
3738 ((cur->type == XML_TEXT_NODE) &&
3739 (IS_BLANK_NODE(cur)))) {
3740 order[j++] = cur;
3741 } else {
3742 order[j++] = cur;
3743 children[i++] = cur;
3744 }
3745 cur = cur->next;
3746 }
3747
3748 /* TODO: retry with a maller set of child if there is a next... */
3749 ret = xmlRelaxNGValidatePartGroup(ctxt, partitions->groups,
3750 partitions->nbgroups, children, nbchildren);
3751 if (ret == 0) {
3752 ctxt->state->seq = NULL;
3753 }
3754
3755 /*
3756 * Cleanup: rebuid the child sequence and free the structure
3757 */
3758 if (order != NULL) {
3759 for (i = 0;i < nbtot;i++) {
3760 if (i == 0)
3761 order[i]->prev = NULL;
3762 else
3763 order[i]->prev = order[i - 1];
3764 if (i == nbtot - 1)
3765 order[i]->next = NULL;
3766 else
3767 order[i]->next = order[i + 1];
3768 }
3769 xmlFree(order);
3770 }
3771 if (children != NULL)
3772 xmlFree(children);
3773
3774 return(ret);
3775
3776error:
3777 if (order != NULL) {
3778 for (i = 0;i < nbtot;i++) {
3779 if (i == 0)
3780 order[i]->prev = NULL;
3781 else
3782 order[i]->prev = order[i - 1];
3783 if (i == nbtot - 1)
3784 order[i]->next = NULL;
3785 else
3786 order[i]->next = order[i + 1];
3787 }
3788 xmlFree(order);
3789 }
3790 if (children != NULL)
3791 xmlFree(children);
3792 return(-1);
3793}
3794
3795/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00003796 * xmlRelaxNGValidateElementContent:
3797 * @ctxt: a Relax-NG validation context
3798 * @define: the list of definition to verify
3799 *
3800 * Validate the given node content against the (list) of definitions
3801 *
3802 * Returns 0 if the validation succeeded or an error code.
3803 */
3804static int
3805xmlRelaxNGValidateElementContent(xmlRelaxNGValidCtxtPtr ctxt,
3806 xmlRelaxNGDefinePtr defines) {
3807 int ret = 0, res;
3808
3809 if (ctxt->state == NULL) {
3810 VALID_CTXT();
3811 VALID_ERROR("Internal: no state\n");
3812 return(-1);
3813 }
3814 while (defines != NULL) {
3815 res = xmlRelaxNGValidateDefinition(ctxt, defines);
3816 if (res < 0)
3817 ret = -1;
3818 defines = defines->next;
3819 }
3820
3821 return(ret);
3822}
3823
3824/**
3825 * xmlRelaxNGValidateDefinition:
3826 * @ctxt: a Relax-NG validation context
3827 * @define: the definition to verify
3828 *
3829 * Validate the current node against the definition
3830 *
3831 * Returns 0 if the validation succeeded or an error code.
3832 */
3833static int
3834xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
3835 xmlRelaxNGDefinePtr define) {
3836 xmlNodePtr node;
3837 int ret = 0, i, tmp, oldflags;
3838 xmlRelaxNGValidStatePtr oldstate, state;
3839
3840 if (define == NULL) {
3841 VALID_CTXT();
3842 VALID_ERROR("internal error: define == NULL\n");
3843 return(-1);
3844 }
3845 if (ctxt->state != NULL) {
3846 node = ctxt->state->seq;
3847 } else {
3848 node = NULL;
3849 }
3850 switch (define->type) {
3851 case XML_RELAXNG_EMPTY:
3852 if (node != NULL) {
3853 VALID_CTXT();
3854 VALID_ERROR("Expecting an empty element\n");
3855 return(-1);
3856 }
3857#ifdef DEBUG
3858 xmlGenericError(xmlGenericErrorContext,
3859 "xmlRelaxNGValidateDefinition(): validated empty\n");
3860#endif
3861 return(0);
3862 case XML_RELAXNG_NOT_ALLOWED:
3863 TODO
3864 break;
3865 case XML_RELAXNG_TEXT:
3866 if (node == NULL)
3867 return(0);
3868 while ((node != NULL) &&
3869 ((node->type == XML_TEXT_NODE) ||
3870 (node->type == XML_CDATA_SECTION_NODE)))
3871 node = node->next;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003872 if (node == ctxt->state->seq) {
3873 VALID_CTXT();
3874 VALID_ERROR("Expecting text content\n");
3875 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003876 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003877 ctxt->state->seq = node;
3878 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003879 case XML_RELAXNG_ELEMENT:
3880 node = xmlRelaxNGSkipIgnored(ctxt, node);
3881 if ((node == NULL) || (node->type != XML_ELEMENT_NODE)) {
3882 VALID_CTXT();
3883 VALID_ERROR("Expecting an element\n");
3884 return(-1);
3885 }
3886 if (define->name != NULL) {
3887 if (!xmlStrEqual(node->name, define->name)) {
3888 VALID_CTXT();
3889 VALID_ERROR("Expecting element %s, got %s\n",
3890 define->name, node->name);
3891 return(-1);
3892 }
3893 }
3894 if ((define->ns != NULL) && (define->ns[0] != 0)) {
3895 if (node->ns == NULL) {
3896 VALID_CTXT();
3897 VALID_ERROR("Expecting a namespace for element %s\n",
3898 node->name);
3899 return(-1);
3900 } else if (!xmlStrEqual(node->ns->href, define->ns)) {
3901 VALID_CTXT();
3902 VALID_ERROR("Expecting element %s has wrong namespace: expecting %s\n",
3903 node->name, define->ns);
3904 return(-1);
3905 }
3906 } else {
3907 if (node->ns != NULL) {
3908 VALID_CTXT();
3909 VALID_ERROR("Expecting no namespace for element %s\n",
3910 node->name);
3911 return(-1);
3912 }
3913 }
3914
3915 state = xmlRelaxNGNewValidState(ctxt, node);
3916 if (state == NULL) {
3917 return(-1);
3918 }
3919
3920 oldstate = ctxt->state;
3921 ctxt->state = state;
3922 if (define->attrs != NULL) {
3923 tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
3924 if (tmp != 0)
3925 ret = -1;
3926 }
3927 if (define->content != NULL) {
3928 tmp = xmlRelaxNGValidateElementContent(ctxt, define->content);
3929 if (tmp != 0)
3930 ret = -1;
3931 }
3932 state = ctxt->state;
3933 if (state->seq != NULL) {
3934 state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq);
3935 if (state->seq != NULL) {
3936 VALID_CTXT();
3937 VALID_ERROR("Extra content for element %s\n",
3938 node->name);
3939 ret = -1;
3940 }
3941 }
3942 for (i = 0;i < state->nbAttrs;i++) {
3943 if (state->attrs[i] != NULL) {
3944 VALID_CTXT();
Daniel Veillardea3f3982003-01-26 19:45:18 +00003945 VALID_ERROR("Invalid attribute %s for element %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00003946 state->attrs[i]->name, node->name);
3947 ret = -1;
3948 }
3949 }
3950 ctxt->state = oldstate;
3951 xmlRelaxNGFreeValidState(state);
3952 if (oldstate != NULL)
3953 oldstate->seq = node->next;
3954
3955
3956#ifdef DEBUG
3957 xmlGenericError(xmlGenericErrorContext,
3958 "xmlRelaxNGValidateDefinition(): validated %s : %d\n",
3959 node->name, ret);
3960#endif
3961 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003962 case XML_RELAXNG_OPTIONAL:
3963 oldflags = ctxt->flags;
3964 ctxt->flags |= FLAGS_IGNORABLE;
3965 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
3966 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
3967 if (ret != 0) {
3968 xmlRelaxNGFreeValidState(ctxt->state);
3969 ctxt->state = oldstate;
3970 ret = 0;
3971 break;
3972 }
3973 xmlRelaxNGFreeValidState(oldstate);
3974 ctxt->flags = oldflags;
3975 ret = 0;
3976 break;
3977 case XML_RELAXNG_ONEORMORE:
3978 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
3979 if (ret != 0) {
3980 break;
3981 }
3982 /* no break on purpose */
Daniel Veillard276be4a2003-01-24 01:03:34 +00003983 case XML_RELAXNG_ZEROORMORE: {
3984 xmlNodePtr cur, temp;
3985
Daniel Veillard6eadf632003-01-23 18:29:16 +00003986 oldflags = ctxt->flags;
3987 ctxt->flags |= FLAGS_IGNORABLE;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003988 cur = ctxt->state->seq;
3989 temp = NULL;
3990 while ((cur != NULL) && (temp != cur)) {
3991 temp = cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003992 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
3993 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
3994 if (ret != 0) {
3995 xmlRelaxNGFreeValidState(ctxt->state);
3996 ctxt->state = oldstate;
3997 ret = 0;
3998 break;
3999 }
4000 xmlRelaxNGFreeValidState(oldstate);
Daniel Veillard276be4a2003-01-24 01:03:34 +00004001 cur = ctxt->state->seq;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004002 }
4003 ctxt->flags = oldflags;
4004 break;
Daniel Veillard276be4a2003-01-24 01:03:34 +00004005 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004006 case XML_RELAXNG_CHOICE: {
4007 xmlRelaxNGDefinePtr list = define->content;
4008
4009 oldflags = ctxt->flags;
4010 ctxt->flags |= FLAGS_IGNORABLE;
4011
4012 while (list != NULL) {
4013 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
4014 ret = xmlRelaxNGValidateDefinition(ctxt, list);
4015 if (ret == 0) {
4016 xmlRelaxNGFreeValidState(oldstate);
4017 break;
4018 }
4019 xmlRelaxNGFreeValidState(ctxt->state);
4020 ctxt->state = oldstate;
4021 list = list->next;
4022 }
4023 ctxt->flags = oldflags;
4024 break;
4025 }
4026 case XML_RELAXNG_GROUP: {
4027 xmlRelaxNGDefinePtr list = define->content;
4028
4029 while (list != NULL) {
4030 ret = xmlRelaxNGValidateDefinition(ctxt, list);
4031 if (ret != 0)
4032 break;
4033 list = list->next;
4034 }
4035 break;
4036 }
4037 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004038 ret = xmlRelaxNGValidateInterleave(ctxt, define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004039 break;
4040 case XML_RELAXNG_ATTRIBUTE:
4041 ret = xmlRelaxNGValidateAttribute(ctxt, define);
4042 break;
4043 case XML_RELAXNG_REF:
4044 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
4045 break;
4046 case XML_RELAXNG_DEF:
4047 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
4048 break;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004049 case XML_RELAXNG_DATATYPE: {
4050 xmlChar *content;
4051
4052 content = xmlNodeGetContent(node);
4053 ret = xmlRelaxNGValidateDatatype(ctxt, content, define);
4054 if (ret == -1) {
4055 VALID_CTXT();
4056 VALID_ERROR("internal error validating %s\n", define->name);
4057 } else if (ret == 0) {
4058 ctxt->state->seq = node->next;
4059 }
4060 /*
4061 * TODO cover the problems with
4062 * <p>12<!-- comment -->34</p>
4063 * TODO detect full element coverage at compilation time.
4064 */
4065 if ((node != NULL) && (node->next != NULL)) {
4066 VALID_CTXT();
4067 VALID_ERROR("The data does not cover the full element %s\n",
4068 node->parent->name);
4069 ret = -1;
4070 }
4071 if (content != NULL)
4072 xmlFree(content);
4073 break;
4074 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00004075 case XML_RELAXNG_VALUE: {
4076 xmlChar *content;
4077 xmlChar *oldvalue;
4078
4079 content = xmlNodeGetContent(node);
4080 oldvalue = ctxt->state->value;
4081 ctxt->state->value = content;
4082 ret = xmlRelaxNGValidateValue(ctxt, define);
4083 ctxt->state->value = oldvalue;
4084 if (ret == -1) {
4085 VALID_CTXT();
4086 VALID_ERROR("internal error validating %s\n", define->name);
4087 } else if (ret == 0) {
4088 ctxt->state->seq = node->next;
4089 }
4090 /*
4091 * TODO cover the problems with
4092 * <p>12<!-- comment -->34</p>
4093 * TODO detect full element coverage at compilation time.
4094 */
4095 if ((node != NULL) && (node->next != NULL)) {
4096 VALID_CTXT();
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004097 VALID_ERROR("The value does not cover the full element %s\n",
Daniel Veillardea3f3982003-01-26 19:45:18 +00004098 node->parent->name);
4099 ret = -1;
4100 }
4101 if (content != NULL)
4102 xmlFree(content);
4103 break;
4104 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004105 case XML_RELAXNG_LIST: {
4106 xmlChar *content;
4107 xmlChar *oldvalue, *oldendvalue;
4108 int len;
Daniel Veillardea3f3982003-01-26 19:45:18 +00004109
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004110 content = xmlNodeGetContent(node);
4111 len = xmlStrlen(content);
4112 oldvalue = ctxt->state->value;
4113 oldendvalue = ctxt->state->endvalue;
4114 ctxt->state->value = content;
4115 ctxt->state->endvalue = content + len;
4116 ret = xmlRelaxNGValidateValue(ctxt, define);
4117 ctxt->state->value = oldvalue;
4118 ctxt->state->endvalue = oldendvalue;
4119 if (ret == -1) {
4120 VALID_CTXT();
4121 VALID_ERROR("internal error validating list\n");
4122 } else if (ret == 0) {
4123 ctxt->state->seq = node->next;
4124 }
4125 /*
4126 * TODO cover the problems with
4127 * <p>12<!-- comment -->34</p>
4128 * TODO detect full element coverage at compilation time.
4129 */
4130 if ((node != NULL) && (node->next != NULL)) {
4131 VALID_CTXT();
4132 VALID_ERROR("The list does not cover the full element %s\n",
4133 node->parent->name);
4134 ret = -1;
4135 }
4136 if (content != NULL)
4137 xmlFree(content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004138 break;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004139 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004140 }
4141 return(ret);
4142}
4143
4144/**
4145 * xmlRelaxNGValidateDocument:
4146 * @ctxt: a Relax-NG validation context
4147 * @doc: the document
4148 *
4149 * Validate the given document
4150 *
4151 * Returns 0 if the validation succeeded or an error code.
4152 */
4153static int
4154xmlRelaxNGValidateDocument(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
4155 int ret;
4156 xmlRelaxNGPtr schema;
4157 xmlRelaxNGGrammarPtr grammar;
4158 xmlRelaxNGValidStatePtr state;
4159
4160 if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL))
4161 return(-1);
4162
4163 schema = ctxt->schema;
4164 grammar = schema->topgrammar;
4165 if (grammar == NULL) {
4166 VALID_CTXT();
4167 VALID_ERROR("No top grammar defined\n");
4168 return(-1);
4169 }
4170 state = xmlRelaxNGNewValidState(ctxt, NULL);
4171 ctxt->state = state;
4172 ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start);
4173 state = ctxt->state;
4174 if ((state != NULL) && (state->seq != NULL)) {
4175 xmlNodePtr node;
4176
4177 node = state->seq;
4178 node = xmlRelaxNGSkipIgnored(ctxt, node);
4179 if (node != NULL) {
4180 VALID_CTXT();
4181 VALID_ERROR("extra data on the document\n");
4182 ret = -1;
4183 }
4184 }
4185 xmlRelaxNGFreeValidState(state);
4186
4187 return(ret);
4188}
4189
4190/************************************************************************
4191 * *
4192 * Validation interfaces *
4193 * *
4194 ************************************************************************/
4195/**
4196 * xmlRelaxNGNewValidCtxt:
4197 * @schema: a precompiled XML RelaxNGs
4198 *
4199 * Create an XML RelaxNGs validation context based on the given schema
4200 *
4201 * Returns the validation context or NULL in case of error
4202 */
4203xmlRelaxNGValidCtxtPtr
4204xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema) {
4205 xmlRelaxNGValidCtxtPtr ret;
4206
4207 ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt));
4208 if (ret == NULL) {
4209 xmlGenericError(xmlGenericErrorContext,
4210 "Failed to allocate new schama validation context\n");
4211 return (NULL);
4212 }
4213 memset(ret, 0, sizeof(xmlRelaxNGValidCtxt));
4214 ret->schema = schema;
4215 return (ret);
4216}
4217
4218/**
4219 * xmlRelaxNGFreeValidCtxt:
4220 * @ctxt: the schema validation context
4221 *
4222 * Free the resources associated to the schema validation context
4223 */
4224void
4225xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt) {
4226 if (ctxt == NULL)
4227 return;
4228 xmlFree(ctxt);
4229}
4230
4231/**
4232 * xmlRelaxNGSetValidErrors:
4233 * @ctxt: a Relax-NG validation context
4234 * @err: the error function
4235 * @warn: the warning function
4236 * @ctx: the functions context
4237 *
4238 * Set the error and warning callback informations
4239 */
4240void
4241xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
4242 xmlRelaxNGValidityErrorFunc err,
4243 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
4244 if (ctxt == NULL)
4245 return;
4246 ctxt->error = err;
4247 ctxt->warning = warn;
4248 ctxt->userData = ctx;
4249}
4250
4251/**
4252 * xmlRelaxNGValidateDoc:
4253 * @ctxt: a Relax-NG validation context
4254 * @doc: a parsed document tree
4255 *
4256 * Validate a document tree in memory.
4257 *
4258 * Returns 0 if the document is valid, a positive error code
4259 * number otherwise and -1 in case of internal or API error.
4260 */
4261int
4262xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
4263 int ret;
4264
4265 if ((ctxt == NULL) || (doc == NULL))
4266 return(-1);
4267
4268 ctxt->doc = doc;
4269
4270 ret = xmlRelaxNGValidateDocument(ctxt, doc);
4271 return(ret);
4272}
4273
4274#endif /* LIBXML_SCHEMAS_ENABLED */
4275