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