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