blob: 61af8c0d7d2c2e2a0bd7c336337a19ac3af0f936 [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>
27
28/*
29 * The Relax-NG namespace
30 */
31static const xmlChar *xmlRelaxNGNs = (const xmlChar *)
32 "http://relaxng.org/ns/structure/1.0";
33
34#define IS_RELAXNG(node, type) \
35 ((node != NULL) && (node->ns != NULL) && \
36 (xmlStrEqual(node->name, (const xmlChar *) type)) && \
37 (xmlStrEqual(node->ns->href, xmlRelaxNGNs)))
38
39
40#define DEBUG 1 /* very verbose output */
41#define DEBUG_CONTENT 1
42#define DEBUG_TYPE 1
Daniel Veillard276be4a2003-01-24 01:03:34 +000043#define DEBUG_VALID 1
Daniel Veillard6eadf632003-01-23 18:29:16 +000044
45#define UNBOUNDED (1 << 30)
46#define TODO \
47 xmlGenericError(xmlGenericErrorContext, \
48 "Unimplemented block at %s:%d\n", \
49 __FILE__, __LINE__);
50
51typedef struct _xmlRelaxNGSchema xmlRelaxNGSchema;
52typedef xmlRelaxNGSchema *xmlRelaxNGSchemaPtr;
53
54typedef struct _xmlRelaxNGDefine xmlRelaxNGDefine;
55typedef xmlRelaxNGDefine *xmlRelaxNGDefinePtr;
56
57typedef enum {
58 XML_RELAXNG_COMBINE_UNDEFINED = 0, /* undefined */
59 XML_RELAXNG_COMBINE_CHOICE, /* choice */
60 XML_RELAXNG_COMBINE_INTERLEAVE /* interleave */
61} xmlRelaxNGCombine;
62
63typedef struct _xmlRelaxNGGrammar xmlRelaxNGGrammar;
64typedef xmlRelaxNGGrammar *xmlRelaxNGGrammarPtr;
65
66struct _xmlRelaxNGGrammar {
67 xmlRelaxNGGrammarPtr parent;/* the parent grammar if any */
68 xmlRelaxNGGrammarPtr children;/* the children grammar if any */
69 xmlRelaxNGGrammarPtr next; /* the next grammar if any */
70 xmlRelaxNGDefinePtr start; /* <start> content */
71 xmlRelaxNGCombine combine; /* the default combine value */
72 xmlHashTablePtr defs; /* define* */
73 xmlHashTablePtr refs; /* references */
74};
75
76
Daniel Veillard6eadf632003-01-23 18:29:16 +000077typedef enum {
78 XML_RELAXNG_EMPTY = 0, /* an empty pattern */
79 XML_RELAXNG_NOT_ALLOWED, /* not allowed top */
80 XML_RELAXNG_TEXT, /* textual content */
81 XML_RELAXNG_ELEMENT, /* an element */
82 XML_RELAXNG_DATATYPE, /* extenal data type definition */
83 XML_RELAXNG_VALUE, /* value from an extenal data type definition */
84 XML_RELAXNG_LIST, /* a list of patterns */
85 XML_RELAXNG_ATTRIBUTE, /* an attrbute following a pattern */
86 XML_RELAXNG_DEF, /* a definition */
87 XML_RELAXNG_REF, /* reference to a definition */
88 XML_RELAXNG_OPTIONAL, /* optional patterns */
89 XML_RELAXNG_ZEROORMORE, /* zero or more non empty patterns */
90 XML_RELAXNG_ONEORMORE, /* one or more non empty patterns */
91 XML_RELAXNG_CHOICE, /* a choice between non empty patterns */
92 XML_RELAXNG_GROUP, /* a pair/group of non empty patterns */
93 XML_RELAXNG_INTERLEAVE /* interleaving choice of non-empty patterns */
94} xmlRelaxNGType;
95
96struct _xmlRelaxNGDefine {
97 xmlRelaxNGType type; /* the type of definition */
98 xmlNodePtr node; /* the node in the source */
99 xmlChar *name; /* the element local name if present */
100 xmlChar *ns; /* the namespace local name if present */
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000101 void *data; /* data lib or specific pointer */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000102 xmlRelaxNGDefinePtr content;/* the expected content */
103 xmlRelaxNGDefinePtr next; /* list within grouping sequences */
104 xmlRelaxNGDefinePtr attrs; /* list of attributes for elements */
105 xmlRelaxNGDefinePtr nextHash;/* next define in defs/refs hash tables */
106};
107
108/**
109 * _xmlRelaxNG:
110 *
111 * A RelaxNGs definition
112 */
113struct _xmlRelaxNG {
114 xmlRelaxNGGrammarPtr topgrammar;
115 xmlDocPtr doc;
116
117 xmlHashTablePtr defs; /* define */
118 xmlHashTablePtr refs; /* references */
119 void *_private; /* unused by the library for users or bindings */
120};
121
122typedef enum {
123 XML_RELAXNG_ERR_OK = 0,
124 XML_RELAXNG_ERR_NOROOT = 1,
125 XML_RELAXNG_ERR_
126} xmlRelaxNGValidError;
127
128#define XML_RELAXNG_IN_ATTRIBUTE 1
129
130struct _xmlRelaxNGParserCtxt {
131 void *userData; /* user specific data block */
132 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
133 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
134 xmlRelaxNGValidError err;
135
136 xmlRelaxNGPtr schema; /* The schema in use */
137 xmlRelaxNGGrammarPtr grammar; /* the current grammar */
138 int flags; /* parser flags */
139 int nbErrors; /* number of errors at parse time */
140 int nbWarnings; /* number of warnings at parse time */
Daniel Veillard276be4a2003-01-24 01:03:34 +0000141 const xmlChar *define; /* the current define scope */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000142
143 xmlChar *URL;
144 xmlDocPtr doc;
145
146 const char *buffer;
147 int size;
148
149 /*
150 * Used to build complex element content models
151 */
152 xmlAutomataPtr am;
153 xmlAutomataStatePtr start;
154 xmlAutomataStatePtr end;
155 xmlAutomataStatePtr state;
156};
157
158#define FLAGS_IGNORABLE 1
159#define FLAGS_NEGATIVE 2
160
161/**
162 * xmlRelaxNGValidState:
163 *
164 * A RelaxNGs validation state
165 */
166#define MAX_ATTR 20
167typedef struct _xmlRelaxNGValidState xmlRelaxNGValidState;
168typedef xmlRelaxNGValidState *xmlRelaxNGValidStatePtr;
169struct _xmlRelaxNGValidState {
170 xmlNodePtr node; /* the current node */
171 xmlNodePtr seq; /* the sequence of children left to validate */
172 int nbAttrs; /* the number of attributes */
173 xmlChar *value; /* the value when operating on string */
174 xmlAttrPtr attrs[1]; /* the array of attributes */
175};
176
177/**
178 * xmlRelaxNGValidCtxt:
179 *
180 * A RelaxNGs validation context
181 */
182
183struct _xmlRelaxNGValidCtxt {
184 void *userData; /* user specific data block */
185 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
186 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
187
188 xmlRelaxNGPtr schema; /* The schema in use */
189 xmlDocPtr doc; /* the document being validated */
190 xmlRelaxNGValidStatePtr state; /* the current validation state */
191 int flags; /* validation flags */
192};
193
194/************************************************************************
195 * *
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000196 * Preliminary type checking interfaces *
197 * *
198 ************************************************************************/
199/**
200 * xmlRelaxNGTypeHave:
201 * @data: data needed for the library
202 * @type: the type name
203 * @value: the value to check
204 *
205 * Function provided by a type library to check if a type is exported
206 *
207 * Returns 1 if yes, 0 if no and -1 in case of error.
208 */
209typedef int (*xmlRelaxNGTypeHave) (void *data, const xmlChar *type);
210
211/**
212 * xmlRelaxNGTypeCheck:
213 * @data: data needed for the library
214 * @type: the type name
215 * @value: the value to check
216 *
217 * Function provided by a type library to check if a value match a type
218 *
219 * Returns 1 if yes, 0 if no and -1 in case of error.
220 */
221typedef int (*xmlRelaxNGTypeCheck) (void *data, const xmlChar *type,
222 const xmlChar *value);
223
224/**
225 * xmlRelaxNGTypeCompare:
226 * @data: data needed for the library
227 * @type: the type name
228 * @value1: the first value
229 * @value2: the second value
230 *
231 * Function provided by a type library to compare two values accordingly
232 * to a type.
233 *
234 * Returns 1 if yes, 0 if no and -1 in case of error.
235 */
236typedef int (*xmlRelaxNGTypeCompare) (void *data, const xmlChar *type,
237 const xmlChar *value1,
238 const xmlChar *value2);
239typedef struct _xmlRelaxNGTypeLibrary xmlRelaxNGTypeLibrary;
240typedef xmlRelaxNGTypeLibrary *xmlRelaxNGTypeLibraryPtr;
241struct _xmlRelaxNGTypeLibrary {
242 const xmlChar *namespace; /* the datatypeLibrary value */
243 void *data; /* data needed for the library */
244 xmlRelaxNGTypeHave have; /* the export function */
245 xmlRelaxNGTypeCheck check; /* the checking function */
246 xmlRelaxNGTypeCompare comp; /* the compare function */
247};
248
249/************************************************************************
250 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +0000251 * Allocation functions *
252 * *
253 ************************************************************************/
254static void xmlRelaxNGFreeDefineList(xmlRelaxNGDefinePtr defines);
255static void xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar);
256static void xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define);
257
258/**
259 * xmlRelaxNGNewRelaxNG:
260 * @ctxt: a Relax-NG validation context (optional)
261 *
262 * Allocate a new RelaxNG structure.
263 *
264 * Returns the newly allocated structure or NULL in case or error
265 */
266static xmlRelaxNGPtr
267xmlRelaxNGNewRelaxNG(xmlRelaxNGParserCtxtPtr ctxt)
268{
269 xmlRelaxNGPtr ret;
270
271 ret = (xmlRelaxNGPtr) xmlMalloc(sizeof(xmlRelaxNG));
272 if (ret == NULL) {
273 if ((ctxt != NULL) && (ctxt->error != NULL))
274 ctxt->error(ctxt->userData, "Out of memory\n");
275 ctxt->nbErrors++;
276 return (NULL);
277 }
278 memset(ret, 0, sizeof(xmlRelaxNG));
279
280 return (ret);
281}
282
283/**
284 * xmlRelaxNGFree:
285 * @schema: a schema structure
286 *
287 * Deallocate a RelaxNG structure.
288 */
289void
290xmlRelaxNGFree(xmlRelaxNGPtr schema)
291{
292 if (schema == NULL)
293 return;
294
295#if 0
296 if (schema->elemDecl != NULL)
297 xmlHashFree(schema->elemDecl,
298 (xmlHashDeallocator) xmlRelaxNGFreeElement);
299 if (schema->typeDecl != NULL)
300 xmlHashFree(schema->typeDecl,
301 (xmlHashDeallocator) xmlRelaxNGFreeType);
302#endif
303
304 if (schema->topgrammar != NULL)
305 xmlRelaxNGFreeGrammar(schema->topgrammar);
306 if (schema->doc != NULL)
307 xmlFreeDoc(schema->doc);
308
309 xmlFree(schema);
310}
311
312/**
313 * xmlRelaxNGNewGrammar:
314 * @ctxt: a Relax-NG validation context (optional)
315 *
316 * Allocate a new RelaxNG grammar.
317 *
318 * Returns the newly allocated structure or NULL in case or error
319 */
320static xmlRelaxNGGrammarPtr
321xmlRelaxNGNewGrammar(xmlRelaxNGParserCtxtPtr ctxt)
322{
323 xmlRelaxNGGrammarPtr ret;
324
325 ret = (xmlRelaxNGGrammarPtr) xmlMalloc(sizeof(xmlRelaxNGGrammar));
326 if (ret == NULL) {
327 if ((ctxt != NULL) && (ctxt->error != NULL))
328 ctxt->error(ctxt->userData, "Out of memory\n");
329 ctxt->nbErrors++;
330 return (NULL);
331 }
332 memset(ret, 0, sizeof(xmlRelaxNGGrammar));
333
334 return (ret);
335}
336
337/**
Daniel Veillard276be4a2003-01-24 01:03:34 +0000338 * xmlRelaxNGFreeDefineHash:
339 * @defines: a list of define structures
340 *
341 * Deallocate a RelaxNG definition in the hash table
342 */
343static void
344xmlRelaxNGFreeDefineHash(xmlRelaxNGDefinePtr defines)
345{
346 xmlRelaxNGDefinePtr next;
347
348 while (defines != NULL) {
349 next = defines->nextHash;
350 xmlRelaxNGFreeDefine(defines);
351 defines = next;
352 }
353}
354
355/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000356 * xmlRelaxNGFreeGrammar:
357 * @grammar: a grammar structure
358 *
359 * Deallocate a RelaxNG grammar structure.
360 */
361static void
362xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar)
363{
364 if (grammar == NULL)
365 return;
366
367 if (grammar->start != NULL)
368 xmlRelaxNGFreeDefine(grammar->start);
369 if (grammar->refs != NULL) {
370 xmlHashFree(grammar->refs, NULL);
371 }
372 if (grammar->defs != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +0000373 xmlHashFree(grammar->defs, (xmlHashDeallocator)
374 xmlRelaxNGFreeDefineHash);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000375 }
376
377 xmlFree(grammar);
378}
379
380/**
381 * xmlRelaxNGNewDefine:
382 * @ctxt: a Relax-NG validation context
383 * @node: the node in the input document.
384 *
385 * Allocate a new RelaxNG define.
386 *
387 * Returns the newly allocated structure or NULL in case or error
388 */
389static xmlRelaxNGDefinePtr
390xmlRelaxNGNewDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
391{
392 xmlRelaxNGDefinePtr ret;
393
394 ret = (xmlRelaxNGDefinePtr) xmlMalloc(sizeof(xmlRelaxNGDefine));
395 if (ret == NULL) {
396 if ((ctxt != NULL) && (ctxt->error != NULL))
397 ctxt->error(ctxt->userData, "Out of memory\n");
398 ctxt->nbErrors++;
399 return (NULL);
400 }
401 memset(ret, 0, sizeof(xmlRelaxNGDefine));
402 ret->node = node;
403
404 return (ret);
405}
406
407/**
408 * xmlRelaxNGFreeDefineList:
409 * @defines: a list of define structures
410 *
411 * Deallocate a RelaxNG define structures.
412 */
413static void
414xmlRelaxNGFreeDefineList(xmlRelaxNGDefinePtr defines)
415{
416 xmlRelaxNGDefinePtr next;
417
418 while (defines != NULL) {
419 next = defines->next;
420 xmlRelaxNGFreeDefine(defines);
421 defines = next;
422 }
423}
424
425/**
426 * xmlRelaxNGFreeDefine:
427 * @define: a define structure
428 *
429 * Deallocate a RelaxNG define structure.
430 */
431static void
432xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define)
433{
434 if (define == NULL)
435 return;
436
437 if (define->name != NULL)
438 xmlFree(define->name);
439 if (define->ns != NULL)
440 xmlFree(define->ns);
441 if (define->attrs != NULL)
442 xmlRelaxNGFreeDefineList(define->attrs);
Daniel Veillard276be4a2003-01-24 01:03:34 +0000443 if ((define->content != NULL) &&
444 (define->type != XML_RELAXNG_REF))
Daniel Veillard6eadf632003-01-23 18:29:16 +0000445 xmlRelaxNGFreeDefineList(define->content);
446 xmlFree(define);
447}
448
449/**
450 * xmlRelaxNGNewValidState:
451 * @ctxt: a Relax-NG validation context
452 * @node: the current node or NULL for the document
453 *
454 * Allocate a new RelaxNG validation state
455 *
456 * Returns the newly allocated structure or NULL in case or error
457 */
458static xmlRelaxNGValidStatePtr
459xmlRelaxNGNewValidState(xmlRelaxNGValidCtxtPtr ctxt, xmlNodePtr node)
460{
461 xmlRelaxNGValidStatePtr ret;
462 xmlAttrPtr attr;
463 xmlAttrPtr attrs[MAX_ATTR];
464 int nbAttrs = 0;
465 xmlNodePtr root = NULL;
466
467 if (node == NULL) {
468 root = xmlDocGetRootElement(ctxt->doc);
469 if (root == NULL)
470 return(NULL);
471 } else {
472 attr = node->properties;
473 while (attr != NULL) {
474 if (nbAttrs < MAX_ATTR)
475 attrs[nbAttrs++] = attr;
476 else
477 nbAttrs++;
478 attr = attr->next;
479 }
480 }
481
482 if (nbAttrs < MAX_ATTR)
483 attrs[nbAttrs] = NULL;
484 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(sizeof(xmlRelaxNGValidState) +
485 nbAttrs * sizeof(xmlAttrPtr));
486 if (ret == NULL) {
487 if ((ctxt != NULL) && (ctxt->error != NULL))
488 ctxt->error(ctxt->userData, "Out of memory\n");
489 return (NULL);
490 }
491 if (node == NULL) {
492 ret->node = (xmlNodePtr) ctxt->doc;
493 ret->seq = root;
494 ret->nbAttrs = 0;
495 } else {
496 ret->node = node;
497 ret->seq = node->children;
498 ret->nbAttrs = nbAttrs;
499 if (nbAttrs > 0) {
500 if (nbAttrs < MAX_ATTR) {
501 memcpy(&(ret->attrs[0]), attrs,
502 sizeof(xmlAttrPtr) * (nbAttrs + 1));
503 } else {
504 attr = node->properties;
505 nbAttrs = 0;
506 while (attr != NULL) {
507 ret->attrs[nbAttrs++] = attr;
508 attr = attr->next;
509 }
510 ret->attrs[nbAttrs] = NULL;
511 }
512 }
513 }
514 return (ret);
515}
516
517/**
518 * xmlRelaxNGCopyValidState:
519 * @ctxt: a Relax-NG validation context
520 * @state: a validation state
521 *
522 * Copy the validation state
523 *
524 * Returns the newly allocated structure or NULL in case or error
525 */
526static xmlRelaxNGValidStatePtr
527xmlRelaxNGCopyValidState(xmlRelaxNGValidCtxtPtr ctxt,
528 xmlRelaxNGValidStatePtr state)
529{
530 xmlRelaxNGValidStatePtr ret;
531 unsigned int size;
532
533 if (state == NULL)
534 return(NULL);
535
536 size = sizeof(xmlRelaxNGValidState) +
537 state->nbAttrs * sizeof(xmlAttrPtr);
538 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(size);
539 if (ret == NULL) {
540 if ((ctxt != NULL) && (ctxt->error != NULL))
541 ctxt->error(ctxt->userData, "Out of memory\n");
542 return (NULL);
543 }
544 memcpy(ret, state, size);
545 return(ret);
546}
547
548/**
549 * xmlRelaxNGFreeValidState:
550 * @state: a validation state structure
551 *
552 * Deallocate a RelaxNG validation state structure.
553 */
554static void
555xmlRelaxNGFreeValidState(xmlRelaxNGValidStatePtr state)
556{
557 if (state == NULL)
558 return;
559
560 xmlFree(state);
561}
562
563/************************************************************************
564 * *
565 * Error functions *
566 * *
567 ************************************************************************/
568
569#define VALID_CTXT() \
570 if (ctxt->flags == 0) xmlGenericError(xmlGenericErrorContext, \
571 "error detected at %s:%d\n", \
572 __FILE__, __LINE__);
573#define VALID_ERROR if (ctxt->flags == 0) printf
574
575#if 0
576/**
577 * xmlRelaxNGErrorContext:
578 * @ctxt: the parsing context
579 * @schema: the schema being built
580 * @node: the node being processed
581 * @child: the child being processed
582 *
583 * Dump a RelaxNGType structure
584 */
585static void
586xmlRelaxNGErrorContext(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGPtr schema,
587 xmlNodePtr node, xmlNodePtr child)
588{
589 int line = 0;
590 const xmlChar *file = NULL;
591 const xmlChar *name = NULL;
592 const char *type = "error";
593
594 if ((ctxt == NULL) || (ctxt->error == NULL))
595 return;
596
597 if (child != NULL)
598 node = child;
599
600 if (node != NULL) {
601 if ((node->type == XML_DOCUMENT_NODE) ||
602 (node->type == XML_HTML_DOCUMENT_NODE)) {
603 xmlDocPtr doc = (xmlDocPtr) node;
604
605 file = doc->URL;
606 } else {
607 /*
608 * Try to find contextual informations to report
609 */
610 if (node->type == XML_ELEMENT_NODE) {
611 line = (int) node->content;
612 } else if ((node->prev != NULL) &&
613 (node->prev->type == XML_ELEMENT_NODE)) {
614 line = (int) node->prev->content;
615 } else if ((node->parent != NULL) &&
616 (node->parent->type == XML_ELEMENT_NODE)) {
617 line = (int) node->parent->content;
618 }
619 if ((node->doc != NULL) && (node->doc->URL != NULL))
620 file = node->doc->URL;
621 if (node->name != NULL)
622 name = node->name;
623 }
624 }
625
626 if (ctxt != NULL)
627 type = "compilation error";
628 else if (schema != NULL)
629 type = "runtime error";
630
631 if ((file != NULL) && (line != 0) && (name != NULL))
632 ctxt->error(ctxt->userData, "%s: file %s line %d element %s\n",
633 type, file, line, name);
634 else if ((file != NULL) && (name != NULL))
635 ctxt->error(ctxt->userData, "%s: file %s element %s\n",
636 type, file, name);
637 else if ((file != NULL) && (line != 0))
638 ctxt->error(ctxt->userData, "%s: file %s line %d\n", type, file, line);
639 else if (file != NULL)
640 ctxt->error(ctxt->userData, "%s: file %s\n", type, file);
641 else if (name != NULL)
642 ctxt->error(ctxt->userData, "%s: element %s\n", type, name);
643 else
644 ctxt->error(ctxt->userData, "%s\n", type);
645}
646#endif
647
648/************************************************************************
649 * *
650 * Type library hooks *
651 * *
652 ************************************************************************/
653
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000654/**
655 * xmlRelaxNGSchemaTypeHave:
656 * @data: data needed for the library
657 * @type: the type name
658 *
659 * Check if the given type is provided by
660 * the W3C XMLSchema Datatype library.
661 *
662 * Returns 1 if yes, 0 if no and -1 in case of error.
663 */
664static int
665xmlRelaxNGSchemaTypeHave(void *data ATTRIBUTE_UNUSED,
666 const xmlChar *type ATTRIBUTE_UNUSED) {
667 TODO
668 return(1);
669}
670
671/**
672 * xmlRelaxNGSchemaTypeCheck:
673 * @data: data needed for the library
674 * @type: the type name
675 * @value: the value to check
676 *
677 * Check if the given type and value are validated by
678 * the W3C XMLSchema Datatype library.
679 *
680 * Returns 1 if yes, 0 if no and -1 in case of error.
681 */
682static int
683xmlRelaxNGSchemaTypeCheck(void *data ATTRIBUTE_UNUSED,
684 const xmlChar *type ATTRIBUTE_UNUSED,
685 const xmlChar *value ATTRIBUTE_UNUSED) {
686 TODO
687 return(1);
688}
689
690/**
691 * xmlRelaxNGSchemaTypeCompare:
692 * @data: data needed for the library
693 * @type: the type name
694 * @value1: the first value
695 * @value2: the second value
696 *
697 * Compare two values accordingly a type from the W3C XMLSchema
698 * Datatype library.
699 *
700 * Returns 1 if yes, 0 if no and -1 in case of error.
701 */
702static int
703xmlRelaxNGSchemaTypeCompare(void *data ATTRIBUTE_UNUSED,
704 const xmlChar *type ATTRIBUTE_UNUSED,
705 const xmlChar *value1 ATTRIBUTE_UNUSED,
706 const xmlChar *value2 ATTRIBUTE_UNUSED) {
707 TODO
708 return(1);
709}
710
711/**
712 * xmlRelaxNGDefaultTypeHave:
713 * @data: data needed for the library
714 * @type: the type name
715 *
716 * Check if the given type is provided by
717 * the default datatype library.
718 *
719 * Returns 1 if yes, 0 if no and -1 in case of error.
720 */
721static int
722xmlRelaxNGDefaultTypeHave(void *data ATTRIBUTE_UNUSED, const xmlChar *type) {
723 if (type == NULL)
724 return(-1);
725 if (xmlStrEqual(type, BAD_CAST "string"))
726 return(1);
727 if (xmlStrEqual(type, BAD_CAST "token"))
728 return(1);
729 return(0);
730}
731
732/**
733 * xmlRelaxNGDefaultTypeCheck:
734 * @data: data needed for the library
735 * @type: the type name
736 * @value: the value to check
737 *
738 * Check if the given type and value are validated by
739 * the default datatype library.
740 *
741 * Returns 1 if yes, 0 if no and -1 in case of error.
742 */
743static int
744xmlRelaxNGDefaultTypeCheck(void *data ATTRIBUTE_UNUSED,
745 const xmlChar *type ATTRIBUTE_UNUSED,
746 const xmlChar *value ATTRIBUTE_UNUSED) {
747 return(1);
748}
749
750/**
751 * xmlRelaxNGDefaultTypeCompare:
752 * @data: data needed for the library
753 * @type: the type name
754 * @value1: the first value
755 * @value2: the second value
756 *
757 * Compare two values accordingly a type from the default
758 * datatype library.
759 *
760 * Returns 1 if yes, 0 if no and -1 in case of error.
761 */
762static int
763xmlRelaxNGDefaultTypeCompare(void *data ATTRIBUTE_UNUSED,
764 const xmlChar *type ATTRIBUTE_UNUSED,
765 const xmlChar *value1 ATTRIBUTE_UNUSED,
766 const xmlChar *value2 ATTRIBUTE_UNUSED) {
767 TODO
768 return(1);
769}
770
771static int xmlRelaxNGTypeInitialized = 0;
772static xmlHashTablePtr xmlRelaxNGRegisteredTypes = NULL;
773
774/**
775 * xmlRelaxNGFreeTypeLibrary:
776 * @lib: the type library structure
777 * @namespace: the URI bound to the library
778 *
779 * Free the structure associated to the type library
780 */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000781static void
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000782xmlRelaxNGFreeTypeLibrary(xmlRelaxNGTypeLibraryPtr lib,
783 const xmlChar *namespace ATTRIBUTE_UNUSED) {
784 if (lib == NULL)
785 return;
786 if (lib->namespace != NULL)
787 xmlFree((xmlChar *)lib->namespace);
788 xmlFree(lib);
789}
790
791/**
792 * xmlRelaxNGRegisterTypeLibrary:
793 * @namespace: the URI bound to the library
794 * @data: data associated to the library
795 * @have: the provide function
796 * @check: the checking function
797 * @comp: the comparison function
798 *
799 * Register a new type library
800 *
801 * Returns 0 in case of success and -1 in case of error.
802 */
803static int
804xmlRelaxNGRegisterTypeLibrary(const xmlChar *namespace, void *data,
805 xmlRelaxNGTypeHave have, xmlRelaxNGTypeCheck check,
806 xmlRelaxNGTypeCompare comp) {
807 xmlRelaxNGTypeLibraryPtr lib;
808 int ret;
809
810 if ((xmlRelaxNGRegisteredTypes == NULL) || (namespace == NULL) ||
811 (check == NULL) || (comp == NULL))
812 return(-1);
813 if (xmlHashLookup(xmlRelaxNGRegisteredTypes, namespace) != NULL) {
814 xmlGenericError(xmlGenericErrorContext,
815 "Relax-NG types library '%s' already registered\n",
816 namespace);
817 return(-1);
818 }
819 lib = (xmlRelaxNGTypeLibraryPtr) xmlMalloc(sizeof(xmlRelaxNGTypeLibrary));
820 if (lib == NULL) {
821 xmlGenericError(xmlGenericErrorContext,
822 "Relax-NG types library '%s' malloc() failed\n",
823 namespace);
824 return (-1);
825 }
826 memset(lib, 0, sizeof(xmlRelaxNGTypeLibrary));
827 lib->namespace = xmlStrdup(namespace);
828 lib->data = data;
829 lib->have = have;
830 lib->comp = comp;
831 lib->check = check;
832 ret = xmlHashAddEntry(xmlRelaxNGRegisteredTypes, namespace, lib);
833 if (ret < 0) {
834 xmlGenericError(xmlGenericErrorContext,
835 "Relax-NG types library failed to register '%s'\n",
836 namespace);
837 xmlRelaxNGFreeTypeLibrary(lib, namespace);
838 return(-1);
839 }
840 return(0);
841}
842
843/**
844 * xmlRelaxNGInitTypes:
845 *
846 * Initilize the default type libraries.
847 *
848 * Returns 0 in case of success and -1 in case of error.
849 */
850static int
Daniel Veillard6eadf632003-01-23 18:29:16 +0000851xmlRelaxNGInitTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000852 if (xmlRelaxNGTypeInitialized != 0)
853 return(0);
854 xmlRelaxNGRegisteredTypes = xmlHashCreate(10);
855 if (xmlRelaxNGRegisteredTypes == NULL) {
856 xmlGenericError(xmlGenericErrorContext,
857 "Failed to allocate sh table for Relax-NG types\n");
858 return(-1);
859 }
860 xmlRelaxNGRegisterTypeLibrary(
861 BAD_CAST "http://www.w3.org/2001/XMLSchema-datatypes",
862 NULL,
863 xmlRelaxNGSchemaTypeHave,
864 xmlRelaxNGSchemaTypeCheck,
865 xmlRelaxNGSchemaTypeCompare);
866 xmlRelaxNGRegisterTypeLibrary(
867 xmlRelaxNGNs,
868 NULL,
869 xmlRelaxNGDefaultTypeHave,
870 xmlRelaxNGDefaultTypeCheck,
871 xmlRelaxNGDefaultTypeCompare);
872 xmlRelaxNGTypeInitialized = 1;
873 return(0);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000874}
875
876/**
877 * xmlRelaxNGCleanupTypes:
878 *
879 * Cleanup the default Schemas type library associated to RelaxNG
880 */
881void
882xmlRelaxNGCleanupTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000883 if (xmlRelaxNGTypeInitialized == 0)
884 return;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000885 xmlSchemaCleanupTypes();
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000886 xmlHashFree(xmlRelaxNGRegisteredTypes, (xmlHashDeallocator)
887 xmlRelaxNGFreeTypeLibrary);
888 xmlRelaxNGTypeInitialized = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000889}
890
891/************************************************************************
892 * *
893 * Parsing functions *
894 * *
895 ************************************************************************/
896
897static xmlRelaxNGDefinePtr xmlRelaxNGParseAttribute(
898 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
899static xmlRelaxNGDefinePtr xmlRelaxNGParseElement(
900 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
901static xmlRelaxNGDefinePtr xmlRelaxNGParsePatterns(
902 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
903
904
905#define IS_BLANK_NODE(n) \
906 (((n)->type == XML_TEXT_NODE) && (xmlRelaxNGIsBlank((n)->content)))
907
908/**
909 * xmlRelaxNGIsBlank:
910 * @str: a string
911 *
912 * Check if a string is ignorable c.f. 4.2. Whitespace
913 *
914 * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
915 */
916static int
917xmlRelaxNGIsBlank(xmlChar *str) {
918 if (str == NULL)
919 return(1);
920 while (*str != 0) {
921 if (!(IS_BLANK(*str))) return(0);
922 str++;
923 }
924 return(1);
925}
926
Daniel Veillard6eadf632003-01-23 18:29:16 +0000927/**
928 * xmlRelaxNGGetDataTypeLibrary:
929 * @ctxt: a Relax-NG parser context
930 * @node: the current data or value element
931 *
932 * Applies algorithm from 4.3. datatypeLibrary attribute
933 *
934 * Returns the datatypeLibary value or NULL if not found
935 */
936static xmlChar *
937xmlRelaxNGGetDataTypeLibrary(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
938 xmlNodePtr node) {
939 xmlChar *ret, *escape;
940
Daniel Veillard6eadf632003-01-23 18:29:16 +0000941 if ((IS_RELAXNG(node, "data")) || (IS_RELAXNG(node, "value"))) {
942 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
943 if (ret != NULL) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000944 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +0000945 if (escape == NULL) {
946 return(ret);
947 }
948 xmlFree(ret);
949 return(escape);
950 }
951 }
952 node = node->parent;
953 while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) {
954 if (IS_RELAXNG(node, "element")) {
955 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
956 if (ret != NULL) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000957 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +0000958 if (escape == NULL) {
959 return(ret);
960 }
961 xmlFree(ret);
962 return(escape);
963 }
964 }
965 node = node->parent;
966 }
967 return(NULL);
968}
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000969
970/**
971 * xmlRelaxNGParseData:
972 * @ctxt: a Relax-NG parser context
973 * @node: the data node.
974 *
975 * parse the content of a RelaxNG data node.
976 *
977 * Returns the definition pointer or NULL in case of error
978 */
979static xmlRelaxNGDefinePtr
980xmlRelaxNGParseData(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
981 xmlRelaxNGDefinePtr def = NULL;
982 xmlRelaxNGTypeLibraryPtr lib;
983 xmlChar *type;
984 xmlChar *library;
985 xmlNodePtr content;
986 int tmp;
987
988 type = xmlGetProp(node, BAD_CAST "type");
989 if (type == NULL) {
990 if (ctxt->error != NULL)
991 ctxt->error(ctxt->userData,
992 "data has no type\n");
993 ctxt->nbErrors++;
994 return(NULL);
995 }
996 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
997 if (library == NULL)
998 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
999
1000 def = xmlRelaxNGNewDefine(ctxt, node);
1001 if (def == NULL) {
1002 xmlFree(type);
1003 return(NULL);
1004 }
1005 def->type = XML_RELAXNG_DATATYPE;
1006 def->name = type;
1007 def->ns = library;
1008
1009 lib = (xmlRelaxNGTypeLibraryPtr)
1010 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1011 if (lib == NULL) {
1012 if (ctxt->error != NULL)
1013 ctxt->error(ctxt->userData,
1014 "Use of unregistered type library '%s'\n",
1015 library);
1016 ctxt->nbErrors++;
1017 def->data = NULL;
1018 } else {
1019 def->data = lib;
1020 if (lib->have == NULL) {
1021 ctxt->error(ctxt->userData,
1022 "Internal error with type library '%s': no 'have'\n",
1023 library);
1024 ctxt->nbErrors++;
1025 } else {
1026 tmp = lib->have(lib->data, def->name);
1027 if (tmp != 1) {
1028 ctxt->error(ctxt->userData,
1029 "Error type '%s' is not exported by type library '%s'\n",
1030 def->name, library);
1031 ctxt->nbErrors++;
1032 }
1033 }
1034 }
1035 content = node->children;
1036 while (content != NULL) {
1037 TODO
1038 content = content->next;
1039 }
1040
1041 return(def);
1042}
1043
Daniel Veillard6eadf632003-01-23 18:29:16 +00001044
1045/**
Daniel Veillard276be4a2003-01-24 01:03:34 +00001046 * xmlRelaxNGParseDefine:
1047 * @ctxt: a Relax-NG parser context
1048 * @node: the define node
1049 *
1050 * parse the content of a RelaxNG define element node.
1051 *
1052 * Returns the definition pointer or NULL in case of error.
1053 */
1054static int
1055xmlRelaxNGParseDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1056 xmlChar *name;
1057 int ret = 0, tmp;
1058 xmlRelaxNGDefinePtr def;
1059 const xmlChar *olddefine;
1060
1061 name = xmlGetProp(node, BAD_CAST "name");
1062 if (name == NULL) {
1063 if (ctxt->error != NULL)
1064 ctxt->error(ctxt->userData,
1065 "define has no name\n");
1066 ctxt->nbErrors++;
1067 } else {
1068 def = xmlRelaxNGNewDefine(ctxt, node);
1069 if (def == NULL) {
1070 xmlFree(name);
1071 return(-1);
1072 }
1073 def->type = XML_RELAXNG_DEF;
1074 def->name = name;
1075 if (node->children == NULL) {
1076 if (ctxt->error != NULL)
1077 ctxt->error(ctxt->userData,
1078 "define has no children\n");
1079 ctxt->nbErrors++;
1080 } else {
1081 olddefine = ctxt->define;
1082 ctxt->define = name;
1083 def->content = xmlRelaxNGParsePatterns(ctxt,
1084 node->children);
1085 ctxt->define = olddefine;
1086 }
1087 if (ctxt->grammar->defs == NULL)
1088 ctxt->grammar->defs = xmlHashCreate(10);
1089 if (ctxt->grammar->defs == NULL) {
1090 if (ctxt->error != NULL)
1091 ctxt->error(ctxt->userData,
1092 "Could not create definition hash\n");
1093 ctxt->nbErrors++;
1094 ret = -1;
1095 xmlRelaxNGFreeDefine(def);
1096 } else {
1097 tmp = xmlHashAddEntry(ctxt->grammar->defs, name, def);
1098 if (tmp < 0) {
1099 TODO
1100 /* store and implement 4.17 on combining */
1101 ctxt->nbErrors++;
1102 ret = -1;
1103 xmlRelaxNGFreeDefine(def);
1104 }
1105 }
1106 }
1107 return(ret);
1108}
1109
1110/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00001111 * xmlRelaxNGParsePattern:
1112 * @ctxt: a Relax-NG parser context
1113 * @node: the pattern node.
1114 *
1115 * parse the content of a RelaxNG pattern node.
1116 *
Daniel Veillard276be4a2003-01-24 01:03:34 +00001117 * Returns the definition pointer or NULL in case of error or if no
1118 * pattern is generated.
Daniel Veillard6eadf632003-01-23 18:29:16 +00001119 */
1120static xmlRelaxNGDefinePtr
1121xmlRelaxNGParsePattern(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1122 xmlRelaxNGDefinePtr def = NULL;
1123
1124 if (IS_RELAXNG(node, "element")) {
1125 def = xmlRelaxNGParseElement(ctxt, node);
1126 } else if (IS_RELAXNG(node, "attribute")) {
1127 def = xmlRelaxNGParseAttribute(ctxt, node);
1128 } else if (IS_RELAXNG(node, "empty")) {
1129 def = xmlRelaxNGNewDefine(ctxt, node);
1130 if (def == NULL)
1131 return(NULL);
1132 def->type = XML_RELAXNG_EMPTY;
1133 } else if (IS_RELAXNG(node, "text")) {
1134 def = xmlRelaxNGNewDefine(ctxt, node);
1135 if (def == NULL)
1136 return(NULL);
1137 def->type = XML_RELAXNG_TEXT;
1138 if (node->children != NULL) {
1139 if (ctxt->error != NULL)
1140 ctxt->error(ctxt->userData, "text: had a child node\n");
1141 ctxt->nbErrors++;
1142 }
1143 } else if (IS_RELAXNG(node, "zeroOrMore")) {
1144 def = xmlRelaxNGNewDefine(ctxt, node);
1145 if (def == NULL)
1146 return(NULL);
1147 def->type = XML_RELAXNG_ZEROORMORE;
1148 def->content = xmlRelaxNGParsePatterns(ctxt, node->children);
1149 } else if (IS_RELAXNG(node, "oneOrMore")) {
1150 def = xmlRelaxNGNewDefine(ctxt, node);
1151 if (def == NULL)
1152 return(NULL);
1153 def->type = XML_RELAXNG_ZEROORMORE;
1154 def->content = xmlRelaxNGParsePatterns(ctxt, node->children);
1155 } else if (IS_RELAXNG(node, "optional")) {
1156 def = xmlRelaxNGNewDefine(ctxt, node);
1157 if (def == NULL)
1158 return(NULL);
1159 def->type = XML_RELAXNG_OPTIONAL;
1160 def->content = xmlRelaxNGParsePatterns(ctxt, node->children);
1161 } else if (IS_RELAXNG(node, "choice")) {
1162 def = xmlRelaxNGNewDefine(ctxt, node);
1163 if (def == NULL)
1164 return(NULL);
1165 def->type = XML_RELAXNG_CHOICE;
1166 def->content = xmlRelaxNGParsePatterns(ctxt, node->children);
1167 } else if (IS_RELAXNG(node, "group")) {
1168 def = xmlRelaxNGNewDefine(ctxt, node);
1169 if (def == NULL)
1170 return(NULL);
1171 def->type = XML_RELAXNG_GROUP;
1172 def->content = xmlRelaxNGParsePatterns(ctxt, node->children);
1173 } else if (IS_RELAXNG(node, "ref")) {
1174 def = xmlRelaxNGNewDefine(ctxt, node);
1175 if (def == NULL)
1176 return(NULL);
1177 def->type = XML_RELAXNG_REF;
1178 def->name = xmlGetProp(node, BAD_CAST "name");
1179 if (def->name == NULL) {
1180 if (ctxt->error != NULL)
1181 ctxt->error(ctxt->userData,
1182 "ref has no name\n");
1183 ctxt->nbErrors++;
Daniel Veillard276be4a2003-01-24 01:03:34 +00001184 } else {
1185 if ((ctxt->define != NULL) &&
1186 (xmlStrEqual(ctxt->define, def->name))) {
1187 if (ctxt->error != NULL)
1188 ctxt->error(ctxt->userData,
1189 "Recursive reference to %s not in an element\n",
1190 def->name);
1191 ctxt->nbErrors++;
1192 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00001193 }
1194 if (node->children != NULL) {
1195 if (ctxt->error != NULL)
1196 ctxt->error(ctxt->userData,
1197 "ref is not empty\n");
1198 ctxt->nbErrors++;
1199 }
1200 if (ctxt->grammar->refs == NULL)
1201 ctxt->grammar->refs = xmlHashCreate(10);
1202 if (ctxt->grammar->refs == NULL) {
1203 if (ctxt->error != NULL)
1204 ctxt->error(ctxt->userData,
1205 "Could not create references hash\n");
1206 ctxt->nbErrors++;
1207 xmlRelaxNGFreeDefine(def);
1208 def = NULL;
1209 } else {
1210 int tmp;
1211
1212 tmp = xmlHashAddEntry(ctxt->grammar->refs, def->name, def);
1213 if (tmp < 0) {
1214 xmlRelaxNGDefinePtr prev;
1215
1216 prev = (xmlRelaxNGDefinePtr)
1217 xmlHashLookup(ctxt->grammar->refs, def->name);
1218 if (prev == NULL) {
1219 if (ctxt->error != NULL)
1220 ctxt->error(ctxt->userData,
1221 "Internal error refs definitions '%s'\n",
1222 def->name);
1223 ctxt->nbErrors++;
1224 xmlRelaxNGFreeDefine(def);
1225 def = NULL;
1226 } else {
1227 def->nextHash = prev->nextHash;
1228 prev->nextHash = def;
1229 }
1230 }
1231 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001232 } else if (IS_RELAXNG(node, "data")) {
1233 def = xmlRelaxNGParseData(ctxt, node);
Daniel Veillard276be4a2003-01-24 01:03:34 +00001234 } else if (IS_RELAXNG(node, "define")) {
1235 xmlRelaxNGParseDefine(ctxt, node);
1236 def = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001237 } else {
1238 TODO
1239 }
1240 return(def);
1241}
1242
1243/**
1244 * xmlRelaxNGParseAttribute:
1245 * @ctxt: a Relax-NG parser context
1246 * @node: the element node
1247 *
1248 * parse the content of a RelaxNG attribute node.
1249 *
1250 * Returns the definition pointer or NULL in case of error.
1251 */
1252static xmlRelaxNGDefinePtr
1253xmlRelaxNGParseAttribute(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1254 xmlRelaxNGDefinePtr ret, cur, last;
1255 xmlNodePtr child;
1256 xmlChar *val;
1257 int old_flags;
1258
1259 ret = xmlRelaxNGNewDefine(ctxt, node);
1260 if (ret == NULL)
1261 return(NULL);
1262 ret->type = XML_RELAXNG_ATTRIBUTE;
1263 child = node->children;
1264 if (child == NULL) {
1265 if (ctxt->error != NULL)
1266 ctxt->error(ctxt->userData,
1267 "xmlRelaxNGParseattribute: attribute has no children\n");
1268 ctxt->nbErrors++;
1269 return(ret);
1270 }
1271 old_flags = ctxt->flags;
1272 ctxt->flags |= XML_RELAXNG_IN_ATTRIBUTE;
1273 if (IS_RELAXNG(child, "name")) {
1274 val = xmlNodeGetContent(child);
1275 ret->name = val;
1276 val = xmlGetProp(child, BAD_CAST "ns");
1277 ret->ns = val;
1278 } else if (IS_RELAXNG(child, "anyName")) {
1279 TODO
1280 } else if (IS_RELAXNG(child, "nsName")) {
1281 TODO
1282 } else if (IS_RELAXNG(child, "choice")) {
1283 TODO
1284 } else {
1285 if (ctxt->error != NULL)
1286 ctxt->error(ctxt->userData,
1287 "element: expecting name, anyName, nsName or choice : got %s\n",
1288 child->name);
1289 ctxt->nbErrors++;
1290 ctxt->flags = old_flags;
1291 return(ret);
1292 }
1293 child = child->next;
1294 last = NULL;
1295 while (child != NULL) {
1296 cur = xmlRelaxNGParsePattern(ctxt, child);
1297 if (cur != NULL) {
1298 switch (cur->type) {
1299 case XML_RELAXNG_EMPTY:
1300 case XML_RELAXNG_NOT_ALLOWED:
1301 case XML_RELAXNG_TEXT:
1302 case XML_RELAXNG_ELEMENT:
1303 case XML_RELAXNG_DATATYPE:
1304 case XML_RELAXNG_VALUE:
1305 case XML_RELAXNG_LIST:
1306 case XML_RELAXNG_REF:
1307 case XML_RELAXNG_DEF:
1308 case XML_RELAXNG_ONEORMORE:
1309 case XML_RELAXNG_ZEROORMORE:
1310 case XML_RELAXNG_OPTIONAL:
1311 case XML_RELAXNG_CHOICE:
1312 case XML_RELAXNG_GROUP:
1313 case XML_RELAXNG_INTERLEAVE:
1314 if (last == NULL) {
1315 ret->content = last = cur;
1316 } else {
1317 if ((last->type == XML_RELAXNG_ELEMENT) &&
1318 (ret->content == last)) {
1319 ret->content = xmlRelaxNGNewDefine(ctxt, node);
1320 if (ret->content != NULL) {
1321 ret->content->type = XML_RELAXNG_GROUP;
1322 ret->content->content = last;
1323 } else {
1324 ret->content = last;
1325 }
1326 }
1327 last->next = cur;
1328 last = cur;
1329 }
1330 break;
1331 case XML_RELAXNG_ATTRIBUTE:
1332 cur->next = ret->attrs;
1333 ret->attrs = cur;
1334 break;
1335 }
1336 }
1337 child = child->next;
1338 }
1339 ctxt->flags = old_flags;
1340 return(ret);
1341}
1342
1343/**
1344 * xmlRelaxNGParseElement:
1345 * @ctxt: a Relax-NG parser context
1346 * @node: the element node
1347 *
1348 * parse the content of a RelaxNG element node.
1349 *
1350 * Returns the definition pointer or NULL in case of error.
1351 */
1352static xmlRelaxNGDefinePtr
1353xmlRelaxNGParseElement(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1354 xmlRelaxNGDefinePtr ret, cur, last;
1355 xmlNodePtr child;
1356 xmlChar *val;
Daniel Veillard276be4a2003-01-24 01:03:34 +00001357 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001358
1359 ret = xmlRelaxNGNewDefine(ctxt, node);
1360 if (ret == NULL)
1361 return(NULL);
1362 ret->type = XML_RELAXNG_ELEMENT;
1363 child = node->children;
1364 if (child == NULL) {
1365 if (ctxt->error != NULL)
1366 ctxt->error(ctxt->userData,
1367 "xmlRelaxNGParseElement: element has no children\n");
1368 ctxt->nbErrors++;
1369 return(ret);
1370 }
1371 if (IS_RELAXNG(child, "name")) {
1372 val = xmlNodeGetContent(child);
1373 ret->name = val;
1374 val = xmlGetProp(child, BAD_CAST "ns");
1375 ret->ns = val;
1376 } else if (IS_RELAXNG(child, "anyName")) {
1377 TODO
1378 } else if (IS_RELAXNG(child, "nsName")) {
1379 TODO
1380 } else if (IS_RELAXNG(child, "choice")) {
1381 TODO
1382 } else {
1383 if (ctxt->error != NULL)
1384 ctxt->error(ctxt->userData,
1385 "element: expecting name, anyName, nsName or choice : got %s\n",
1386 child->name);
1387 ctxt->nbErrors++;
1388 return(ret);
1389 }
1390 child = child->next;
1391 if (child == NULL) {
1392 if (ctxt->error != NULL)
1393 ctxt->error(ctxt->userData,
1394 "xmlRelaxNGParseElement: element has no content\n");
1395 ctxt->nbErrors++;
1396 return(ret);
1397 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00001398 olddefine = ctxt->define;
1399 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001400 last = NULL;
1401 while (child != NULL) {
1402 cur = xmlRelaxNGParsePattern(ctxt, child);
1403 if (cur != NULL) {
1404 switch (cur->type) {
1405 case XML_RELAXNG_EMPTY:
1406 case XML_RELAXNG_NOT_ALLOWED:
1407 case XML_RELAXNG_TEXT:
1408 case XML_RELAXNG_ELEMENT:
1409 case XML_RELAXNG_DATATYPE:
1410 case XML_RELAXNG_VALUE:
1411 case XML_RELAXNG_LIST:
1412 case XML_RELAXNG_REF:
1413 case XML_RELAXNG_DEF:
1414 case XML_RELAXNG_ZEROORMORE:
1415 case XML_RELAXNG_ONEORMORE:
1416 case XML_RELAXNG_OPTIONAL:
1417 case XML_RELAXNG_CHOICE:
1418 case XML_RELAXNG_GROUP:
1419 case XML_RELAXNG_INTERLEAVE:
1420 if (last == NULL) {
1421 ret->content = last = cur;
1422 } else {
1423 if ((last->type == XML_RELAXNG_ELEMENT) &&
1424 (ret->content == last)) {
1425 ret->content = xmlRelaxNGNewDefine(ctxt, node);
1426 if (ret->content != NULL) {
1427 ret->content->type = XML_RELAXNG_GROUP;
1428 ret->content->content = last;
1429 } else {
1430 ret->content = last;
1431 }
1432 }
1433 last->next = cur;
1434 last = cur;
1435 }
1436 break;
1437 case XML_RELAXNG_ATTRIBUTE:
1438 cur->next = ret->attrs;
1439 ret->attrs = cur;
1440 break;
1441 }
1442 }
1443 child = child->next;
1444 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00001445 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001446 return(ret);
1447}
1448
1449/**
1450 * xmlRelaxNGParsePatterns:
1451 * @ctxt: a Relax-NG parser context
1452 * @nodes: list of nodes
1453 *
1454 * parse the content of a RelaxNG start node.
1455 *
1456 * Returns the definition pointer or NULL in case of error.
1457 */
1458static xmlRelaxNGDefinePtr
1459xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
1460 xmlRelaxNGDefinePtr def = NULL, last = NULL, cur;
1461
1462 while (nodes != NULL) {
1463 if (IS_RELAXNG(nodes, "element")) {
1464 cur = xmlRelaxNGParseElement(ctxt, nodes);
1465 if (def == NULL) {
1466 def = last = cur;
1467 } else {
1468 if ((def->type == XML_RELAXNG_ELEMENT) && (def == last)) {
1469 def = xmlRelaxNGNewDefine(ctxt, nodes);
1470 def->type = XML_RELAXNG_GROUP;
1471 def->content = last;
1472 }
1473 last->next = cur;
1474 last = cur;
1475 }
1476 } else {
1477 cur = xmlRelaxNGParsePattern(ctxt, nodes);
1478 if (def == NULL) {
1479 def = last = cur;
1480 } else {
1481 last->next = cur;
1482 last = cur;
1483 }
1484 }
1485 nodes = nodes->next;
1486 }
1487 return(def);
1488}
1489
1490/**
1491 * xmlRelaxNGParseStart:
1492 * @ctxt: a Relax-NG parser context
1493 * @nodes: start children nodes
1494 *
1495 * parse the content of a RelaxNG start node.
1496 *
1497 * Returns 0 in case of success, -1 in case of error
1498 */
1499static int
1500xmlRelaxNGParseStart(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
1501 int ret = 0;
1502 xmlRelaxNGDefinePtr def = NULL;
1503
1504 while (nodes != NULL) {
1505 if (IS_RELAXNG(nodes, "empty")) {
1506 TODO
1507 xmlElemDump(stdout, nodes->doc, nodes);
1508 } else if (IS_RELAXNG(nodes, "notAllowed")) {
1509 TODO
1510 xmlElemDump(stdout, nodes->doc, nodes);
1511 } else {
1512 def = xmlRelaxNGParsePatterns(ctxt, nodes);
1513 ctxt->grammar->start = def;
1514 }
1515 nodes = nodes->next;
1516 }
1517 return(ret);
1518}
1519
1520/**
1521 * xmlRelaxNGParseGrammarContent:
1522 * @ctxt: a Relax-NG parser context
1523 * @nodes: grammar children nodes
1524 *
1525 * parse the content of a RelaxNG grammar node.
1526 *
1527 * Returns 0 in case of success, -1 in case of error
1528 */
1529static int
1530xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt
1531 ATTRIBUTE_UNUSED, xmlNodePtr nodes)
1532{
Daniel Veillard276be4a2003-01-24 01:03:34 +00001533 int ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001534
1535 if (nodes == NULL) {
1536 if (ctxt->error != NULL)
1537 ctxt->error(ctxt->userData,
1538 "grammar has no children\n");
1539 ctxt->nbErrors++;
1540 return(-1);
1541 }
1542 if (IS_RELAXNG(nodes, "start")) {
1543 if (nodes->children == NULL) {
1544 if (ctxt->error != NULL)
1545 ctxt->error(ctxt->userData,
1546 "grammar has no children\n");
1547 ctxt->nbErrors++;
1548 } else {
1549 xmlRelaxNGParseStart(ctxt, nodes->children);
1550 }
1551 nodes = nodes->next;
1552 } else {
1553 if (ctxt->error != NULL)
1554 ctxt->error(ctxt->userData,
1555 "grammar first child must be a <start>\n");
1556 ctxt->nbErrors++;
1557 return(-1);
1558 }
1559 while (nodes != NULL) {
1560 if (IS_RELAXNG(nodes, "define")) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00001561 ret = xmlRelaxNGParseDefine(ctxt, nodes);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001562 } else {
1563 if (ctxt->error != NULL)
1564 ctxt->error(ctxt->userData,
1565 "grammar allows onlys <define> child after <start>\n");
1566 ctxt->nbErrors++;
1567 ret = -1;
1568 }
1569 nodes = nodes->next;
1570 }
1571 return (ret);
1572}
1573
1574/**
1575 * xmlRelaxNGCheckReference:
1576 * @ref: the ref
1577 * @ctxt: a Relax-NG parser context
1578 * @name: the name associated to the defines
1579 *
1580 * Applies the 4.17. combine attribute rule for all the define
1581 * element of a given grammar using the same name.
1582 */
1583static void
1584xmlRelaxNGCheckReference(xmlRelaxNGDefinePtr ref,
1585 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
1586 xmlRelaxNGGrammarPtr grammar;
Daniel Veillard276be4a2003-01-24 01:03:34 +00001587 xmlRelaxNGDefinePtr def, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001588
1589 grammar = ctxt->grammar;
1590 if (grammar == NULL) {
1591 if (ctxt->error != NULL)
1592 ctxt->error(ctxt->userData,
1593 "Internal error: no grammar in CheckReference %s\n",
1594 name);
1595 ctxt->nbErrors++;
1596 return;
1597 }
1598 if (ref->content != NULL) {
1599 if (ctxt->error != NULL)
1600 ctxt->error(ctxt->userData,
1601 "Internal error: reference has content in CheckReference %s\n",
1602 name);
1603 ctxt->nbErrors++;
1604 return;
1605 }
1606 if (grammar->defs != NULL) {
1607 def = xmlHashLookup(grammar->defs, name);
1608 if (def != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00001609 cur = ref;
1610 while (cur != NULL) {
1611 cur->content = def;
1612 cur = cur->nextHash;
1613 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00001614 } else {
1615 TODO
1616 }
1617 }
1618 /*
1619 * TODO: make a closure and verify there is no loop !
1620 */
1621}
1622
1623/**
1624 * xmlRelaxNGCheckCombine:
1625 * @define: the define(s) list
1626 * @ctxt: a Relax-NG parser context
1627 * @name: the name associated to the defines
1628 *
1629 * Applies the 4.17. combine attribute rule for all the define
1630 * element of a given grammar using the same name.
1631 */
1632static void
1633xmlRelaxNGCheckCombine(xmlRelaxNGDefinePtr define,
1634 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
1635 xmlChar *combine;
1636 int choiceOrInterleave = -1;
1637 int missing = 0;
1638 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
1639
1640 if (define->nextHash == NULL)
1641 return;
1642 cur = define;
1643 while (cur != NULL) {
1644 combine = xmlGetProp(cur->node, BAD_CAST "combine");
1645 if (combine != NULL) {
1646 if (xmlStrEqual(combine, BAD_CAST "choice")) {
1647 if (choiceOrInterleave == -1)
1648 choiceOrInterleave = 1;
1649 else if (choiceOrInterleave == 0) {
1650 if (ctxt->error != NULL)
1651 ctxt->error(ctxt->userData,
1652 "Defines for %s use both 'choice' and 'interleave'\n",
1653 name);
1654 ctxt->nbErrors++;
1655 }
1656 } else if (xmlStrEqual(combine, BAD_CAST "choice")) {
1657 if (choiceOrInterleave == -1)
1658 choiceOrInterleave = 0;
1659 else if (choiceOrInterleave == 1) {
1660 if (ctxt->error != NULL)
1661 ctxt->error(ctxt->userData,
1662 "Defines for %s use both 'choice' and 'interleave'\n",
1663 name);
1664 ctxt->nbErrors++;
1665 }
1666 } else {
1667 if (ctxt->error != NULL)
1668 ctxt->error(ctxt->userData,
1669 "Defines for %s use unknown combine value '%s''\n",
1670 name, combine);
1671 ctxt->nbErrors++;
1672 }
1673 xmlFree(combine);
1674 } else {
1675 if (missing == 0)
1676 missing = 1;
1677 else {
1678 if (ctxt->error != NULL)
1679 ctxt->error(ctxt->userData,
1680 "Some defines for %s lacks the combine attribute\n",
1681 name);
1682 ctxt->nbErrors++;
1683 }
1684 }
1685
1686 cur = cur->nextHash;
1687 }
1688#ifdef DEBUG
1689 xmlGenericError(xmlGenericErrorContext,
1690 "xmlRelaxNGCheckCombine(): merging %s defines: %d\n",
1691 name, choiceOrInterleave);
1692#endif
1693 if (choiceOrInterleave == -1)
1694 choiceOrInterleave = 0;
1695 cur = xmlRelaxNGNewDefine(ctxt, define->node);
1696 if (cur == NULL)
1697 return;
1698 if (choiceOrInterleave == 0)
1699 cur->type = XML_RELAXNG_CHOICE;
1700 else
1701 cur->type = XML_RELAXNG_INTERLEAVE;
1702 tmp = define;
1703 last = NULL;
1704 while (tmp != NULL) {
1705 if (tmp->content != NULL) {
1706 if (tmp->content->next != NULL) {
1707 /*
1708 * we need first to create a wrapper.
1709 */
1710 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
1711 if (tmp2 == NULL)
1712 break;
1713 tmp2->type = XML_RELAXNG_GROUP;
1714 tmp2->content = tmp->content;
1715 } else {
1716 tmp2 = tmp->content;
1717 }
1718 if (last == NULL) {
1719 cur->content = tmp2;
1720 } else {
1721 last->next = tmp2;
1722 }
1723 last = tmp2;
1724 tmp->content = NULL;
1725 }
1726 tmp = tmp->nextHash;
1727 }
1728 define->content = cur;
1729}
1730
1731/**
1732 * xmlRelaxNGCombineStart:
1733 * @ctxt: a Relax-NG parser context
1734 * @grammar: the grammar
1735 *
1736 * Applies the 4.17. combine rule for all the start
1737 * element of a given grammar.
1738 */
1739static void
1740xmlRelaxNGCombineStart(xmlRelaxNGParserCtxtPtr ctxt,
1741 xmlRelaxNGGrammarPtr grammar) {
1742 xmlRelaxNGDefinePtr starts;
1743 xmlChar *combine;
1744 int choiceOrInterleave = -1;
1745 int missing = 0;
1746 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
1747
1748 starts = grammar->start;
1749 if (starts->nextHash == NULL)
1750 return;
1751 cur = starts;
1752 while (cur != NULL) {
1753 combine = xmlGetProp(cur->node, BAD_CAST "combine");
1754 if (combine != NULL) {
1755 if (xmlStrEqual(combine, BAD_CAST "choice")) {
1756 if (choiceOrInterleave == -1)
1757 choiceOrInterleave = 1;
1758 else if (choiceOrInterleave == 0) {
1759 if (ctxt->error != NULL)
1760 ctxt->error(ctxt->userData,
1761 "<start> use both 'choice' and 'interleave'\n");
1762 ctxt->nbErrors++;
1763 }
1764 } else if (xmlStrEqual(combine, BAD_CAST "choice")) {
1765 if (choiceOrInterleave == -1)
1766 choiceOrInterleave = 0;
1767 else if (choiceOrInterleave == 1) {
1768 if (ctxt->error != NULL)
1769 ctxt->error(ctxt->userData,
1770 "<start> use both 'choice' and 'interleave'\n");
1771 ctxt->nbErrors++;
1772 }
1773 } else {
1774 if (ctxt->error != NULL)
1775 ctxt->error(ctxt->userData,
1776 "<start> uses unknown combine value '%s''\n", combine);
1777 ctxt->nbErrors++;
1778 }
1779 xmlFree(combine);
1780 } else {
1781 if (missing == 0)
1782 missing = 1;
1783 else {
1784 if (ctxt->error != NULL)
1785 ctxt->error(ctxt->userData,
1786 "Some <start> elements lacks the combine attribute\n");
1787 ctxt->nbErrors++;
1788 }
1789 }
1790
1791 cur = cur->nextHash;
1792 }
1793#ifdef DEBUG
1794 xmlGenericError(xmlGenericErrorContext,
1795 "xmlRelaxNGCombineStart(): merging <start>: %d\n",
1796 choiceOrInterleave);
1797#endif
1798 if (choiceOrInterleave == -1)
1799 choiceOrInterleave = 0;
1800 cur = xmlRelaxNGNewDefine(ctxt, starts->node);
1801 if (cur == NULL)
1802 return;
1803 if (choiceOrInterleave == 0)
1804 cur->type = XML_RELAXNG_CHOICE;
1805 else
1806 cur->type = XML_RELAXNG_INTERLEAVE;
1807 tmp = starts;
1808 last = NULL;
1809 while (tmp != NULL) {
1810 if (tmp->content != NULL) {
1811 if (tmp->content->next != NULL) {
1812 /*
1813 * we need first to create a wrapper.
1814 */
1815 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
1816 if (tmp2 == NULL)
1817 break;
1818 tmp2->type = XML_RELAXNG_GROUP;
1819 tmp2->content = tmp->content;
1820 } else {
1821 tmp2 = tmp->content;
1822 }
1823 if (last == NULL) {
1824 cur->content = tmp2;
1825 } else {
1826 last->next = tmp2;
1827 }
1828 last = tmp2;
1829 tmp->content = NULL;
1830 }
1831 tmp = tmp->nextHash;
1832 }
1833 starts->content = cur;
1834}
1835
1836/**
1837 * xmlRelaxNGParseGrammar:
1838 * @ctxt: a Relax-NG parser context
1839 * @nodes: grammar children nodes
1840 *
1841 * parse a Relax-NG <grammar> node
1842 *
1843 * Returns the internal xmlRelaxNGGrammarPtr built or
1844 * NULL in case of error
1845 */
1846static xmlRelaxNGGrammarPtr
1847xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
1848 xmlRelaxNGGrammarPtr ret, tmp, old;
1849
Daniel Veillard6eadf632003-01-23 18:29:16 +00001850 ret = xmlRelaxNGNewGrammar(ctxt);
1851 if (ret == NULL)
1852 return(NULL);
1853
1854 /*
1855 * Link the new grammar in the tree
1856 */
1857 ret->parent = ctxt->grammar;
1858 if (ctxt->grammar != NULL) {
1859 tmp = ctxt->grammar->children;
1860 if (tmp == NULL) {
1861 ctxt->grammar->children = ret;
1862 } else {
1863 while (tmp->next != NULL)
1864 tmp = tmp->next;
1865 tmp->next = ret;
1866 }
1867 }
1868
1869 old = ctxt->grammar;
1870 ctxt->grammar = ret;
1871 xmlRelaxNGParseGrammarContent(ctxt, nodes);
1872 ctxt->grammar = ret;
1873
1874 /*
1875 * Apply 4.17 mergingd rules to defines and starts
1876 */
1877 xmlRelaxNGCombineStart(ctxt, ret);
1878 if (ret->defs != NULL) {
1879 xmlHashScan(ret->defs, (xmlHashScanner) xmlRelaxNGCheckCombine,
1880 ctxt);
1881 }
1882
1883 /*
1884 * link together defines and refs in this grammar
1885 */
1886 if (ret->refs != NULL) {
1887 xmlHashScan(ret->refs, (xmlHashScanner) xmlRelaxNGCheckReference,
1888 ctxt);
1889 }
1890 ctxt->grammar = old;
1891 return(ret);
1892}
1893
1894/**
1895 * xmlRelaxNGParseDocument:
1896 * @ctxt: a Relax-NG parser context
1897 * @node: the root node of the RelaxNG schema
1898 *
1899 * parse a Relax-NG definition resource and build an internal
1900 * xmlRelaxNG struture which can be used to validate instances.
1901 *
1902 * Returns the internal XML RelaxNG structure built or
1903 * NULL in case of error
1904 */
1905static xmlRelaxNGPtr
1906xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1907 xmlRelaxNGPtr schema = NULL;
Daniel Veillard276be4a2003-01-24 01:03:34 +00001908 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001909
1910 if ((ctxt == NULL) || (node == NULL))
1911 return (NULL);
1912
1913 schema = xmlRelaxNGNewRelaxNG(ctxt);
1914 if (schema == NULL)
1915 return(NULL);
1916
Daniel Veillard276be4a2003-01-24 01:03:34 +00001917 olddefine = ctxt->define;
1918 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001919 if (IS_RELAXNG(node, "grammar")) {
1920 schema->topgrammar = xmlRelaxNGParseGrammar(ctxt, node->children);
1921 } else {
1922 schema->topgrammar = xmlRelaxNGNewGrammar(ctxt);
1923 if (schema->topgrammar == NULL) {
1924 return(schema);
1925 }
1926 schema->topgrammar->parent = NULL;
1927 ctxt->grammar = schema->topgrammar;
1928 xmlRelaxNGParseStart(ctxt, node);
1929 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00001930 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001931
1932#ifdef DEBUG
1933 if (schema == NULL)
1934 xmlGenericError(xmlGenericErrorContext,
1935 "xmlRelaxNGParseDocument() failed\n");
1936#endif
1937
1938 return (schema);
1939}
1940
1941/************************************************************************
1942 * *
1943 * Reading RelaxNGs *
1944 * *
1945 ************************************************************************/
1946
1947/**
1948 * xmlRelaxNGNewParserCtxt:
1949 * @URL: the location of the schema
1950 *
1951 * Create an XML RelaxNGs parse context for that file/resource expected
1952 * to contain an XML RelaxNGs file.
1953 *
1954 * Returns the parser context or NULL in case of error
1955 */
1956xmlRelaxNGParserCtxtPtr
1957xmlRelaxNGNewParserCtxt(const char *URL) {
1958 xmlRelaxNGParserCtxtPtr ret;
1959
1960 if (URL == NULL)
1961 return(NULL);
1962
1963 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
1964 if (ret == NULL) {
1965 xmlGenericError(xmlGenericErrorContext,
1966 "Failed to allocate new schama parser context for %s\n", URL);
1967 return (NULL);
1968 }
1969 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
1970 ret->URL = xmlStrdup((const xmlChar *)URL);
1971 return (ret);
1972}
1973
1974/**
1975 * xmlRelaxNGNewMemParserCtxt:
1976 * @buffer: a pointer to a char array containing the schemas
1977 * @size: the size of the array
1978 *
1979 * Create an XML RelaxNGs parse context for that memory buffer expected
1980 * to contain an XML RelaxNGs file.
1981 *
1982 * Returns the parser context or NULL in case of error
1983 */
1984xmlRelaxNGParserCtxtPtr
1985xmlRelaxNGNewMemParserCtxt(const char *buffer, int size) {
1986 xmlRelaxNGParserCtxtPtr ret;
1987
1988 if ((buffer == NULL) || (size <= 0))
1989 return(NULL);
1990
1991 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
1992 if (ret == NULL) {
1993 xmlGenericError(xmlGenericErrorContext,
1994 "Failed to allocate new schama parser context\n");
1995 return (NULL);
1996 }
1997 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
1998 ret->buffer = buffer;
1999 ret->size = size;
2000 return (ret);
2001}
2002
2003/**
2004 * xmlRelaxNGFreeParserCtxt:
2005 * @ctxt: the schema parser context
2006 *
2007 * Free the resources associated to the schema parser context
2008 */
2009void
2010xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxtPtr ctxt) {
2011 if (ctxt == NULL)
2012 return;
2013 if (ctxt->URL != NULL)
2014 xmlFree(ctxt->URL);
2015 if (ctxt->doc != NULL)
2016 xmlFreeDoc(ctxt->doc);
2017 xmlFree(ctxt);
2018}
2019
2020
2021/**
2022 * xmlRelaxNGParse:
2023 * @ctxt: a Relax-NG validation context
2024 *
2025 * parse a schema definition resource and build an internal
2026 * XML Shema struture which can be used to validate instances.
2027 * *WARNING* this interface is highly subject to change
2028 *
2029 * Returns the internal XML RelaxNG structure built from the resource or
2030 * NULL in case of error
2031 */
2032xmlRelaxNGPtr
2033xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt)
2034{
2035 xmlRelaxNGPtr ret = NULL;
2036 xmlDocPtr doc;
2037 xmlNodePtr root, cur, delete;
2038
2039 xmlRelaxNGInitTypes();
2040
2041 if (ctxt == NULL)
2042 return (NULL);
2043
2044 /*
2045 * First step is to parse the input document into an DOM/Infoset
2046 */
2047 if (ctxt->URL != NULL) {
2048 doc = xmlParseFile((const char *) ctxt->URL);
2049 if (doc == NULL) {
2050 if (ctxt->error != NULL)
2051 ctxt->error(ctxt->userData,
2052 "xmlRelaxNGParse: could not load %s\n", ctxt->URL);
2053 ctxt->nbErrors++;
2054 return (NULL);
2055 }
2056 } else if (ctxt->buffer != NULL) {
2057 doc = xmlParseMemory(ctxt->buffer, ctxt->size);
2058 if (doc == NULL) {
2059 if (ctxt->error != NULL)
2060 ctxt->error(ctxt->userData,
2061 "xmlRelaxNGParse: could not parse schemas\n");
2062 ctxt->nbErrors++;
2063 return (NULL);
2064 }
2065 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
2066 ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
2067 } else {
2068 if (ctxt->error != NULL)
2069 ctxt->error(ctxt->userData,
2070 "xmlRelaxNGParse: nothing to parse\n");
2071 ctxt->nbErrors++;
2072 return (NULL);
2073 }
2074 ctxt->doc = doc;
2075
2076 /*
2077 * Then extract the root and RelaxNG parse it
2078 */
2079 root = xmlDocGetRootElement(doc);
2080 if (root == NULL) {
2081 if (ctxt->error != NULL)
2082 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
2083 ctxt->URL);
2084 ctxt->nbErrors++;
2085 return (NULL);
2086 }
2087
2088 /*
2089 * Remove all the blank text nodes
2090 */
2091 delete = NULL;
2092 cur = root;
2093 while (cur != NULL) {
2094 if (delete != NULL) {
2095 xmlUnlinkNode(delete);
2096 xmlFreeNode(delete);
2097 delete = NULL;
2098 }
2099 if (cur->type == XML_ELEMENT_NODE) {
2100 /*
2101 * Simplification 4.1. Annotations
2102 */
2103 if ((cur->ns == NULL) ||
2104 (!xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
2105 delete = cur;
2106 goto skip_children;
2107 } else {
2108 if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
2109 TODO
2110 } else if (xmlStrEqual(cur->name, BAD_CAST "include")) {
2111 TODO
2112 } else if ((xmlStrEqual(cur->name, BAD_CAST "element")) ||
2113 (xmlStrEqual(cur->name, BAD_CAST "attribute"))) {
2114 xmlChar *name;
2115 xmlNodePtr text = NULL;
2116
2117 /*
2118 * Simplification 4.8. name attribute of element
2119 * and attribute elements
2120 */
2121 name = xmlGetProp(cur, BAD_CAST "name");
2122 if (name != NULL) {
2123 if (cur->children == NULL) {
2124 text = xmlNewChild(cur, cur->ns, BAD_CAST "name",
2125 name);
2126 } else {
2127 xmlNodePtr node;
2128 node = xmlNewNode(cur->ns, BAD_CAST "name");
2129 if (node != NULL) {
2130 xmlAddPrevSibling(cur->children, node);
2131 text = xmlNewText(name);
2132 xmlAddChild(node, text);
2133 text = node;
2134 }
2135 }
2136 xmlUnsetProp(cur, BAD_CAST "name");
2137 xmlFree(name);
2138 }
2139 if (xmlStrEqual(cur->name, BAD_CAST "attribute")) {
2140 if (text == NULL) {
2141 text = cur->children;
2142 while (text != NULL) {
2143 if ((text->type == XML_ELEMENT_NODE) &&
2144 (xmlStrEqual(text->name, BAD_CAST "name")))
2145 break;
2146 text = text->next;
2147 }
2148 }
2149 if (text == NULL) {
2150 if (ctxt->error != NULL)
2151 ctxt->error(ctxt->userData,
2152 "xmlRelaxNGParse: attribute without name\n");
2153 ctxt->nbErrors++;
2154 } else {
2155 xmlSetProp(text, BAD_CAST "ns", BAD_CAST "");
2156 }
2157 }
2158 } else if ((xmlStrEqual(cur->name, BAD_CAST "name")) ||
2159 (xmlStrEqual(cur->name, BAD_CAST "nsName")) ||
2160 (xmlStrEqual(cur->name, BAD_CAST "value"))) {
2161 /*
2162 * Simplification 4.8. name attribute of element
2163 * and attribute elements
2164 */
2165 if (xmlHasProp(cur, BAD_CAST "ns") == NULL) {
2166 xmlNodePtr node;
2167 xmlChar *ns = NULL;
2168
2169 node = cur->parent;
2170 while ((node != NULL) &&
2171 (node->type == XML_ELEMENT_NODE)) {
2172 ns = xmlGetProp(node, BAD_CAST "ns");
2173 if (ns != NULL) {
2174 break;
2175 }
2176 node = node->parent;
2177 }
2178 if (ns == NULL) {
2179 xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
2180 } else {
2181 xmlSetProp(cur, BAD_CAST "ns", ns);
2182 xmlFree(ns);
2183 }
2184 }
2185 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
2186 xmlChar *name, *local, *prefix;
2187
2188 /*
2189 * Simplification: 4.10. QNames
2190 */
2191 name = xmlNodeGetContent(cur);
2192 if (name != NULL) {
2193 local = xmlSplitQName2(name, &prefix);
2194 if (local != NULL) {
2195 xmlNsPtr ns;
2196
2197 ns = xmlSearchNs(cur->doc, cur, prefix);
2198 if (ns == NULL) {
2199 if (ctxt->error != NULL)
2200 ctxt->error(ctxt->userData,
2201 "xmlRelaxNGParse: no namespace for prefix %s\n", prefix);
2202 ctxt->nbErrors++;
2203 } else {
2204 xmlSetProp(cur, BAD_CAST "ns", ns->href);
2205 xmlNodeSetContent(cur, local);
2206 }
2207 xmlFree(local);
2208 xmlFree(prefix);
2209 }
2210 xmlFree(name);
2211 }
2212 }
2213 }
2214 }
2215 }
2216 /*
2217 * Simplification 4.2 whitespaces
2218 */
2219 else if (cur->type == XML_TEXT_NODE) {
2220 if (IS_BLANK_NODE(cur)) {
2221 if (cur->parent->type == XML_ELEMENT_NODE) {
2222 if ((!xmlStrEqual(cur->parent->name, BAD_CAST "value")) &&
2223 (!xmlStrEqual(cur->parent->name, BAD_CAST "param")))
2224 delete = cur;
2225 } else {
2226 delete = cur;
2227 goto skip_children;
2228 }
2229 }
2230 } else if (cur->type != XML_CDATA_SECTION_NODE) {
2231 delete = cur;
2232 goto skip_children;
2233 }
2234
2235 /*
2236 * Skip to next node
2237 */
2238 if (cur->children != NULL) {
2239 if ((cur->children->type != XML_ENTITY_DECL) &&
2240 (cur->children->type != XML_ENTITY_REF_NODE) &&
2241 (cur->children->type != XML_ENTITY_NODE)) {
2242 cur = cur->children;
2243 continue;
2244 }
2245 }
2246skip_children:
2247 if (cur->next != NULL) {
2248 cur = cur->next;
2249 continue;
2250 }
2251
2252 do {
2253 cur = cur->parent;
2254 if (cur == NULL)
2255 break;
2256 if (cur == root) {
2257 cur = NULL;
2258 break;
2259 }
2260 if (cur->next != NULL) {
2261 cur = cur->next;
2262 break;
2263 }
2264 } while (cur != NULL);
2265 }
2266 if (delete != NULL) {
2267 xmlUnlinkNode(delete);
2268 xmlFreeNode(delete);
2269 delete = NULL;
2270 }
2271
2272 /*
2273 * Then do the parsing for good
2274 */
2275 root = xmlDocGetRootElement(doc);
2276 if (root == NULL) {
2277 if (ctxt->error != NULL)
2278 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
2279 ctxt->URL);
2280 ctxt->nbErrors++;
2281 return (NULL);
2282 }
2283 ret = xmlRelaxNGParseDocument(ctxt, root);
2284 if (ret == NULL)
2285 return(NULL);
2286
2287 /*
2288 * Check the ref/defines links
2289 */
2290
2291 /*
2292 * if there was a parsing error return NULL
2293 */
2294 if (ctxt->nbErrors > 0) {
2295 xmlRelaxNGFree(ret);
2296 return(NULL);
2297 }
2298
2299 /*
2300 * Transfer the pointer for cleanup at the schema level.
2301 */
2302 ret->doc = doc;
2303 ctxt->doc = NULL;
2304
2305 return (ret);
2306}
2307
2308/**
2309 * xmlRelaxNGSetParserErrors:
2310 * @ctxt: a Relax-NG validation context
2311 * @err: the error callback
2312 * @warn: the warning callback
2313 * @ctx: contextual data for the callbacks
2314 *
2315 * Set the callback functions used to handle errors for a validation context
2316 */
2317void
2318xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
2319 xmlRelaxNGValidityErrorFunc err,
2320 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
2321 if (ctxt == NULL)
2322 return;
2323 ctxt->error = err;
2324 ctxt->warning = warn;
2325 ctxt->userData = ctx;
2326}
2327/************************************************************************
2328 * *
2329 * Dump back a compiled form *
2330 * *
2331 ************************************************************************/
2332static void xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define);
2333
2334/**
2335 * xmlRelaxNGDumpDefines:
2336 * @output: the file output
2337 * @defines: a list of define structures
2338 *
2339 * Dump a RelaxNG structure back
2340 */
2341static void
2342xmlRelaxNGDumpDefines(FILE * output, xmlRelaxNGDefinePtr defines) {
2343 while (defines != NULL) {
2344 xmlRelaxNGDumpDefine(output, defines);
2345 defines = defines->next;
2346 }
2347}
2348
2349/**
2350 * xmlRelaxNGDumpDefine:
2351 * @output: the file output
2352 * @define: a define structure
2353 *
2354 * Dump a RelaxNG structure back
2355 */
2356static void
2357xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define) {
2358 if (define == NULL)
2359 return;
2360 switch(define->type) {
2361 case XML_RELAXNG_EMPTY:
2362 fprintf(output, "<empty/>\n");
2363 break;
2364 case XML_RELAXNG_NOT_ALLOWED:
2365 fprintf(output, "<notAllowed/>\n");
2366 break;
2367 case XML_RELAXNG_TEXT:
2368 fprintf(output, "<text/>\n");
2369 break;
2370 case XML_RELAXNG_ELEMENT:
2371 fprintf(output, "<element>\n");
2372 if (define->name != NULL) {
2373 fprintf(output, "<name");
2374 if (define->ns != NULL)
2375 fprintf(output, " ns=\"%s\"", define->ns);
2376 fprintf(output, ">%s</name>\n", define->name);
2377 }
2378 xmlRelaxNGDumpDefines(output, define->attrs);
2379 xmlRelaxNGDumpDefines(output, define->content);
2380 fprintf(output, "</element>\n");
2381 break;
2382 case XML_RELAXNG_LIST:
2383 fprintf(output, "<list>\n");
2384 xmlRelaxNGDumpDefines(output, define->content);
2385 fprintf(output, "</list>\n");
2386 break;
2387 case XML_RELAXNG_ONEORMORE:
2388 fprintf(output, "<oneOrMore>\n");
2389 xmlRelaxNGDumpDefines(output, define->content);
2390 fprintf(output, "</oneOrMore>\n");
2391 break;
2392 case XML_RELAXNG_ZEROORMORE:
2393 fprintf(output, "<zeroOrMore>\n");
2394 xmlRelaxNGDumpDefines(output, define->content);
2395 fprintf(output, "</zeroOrMore>\n");
2396 break;
2397 case XML_RELAXNG_CHOICE:
2398 fprintf(output, "<choice>\n");
2399 xmlRelaxNGDumpDefines(output, define->content);
2400 fprintf(output, "</choice>\n");
2401 break;
2402 case XML_RELAXNG_GROUP:
2403 fprintf(output, "<group>\n");
2404 xmlRelaxNGDumpDefines(output, define->content);
2405 fprintf(output, "</group>\n");
2406 break;
2407 case XML_RELAXNG_INTERLEAVE:
2408 fprintf(output, "<interleave>\n");
2409 xmlRelaxNGDumpDefines(output, define->content);
2410 fprintf(output, "</interleave>\n");
2411 break;
2412 case XML_RELAXNG_OPTIONAL:
2413 fprintf(output, "<optional>\n");
2414 xmlRelaxNGDumpDefines(output, define->content);
2415 fprintf(output, "</optional>\n");
2416 break;
2417 case XML_RELAXNG_ATTRIBUTE:
2418 fprintf(output, "<attribute>\n");
2419 xmlRelaxNGDumpDefines(output, define->content);
2420 fprintf(output, "</attribute>\n");
2421 break;
2422 case XML_RELAXNG_DEF:
2423 fprintf(output, "<define");
2424 if (define->name != NULL)
2425 fprintf(output, " name=\"%s\"", define->name);
2426 fprintf(output, ">\n");
2427 xmlRelaxNGDumpDefines(output, define->content);
2428 fprintf(output, "</define>\n");
2429 break;
2430 case XML_RELAXNG_REF:
2431 fprintf(output, "<ref");
2432 if (define->name != NULL)
2433 fprintf(output, " name=\"%s\"", define->name);
2434 fprintf(output, ">\n");
2435 xmlRelaxNGDumpDefines(output, define->content);
2436 fprintf(output, "</ref>\n");
2437 break;
2438 case XML_RELAXNG_DATATYPE:
2439 case XML_RELAXNG_VALUE:
2440 TODO
2441 break;
2442 }
2443}
2444
2445/**
2446 * xmlRelaxNGDumpGrammar:
2447 * @output: the file output
2448 * @grammar: a grammar structure
2449 * @top: is this a top grammar
2450 *
2451 * Dump a RelaxNG structure back
2452 */
2453static void
2454xmlRelaxNGDumpGrammar(FILE * output, xmlRelaxNGGrammarPtr grammar, int top)
2455{
2456 if (grammar == NULL)
2457 return;
2458
2459 fprintf(output, "<grammar");
2460 if (top)
2461 fprintf(output,
2462 " xmlns=\"http://relaxng.org/ns/structure/1.0\"");
2463 switch(grammar->combine) {
2464 case XML_RELAXNG_COMBINE_UNDEFINED:
2465 break;
2466 case XML_RELAXNG_COMBINE_CHOICE:
2467 fprintf(output, " combine=\"choice\"");
2468 break;
2469 case XML_RELAXNG_COMBINE_INTERLEAVE:
2470 fprintf(output, " combine=\"interleave\"");
2471 break;
2472 default:
2473 fprintf(output, " <!-- invalid combine value -->");
2474 }
2475 fprintf(output, ">\n");
2476 if (grammar->start == NULL) {
2477 fprintf(output, " <!-- grammar had no start -->");
2478 } else {
2479 fprintf(output, "<start>\n");
2480 xmlRelaxNGDumpDefine(output, grammar->start);
2481 fprintf(output, "</start>\n");
2482 }
2483 /* TODO ? Dump the defines ? */
2484 fprintf(output, "</grammar>\n");
2485}
2486
2487/**
2488 * xmlRelaxNGDump:
2489 * @output: the file output
2490 * @schema: a schema structure
2491 *
2492 * Dump a RelaxNG structure back
2493 */
2494void
2495xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema)
2496{
2497 if (schema == NULL) {
2498 fprintf(output, "RelaxNG empty or failed to compile\n");
2499 return;
2500 }
2501 fprintf(output, "RelaxNG: ");
2502 if (schema->doc == NULL) {
2503 fprintf(output, "no document\n");
2504 } else if (schema->doc->URL != NULL) {
2505 fprintf(output, "%s\n", schema->doc->URL);
2506 } else {
2507 fprintf(output, "\n");
2508 }
2509 if (schema->topgrammar == NULL) {
2510 fprintf(output, "RelaxNG has no top grammar\n");
2511 return;
2512 }
2513 xmlRelaxNGDumpGrammar(output, schema->topgrammar, 1);
2514}
2515
2516/************************************************************************
2517 * *
2518 * Validation implementation *
2519 * *
2520 ************************************************************************/
2521static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
2522 xmlRelaxNGDefinePtr define);
2523
2524/**
2525 * xmlRelaxNGSkipIgnored:
2526 * @ctxt: a schema validation context
2527 * @node: the top node.
2528 *
2529 * Skip ignorable nodes in that context
2530 *
2531 * Returns the new sibling or NULL in case of error.
2532 */
2533static xmlNodePtr
2534xmlRelaxNGSkipIgnored(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
2535 xmlNodePtr node) {
2536 /*
2537 * TODO complete and handle entities
2538 */
2539 while ((node != NULL) &&
2540 ((node->type == XML_COMMENT_NODE) ||
2541 ((node->type == XML_TEXT_NODE) &&
2542 (IS_BLANK_NODE(node))))) {
2543 node = node->next;
2544 }
2545 return(node);
2546}
2547
2548/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002549 * xmlRelaxNGValidateDatatype:
2550 * @ctxt: a Relax-NG validation context
2551 * @value: the string value
2552 * @type: the datatype definition
2553 *
2554 * Validate the given value against the dataype
2555 *
2556 * Returns 0 if the validation succeeded or an error code.
2557 */
2558static int
2559xmlRelaxNGValidateDatatype(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *value,
2560 xmlRelaxNGDefinePtr define) {
2561 int ret;
2562 xmlRelaxNGTypeLibraryPtr lib;
2563
2564 if ((define == NULL) || (define->data == NULL)) {
2565 return(-1);
2566 }
2567 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
2568 if (lib->check != NULL)
2569 ret = lib->check(lib->data, define->name, value);
2570 else
2571 ret = -1;
2572 if (ret < 0) {
2573 VALID_CTXT();
2574 VALID_ERROR("Internal: failed to validate type %s\n", define->name);
2575 return(-1);
2576 } else if (ret == 1) {
2577 ret = 0;
2578 } else {
2579 VALID_CTXT();
2580 VALID_ERROR("Type %s doesn't allow value %s\n", define->name, value);
2581 return(-1);
2582 ret = -1;
2583 }
2584 return(ret);
2585}
2586
2587/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00002588 * xmlRelaxNGValidateValue:
2589 * @ctxt: a Relax-NG validation context
2590 * @define: the definition to verify
2591 *
2592 * Validate the given definition for the current value
2593 *
2594 * Returns 0 if the validation succeeded or an error code.
2595 */
2596static int
2597xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
2598 xmlRelaxNGDefinePtr define) {
2599 int ret = 0;
2600 xmlChar *value;
2601
2602 value = ctxt->state->value;
2603 switch (define->type) {
2604 case XML_RELAXNG_EMPTY:
2605 if ((value != NULL) && (value[0] != '0'))
2606 ret = -1;
2607 break;
2608 case XML_RELAXNG_TEXT:
2609 break;
2610 default:
2611 TODO
2612 ret = -1;
2613 }
2614 return(ret);
2615}
2616
2617/**
2618 * xmlRelaxNGValidateValueContent:
2619 * @ctxt: a Relax-NG validation context
2620 * @defines: the list of definitions to verify
2621 *
2622 * Validate the given definitions for the current value
2623 *
2624 * Returns 0 if the validation succeeded or an error code.
2625 */
2626static int
2627xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt,
2628 xmlRelaxNGDefinePtr defines) {
2629 int ret = 0;
2630
2631 while (defines != NULL) {
2632 ret = xmlRelaxNGValidateValue(ctxt, defines);
2633 if (ret != 0)
2634 break;
2635 defines = defines->next;
2636 }
2637 return(ret);
2638}
2639
2640/**
2641 * xmlRelaxNGValidateAttribute:
2642 * @ctxt: a Relax-NG validation context
2643 * @define: the definition to verify
2644 *
2645 * Validate the given attribute definition for that node
2646 *
2647 * Returns 0 if the validation succeeded or an error code.
2648 */
2649static int
2650xmlRelaxNGValidateAttribute(xmlRelaxNGValidCtxtPtr ctxt,
2651 xmlRelaxNGDefinePtr define) {
2652 int ret = 0, i;
2653 xmlChar *value, *oldvalue;
2654 xmlAttrPtr prop = NULL, tmp;
2655
2656 if (define->name != NULL) {
2657 for (i = 0;i < ctxt->state->nbAttrs;i++) {
2658 tmp = ctxt->state->attrs[i];
2659 if ((tmp != NULL) && (xmlStrEqual(define->name, tmp->name))) {
2660 if ((((define->ns == NULL) || (define->ns[0] == 0)) &&
2661 (tmp->ns == NULL)) ||
2662 ((tmp->ns != NULL) &&
2663 (xmlStrEqual(define->ns, tmp->ns->href)))) {
2664 prop = tmp;
2665 break;
2666 }
2667 }
2668 }
2669 if (prop != NULL) {
2670 value = xmlNodeListGetString(prop->doc, prop->children, 1);
2671 oldvalue = ctxt->state->value;
2672 ctxt->state->value = value;
2673 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
2674 value = ctxt->state->value;
2675 ctxt->state->value = oldvalue;
2676 if (value != NULL)
2677 xmlFree(value);
2678 if (ret == 0) {
2679 /*
2680 * flag the attribute as processed
2681 */
2682 ctxt->state->attrs[i] = NULL;
2683 }
2684 } else {
2685 ret = -1;
2686 }
2687#ifdef DEBUG
2688 xmlGenericError(xmlGenericErrorContext,
2689 "xmlRelaxNGValidateAttribute(%s): %d\n", define->name, ret);
2690#endif
2691 } else {
2692 TODO
2693 }
2694
2695 return(ret);
2696}
2697
2698/**
2699 * xmlRelaxNGValidateAttributeList:
2700 * @ctxt: a Relax-NG validation context
2701 * @define: the list of definition to verify
2702 *
2703 * Validate the given node against the list of attribute definitions
2704 *
2705 * Returns 0 if the validation succeeded or an error code.
2706 */
2707static int
2708xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt,
2709 xmlRelaxNGDefinePtr defines) {
2710 int ret = 0;
2711 while (defines != NULL) {
2712 if (xmlRelaxNGValidateAttribute(ctxt, defines) != 0)
2713 ret = -1;
2714 defines = defines->next;
2715 }
2716 return(ret);
2717}
2718
2719/**
2720 * xmlRelaxNGValidateElementContent:
2721 * @ctxt: a Relax-NG validation context
2722 * @define: the list of definition to verify
2723 *
2724 * Validate the given node content against the (list) of definitions
2725 *
2726 * Returns 0 if the validation succeeded or an error code.
2727 */
2728static int
2729xmlRelaxNGValidateElementContent(xmlRelaxNGValidCtxtPtr ctxt,
2730 xmlRelaxNGDefinePtr defines) {
2731 int ret = 0, res;
2732
2733 if (ctxt->state == NULL) {
2734 VALID_CTXT();
2735 VALID_ERROR("Internal: no state\n");
2736 return(-1);
2737 }
2738 while (defines != NULL) {
2739 res = xmlRelaxNGValidateDefinition(ctxt, defines);
2740 if (res < 0)
2741 ret = -1;
2742 defines = defines->next;
2743 }
2744
2745 return(ret);
2746}
2747
2748/**
2749 * xmlRelaxNGValidateDefinition:
2750 * @ctxt: a Relax-NG validation context
2751 * @define: the definition to verify
2752 *
2753 * Validate the current node against the definition
2754 *
2755 * Returns 0 if the validation succeeded or an error code.
2756 */
2757static int
2758xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
2759 xmlRelaxNGDefinePtr define) {
2760 xmlNodePtr node;
2761 int ret = 0, i, tmp, oldflags;
2762 xmlRelaxNGValidStatePtr oldstate, state;
2763
2764 if (define == NULL) {
2765 VALID_CTXT();
2766 VALID_ERROR("internal error: define == NULL\n");
2767 return(-1);
2768 }
2769 if (ctxt->state != NULL) {
2770 node = ctxt->state->seq;
2771 } else {
2772 node = NULL;
2773 }
2774 switch (define->type) {
2775 case XML_RELAXNG_EMPTY:
2776 if (node != NULL) {
2777 VALID_CTXT();
2778 VALID_ERROR("Expecting an empty element\n");
2779 return(-1);
2780 }
2781#ifdef DEBUG
2782 xmlGenericError(xmlGenericErrorContext,
2783 "xmlRelaxNGValidateDefinition(): validated empty\n");
2784#endif
2785 return(0);
2786 case XML_RELAXNG_NOT_ALLOWED:
2787 TODO
2788 break;
2789 case XML_RELAXNG_TEXT:
2790 if (node == NULL)
2791 return(0);
2792 while ((node != NULL) &&
2793 ((node->type == XML_TEXT_NODE) ||
2794 (node->type == XML_CDATA_SECTION_NODE)))
2795 node = node->next;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002796 if (node == ctxt->state->seq) {
2797 VALID_CTXT();
2798 VALID_ERROR("Expecting text content\n");
2799 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002800 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002801 ctxt->state->seq = node;
2802 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002803 case XML_RELAXNG_ELEMENT:
2804 node = xmlRelaxNGSkipIgnored(ctxt, node);
2805 if ((node == NULL) || (node->type != XML_ELEMENT_NODE)) {
2806 VALID_CTXT();
2807 VALID_ERROR("Expecting an element\n");
2808 return(-1);
2809 }
2810 if (define->name != NULL) {
2811 if (!xmlStrEqual(node->name, define->name)) {
2812 VALID_CTXT();
2813 VALID_ERROR("Expecting element %s, got %s\n",
2814 define->name, node->name);
2815 return(-1);
2816 }
2817 }
2818 if ((define->ns != NULL) && (define->ns[0] != 0)) {
2819 if (node->ns == NULL) {
2820 VALID_CTXT();
2821 VALID_ERROR("Expecting a namespace for element %s\n",
2822 node->name);
2823 return(-1);
2824 } else if (!xmlStrEqual(node->ns->href, define->ns)) {
2825 VALID_CTXT();
2826 VALID_ERROR("Expecting element %s has wrong namespace: expecting %s\n",
2827 node->name, define->ns);
2828 return(-1);
2829 }
2830 } else {
2831 if (node->ns != NULL) {
2832 VALID_CTXT();
2833 VALID_ERROR("Expecting no namespace for element %s\n",
2834 node->name);
2835 return(-1);
2836 }
2837 }
2838
2839 state = xmlRelaxNGNewValidState(ctxt, node);
2840 if (state == NULL) {
2841 return(-1);
2842 }
2843
2844 oldstate = ctxt->state;
2845 ctxt->state = state;
2846 if (define->attrs != NULL) {
2847 tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
2848 if (tmp != 0)
2849 ret = -1;
2850 }
2851 if (define->content != NULL) {
2852 tmp = xmlRelaxNGValidateElementContent(ctxt, define->content);
2853 if (tmp != 0)
2854 ret = -1;
2855 }
2856 state = ctxt->state;
2857 if (state->seq != NULL) {
2858 state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq);
2859 if (state->seq != NULL) {
2860 VALID_CTXT();
2861 VALID_ERROR("Extra content for element %s\n",
2862 node->name);
2863 ret = -1;
2864 }
2865 }
2866 for (i = 0;i < state->nbAttrs;i++) {
2867 if (state->attrs[i] != NULL) {
2868 VALID_CTXT();
2869 VALID_ERROR("Extra attribute %s for element %s\n",
2870 state->attrs[i]->name, node->name);
2871 ret = -1;
2872 }
2873 }
2874 ctxt->state = oldstate;
2875 xmlRelaxNGFreeValidState(state);
2876 if (oldstate != NULL)
2877 oldstate->seq = node->next;
2878
2879
2880#ifdef DEBUG
2881 xmlGenericError(xmlGenericErrorContext,
2882 "xmlRelaxNGValidateDefinition(): validated %s : %d\n",
2883 node->name, ret);
2884#endif
2885 break;
2886 case XML_RELAXNG_LIST:
2887 TODO
2888 break;
2889 case XML_RELAXNG_OPTIONAL:
2890 oldflags = ctxt->flags;
2891 ctxt->flags |= FLAGS_IGNORABLE;
2892 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
2893 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
2894 if (ret != 0) {
2895 xmlRelaxNGFreeValidState(ctxt->state);
2896 ctxt->state = oldstate;
2897 ret = 0;
2898 break;
2899 }
2900 xmlRelaxNGFreeValidState(oldstate);
2901 ctxt->flags = oldflags;
2902 ret = 0;
2903 break;
2904 case XML_RELAXNG_ONEORMORE:
2905 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
2906 if (ret != 0) {
2907 break;
2908 }
2909 /* no break on purpose */
Daniel Veillard276be4a2003-01-24 01:03:34 +00002910 case XML_RELAXNG_ZEROORMORE: {
2911 xmlNodePtr cur, temp;
2912
Daniel Veillard6eadf632003-01-23 18:29:16 +00002913 oldflags = ctxt->flags;
2914 ctxt->flags |= FLAGS_IGNORABLE;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002915 cur = ctxt->state->seq;
2916 temp = NULL;
2917 while ((cur != NULL) && (temp != cur)) {
2918 temp = cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002919 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
2920 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
2921 if (ret != 0) {
2922 xmlRelaxNGFreeValidState(ctxt->state);
2923 ctxt->state = oldstate;
2924 ret = 0;
2925 break;
2926 }
2927 xmlRelaxNGFreeValidState(oldstate);
Daniel Veillard276be4a2003-01-24 01:03:34 +00002928 cur = ctxt->state->seq;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002929 }
2930 ctxt->flags = oldflags;
2931 break;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002932 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002933 case XML_RELAXNG_CHOICE: {
2934 xmlRelaxNGDefinePtr list = define->content;
2935
2936 oldflags = ctxt->flags;
2937 ctxt->flags |= FLAGS_IGNORABLE;
2938
2939 while (list != NULL) {
2940 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
2941 ret = xmlRelaxNGValidateDefinition(ctxt, list);
2942 if (ret == 0) {
2943 xmlRelaxNGFreeValidState(oldstate);
2944 break;
2945 }
2946 xmlRelaxNGFreeValidState(ctxt->state);
2947 ctxt->state = oldstate;
2948 list = list->next;
2949 }
2950 ctxt->flags = oldflags;
2951 break;
2952 }
2953 case XML_RELAXNG_GROUP: {
2954 xmlRelaxNGDefinePtr list = define->content;
2955
2956 while (list != NULL) {
2957 ret = xmlRelaxNGValidateDefinition(ctxt, list);
2958 if (ret != 0)
2959 break;
2960 list = list->next;
2961 }
2962 break;
2963 }
2964 case XML_RELAXNG_INTERLEAVE:
2965 TODO
2966 break;
2967 case XML_RELAXNG_ATTRIBUTE:
2968 ret = xmlRelaxNGValidateAttribute(ctxt, define);
2969 break;
2970 case XML_RELAXNG_REF:
2971 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
2972 break;
2973 case XML_RELAXNG_DEF:
2974 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
2975 break;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002976 case XML_RELAXNG_DATATYPE: {
2977 xmlChar *content;
2978
2979 content = xmlNodeGetContent(node);
2980 ret = xmlRelaxNGValidateDatatype(ctxt, content, define);
2981 if (ret == -1) {
2982 VALID_CTXT();
2983 VALID_ERROR("internal error validating %s\n", define->name);
2984 } else if (ret == 0) {
2985 ctxt->state->seq = node->next;
2986 }
2987 /*
2988 * TODO cover the problems with
2989 * <p>12<!-- comment -->34</p>
2990 * TODO detect full element coverage at compilation time.
2991 */
2992 if ((node != NULL) && (node->next != NULL)) {
2993 VALID_CTXT();
2994 VALID_ERROR("The data does not cover the full element %s\n",
2995 node->parent->name);
2996 ret = -1;
2997 }
2998 if (content != NULL)
2999 xmlFree(content);
3000 break;
3001 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003002 case XML_RELAXNG_VALUE:
3003 TODO
3004 break;
3005 }
3006 return(ret);
3007}
3008
3009/**
3010 * xmlRelaxNGValidateDocument:
3011 * @ctxt: a Relax-NG validation context
3012 * @doc: the document
3013 *
3014 * Validate the given document
3015 *
3016 * Returns 0 if the validation succeeded or an error code.
3017 */
3018static int
3019xmlRelaxNGValidateDocument(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
3020 int ret;
3021 xmlRelaxNGPtr schema;
3022 xmlRelaxNGGrammarPtr grammar;
3023 xmlRelaxNGValidStatePtr state;
3024
3025 if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL))
3026 return(-1);
3027
3028 schema = ctxt->schema;
3029 grammar = schema->topgrammar;
3030 if (grammar == NULL) {
3031 VALID_CTXT();
3032 VALID_ERROR("No top grammar defined\n");
3033 return(-1);
3034 }
3035 state = xmlRelaxNGNewValidState(ctxt, NULL);
3036 ctxt->state = state;
3037 ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start);
3038 state = ctxt->state;
3039 if ((state != NULL) && (state->seq != NULL)) {
3040 xmlNodePtr node;
3041
3042 node = state->seq;
3043 node = xmlRelaxNGSkipIgnored(ctxt, node);
3044 if (node != NULL) {
3045 VALID_CTXT();
3046 VALID_ERROR("extra data on the document\n");
3047 ret = -1;
3048 }
3049 }
3050 xmlRelaxNGFreeValidState(state);
3051
3052 return(ret);
3053}
3054
3055/************************************************************************
3056 * *
3057 * Validation interfaces *
3058 * *
3059 ************************************************************************/
3060/**
3061 * xmlRelaxNGNewValidCtxt:
3062 * @schema: a precompiled XML RelaxNGs
3063 *
3064 * Create an XML RelaxNGs validation context based on the given schema
3065 *
3066 * Returns the validation context or NULL in case of error
3067 */
3068xmlRelaxNGValidCtxtPtr
3069xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema) {
3070 xmlRelaxNGValidCtxtPtr ret;
3071
3072 ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt));
3073 if (ret == NULL) {
3074 xmlGenericError(xmlGenericErrorContext,
3075 "Failed to allocate new schama validation context\n");
3076 return (NULL);
3077 }
3078 memset(ret, 0, sizeof(xmlRelaxNGValidCtxt));
3079 ret->schema = schema;
3080 return (ret);
3081}
3082
3083/**
3084 * xmlRelaxNGFreeValidCtxt:
3085 * @ctxt: the schema validation context
3086 *
3087 * Free the resources associated to the schema validation context
3088 */
3089void
3090xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt) {
3091 if (ctxt == NULL)
3092 return;
3093 xmlFree(ctxt);
3094}
3095
3096/**
3097 * xmlRelaxNGSetValidErrors:
3098 * @ctxt: a Relax-NG validation context
3099 * @err: the error function
3100 * @warn: the warning function
3101 * @ctx: the functions context
3102 *
3103 * Set the error and warning callback informations
3104 */
3105void
3106xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
3107 xmlRelaxNGValidityErrorFunc err,
3108 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
3109 if (ctxt == NULL)
3110 return;
3111 ctxt->error = err;
3112 ctxt->warning = warn;
3113 ctxt->userData = ctx;
3114}
3115
3116/**
3117 * xmlRelaxNGValidateDoc:
3118 * @ctxt: a Relax-NG validation context
3119 * @doc: a parsed document tree
3120 *
3121 * Validate a document tree in memory.
3122 *
3123 * Returns 0 if the document is valid, a positive error code
3124 * number otherwise and -1 in case of internal or API error.
3125 */
3126int
3127xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
3128 int ret;
3129
3130 if ((ctxt == NULL) || (doc == NULL))
3131 return(-1);
3132
3133 ctxt->doc = doc;
3134
3135 ret = xmlRelaxNGValidateDocument(ctxt, doc);
3136 return(ret);
3137}
3138
3139#endif /* LIBXML_SCHEMAS_ENABLED */
3140