blob: 2391f72f8ff71a1627d6fc3db8228527c931780f [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 ************************************************************************/
656
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000657/**
658 * xmlRelaxNGSchemaTypeHave:
659 * @data: data needed for the library
660 * @type: the type name
661 *
662 * Check if the given type is provided by
663 * the W3C XMLSchema Datatype library.
664 *
665 * Returns 1 if yes, 0 if no and -1 in case of error.
666 */
667static int
668xmlRelaxNGSchemaTypeHave(void *data ATTRIBUTE_UNUSED,
669 const xmlChar *type ATTRIBUTE_UNUSED) {
670 TODO
671 return(1);
672}
673
674/**
675 * xmlRelaxNGSchemaTypeCheck:
676 * @data: data needed for the library
677 * @type: the type name
678 * @value: the value to check
679 *
680 * Check if the given type and value are validated by
681 * the W3C XMLSchema Datatype library.
682 *
683 * Returns 1 if yes, 0 if no and -1 in case of error.
684 */
685static int
686xmlRelaxNGSchemaTypeCheck(void *data ATTRIBUTE_UNUSED,
687 const xmlChar *type ATTRIBUTE_UNUSED,
688 const xmlChar *value ATTRIBUTE_UNUSED) {
689 TODO
690 return(1);
691}
692
693/**
694 * xmlRelaxNGSchemaTypeCompare:
695 * @data: data needed for the library
696 * @type: the type name
697 * @value1: the first value
698 * @value2: the second value
699 *
700 * Compare two values accordingly a type from the W3C XMLSchema
701 * Datatype library.
702 *
703 * Returns 1 if yes, 0 if no and -1 in case of error.
704 */
705static int
706xmlRelaxNGSchemaTypeCompare(void *data ATTRIBUTE_UNUSED,
707 const xmlChar *type ATTRIBUTE_UNUSED,
708 const xmlChar *value1 ATTRIBUTE_UNUSED,
709 const xmlChar *value2 ATTRIBUTE_UNUSED) {
710 TODO
711 return(1);
712}
713
714/**
715 * xmlRelaxNGDefaultTypeHave:
716 * @data: data needed for the library
717 * @type: the type name
718 *
719 * Check if the given type is provided by
720 * the default datatype library.
721 *
722 * Returns 1 if yes, 0 if no and -1 in case of error.
723 */
724static int
725xmlRelaxNGDefaultTypeHave(void *data ATTRIBUTE_UNUSED, const xmlChar *type) {
726 if (type == NULL)
727 return(-1);
728 if (xmlStrEqual(type, BAD_CAST "string"))
729 return(1);
730 if (xmlStrEqual(type, BAD_CAST "token"))
731 return(1);
732 return(0);
733}
734
735/**
736 * xmlRelaxNGDefaultTypeCheck:
737 * @data: data needed for the library
738 * @type: the type name
739 * @value: the value to check
740 *
741 * Check if the given type and value are validated by
742 * the default datatype library.
743 *
744 * Returns 1 if yes, 0 if no and -1 in case of error.
745 */
746static int
747xmlRelaxNGDefaultTypeCheck(void *data ATTRIBUTE_UNUSED,
748 const xmlChar *type ATTRIBUTE_UNUSED,
749 const xmlChar *value ATTRIBUTE_UNUSED) {
750 return(1);
751}
752
753/**
754 * xmlRelaxNGDefaultTypeCompare:
755 * @data: data needed for the library
756 * @type: the type name
757 * @value1: the first value
758 * @value2: the second value
759 *
760 * Compare two values accordingly a type from the default
761 * datatype library.
762 *
763 * Returns 1 if yes, 0 if no and -1 in case of error.
764 */
765static int
766xmlRelaxNGDefaultTypeCompare(void *data ATTRIBUTE_UNUSED,
767 const xmlChar *type ATTRIBUTE_UNUSED,
768 const xmlChar *value1 ATTRIBUTE_UNUSED,
769 const xmlChar *value2 ATTRIBUTE_UNUSED) {
770 TODO
771 return(1);
772}
773
774static int xmlRelaxNGTypeInitialized = 0;
775static xmlHashTablePtr xmlRelaxNGRegisteredTypes = NULL;
776
777/**
778 * xmlRelaxNGFreeTypeLibrary:
779 * @lib: the type library structure
780 * @namespace: the URI bound to the library
781 *
782 * Free the structure associated to the type library
783 */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000784static void
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000785xmlRelaxNGFreeTypeLibrary(xmlRelaxNGTypeLibraryPtr lib,
786 const xmlChar *namespace ATTRIBUTE_UNUSED) {
787 if (lib == NULL)
788 return;
789 if (lib->namespace != NULL)
790 xmlFree((xmlChar *)lib->namespace);
791 xmlFree(lib);
792}
793
794/**
795 * xmlRelaxNGRegisterTypeLibrary:
796 * @namespace: the URI bound to the library
797 * @data: data associated to the library
798 * @have: the provide function
799 * @check: the checking function
800 * @comp: the comparison function
801 *
802 * Register a new type library
803 *
804 * Returns 0 in case of success and -1 in case of error.
805 */
806static int
807xmlRelaxNGRegisterTypeLibrary(const xmlChar *namespace, void *data,
808 xmlRelaxNGTypeHave have, xmlRelaxNGTypeCheck check,
809 xmlRelaxNGTypeCompare comp) {
810 xmlRelaxNGTypeLibraryPtr lib;
811 int ret;
812
813 if ((xmlRelaxNGRegisteredTypes == NULL) || (namespace == NULL) ||
814 (check == NULL) || (comp == NULL))
815 return(-1);
816 if (xmlHashLookup(xmlRelaxNGRegisteredTypes, namespace) != NULL) {
817 xmlGenericError(xmlGenericErrorContext,
818 "Relax-NG types library '%s' already registered\n",
819 namespace);
820 return(-1);
821 }
822 lib = (xmlRelaxNGTypeLibraryPtr) xmlMalloc(sizeof(xmlRelaxNGTypeLibrary));
823 if (lib == NULL) {
824 xmlGenericError(xmlGenericErrorContext,
825 "Relax-NG types library '%s' malloc() failed\n",
826 namespace);
827 return (-1);
828 }
829 memset(lib, 0, sizeof(xmlRelaxNGTypeLibrary));
830 lib->namespace = xmlStrdup(namespace);
831 lib->data = data;
832 lib->have = have;
833 lib->comp = comp;
834 lib->check = check;
835 ret = xmlHashAddEntry(xmlRelaxNGRegisteredTypes, namespace, lib);
836 if (ret < 0) {
837 xmlGenericError(xmlGenericErrorContext,
838 "Relax-NG types library failed to register '%s'\n",
839 namespace);
840 xmlRelaxNGFreeTypeLibrary(lib, namespace);
841 return(-1);
842 }
843 return(0);
844}
845
846/**
847 * xmlRelaxNGInitTypes:
848 *
849 * Initilize the default type libraries.
850 *
851 * Returns 0 in case of success and -1 in case of error.
852 */
853static int
Daniel Veillard6eadf632003-01-23 18:29:16 +0000854xmlRelaxNGInitTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000855 if (xmlRelaxNGTypeInitialized != 0)
856 return(0);
857 xmlRelaxNGRegisteredTypes = xmlHashCreate(10);
858 if (xmlRelaxNGRegisteredTypes == NULL) {
859 xmlGenericError(xmlGenericErrorContext,
860 "Failed to allocate sh table for Relax-NG types\n");
861 return(-1);
862 }
863 xmlRelaxNGRegisterTypeLibrary(
864 BAD_CAST "http://www.w3.org/2001/XMLSchema-datatypes",
865 NULL,
866 xmlRelaxNGSchemaTypeHave,
867 xmlRelaxNGSchemaTypeCheck,
868 xmlRelaxNGSchemaTypeCompare);
869 xmlRelaxNGRegisterTypeLibrary(
870 xmlRelaxNGNs,
871 NULL,
872 xmlRelaxNGDefaultTypeHave,
873 xmlRelaxNGDefaultTypeCheck,
874 xmlRelaxNGDefaultTypeCompare);
875 xmlRelaxNGTypeInitialized = 1;
876 return(0);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000877}
878
879/**
880 * xmlRelaxNGCleanupTypes:
881 *
882 * Cleanup the default Schemas type library associated to RelaxNG
883 */
884void
885xmlRelaxNGCleanupTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000886 if (xmlRelaxNGTypeInitialized == 0)
887 return;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000888 xmlSchemaCleanupTypes();
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000889 xmlHashFree(xmlRelaxNGRegisteredTypes, (xmlHashDeallocator)
890 xmlRelaxNGFreeTypeLibrary);
891 xmlRelaxNGTypeInitialized = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000892}
893
894/************************************************************************
895 * *
896 * Parsing functions *
897 * *
898 ************************************************************************/
899
900static xmlRelaxNGDefinePtr xmlRelaxNGParseAttribute(
901 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
902static xmlRelaxNGDefinePtr xmlRelaxNGParseElement(
903 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
904static xmlRelaxNGDefinePtr xmlRelaxNGParsePatterns(
905 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
906
907
908#define IS_BLANK_NODE(n) \
909 (((n)->type == XML_TEXT_NODE) && (xmlRelaxNGIsBlank((n)->content)))
910
911/**
912 * xmlRelaxNGIsBlank:
913 * @str: a string
914 *
915 * Check if a string is ignorable c.f. 4.2. Whitespace
916 *
917 * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
918 */
919static int
920xmlRelaxNGIsBlank(xmlChar *str) {
921 if (str == NULL)
922 return(1);
923 while (*str != 0) {
924 if (!(IS_BLANK(*str))) return(0);
925 str++;
926 }
927 return(1);
928}
929
Daniel Veillard6eadf632003-01-23 18:29:16 +0000930/**
931 * xmlRelaxNGGetDataTypeLibrary:
932 * @ctxt: a Relax-NG parser context
933 * @node: the current data or value element
934 *
935 * Applies algorithm from 4.3. datatypeLibrary attribute
936 *
937 * Returns the datatypeLibary value or NULL if not found
938 */
939static xmlChar *
940xmlRelaxNGGetDataTypeLibrary(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
941 xmlNodePtr node) {
942 xmlChar *ret, *escape;
943
Daniel Veillard6eadf632003-01-23 18:29:16 +0000944 if ((IS_RELAXNG(node, "data")) || (IS_RELAXNG(node, "value"))) {
945 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
946 if (ret != NULL) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000947 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +0000948 if (escape == NULL) {
949 return(ret);
950 }
951 xmlFree(ret);
952 return(escape);
953 }
954 }
955 node = node->parent;
956 while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) {
957 if (IS_RELAXNG(node, "element")) {
958 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
959 if (ret != NULL) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000960 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +0000961 if (escape == NULL) {
962 return(ret);
963 }
964 xmlFree(ret);
965 return(escape);
966 }
967 }
968 node = node->parent;
969 }
970 return(NULL);
971}
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000972
973/**
Daniel Veillardedc91922003-01-26 00:52:04 +0000974 * xmlRelaxNGParseValue:
975 * @ctxt: a Relax-NG parser context
976 * @node: the data node.
977 *
978 * parse the content of a RelaxNG value node.
979 *
980 * Returns the definition pointer or NULL in case of error
981 */
982static xmlRelaxNGDefinePtr
983xmlRelaxNGParseValue(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
984 xmlRelaxNGDefinePtr def = NULL;
985 xmlRelaxNGTypeLibraryPtr lib;
986 xmlChar *type;
987 xmlChar *library;
988 int tmp;
989
990 def = xmlRelaxNGNewDefine(ctxt, node);
991 if (def == NULL)
992 return(NULL);
993 def->type = XML_RELAXNG_VALUE;
994
995 type = xmlGetProp(node, BAD_CAST "type");
996 if (type != NULL) {
997 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
998 if (library == NULL)
999 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1000
1001 def->name = type;
1002 def->ns = library;
1003
1004 lib = (xmlRelaxNGTypeLibraryPtr)
1005 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1006 if (lib == NULL) {
1007 if (ctxt->error != NULL)
1008 ctxt->error(ctxt->userData,
1009 "Use of unregistered type library '%s'\n",
1010 library);
1011 ctxt->nbErrors++;
1012 def->data = NULL;
1013 } else {
1014 def->data = lib;
1015 if (lib->have == NULL) {
1016 ctxt->error(ctxt->userData,
1017 "Internal error with type library '%s': no 'have'\n",
1018 library);
1019 ctxt->nbErrors++;
1020 } else {
1021 tmp = lib->have(lib->data, def->name);
1022 if (tmp != 1) {
1023 ctxt->error(ctxt->userData,
1024 "Error type '%s' is not exported by type library '%s'\n",
1025 def->name, library);
1026 ctxt->nbErrors++;
1027 }
1028 }
1029 }
1030 }
1031 if (node->children == NULL) {
1032 if (ctxt->error != NULL)
1033 ctxt->error(ctxt->userData,
1034 "Element <value> has no content\n");
1035 ctxt->nbErrors++;
1036 } else if ((node->children->type != XML_TEXT_NODE) ||
1037 (node->children->next != NULL)) {
1038 if (ctxt->error != NULL)
1039 ctxt->error(ctxt->userData,
1040 "Expecting a single text value for <value>content\n");
1041 ctxt->nbErrors++;
1042 } else {
1043 def->value = xmlNodeGetContent(node);
1044 if (def->value == NULL) {
1045 if (ctxt->error != NULL)
1046 ctxt->error(ctxt->userData,
1047 "Element <value> has no content\n");
1048 ctxt->nbErrors++;
1049 }
1050 }
1051 /* TODO check ahead of time that the value is okay per the type */
1052 return(def);
1053}
1054
1055/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001056 * xmlRelaxNGParseData:
1057 * @ctxt: a Relax-NG parser context
1058 * @node: the data node.
1059 *
1060 * parse the content of a RelaxNG data node.
1061 *
1062 * Returns the definition pointer or NULL in case of error
1063 */
1064static xmlRelaxNGDefinePtr
1065xmlRelaxNGParseData(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1066 xmlRelaxNGDefinePtr def = NULL;
1067 xmlRelaxNGTypeLibraryPtr lib;
1068 xmlChar *type;
1069 xmlChar *library;
1070 xmlNodePtr content;
1071 int tmp;
1072
1073 type = xmlGetProp(node, BAD_CAST "type");
1074 if (type == NULL) {
1075 if (ctxt->error != NULL)
1076 ctxt->error(ctxt->userData,
1077 "data has no type\n");
1078 ctxt->nbErrors++;
1079 return(NULL);
1080 }
1081 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1082 if (library == NULL)
1083 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1084
1085 def = xmlRelaxNGNewDefine(ctxt, node);
1086 if (def == NULL) {
1087 xmlFree(type);
1088 return(NULL);
1089 }
1090 def->type = XML_RELAXNG_DATATYPE;
1091 def->name = type;
1092 def->ns = library;
1093
1094 lib = (xmlRelaxNGTypeLibraryPtr)
1095 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1096 if (lib == NULL) {
1097 if (ctxt->error != NULL)
1098 ctxt->error(ctxt->userData,
1099 "Use of unregistered type library '%s'\n",
1100 library);
1101 ctxt->nbErrors++;
1102 def->data = NULL;
1103 } else {
1104 def->data = lib;
1105 if (lib->have == NULL) {
1106 ctxt->error(ctxt->userData,
1107 "Internal error with type library '%s': no 'have'\n",
1108 library);
1109 ctxt->nbErrors++;
1110 } else {
1111 tmp = lib->have(lib->data, def->name);
1112 if (tmp != 1) {
1113 ctxt->error(ctxt->userData,
1114 "Error type '%s' is not exported by type library '%s'\n",
1115 def->name, library);
1116 ctxt->nbErrors++;
1117 }
1118 }
1119 }
1120 content = node->children;
1121 while (content != NULL) {
1122 TODO
1123 content = content->next;
1124 }
1125
1126 return(def);
1127}
1128
Daniel Veillard6eadf632003-01-23 18:29:16 +00001129
1130/**
Daniel Veillard276be4a2003-01-24 01:03:34 +00001131 * xmlRelaxNGParseDefine:
1132 * @ctxt: a Relax-NG parser context
1133 * @node: the define node
1134 *
1135 * parse the content of a RelaxNG define element node.
1136 *
1137 * Returns the definition pointer or NULL in case of error.
1138 */
1139static int
1140xmlRelaxNGParseDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1141 xmlChar *name;
1142 int ret = 0, tmp;
1143 xmlRelaxNGDefinePtr def;
1144 const xmlChar *olddefine;
1145
1146 name = xmlGetProp(node, BAD_CAST "name");
1147 if (name == NULL) {
1148 if (ctxt->error != NULL)
1149 ctxt->error(ctxt->userData,
1150 "define has no name\n");
1151 ctxt->nbErrors++;
1152 } else {
1153 def = xmlRelaxNGNewDefine(ctxt, node);
1154 if (def == NULL) {
1155 xmlFree(name);
1156 return(-1);
1157 }
1158 def->type = XML_RELAXNG_DEF;
1159 def->name = name;
1160 if (node->children == NULL) {
1161 if (ctxt->error != NULL)
1162 ctxt->error(ctxt->userData,
1163 "define has no children\n");
1164 ctxt->nbErrors++;
1165 } else {
1166 olddefine = ctxt->define;
1167 ctxt->define = name;
1168 def->content = xmlRelaxNGParsePatterns(ctxt,
1169 node->children);
1170 ctxt->define = olddefine;
1171 }
1172 if (ctxt->grammar->defs == NULL)
1173 ctxt->grammar->defs = xmlHashCreate(10);
1174 if (ctxt->grammar->defs == NULL) {
1175 if (ctxt->error != NULL)
1176 ctxt->error(ctxt->userData,
1177 "Could not create definition hash\n");
1178 ctxt->nbErrors++;
1179 ret = -1;
1180 xmlRelaxNGFreeDefine(def);
1181 } else {
1182 tmp = xmlHashAddEntry(ctxt->grammar->defs, name, def);
1183 if (tmp < 0) {
1184 TODO
1185 /* store and implement 4.17 on combining */
1186 ctxt->nbErrors++;
1187 ret = -1;
1188 xmlRelaxNGFreeDefine(def);
1189 }
1190 }
1191 }
1192 return(ret);
1193}
1194
1195/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00001196 * xmlRelaxNGParsePattern:
1197 * @ctxt: a Relax-NG parser context
1198 * @node: the pattern node.
1199 *
1200 * parse the content of a RelaxNG pattern node.
1201 *
Daniel Veillard276be4a2003-01-24 01:03:34 +00001202 * Returns the definition pointer or NULL in case of error or if no
1203 * pattern is generated.
Daniel Veillard6eadf632003-01-23 18:29:16 +00001204 */
1205static xmlRelaxNGDefinePtr
1206xmlRelaxNGParsePattern(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1207 xmlRelaxNGDefinePtr def = NULL;
1208
1209 if (IS_RELAXNG(node, "element")) {
1210 def = xmlRelaxNGParseElement(ctxt, node);
1211 } else if (IS_RELAXNG(node, "attribute")) {
1212 def = xmlRelaxNGParseAttribute(ctxt, node);
1213 } else if (IS_RELAXNG(node, "empty")) {
1214 def = xmlRelaxNGNewDefine(ctxt, node);
1215 if (def == NULL)
1216 return(NULL);
1217 def->type = XML_RELAXNG_EMPTY;
1218 } else if (IS_RELAXNG(node, "text")) {
1219 def = xmlRelaxNGNewDefine(ctxt, node);
1220 if (def == NULL)
1221 return(NULL);
1222 def->type = XML_RELAXNG_TEXT;
1223 if (node->children != NULL) {
1224 if (ctxt->error != NULL)
1225 ctxt->error(ctxt->userData, "text: had a child node\n");
1226 ctxt->nbErrors++;
1227 }
1228 } else if (IS_RELAXNG(node, "zeroOrMore")) {
1229 def = xmlRelaxNGNewDefine(ctxt, node);
1230 if (def == NULL)
1231 return(NULL);
1232 def->type = XML_RELAXNG_ZEROORMORE;
1233 def->content = xmlRelaxNGParsePatterns(ctxt, node->children);
1234 } else if (IS_RELAXNG(node, "oneOrMore")) {
1235 def = xmlRelaxNGNewDefine(ctxt, node);
1236 if (def == NULL)
1237 return(NULL);
1238 def->type = XML_RELAXNG_ZEROORMORE;
1239 def->content = xmlRelaxNGParsePatterns(ctxt, node->children);
1240 } else if (IS_RELAXNG(node, "optional")) {
1241 def = xmlRelaxNGNewDefine(ctxt, node);
1242 if (def == NULL)
1243 return(NULL);
1244 def->type = XML_RELAXNG_OPTIONAL;
1245 def->content = xmlRelaxNGParsePatterns(ctxt, node->children);
1246 } else if (IS_RELAXNG(node, "choice")) {
1247 def = xmlRelaxNGNewDefine(ctxt, node);
1248 if (def == NULL)
1249 return(NULL);
1250 def->type = XML_RELAXNG_CHOICE;
1251 def->content = xmlRelaxNGParsePatterns(ctxt, node->children);
1252 } else if (IS_RELAXNG(node, "group")) {
1253 def = xmlRelaxNGNewDefine(ctxt, node);
1254 if (def == NULL)
1255 return(NULL);
1256 def->type = XML_RELAXNG_GROUP;
1257 def->content = xmlRelaxNGParsePatterns(ctxt, node->children);
1258 } else if (IS_RELAXNG(node, "ref")) {
1259 def = xmlRelaxNGNewDefine(ctxt, node);
1260 if (def == NULL)
1261 return(NULL);
1262 def->type = XML_RELAXNG_REF;
1263 def->name = xmlGetProp(node, BAD_CAST "name");
1264 if (def->name == NULL) {
1265 if (ctxt->error != NULL)
1266 ctxt->error(ctxt->userData,
1267 "ref has no name\n");
1268 ctxt->nbErrors++;
Daniel Veillard276be4a2003-01-24 01:03:34 +00001269 } else {
1270 if ((ctxt->define != NULL) &&
1271 (xmlStrEqual(ctxt->define, def->name))) {
1272 if (ctxt->error != NULL)
1273 ctxt->error(ctxt->userData,
1274 "Recursive reference to %s not in an element\n",
1275 def->name);
1276 ctxt->nbErrors++;
1277 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00001278 }
1279 if (node->children != NULL) {
1280 if (ctxt->error != NULL)
1281 ctxt->error(ctxt->userData,
1282 "ref is not empty\n");
1283 ctxt->nbErrors++;
1284 }
1285 if (ctxt->grammar->refs == NULL)
1286 ctxt->grammar->refs = xmlHashCreate(10);
1287 if (ctxt->grammar->refs == NULL) {
1288 if (ctxt->error != NULL)
1289 ctxt->error(ctxt->userData,
1290 "Could not create references hash\n");
1291 ctxt->nbErrors++;
1292 xmlRelaxNGFreeDefine(def);
1293 def = NULL;
1294 } else {
1295 int tmp;
1296
1297 tmp = xmlHashAddEntry(ctxt->grammar->refs, def->name, def);
1298 if (tmp < 0) {
1299 xmlRelaxNGDefinePtr prev;
1300
1301 prev = (xmlRelaxNGDefinePtr)
1302 xmlHashLookup(ctxt->grammar->refs, def->name);
1303 if (prev == NULL) {
1304 if (ctxt->error != NULL)
1305 ctxt->error(ctxt->userData,
1306 "Internal error refs definitions '%s'\n",
1307 def->name);
1308 ctxt->nbErrors++;
1309 xmlRelaxNGFreeDefine(def);
1310 def = NULL;
1311 } else {
1312 def->nextHash = prev->nextHash;
1313 prev->nextHash = def;
1314 }
1315 }
1316 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001317 } else if (IS_RELAXNG(node, "data")) {
1318 def = xmlRelaxNGParseData(ctxt, node);
Daniel Veillard276be4a2003-01-24 01:03:34 +00001319 } else if (IS_RELAXNG(node, "define")) {
1320 xmlRelaxNGParseDefine(ctxt, node);
1321 def = NULL;
Daniel Veillardedc91922003-01-26 00:52:04 +00001322 } else if (IS_RELAXNG(node, "value")) {
1323 def = xmlRelaxNGParseValue(ctxt, node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001324 } else {
1325 TODO
1326 }
1327 return(def);
1328}
1329
1330/**
1331 * xmlRelaxNGParseAttribute:
1332 * @ctxt: a Relax-NG parser context
1333 * @node: the element node
1334 *
1335 * parse the content of a RelaxNG attribute node.
1336 *
1337 * Returns the definition pointer or NULL in case of error.
1338 */
1339static xmlRelaxNGDefinePtr
1340xmlRelaxNGParseAttribute(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1341 xmlRelaxNGDefinePtr ret, cur, last;
1342 xmlNodePtr child;
1343 xmlChar *val;
1344 int old_flags;
1345
1346 ret = xmlRelaxNGNewDefine(ctxt, node);
1347 if (ret == NULL)
1348 return(NULL);
1349 ret->type = XML_RELAXNG_ATTRIBUTE;
1350 child = node->children;
1351 if (child == NULL) {
1352 if (ctxt->error != NULL)
1353 ctxt->error(ctxt->userData,
1354 "xmlRelaxNGParseattribute: attribute has no children\n");
1355 ctxt->nbErrors++;
1356 return(ret);
1357 }
1358 old_flags = ctxt->flags;
1359 ctxt->flags |= XML_RELAXNG_IN_ATTRIBUTE;
1360 if (IS_RELAXNG(child, "name")) {
1361 val = xmlNodeGetContent(child);
1362 ret->name = val;
1363 val = xmlGetProp(child, BAD_CAST "ns");
1364 ret->ns = val;
1365 } else if (IS_RELAXNG(child, "anyName")) {
1366 TODO
1367 } else if (IS_RELAXNG(child, "nsName")) {
1368 TODO
1369 } else if (IS_RELAXNG(child, "choice")) {
1370 TODO
1371 } else {
1372 if (ctxt->error != NULL)
1373 ctxt->error(ctxt->userData,
1374 "element: expecting name, anyName, nsName or choice : got %s\n",
1375 child->name);
1376 ctxt->nbErrors++;
1377 ctxt->flags = old_flags;
1378 return(ret);
1379 }
1380 child = child->next;
1381 last = NULL;
1382 while (child != NULL) {
1383 cur = xmlRelaxNGParsePattern(ctxt, child);
1384 if (cur != NULL) {
1385 switch (cur->type) {
1386 case XML_RELAXNG_EMPTY:
1387 case XML_RELAXNG_NOT_ALLOWED:
1388 case XML_RELAXNG_TEXT:
1389 case XML_RELAXNG_ELEMENT:
1390 case XML_RELAXNG_DATATYPE:
1391 case XML_RELAXNG_VALUE:
1392 case XML_RELAXNG_LIST:
1393 case XML_RELAXNG_REF:
1394 case XML_RELAXNG_DEF:
1395 case XML_RELAXNG_ONEORMORE:
1396 case XML_RELAXNG_ZEROORMORE:
1397 case XML_RELAXNG_OPTIONAL:
1398 case XML_RELAXNG_CHOICE:
1399 case XML_RELAXNG_GROUP:
1400 case XML_RELAXNG_INTERLEAVE:
1401 if (last == NULL) {
1402 ret->content = last = cur;
1403 } else {
1404 if ((last->type == XML_RELAXNG_ELEMENT) &&
1405 (ret->content == last)) {
1406 ret->content = xmlRelaxNGNewDefine(ctxt, node);
1407 if (ret->content != NULL) {
1408 ret->content->type = XML_RELAXNG_GROUP;
1409 ret->content->content = last;
1410 } else {
1411 ret->content = last;
1412 }
1413 }
1414 last->next = cur;
1415 last = cur;
1416 }
1417 break;
1418 case XML_RELAXNG_ATTRIBUTE:
1419 cur->next = ret->attrs;
1420 ret->attrs = cur;
1421 break;
1422 }
1423 }
1424 child = child->next;
1425 }
1426 ctxt->flags = old_flags;
1427 return(ret);
1428}
1429
1430/**
1431 * xmlRelaxNGParseElement:
1432 * @ctxt: a Relax-NG parser context
1433 * @node: the element node
1434 *
1435 * parse the content of a RelaxNG element node.
1436 *
1437 * Returns the definition pointer or NULL in case of error.
1438 */
1439static xmlRelaxNGDefinePtr
1440xmlRelaxNGParseElement(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1441 xmlRelaxNGDefinePtr ret, cur, last;
1442 xmlNodePtr child;
1443 xmlChar *val;
Daniel Veillard276be4a2003-01-24 01:03:34 +00001444 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001445
1446 ret = xmlRelaxNGNewDefine(ctxt, node);
1447 if (ret == NULL)
1448 return(NULL);
1449 ret->type = XML_RELAXNG_ELEMENT;
1450 child = node->children;
1451 if (child == NULL) {
1452 if (ctxt->error != NULL)
1453 ctxt->error(ctxt->userData,
1454 "xmlRelaxNGParseElement: element has no children\n");
1455 ctxt->nbErrors++;
1456 return(ret);
1457 }
1458 if (IS_RELAXNG(child, "name")) {
1459 val = xmlNodeGetContent(child);
1460 ret->name = val;
1461 val = xmlGetProp(child, BAD_CAST "ns");
1462 ret->ns = val;
1463 } else if (IS_RELAXNG(child, "anyName")) {
1464 TODO
1465 } else if (IS_RELAXNG(child, "nsName")) {
1466 TODO
1467 } else if (IS_RELAXNG(child, "choice")) {
1468 TODO
1469 } else {
1470 if (ctxt->error != NULL)
1471 ctxt->error(ctxt->userData,
1472 "element: expecting name, anyName, nsName or choice : got %s\n",
1473 child->name);
1474 ctxt->nbErrors++;
1475 return(ret);
1476 }
1477 child = child->next;
1478 if (child == NULL) {
1479 if (ctxt->error != NULL)
1480 ctxt->error(ctxt->userData,
1481 "xmlRelaxNGParseElement: element has no content\n");
1482 ctxt->nbErrors++;
1483 return(ret);
1484 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00001485 olddefine = ctxt->define;
1486 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001487 last = NULL;
1488 while (child != NULL) {
1489 cur = xmlRelaxNGParsePattern(ctxt, child);
1490 if (cur != NULL) {
1491 switch (cur->type) {
1492 case XML_RELAXNG_EMPTY:
1493 case XML_RELAXNG_NOT_ALLOWED:
1494 case XML_RELAXNG_TEXT:
1495 case XML_RELAXNG_ELEMENT:
1496 case XML_RELAXNG_DATATYPE:
1497 case XML_RELAXNG_VALUE:
1498 case XML_RELAXNG_LIST:
1499 case XML_RELAXNG_REF:
1500 case XML_RELAXNG_DEF:
1501 case XML_RELAXNG_ZEROORMORE:
1502 case XML_RELAXNG_ONEORMORE:
1503 case XML_RELAXNG_OPTIONAL:
1504 case XML_RELAXNG_CHOICE:
1505 case XML_RELAXNG_GROUP:
1506 case XML_RELAXNG_INTERLEAVE:
1507 if (last == NULL) {
1508 ret->content = last = cur;
1509 } else {
1510 if ((last->type == XML_RELAXNG_ELEMENT) &&
1511 (ret->content == last)) {
1512 ret->content = xmlRelaxNGNewDefine(ctxt, node);
1513 if (ret->content != NULL) {
1514 ret->content->type = XML_RELAXNG_GROUP;
1515 ret->content->content = last;
1516 } else {
1517 ret->content = last;
1518 }
1519 }
1520 last->next = cur;
1521 last = cur;
1522 }
1523 break;
1524 case XML_RELAXNG_ATTRIBUTE:
1525 cur->next = ret->attrs;
1526 ret->attrs = cur;
1527 break;
1528 }
1529 }
1530 child = child->next;
1531 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00001532 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001533 return(ret);
1534}
1535
1536/**
1537 * xmlRelaxNGParsePatterns:
1538 * @ctxt: a Relax-NG parser context
1539 * @nodes: list of nodes
1540 *
1541 * parse the content of a RelaxNG start node.
1542 *
1543 * Returns the definition pointer or NULL in case of error.
1544 */
1545static xmlRelaxNGDefinePtr
1546xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
1547 xmlRelaxNGDefinePtr def = NULL, last = NULL, cur;
1548
1549 while (nodes != NULL) {
1550 if (IS_RELAXNG(nodes, "element")) {
1551 cur = xmlRelaxNGParseElement(ctxt, nodes);
1552 if (def == NULL) {
1553 def = last = cur;
1554 } else {
1555 if ((def->type == XML_RELAXNG_ELEMENT) && (def == last)) {
1556 def = xmlRelaxNGNewDefine(ctxt, nodes);
1557 def->type = XML_RELAXNG_GROUP;
1558 def->content = last;
1559 }
1560 last->next = cur;
1561 last = cur;
1562 }
1563 } else {
1564 cur = xmlRelaxNGParsePattern(ctxt, nodes);
1565 if (def == NULL) {
1566 def = last = cur;
1567 } else {
1568 last->next = cur;
1569 last = cur;
1570 }
1571 }
1572 nodes = nodes->next;
1573 }
1574 return(def);
1575}
1576
1577/**
1578 * xmlRelaxNGParseStart:
1579 * @ctxt: a Relax-NG parser context
1580 * @nodes: start children nodes
1581 *
1582 * parse the content of a RelaxNG start node.
1583 *
1584 * Returns 0 in case of success, -1 in case of error
1585 */
1586static int
1587xmlRelaxNGParseStart(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
1588 int ret = 0;
1589 xmlRelaxNGDefinePtr def = NULL;
1590
1591 while (nodes != NULL) {
1592 if (IS_RELAXNG(nodes, "empty")) {
1593 TODO
1594 xmlElemDump(stdout, nodes->doc, nodes);
1595 } else if (IS_RELAXNG(nodes, "notAllowed")) {
1596 TODO
1597 xmlElemDump(stdout, nodes->doc, nodes);
1598 } else {
1599 def = xmlRelaxNGParsePatterns(ctxt, nodes);
1600 ctxt->grammar->start = def;
1601 }
1602 nodes = nodes->next;
1603 }
1604 return(ret);
1605}
1606
1607/**
1608 * xmlRelaxNGParseGrammarContent:
1609 * @ctxt: a Relax-NG parser context
1610 * @nodes: grammar children nodes
1611 *
1612 * parse the content of a RelaxNG grammar node.
1613 *
1614 * Returns 0 in case of success, -1 in case of error
1615 */
1616static int
1617xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt
1618 ATTRIBUTE_UNUSED, xmlNodePtr nodes)
1619{
Daniel Veillard276be4a2003-01-24 01:03:34 +00001620 int ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001621
1622 if (nodes == NULL) {
1623 if (ctxt->error != NULL)
1624 ctxt->error(ctxt->userData,
1625 "grammar has no children\n");
1626 ctxt->nbErrors++;
1627 return(-1);
1628 }
1629 if (IS_RELAXNG(nodes, "start")) {
1630 if (nodes->children == NULL) {
1631 if (ctxt->error != NULL)
1632 ctxt->error(ctxt->userData,
1633 "grammar has no children\n");
1634 ctxt->nbErrors++;
1635 } else {
1636 xmlRelaxNGParseStart(ctxt, nodes->children);
1637 }
1638 nodes = nodes->next;
1639 } else {
1640 if (ctxt->error != NULL)
1641 ctxt->error(ctxt->userData,
1642 "grammar first child must be a <start>\n");
1643 ctxt->nbErrors++;
1644 return(-1);
1645 }
1646 while (nodes != NULL) {
1647 if (IS_RELAXNG(nodes, "define")) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00001648 ret = xmlRelaxNGParseDefine(ctxt, nodes);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001649 } else {
1650 if (ctxt->error != NULL)
1651 ctxt->error(ctxt->userData,
1652 "grammar allows onlys <define> child after <start>\n");
1653 ctxt->nbErrors++;
1654 ret = -1;
1655 }
1656 nodes = nodes->next;
1657 }
1658 return (ret);
1659}
1660
1661/**
1662 * xmlRelaxNGCheckReference:
1663 * @ref: the ref
1664 * @ctxt: a Relax-NG parser context
1665 * @name: the name associated to the defines
1666 *
1667 * Applies the 4.17. combine attribute rule for all the define
1668 * element of a given grammar using the same name.
1669 */
1670static void
1671xmlRelaxNGCheckReference(xmlRelaxNGDefinePtr ref,
1672 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
1673 xmlRelaxNGGrammarPtr grammar;
Daniel Veillard276be4a2003-01-24 01:03:34 +00001674 xmlRelaxNGDefinePtr def, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001675
1676 grammar = ctxt->grammar;
1677 if (grammar == NULL) {
1678 if (ctxt->error != NULL)
1679 ctxt->error(ctxt->userData,
1680 "Internal error: no grammar in CheckReference %s\n",
1681 name);
1682 ctxt->nbErrors++;
1683 return;
1684 }
1685 if (ref->content != NULL) {
1686 if (ctxt->error != NULL)
1687 ctxt->error(ctxt->userData,
1688 "Internal error: reference has content in CheckReference %s\n",
1689 name);
1690 ctxt->nbErrors++;
1691 return;
1692 }
1693 if (grammar->defs != NULL) {
1694 def = xmlHashLookup(grammar->defs, name);
1695 if (def != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00001696 cur = ref;
1697 while (cur != NULL) {
1698 cur->content = def;
1699 cur = cur->nextHash;
1700 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00001701 } else {
1702 TODO
1703 }
1704 }
1705 /*
1706 * TODO: make a closure and verify there is no loop !
1707 */
1708}
1709
1710/**
1711 * xmlRelaxNGCheckCombine:
1712 * @define: the define(s) list
1713 * @ctxt: a Relax-NG parser context
1714 * @name: the name associated to the defines
1715 *
1716 * Applies the 4.17. combine attribute rule for all the define
1717 * element of a given grammar using the same name.
1718 */
1719static void
1720xmlRelaxNGCheckCombine(xmlRelaxNGDefinePtr define,
1721 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
1722 xmlChar *combine;
1723 int choiceOrInterleave = -1;
1724 int missing = 0;
1725 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
1726
1727 if (define->nextHash == NULL)
1728 return;
1729 cur = define;
1730 while (cur != NULL) {
1731 combine = xmlGetProp(cur->node, BAD_CAST "combine");
1732 if (combine != NULL) {
1733 if (xmlStrEqual(combine, BAD_CAST "choice")) {
1734 if (choiceOrInterleave == -1)
1735 choiceOrInterleave = 1;
1736 else if (choiceOrInterleave == 0) {
1737 if (ctxt->error != NULL)
1738 ctxt->error(ctxt->userData,
1739 "Defines for %s use both 'choice' and 'interleave'\n",
1740 name);
1741 ctxt->nbErrors++;
1742 }
1743 } else if (xmlStrEqual(combine, BAD_CAST "choice")) {
1744 if (choiceOrInterleave == -1)
1745 choiceOrInterleave = 0;
1746 else if (choiceOrInterleave == 1) {
1747 if (ctxt->error != NULL)
1748 ctxt->error(ctxt->userData,
1749 "Defines for %s use both 'choice' and 'interleave'\n",
1750 name);
1751 ctxt->nbErrors++;
1752 }
1753 } else {
1754 if (ctxt->error != NULL)
1755 ctxt->error(ctxt->userData,
1756 "Defines for %s use unknown combine value '%s''\n",
1757 name, combine);
1758 ctxt->nbErrors++;
1759 }
1760 xmlFree(combine);
1761 } else {
1762 if (missing == 0)
1763 missing = 1;
1764 else {
1765 if (ctxt->error != NULL)
1766 ctxt->error(ctxt->userData,
1767 "Some defines for %s lacks the combine attribute\n",
1768 name);
1769 ctxt->nbErrors++;
1770 }
1771 }
1772
1773 cur = cur->nextHash;
1774 }
1775#ifdef DEBUG
1776 xmlGenericError(xmlGenericErrorContext,
1777 "xmlRelaxNGCheckCombine(): merging %s defines: %d\n",
1778 name, choiceOrInterleave);
1779#endif
1780 if (choiceOrInterleave == -1)
1781 choiceOrInterleave = 0;
1782 cur = xmlRelaxNGNewDefine(ctxt, define->node);
1783 if (cur == NULL)
1784 return;
1785 if (choiceOrInterleave == 0)
1786 cur->type = XML_RELAXNG_CHOICE;
1787 else
1788 cur->type = XML_RELAXNG_INTERLEAVE;
1789 tmp = define;
1790 last = NULL;
1791 while (tmp != NULL) {
1792 if (tmp->content != NULL) {
1793 if (tmp->content->next != NULL) {
1794 /*
1795 * we need first to create a wrapper.
1796 */
1797 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
1798 if (tmp2 == NULL)
1799 break;
1800 tmp2->type = XML_RELAXNG_GROUP;
1801 tmp2->content = tmp->content;
1802 } else {
1803 tmp2 = tmp->content;
1804 }
1805 if (last == NULL) {
1806 cur->content = tmp2;
1807 } else {
1808 last->next = tmp2;
1809 }
1810 last = tmp2;
1811 tmp->content = NULL;
1812 }
1813 tmp = tmp->nextHash;
1814 }
1815 define->content = cur;
1816}
1817
1818/**
1819 * xmlRelaxNGCombineStart:
1820 * @ctxt: a Relax-NG parser context
1821 * @grammar: the grammar
1822 *
1823 * Applies the 4.17. combine rule for all the start
1824 * element of a given grammar.
1825 */
1826static void
1827xmlRelaxNGCombineStart(xmlRelaxNGParserCtxtPtr ctxt,
1828 xmlRelaxNGGrammarPtr grammar) {
1829 xmlRelaxNGDefinePtr starts;
1830 xmlChar *combine;
1831 int choiceOrInterleave = -1;
1832 int missing = 0;
1833 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
1834
1835 starts = grammar->start;
1836 if (starts->nextHash == NULL)
1837 return;
1838 cur = starts;
1839 while (cur != NULL) {
1840 combine = xmlGetProp(cur->node, BAD_CAST "combine");
1841 if (combine != NULL) {
1842 if (xmlStrEqual(combine, BAD_CAST "choice")) {
1843 if (choiceOrInterleave == -1)
1844 choiceOrInterleave = 1;
1845 else if (choiceOrInterleave == 0) {
1846 if (ctxt->error != NULL)
1847 ctxt->error(ctxt->userData,
1848 "<start> use both 'choice' and 'interleave'\n");
1849 ctxt->nbErrors++;
1850 }
1851 } else if (xmlStrEqual(combine, BAD_CAST "choice")) {
1852 if (choiceOrInterleave == -1)
1853 choiceOrInterleave = 0;
1854 else if (choiceOrInterleave == 1) {
1855 if (ctxt->error != NULL)
1856 ctxt->error(ctxt->userData,
1857 "<start> use both 'choice' and 'interleave'\n");
1858 ctxt->nbErrors++;
1859 }
1860 } else {
1861 if (ctxt->error != NULL)
1862 ctxt->error(ctxt->userData,
1863 "<start> uses unknown combine value '%s''\n", combine);
1864 ctxt->nbErrors++;
1865 }
1866 xmlFree(combine);
1867 } else {
1868 if (missing == 0)
1869 missing = 1;
1870 else {
1871 if (ctxt->error != NULL)
1872 ctxt->error(ctxt->userData,
1873 "Some <start> elements lacks the combine attribute\n");
1874 ctxt->nbErrors++;
1875 }
1876 }
1877
1878 cur = cur->nextHash;
1879 }
1880#ifdef DEBUG
1881 xmlGenericError(xmlGenericErrorContext,
1882 "xmlRelaxNGCombineStart(): merging <start>: %d\n",
1883 choiceOrInterleave);
1884#endif
1885 if (choiceOrInterleave == -1)
1886 choiceOrInterleave = 0;
1887 cur = xmlRelaxNGNewDefine(ctxt, starts->node);
1888 if (cur == NULL)
1889 return;
1890 if (choiceOrInterleave == 0)
1891 cur->type = XML_RELAXNG_CHOICE;
1892 else
1893 cur->type = XML_RELAXNG_INTERLEAVE;
1894 tmp = starts;
1895 last = NULL;
1896 while (tmp != NULL) {
1897 if (tmp->content != NULL) {
1898 if (tmp->content->next != NULL) {
1899 /*
1900 * we need first to create a wrapper.
1901 */
1902 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
1903 if (tmp2 == NULL)
1904 break;
1905 tmp2->type = XML_RELAXNG_GROUP;
1906 tmp2->content = tmp->content;
1907 } else {
1908 tmp2 = tmp->content;
1909 }
1910 if (last == NULL) {
1911 cur->content = tmp2;
1912 } else {
1913 last->next = tmp2;
1914 }
1915 last = tmp2;
1916 tmp->content = NULL;
1917 }
1918 tmp = tmp->nextHash;
1919 }
1920 starts->content = cur;
1921}
1922
1923/**
1924 * xmlRelaxNGParseGrammar:
1925 * @ctxt: a Relax-NG parser context
1926 * @nodes: grammar children nodes
1927 *
1928 * parse a Relax-NG <grammar> node
1929 *
1930 * Returns the internal xmlRelaxNGGrammarPtr built or
1931 * NULL in case of error
1932 */
1933static xmlRelaxNGGrammarPtr
1934xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
1935 xmlRelaxNGGrammarPtr ret, tmp, old;
1936
Daniel Veillard6eadf632003-01-23 18:29:16 +00001937 ret = xmlRelaxNGNewGrammar(ctxt);
1938 if (ret == NULL)
1939 return(NULL);
1940
1941 /*
1942 * Link the new grammar in the tree
1943 */
1944 ret->parent = ctxt->grammar;
1945 if (ctxt->grammar != NULL) {
1946 tmp = ctxt->grammar->children;
1947 if (tmp == NULL) {
1948 ctxt->grammar->children = ret;
1949 } else {
1950 while (tmp->next != NULL)
1951 tmp = tmp->next;
1952 tmp->next = ret;
1953 }
1954 }
1955
1956 old = ctxt->grammar;
1957 ctxt->grammar = ret;
1958 xmlRelaxNGParseGrammarContent(ctxt, nodes);
1959 ctxt->grammar = ret;
1960
1961 /*
1962 * Apply 4.17 mergingd rules to defines and starts
1963 */
1964 xmlRelaxNGCombineStart(ctxt, ret);
1965 if (ret->defs != NULL) {
1966 xmlHashScan(ret->defs, (xmlHashScanner) xmlRelaxNGCheckCombine,
1967 ctxt);
1968 }
1969
1970 /*
1971 * link together defines and refs in this grammar
1972 */
1973 if (ret->refs != NULL) {
1974 xmlHashScan(ret->refs, (xmlHashScanner) xmlRelaxNGCheckReference,
1975 ctxt);
1976 }
1977 ctxt->grammar = old;
1978 return(ret);
1979}
1980
1981/**
1982 * xmlRelaxNGParseDocument:
1983 * @ctxt: a Relax-NG parser context
1984 * @node: the root node of the RelaxNG schema
1985 *
1986 * parse a Relax-NG definition resource and build an internal
1987 * xmlRelaxNG struture which can be used to validate instances.
1988 *
1989 * Returns the internal XML RelaxNG structure built or
1990 * NULL in case of error
1991 */
1992static xmlRelaxNGPtr
1993xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1994 xmlRelaxNGPtr schema = NULL;
Daniel Veillard276be4a2003-01-24 01:03:34 +00001995 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001996
1997 if ((ctxt == NULL) || (node == NULL))
1998 return (NULL);
1999
2000 schema = xmlRelaxNGNewRelaxNG(ctxt);
2001 if (schema == NULL)
2002 return(NULL);
2003
Daniel Veillard276be4a2003-01-24 01:03:34 +00002004 olddefine = ctxt->define;
2005 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002006 if (IS_RELAXNG(node, "grammar")) {
2007 schema->topgrammar = xmlRelaxNGParseGrammar(ctxt, node->children);
2008 } else {
2009 schema->topgrammar = xmlRelaxNGNewGrammar(ctxt);
2010 if (schema->topgrammar == NULL) {
2011 return(schema);
2012 }
2013 schema->topgrammar->parent = NULL;
2014 ctxt->grammar = schema->topgrammar;
2015 xmlRelaxNGParseStart(ctxt, node);
2016 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002017 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002018
2019#ifdef DEBUG
2020 if (schema == NULL)
2021 xmlGenericError(xmlGenericErrorContext,
2022 "xmlRelaxNGParseDocument() failed\n");
2023#endif
2024
2025 return (schema);
2026}
2027
2028/************************************************************************
2029 * *
2030 * Reading RelaxNGs *
2031 * *
2032 ************************************************************************/
2033
2034/**
2035 * xmlRelaxNGNewParserCtxt:
2036 * @URL: the location of the schema
2037 *
2038 * Create an XML RelaxNGs parse context for that file/resource expected
2039 * to contain an XML RelaxNGs file.
2040 *
2041 * Returns the parser context or NULL in case of error
2042 */
2043xmlRelaxNGParserCtxtPtr
2044xmlRelaxNGNewParserCtxt(const char *URL) {
2045 xmlRelaxNGParserCtxtPtr ret;
2046
2047 if (URL == NULL)
2048 return(NULL);
2049
2050 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
2051 if (ret == NULL) {
2052 xmlGenericError(xmlGenericErrorContext,
2053 "Failed to allocate new schama parser context for %s\n", URL);
2054 return (NULL);
2055 }
2056 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
2057 ret->URL = xmlStrdup((const xmlChar *)URL);
2058 return (ret);
2059}
2060
2061/**
2062 * xmlRelaxNGNewMemParserCtxt:
2063 * @buffer: a pointer to a char array containing the schemas
2064 * @size: the size of the array
2065 *
2066 * Create an XML RelaxNGs parse context for that memory buffer expected
2067 * to contain an XML RelaxNGs file.
2068 *
2069 * Returns the parser context or NULL in case of error
2070 */
2071xmlRelaxNGParserCtxtPtr
2072xmlRelaxNGNewMemParserCtxt(const char *buffer, int size) {
2073 xmlRelaxNGParserCtxtPtr ret;
2074
2075 if ((buffer == NULL) || (size <= 0))
2076 return(NULL);
2077
2078 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
2079 if (ret == NULL) {
2080 xmlGenericError(xmlGenericErrorContext,
2081 "Failed to allocate new schama parser context\n");
2082 return (NULL);
2083 }
2084 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
2085 ret->buffer = buffer;
2086 ret->size = size;
2087 return (ret);
2088}
2089
2090/**
2091 * xmlRelaxNGFreeParserCtxt:
2092 * @ctxt: the schema parser context
2093 *
2094 * Free the resources associated to the schema parser context
2095 */
2096void
2097xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxtPtr ctxt) {
2098 if (ctxt == NULL)
2099 return;
2100 if (ctxt->URL != NULL)
2101 xmlFree(ctxt->URL);
2102 if (ctxt->doc != NULL)
2103 xmlFreeDoc(ctxt->doc);
2104 xmlFree(ctxt);
2105}
2106
2107
2108/**
2109 * xmlRelaxNGParse:
2110 * @ctxt: a Relax-NG validation context
2111 *
2112 * parse a schema definition resource and build an internal
2113 * XML Shema struture which can be used to validate instances.
2114 * *WARNING* this interface is highly subject to change
2115 *
2116 * Returns the internal XML RelaxNG structure built from the resource or
2117 * NULL in case of error
2118 */
2119xmlRelaxNGPtr
2120xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt)
2121{
2122 xmlRelaxNGPtr ret = NULL;
2123 xmlDocPtr doc;
2124 xmlNodePtr root, cur, delete;
2125
2126 xmlRelaxNGInitTypes();
2127
2128 if (ctxt == NULL)
2129 return (NULL);
2130
2131 /*
2132 * First step is to parse the input document into an DOM/Infoset
2133 */
2134 if (ctxt->URL != NULL) {
2135 doc = xmlParseFile((const char *) ctxt->URL);
2136 if (doc == NULL) {
2137 if (ctxt->error != NULL)
2138 ctxt->error(ctxt->userData,
2139 "xmlRelaxNGParse: could not load %s\n", ctxt->URL);
2140 ctxt->nbErrors++;
2141 return (NULL);
2142 }
2143 } else if (ctxt->buffer != NULL) {
2144 doc = xmlParseMemory(ctxt->buffer, ctxt->size);
2145 if (doc == NULL) {
2146 if (ctxt->error != NULL)
2147 ctxt->error(ctxt->userData,
2148 "xmlRelaxNGParse: could not parse schemas\n");
2149 ctxt->nbErrors++;
2150 return (NULL);
2151 }
2152 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
2153 ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
2154 } else {
2155 if (ctxt->error != NULL)
2156 ctxt->error(ctxt->userData,
2157 "xmlRelaxNGParse: nothing to parse\n");
2158 ctxt->nbErrors++;
2159 return (NULL);
2160 }
2161 ctxt->doc = doc;
2162
2163 /*
2164 * Then extract the root and RelaxNG parse it
2165 */
2166 root = xmlDocGetRootElement(doc);
2167 if (root == NULL) {
2168 if (ctxt->error != NULL)
2169 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
2170 ctxt->URL);
2171 ctxt->nbErrors++;
2172 return (NULL);
2173 }
2174
2175 /*
2176 * Remove all the blank text nodes
2177 */
2178 delete = NULL;
2179 cur = root;
2180 while (cur != NULL) {
2181 if (delete != NULL) {
2182 xmlUnlinkNode(delete);
2183 xmlFreeNode(delete);
2184 delete = NULL;
2185 }
2186 if (cur->type == XML_ELEMENT_NODE) {
2187 /*
2188 * Simplification 4.1. Annotations
2189 */
2190 if ((cur->ns == NULL) ||
2191 (!xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
2192 delete = cur;
2193 goto skip_children;
2194 } else {
2195 if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
2196 TODO
2197 } else if (xmlStrEqual(cur->name, BAD_CAST "include")) {
2198 TODO
2199 } else if ((xmlStrEqual(cur->name, BAD_CAST "element")) ||
2200 (xmlStrEqual(cur->name, BAD_CAST "attribute"))) {
2201 xmlChar *name;
2202 xmlNodePtr text = NULL;
2203
2204 /*
2205 * Simplification 4.8. name attribute of element
2206 * and attribute elements
2207 */
2208 name = xmlGetProp(cur, BAD_CAST "name");
2209 if (name != NULL) {
2210 if (cur->children == NULL) {
2211 text = xmlNewChild(cur, cur->ns, BAD_CAST "name",
2212 name);
2213 } else {
2214 xmlNodePtr node;
2215 node = xmlNewNode(cur->ns, BAD_CAST "name");
2216 if (node != NULL) {
2217 xmlAddPrevSibling(cur->children, node);
2218 text = xmlNewText(name);
2219 xmlAddChild(node, text);
2220 text = node;
2221 }
2222 }
2223 xmlUnsetProp(cur, BAD_CAST "name");
2224 xmlFree(name);
2225 }
2226 if (xmlStrEqual(cur->name, BAD_CAST "attribute")) {
2227 if (text == NULL) {
2228 text = cur->children;
2229 while (text != NULL) {
2230 if ((text->type == XML_ELEMENT_NODE) &&
2231 (xmlStrEqual(text->name, BAD_CAST "name")))
2232 break;
2233 text = text->next;
2234 }
2235 }
2236 if (text == NULL) {
2237 if (ctxt->error != NULL)
2238 ctxt->error(ctxt->userData,
2239 "xmlRelaxNGParse: attribute without name\n");
2240 ctxt->nbErrors++;
2241 } else {
2242 xmlSetProp(text, BAD_CAST "ns", BAD_CAST "");
2243 }
2244 }
2245 } else if ((xmlStrEqual(cur->name, BAD_CAST "name")) ||
2246 (xmlStrEqual(cur->name, BAD_CAST "nsName")) ||
2247 (xmlStrEqual(cur->name, BAD_CAST "value"))) {
2248 /*
2249 * Simplification 4.8. name attribute of element
2250 * and attribute elements
2251 */
2252 if (xmlHasProp(cur, BAD_CAST "ns") == NULL) {
2253 xmlNodePtr node;
2254 xmlChar *ns = NULL;
2255
2256 node = cur->parent;
2257 while ((node != NULL) &&
2258 (node->type == XML_ELEMENT_NODE)) {
2259 ns = xmlGetProp(node, BAD_CAST "ns");
2260 if (ns != NULL) {
2261 break;
2262 }
2263 node = node->parent;
2264 }
2265 if (ns == NULL) {
2266 xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
2267 } else {
2268 xmlSetProp(cur, BAD_CAST "ns", ns);
2269 xmlFree(ns);
2270 }
2271 }
2272 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
2273 xmlChar *name, *local, *prefix;
2274
2275 /*
2276 * Simplification: 4.10. QNames
2277 */
2278 name = xmlNodeGetContent(cur);
2279 if (name != NULL) {
2280 local = xmlSplitQName2(name, &prefix);
2281 if (local != NULL) {
2282 xmlNsPtr ns;
2283
2284 ns = xmlSearchNs(cur->doc, cur, prefix);
2285 if (ns == NULL) {
2286 if (ctxt->error != NULL)
2287 ctxt->error(ctxt->userData,
2288 "xmlRelaxNGParse: no namespace for prefix %s\n", prefix);
2289 ctxt->nbErrors++;
2290 } else {
2291 xmlSetProp(cur, BAD_CAST "ns", ns->href);
2292 xmlNodeSetContent(cur, local);
2293 }
2294 xmlFree(local);
2295 xmlFree(prefix);
2296 }
2297 xmlFree(name);
2298 }
2299 }
2300 }
2301 }
2302 }
2303 /*
2304 * Simplification 4.2 whitespaces
2305 */
2306 else if (cur->type == XML_TEXT_NODE) {
2307 if (IS_BLANK_NODE(cur)) {
2308 if (cur->parent->type == XML_ELEMENT_NODE) {
2309 if ((!xmlStrEqual(cur->parent->name, BAD_CAST "value")) &&
2310 (!xmlStrEqual(cur->parent->name, BAD_CAST "param")))
2311 delete = cur;
2312 } else {
2313 delete = cur;
2314 goto skip_children;
2315 }
2316 }
2317 } else if (cur->type != XML_CDATA_SECTION_NODE) {
2318 delete = cur;
2319 goto skip_children;
2320 }
2321
2322 /*
2323 * Skip to next node
2324 */
2325 if (cur->children != NULL) {
2326 if ((cur->children->type != XML_ENTITY_DECL) &&
2327 (cur->children->type != XML_ENTITY_REF_NODE) &&
2328 (cur->children->type != XML_ENTITY_NODE)) {
2329 cur = cur->children;
2330 continue;
2331 }
2332 }
2333skip_children:
2334 if (cur->next != NULL) {
2335 cur = cur->next;
2336 continue;
2337 }
2338
2339 do {
2340 cur = cur->parent;
2341 if (cur == NULL)
2342 break;
2343 if (cur == root) {
2344 cur = NULL;
2345 break;
2346 }
2347 if (cur->next != NULL) {
2348 cur = cur->next;
2349 break;
2350 }
2351 } while (cur != NULL);
2352 }
2353 if (delete != NULL) {
2354 xmlUnlinkNode(delete);
2355 xmlFreeNode(delete);
2356 delete = NULL;
2357 }
2358
2359 /*
2360 * Then do the parsing for good
2361 */
2362 root = xmlDocGetRootElement(doc);
2363 if (root == NULL) {
2364 if (ctxt->error != NULL)
2365 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
2366 ctxt->URL);
2367 ctxt->nbErrors++;
2368 return (NULL);
2369 }
2370 ret = xmlRelaxNGParseDocument(ctxt, root);
2371 if (ret == NULL)
2372 return(NULL);
2373
2374 /*
2375 * Check the ref/defines links
2376 */
2377
2378 /*
2379 * if there was a parsing error return NULL
2380 */
2381 if (ctxt->nbErrors > 0) {
2382 xmlRelaxNGFree(ret);
2383 return(NULL);
2384 }
2385
2386 /*
2387 * Transfer the pointer for cleanup at the schema level.
2388 */
2389 ret->doc = doc;
2390 ctxt->doc = NULL;
2391
2392 return (ret);
2393}
2394
2395/**
2396 * xmlRelaxNGSetParserErrors:
2397 * @ctxt: a Relax-NG validation context
2398 * @err: the error callback
2399 * @warn: the warning callback
2400 * @ctx: contextual data for the callbacks
2401 *
2402 * Set the callback functions used to handle errors for a validation context
2403 */
2404void
2405xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
2406 xmlRelaxNGValidityErrorFunc err,
2407 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
2408 if (ctxt == NULL)
2409 return;
2410 ctxt->error = err;
2411 ctxt->warning = warn;
2412 ctxt->userData = ctx;
2413}
2414/************************************************************************
2415 * *
2416 * Dump back a compiled form *
2417 * *
2418 ************************************************************************/
2419static void xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define);
2420
2421/**
2422 * xmlRelaxNGDumpDefines:
2423 * @output: the file output
2424 * @defines: a list of define structures
2425 *
2426 * Dump a RelaxNG structure back
2427 */
2428static void
2429xmlRelaxNGDumpDefines(FILE * output, xmlRelaxNGDefinePtr defines) {
2430 while (defines != NULL) {
2431 xmlRelaxNGDumpDefine(output, defines);
2432 defines = defines->next;
2433 }
2434}
2435
2436/**
2437 * xmlRelaxNGDumpDefine:
2438 * @output: the file output
2439 * @define: a define structure
2440 *
2441 * Dump a RelaxNG structure back
2442 */
2443static void
2444xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define) {
2445 if (define == NULL)
2446 return;
2447 switch(define->type) {
2448 case XML_RELAXNG_EMPTY:
2449 fprintf(output, "<empty/>\n");
2450 break;
2451 case XML_RELAXNG_NOT_ALLOWED:
2452 fprintf(output, "<notAllowed/>\n");
2453 break;
2454 case XML_RELAXNG_TEXT:
2455 fprintf(output, "<text/>\n");
2456 break;
2457 case XML_RELAXNG_ELEMENT:
2458 fprintf(output, "<element>\n");
2459 if (define->name != NULL) {
2460 fprintf(output, "<name");
2461 if (define->ns != NULL)
2462 fprintf(output, " ns=\"%s\"", define->ns);
2463 fprintf(output, ">%s</name>\n", define->name);
2464 }
2465 xmlRelaxNGDumpDefines(output, define->attrs);
2466 xmlRelaxNGDumpDefines(output, define->content);
2467 fprintf(output, "</element>\n");
2468 break;
2469 case XML_RELAXNG_LIST:
2470 fprintf(output, "<list>\n");
2471 xmlRelaxNGDumpDefines(output, define->content);
2472 fprintf(output, "</list>\n");
2473 break;
2474 case XML_RELAXNG_ONEORMORE:
2475 fprintf(output, "<oneOrMore>\n");
2476 xmlRelaxNGDumpDefines(output, define->content);
2477 fprintf(output, "</oneOrMore>\n");
2478 break;
2479 case XML_RELAXNG_ZEROORMORE:
2480 fprintf(output, "<zeroOrMore>\n");
2481 xmlRelaxNGDumpDefines(output, define->content);
2482 fprintf(output, "</zeroOrMore>\n");
2483 break;
2484 case XML_RELAXNG_CHOICE:
2485 fprintf(output, "<choice>\n");
2486 xmlRelaxNGDumpDefines(output, define->content);
2487 fprintf(output, "</choice>\n");
2488 break;
2489 case XML_RELAXNG_GROUP:
2490 fprintf(output, "<group>\n");
2491 xmlRelaxNGDumpDefines(output, define->content);
2492 fprintf(output, "</group>\n");
2493 break;
2494 case XML_RELAXNG_INTERLEAVE:
2495 fprintf(output, "<interleave>\n");
2496 xmlRelaxNGDumpDefines(output, define->content);
2497 fprintf(output, "</interleave>\n");
2498 break;
2499 case XML_RELAXNG_OPTIONAL:
2500 fprintf(output, "<optional>\n");
2501 xmlRelaxNGDumpDefines(output, define->content);
2502 fprintf(output, "</optional>\n");
2503 break;
2504 case XML_RELAXNG_ATTRIBUTE:
2505 fprintf(output, "<attribute>\n");
2506 xmlRelaxNGDumpDefines(output, define->content);
2507 fprintf(output, "</attribute>\n");
2508 break;
2509 case XML_RELAXNG_DEF:
2510 fprintf(output, "<define");
2511 if (define->name != NULL)
2512 fprintf(output, " name=\"%s\"", define->name);
2513 fprintf(output, ">\n");
2514 xmlRelaxNGDumpDefines(output, define->content);
2515 fprintf(output, "</define>\n");
2516 break;
2517 case XML_RELAXNG_REF:
2518 fprintf(output, "<ref");
2519 if (define->name != NULL)
2520 fprintf(output, " name=\"%s\"", define->name);
2521 fprintf(output, ">\n");
2522 xmlRelaxNGDumpDefines(output, define->content);
2523 fprintf(output, "</ref>\n");
2524 break;
2525 case XML_RELAXNG_DATATYPE:
2526 case XML_RELAXNG_VALUE:
2527 TODO
2528 break;
2529 }
2530}
2531
2532/**
2533 * xmlRelaxNGDumpGrammar:
2534 * @output: the file output
2535 * @grammar: a grammar structure
2536 * @top: is this a top grammar
2537 *
2538 * Dump a RelaxNG structure back
2539 */
2540static void
2541xmlRelaxNGDumpGrammar(FILE * output, xmlRelaxNGGrammarPtr grammar, int top)
2542{
2543 if (grammar == NULL)
2544 return;
2545
2546 fprintf(output, "<grammar");
2547 if (top)
2548 fprintf(output,
2549 " xmlns=\"http://relaxng.org/ns/structure/1.0\"");
2550 switch(grammar->combine) {
2551 case XML_RELAXNG_COMBINE_UNDEFINED:
2552 break;
2553 case XML_RELAXNG_COMBINE_CHOICE:
2554 fprintf(output, " combine=\"choice\"");
2555 break;
2556 case XML_RELAXNG_COMBINE_INTERLEAVE:
2557 fprintf(output, " combine=\"interleave\"");
2558 break;
2559 default:
2560 fprintf(output, " <!-- invalid combine value -->");
2561 }
2562 fprintf(output, ">\n");
2563 if (grammar->start == NULL) {
2564 fprintf(output, " <!-- grammar had no start -->");
2565 } else {
2566 fprintf(output, "<start>\n");
2567 xmlRelaxNGDumpDefine(output, grammar->start);
2568 fprintf(output, "</start>\n");
2569 }
2570 /* TODO ? Dump the defines ? */
2571 fprintf(output, "</grammar>\n");
2572}
2573
2574/**
2575 * xmlRelaxNGDump:
2576 * @output: the file output
2577 * @schema: a schema structure
2578 *
2579 * Dump a RelaxNG structure back
2580 */
2581void
2582xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema)
2583{
2584 if (schema == NULL) {
2585 fprintf(output, "RelaxNG empty or failed to compile\n");
2586 return;
2587 }
2588 fprintf(output, "RelaxNG: ");
2589 if (schema->doc == NULL) {
2590 fprintf(output, "no document\n");
2591 } else if (schema->doc->URL != NULL) {
2592 fprintf(output, "%s\n", schema->doc->URL);
2593 } else {
2594 fprintf(output, "\n");
2595 }
2596 if (schema->topgrammar == NULL) {
2597 fprintf(output, "RelaxNG has no top grammar\n");
2598 return;
2599 }
2600 xmlRelaxNGDumpGrammar(output, schema->topgrammar, 1);
2601}
2602
2603/************************************************************************
2604 * *
2605 * Validation implementation *
2606 * *
2607 ************************************************************************/
2608static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
2609 xmlRelaxNGDefinePtr define);
2610
2611/**
2612 * xmlRelaxNGSkipIgnored:
2613 * @ctxt: a schema validation context
2614 * @node: the top node.
2615 *
2616 * Skip ignorable nodes in that context
2617 *
2618 * Returns the new sibling or NULL in case of error.
2619 */
2620static xmlNodePtr
2621xmlRelaxNGSkipIgnored(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
2622 xmlNodePtr node) {
2623 /*
2624 * TODO complete and handle entities
2625 */
2626 while ((node != NULL) &&
2627 ((node->type == XML_COMMENT_NODE) ||
2628 ((node->type == XML_TEXT_NODE) &&
2629 (IS_BLANK_NODE(node))))) {
2630 node = node->next;
2631 }
2632 return(node);
2633}
2634
2635/**
Daniel Veillardedc91922003-01-26 00:52:04 +00002636 * xmlRelaxNGNormalize:
2637 * @ctxt: a schema validation context
2638 * @str: the string to normalize
2639 *
2640 * Implements the normalizeWhiteSpace( s ) function from
2641 * section 6.2.9 of the spec
2642 *
2643 * Returns the new string or NULL in case of error.
2644 */
2645static xmlChar *
2646xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *str) {
2647 xmlChar *ret, *p;
2648 const xmlChar *tmp;
2649 int len;
2650
2651 if (str == NULL)
2652 return(NULL);
2653 tmp = str;
2654 while (*tmp != 0) tmp++;
2655 len = tmp - str;
2656
2657 ret = (xmlChar *) xmlMalloc((len + 1) * sizeof(xmlChar));
2658 if (ret == NULL) {
2659 VALID_CTXT();
2660 VALID_ERROR("xmlRelaxNGNormalize: out of memory\n");
2661 return(NULL);
2662 }
2663 p = ret;
2664 while (IS_BLANK(*str)) str++;
2665 while (*str != 0) {
2666 if (IS_BLANK(*str)) {
2667 while (IS_BLANK(*str)) str++;
2668 if (*str == 0)
2669 break;
2670 *p++ = ' ';
2671 } else
2672 *p++ = *str++;
2673 }
2674 *p = 0;
2675 return(ret);
2676}
2677
2678/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002679 * xmlRelaxNGValidateDatatype:
2680 * @ctxt: a Relax-NG validation context
2681 * @value: the string value
2682 * @type: the datatype definition
2683 *
2684 * Validate the given value against the dataype
2685 *
2686 * Returns 0 if the validation succeeded or an error code.
2687 */
2688static int
2689xmlRelaxNGValidateDatatype(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *value,
2690 xmlRelaxNGDefinePtr define) {
2691 int ret;
2692 xmlRelaxNGTypeLibraryPtr lib;
2693
2694 if ((define == NULL) || (define->data == NULL)) {
2695 return(-1);
2696 }
2697 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
2698 if (lib->check != NULL)
2699 ret = lib->check(lib->data, define->name, value);
2700 else
2701 ret = -1;
2702 if (ret < 0) {
2703 VALID_CTXT();
2704 VALID_ERROR("Internal: failed to validate type %s\n", define->name);
2705 return(-1);
2706 } else if (ret == 1) {
2707 ret = 0;
2708 } else {
2709 VALID_CTXT();
2710 VALID_ERROR("Type %s doesn't allow value %s\n", define->name, value);
2711 return(-1);
2712 ret = -1;
2713 }
2714 return(ret);
2715}
2716
2717/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00002718 * xmlRelaxNGValidateValue:
2719 * @ctxt: a Relax-NG validation context
2720 * @define: the definition to verify
2721 *
2722 * Validate the given definition for the current value
2723 *
2724 * Returns 0 if the validation succeeded or an error code.
2725 */
2726static int
2727xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
2728 xmlRelaxNGDefinePtr define) {
Daniel Veillardedc91922003-01-26 00:52:04 +00002729 int ret = 0, oldflags;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002730 xmlChar *value;
2731
2732 value = ctxt->state->value;
2733 switch (define->type) {
2734 case XML_RELAXNG_EMPTY:
2735 if ((value != NULL) && (value[0] != '0'))
2736 ret = -1;
2737 break;
2738 case XML_RELAXNG_TEXT:
2739 break;
Daniel Veillardedc91922003-01-26 00:52:04 +00002740 case XML_RELAXNG_CHOICE: {
2741 xmlRelaxNGDefinePtr list = define->content;
2742
2743 oldflags = ctxt->flags;
2744 ctxt->flags |= FLAGS_IGNORABLE;
2745
2746 while (list != NULL) {
2747 ret = xmlRelaxNGValidateValue(ctxt, list);
2748 if (ret == 0) {
2749 break;
2750 }
2751 list = list->next;
2752 }
2753 ctxt->flags = oldflags;
2754 break;
2755 }
2756 case XML_RELAXNG_VALUE: {
2757 if (!xmlStrEqual(value, define->value)) {
2758 if (define->name != NULL) {
2759 TODO /* value validation w.r.t. the type */
2760 } else {
2761 xmlChar *nval, *nvalue;
2762
2763 /*
2764 * TODO: trivial optimizations are possible by
2765 * computing at compile-time
2766 */
2767 nval = xmlRelaxNGNormalize(ctxt, define->value);
2768 nvalue = xmlRelaxNGNormalize(ctxt, value);
2769
2770 if (!xmlStrEqual(nval, nvalue))
2771 ret = -1;
2772 if (nval != NULL)
2773 xmlFree(nval);
2774 if (nvalue != NULL)
2775 xmlFree(nvalue);
2776 }
2777 }
2778 break;
2779 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002780 default:
2781 TODO
2782 ret = -1;
2783 }
2784 return(ret);
2785}
2786
2787/**
2788 * xmlRelaxNGValidateValueContent:
2789 * @ctxt: a Relax-NG validation context
2790 * @defines: the list of definitions to verify
2791 *
2792 * Validate the given definitions for the current value
2793 *
2794 * Returns 0 if the validation succeeded or an error code.
2795 */
2796static int
2797xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt,
2798 xmlRelaxNGDefinePtr defines) {
2799 int ret = 0;
2800
2801 while (defines != NULL) {
2802 ret = xmlRelaxNGValidateValue(ctxt, defines);
2803 if (ret != 0)
2804 break;
2805 defines = defines->next;
2806 }
2807 return(ret);
2808}
2809
2810/**
2811 * xmlRelaxNGValidateAttribute:
2812 * @ctxt: a Relax-NG validation context
2813 * @define: the definition to verify
2814 *
2815 * Validate the given attribute definition for that node
2816 *
2817 * Returns 0 if the validation succeeded or an error code.
2818 */
2819static int
2820xmlRelaxNGValidateAttribute(xmlRelaxNGValidCtxtPtr ctxt,
2821 xmlRelaxNGDefinePtr define) {
2822 int ret = 0, i;
2823 xmlChar *value, *oldvalue;
2824 xmlAttrPtr prop = NULL, tmp;
2825
2826 if (define->name != NULL) {
2827 for (i = 0;i < ctxt->state->nbAttrs;i++) {
2828 tmp = ctxt->state->attrs[i];
2829 if ((tmp != NULL) && (xmlStrEqual(define->name, tmp->name))) {
2830 if ((((define->ns == NULL) || (define->ns[0] == 0)) &&
2831 (tmp->ns == NULL)) ||
2832 ((tmp->ns != NULL) &&
2833 (xmlStrEqual(define->ns, tmp->ns->href)))) {
2834 prop = tmp;
2835 break;
2836 }
2837 }
2838 }
2839 if (prop != NULL) {
2840 value = xmlNodeListGetString(prop->doc, prop->children, 1);
2841 oldvalue = ctxt->state->value;
2842 ctxt->state->value = value;
2843 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
2844 value = ctxt->state->value;
2845 ctxt->state->value = oldvalue;
2846 if (value != NULL)
2847 xmlFree(value);
2848 if (ret == 0) {
2849 /*
2850 * flag the attribute as processed
2851 */
2852 ctxt->state->attrs[i] = NULL;
2853 }
2854 } else {
2855 ret = -1;
2856 }
2857#ifdef DEBUG
2858 xmlGenericError(xmlGenericErrorContext,
2859 "xmlRelaxNGValidateAttribute(%s): %d\n", define->name, ret);
2860#endif
2861 } else {
2862 TODO
2863 }
2864
2865 return(ret);
2866}
2867
2868/**
2869 * xmlRelaxNGValidateAttributeList:
2870 * @ctxt: a Relax-NG validation context
2871 * @define: the list of definition to verify
2872 *
2873 * Validate the given node against the list of attribute definitions
2874 *
2875 * Returns 0 if the validation succeeded or an error code.
2876 */
2877static int
2878xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt,
2879 xmlRelaxNGDefinePtr defines) {
2880 int ret = 0;
2881 while (defines != NULL) {
2882 if (xmlRelaxNGValidateAttribute(ctxt, defines) != 0)
2883 ret = -1;
2884 defines = defines->next;
2885 }
2886 return(ret);
2887}
2888
2889/**
2890 * xmlRelaxNGValidateElementContent:
2891 * @ctxt: a Relax-NG validation context
2892 * @define: the list of definition to verify
2893 *
2894 * Validate the given node content against the (list) of definitions
2895 *
2896 * Returns 0 if the validation succeeded or an error code.
2897 */
2898static int
2899xmlRelaxNGValidateElementContent(xmlRelaxNGValidCtxtPtr ctxt,
2900 xmlRelaxNGDefinePtr defines) {
2901 int ret = 0, res;
2902
2903 if (ctxt->state == NULL) {
2904 VALID_CTXT();
2905 VALID_ERROR("Internal: no state\n");
2906 return(-1);
2907 }
2908 while (defines != NULL) {
2909 res = xmlRelaxNGValidateDefinition(ctxt, defines);
2910 if (res < 0)
2911 ret = -1;
2912 defines = defines->next;
2913 }
2914
2915 return(ret);
2916}
2917
2918/**
2919 * xmlRelaxNGValidateDefinition:
2920 * @ctxt: a Relax-NG validation context
2921 * @define: the definition to verify
2922 *
2923 * Validate the current node against the definition
2924 *
2925 * Returns 0 if the validation succeeded or an error code.
2926 */
2927static int
2928xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
2929 xmlRelaxNGDefinePtr define) {
2930 xmlNodePtr node;
2931 int ret = 0, i, tmp, oldflags;
2932 xmlRelaxNGValidStatePtr oldstate, state;
2933
2934 if (define == NULL) {
2935 VALID_CTXT();
2936 VALID_ERROR("internal error: define == NULL\n");
2937 return(-1);
2938 }
2939 if (ctxt->state != NULL) {
2940 node = ctxt->state->seq;
2941 } else {
2942 node = NULL;
2943 }
2944 switch (define->type) {
2945 case XML_RELAXNG_EMPTY:
2946 if (node != NULL) {
2947 VALID_CTXT();
2948 VALID_ERROR("Expecting an empty element\n");
2949 return(-1);
2950 }
2951#ifdef DEBUG
2952 xmlGenericError(xmlGenericErrorContext,
2953 "xmlRelaxNGValidateDefinition(): validated empty\n");
2954#endif
2955 return(0);
2956 case XML_RELAXNG_NOT_ALLOWED:
2957 TODO
2958 break;
2959 case XML_RELAXNG_TEXT:
2960 if (node == NULL)
2961 return(0);
2962 while ((node != NULL) &&
2963 ((node->type == XML_TEXT_NODE) ||
2964 (node->type == XML_CDATA_SECTION_NODE)))
2965 node = node->next;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002966 if (node == ctxt->state->seq) {
2967 VALID_CTXT();
2968 VALID_ERROR("Expecting text content\n");
2969 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002970 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002971 ctxt->state->seq = node;
2972 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002973 case XML_RELAXNG_ELEMENT:
2974 node = xmlRelaxNGSkipIgnored(ctxt, node);
2975 if ((node == NULL) || (node->type != XML_ELEMENT_NODE)) {
2976 VALID_CTXT();
2977 VALID_ERROR("Expecting an element\n");
2978 return(-1);
2979 }
2980 if (define->name != NULL) {
2981 if (!xmlStrEqual(node->name, define->name)) {
2982 VALID_CTXT();
2983 VALID_ERROR("Expecting element %s, got %s\n",
2984 define->name, node->name);
2985 return(-1);
2986 }
2987 }
2988 if ((define->ns != NULL) && (define->ns[0] != 0)) {
2989 if (node->ns == NULL) {
2990 VALID_CTXT();
2991 VALID_ERROR("Expecting a namespace for element %s\n",
2992 node->name);
2993 return(-1);
2994 } else if (!xmlStrEqual(node->ns->href, define->ns)) {
2995 VALID_CTXT();
2996 VALID_ERROR("Expecting element %s has wrong namespace: expecting %s\n",
2997 node->name, define->ns);
2998 return(-1);
2999 }
3000 } else {
3001 if (node->ns != NULL) {
3002 VALID_CTXT();
3003 VALID_ERROR("Expecting no namespace for element %s\n",
3004 node->name);
3005 return(-1);
3006 }
3007 }
3008
3009 state = xmlRelaxNGNewValidState(ctxt, node);
3010 if (state == NULL) {
3011 return(-1);
3012 }
3013
3014 oldstate = ctxt->state;
3015 ctxt->state = state;
3016 if (define->attrs != NULL) {
3017 tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
3018 if (tmp != 0)
3019 ret = -1;
3020 }
3021 if (define->content != NULL) {
3022 tmp = xmlRelaxNGValidateElementContent(ctxt, define->content);
3023 if (tmp != 0)
3024 ret = -1;
3025 }
3026 state = ctxt->state;
3027 if (state->seq != NULL) {
3028 state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq);
3029 if (state->seq != NULL) {
3030 VALID_CTXT();
3031 VALID_ERROR("Extra content for element %s\n",
3032 node->name);
3033 ret = -1;
3034 }
3035 }
3036 for (i = 0;i < state->nbAttrs;i++) {
3037 if (state->attrs[i] != NULL) {
3038 VALID_CTXT();
3039 VALID_ERROR("Extra attribute %s for element %s\n",
3040 state->attrs[i]->name, node->name);
3041 ret = -1;
3042 }
3043 }
3044 ctxt->state = oldstate;
3045 xmlRelaxNGFreeValidState(state);
3046 if (oldstate != NULL)
3047 oldstate->seq = node->next;
3048
3049
3050#ifdef DEBUG
3051 xmlGenericError(xmlGenericErrorContext,
3052 "xmlRelaxNGValidateDefinition(): validated %s : %d\n",
3053 node->name, ret);
3054#endif
3055 break;
3056 case XML_RELAXNG_LIST:
3057 TODO
3058 break;
3059 case XML_RELAXNG_OPTIONAL:
3060 oldflags = ctxt->flags;
3061 ctxt->flags |= FLAGS_IGNORABLE;
3062 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
3063 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
3064 if (ret != 0) {
3065 xmlRelaxNGFreeValidState(ctxt->state);
3066 ctxt->state = oldstate;
3067 ret = 0;
3068 break;
3069 }
3070 xmlRelaxNGFreeValidState(oldstate);
3071 ctxt->flags = oldflags;
3072 ret = 0;
3073 break;
3074 case XML_RELAXNG_ONEORMORE:
3075 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
3076 if (ret != 0) {
3077 break;
3078 }
3079 /* no break on purpose */
Daniel Veillard276be4a2003-01-24 01:03:34 +00003080 case XML_RELAXNG_ZEROORMORE: {
3081 xmlNodePtr cur, temp;
3082
Daniel Veillard6eadf632003-01-23 18:29:16 +00003083 oldflags = ctxt->flags;
3084 ctxt->flags |= FLAGS_IGNORABLE;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003085 cur = ctxt->state->seq;
3086 temp = NULL;
3087 while ((cur != NULL) && (temp != cur)) {
3088 temp = cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003089 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
3090 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
3091 if (ret != 0) {
3092 xmlRelaxNGFreeValidState(ctxt->state);
3093 ctxt->state = oldstate;
3094 ret = 0;
3095 break;
3096 }
3097 xmlRelaxNGFreeValidState(oldstate);
Daniel Veillard276be4a2003-01-24 01:03:34 +00003098 cur = ctxt->state->seq;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003099 }
3100 ctxt->flags = oldflags;
3101 break;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003102 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003103 case XML_RELAXNG_CHOICE: {
3104 xmlRelaxNGDefinePtr list = define->content;
3105
3106 oldflags = ctxt->flags;
3107 ctxt->flags |= FLAGS_IGNORABLE;
3108
3109 while (list != NULL) {
3110 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
3111 ret = xmlRelaxNGValidateDefinition(ctxt, list);
3112 if (ret == 0) {
3113 xmlRelaxNGFreeValidState(oldstate);
3114 break;
3115 }
3116 xmlRelaxNGFreeValidState(ctxt->state);
3117 ctxt->state = oldstate;
3118 list = list->next;
3119 }
3120 ctxt->flags = oldflags;
3121 break;
3122 }
3123 case XML_RELAXNG_GROUP: {
3124 xmlRelaxNGDefinePtr list = define->content;
3125
3126 while (list != NULL) {
3127 ret = xmlRelaxNGValidateDefinition(ctxt, list);
3128 if (ret != 0)
3129 break;
3130 list = list->next;
3131 }
3132 break;
3133 }
3134 case XML_RELAXNG_INTERLEAVE:
3135 TODO
3136 break;
3137 case XML_RELAXNG_ATTRIBUTE:
3138 ret = xmlRelaxNGValidateAttribute(ctxt, define);
3139 break;
3140 case XML_RELAXNG_REF:
3141 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
3142 break;
3143 case XML_RELAXNG_DEF:
3144 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
3145 break;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00003146 case XML_RELAXNG_DATATYPE: {
3147 xmlChar *content;
3148
3149 content = xmlNodeGetContent(node);
3150 ret = xmlRelaxNGValidateDatatype(ctxt, content, define);
3151 if (ret == -1) {
3152 VALID_CTXT();
3153 VALID_ERROR("internal error validating %s\n", define->name);
3154 } else if (ret == 0) {
3155 ctxt->state->seq = node->next;
3156 }
3157 /*
3158 * TODO cover the problems with
3159 * <p>12<!-- comment -->34</p>
3160 * TODO detect full element coverage at compilation time.
3161 */
3162 if ((node != NULL) && (node->next != NULL)) {
3163 VALID_CTXT();
3164 VALID_ERROR("The data does not cover the full element %s\n",
3165 node->parent->name);
3166 ret = -1;
3167 }
3168 if (content != NULL)
3169 xmlFree(content);
3170 break;
3171 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003172 case XML_RELAXNG_VALUE:
3173 TODO
3174 break;
3175 }
3176 return(ret);
3177}
3178
3179/**
3180 * xmlRelaxNGValidateDocument:
3181 * @ctxt: a Relax-NG validation context
3182 * @doc: the document
3183 *
3184 * Validate the given document
3185 *
3186 * Returns 0 if the validation succeeded or an error code.
3187 */
3188static int
3189xmlRelaxNGValidateDocument(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
3190 int ret;
3191 xmlRelaxNGPtr schema;
3192 xmlRelaxNGGrammarPtr grammar;
3193 xmlRelaxNGValidStatePtr state;
3194
3195 if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL))
3196 return(-1);
3197
3198 schema = ctxt->schema;
3199 grammar = schema->topgrammar;
3200 if (grammar == NULL) {
3201 VALID_CTXT();
3202 VALID_ERROR("No top grammar defined\n");
3203 return(-1);
3204 }
3205 state = xmlRelaxNGNewValidState(ctxt, NULL);
3206 ctxt->state = state;
3207 ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start);
3208 state = ctxt->state;
3209 if ((state != NULL) && (state->seq != NULL)) {
3210 xmlNodePtr node;
3211
3212 node = state->seq;
3213 node = xmlRelaxNGSkipIgnored(ctxt, node);
3214 if (node != NULL) {
3215 VALID_CTXT();
3216 VALID_ERROR("extra data on the document\n");
3217 ret = -1;
3218 }
3219 }
3220 xmlRelaxNGFreeValidState(state);
3221
3222 return(ret);
3223}
3224
3225/************************************************************************
3226 * *
3227 * Validation interfaces *
3228 * *
3229 ************************************************************************/
3230/**
3231 * xmlRelaxNGNewValidCtxt:
3232 * @schema: a precompiled XML RelaxNGs
3233 *
3234 * Create an XML RelaxNGs validation context based on the given schema
3235 *
3236 * Returns the validation context or NULL in case of error
3237 */
3238xmlRelaxNGValidCtxtPtr
3239xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema) {
3240 xmlRelaxNGValidCtxtPtr ret;
3241
3242 ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt));
3243 if (ret == NULL) {
3244 xmlGenericError(xmlGenericErrorContext,
3245 "Failed to allocate new schama validation context\n");
3246 return (NULL);
3247 }
3248 memset(ret, 0, sizeof(xmlRelaxNGValidCtxt));
3249 ret->schema = schema;
3250 return (ret);
3251}
3252
3253/**
3254 * xmlRelaxNGFreeValidCtxt:
3255 * @ctxt: the schema validation context
3256 *
3257 * Free the resources associated to the schema validation context
3258 */
3259void
3260xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt) {
3261 if (ctxt == NULL)
3262 return;
3263 xmlFree(ctxt);
3264}
3265
3266/**
3267 * xmlRelaxNGSetValidErrors:
3268 * @ctxt: a Relax-NG validation context
3269 * @err: the error function
3270 * @warn: the warning function
3271 * @ctx: the functions context
3272 *
3273 * Set the error and warning callback informations
3274 */
3275void
3276xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
3277 xmlRelaxNGValidityErrorFunc err,
3278 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
3279 if (ctxt == NULL)
3280 return;
3281 ctxt->error = err;
3282 ctxt->warning = warn;
3283 ctxt->userData = ctx;
3284}
3285
3286/**
3287 * xmlRelaxNGValidateDoc:
3288 * @ctxt: a Relax-NG validation context
3289 * @doc: a parsed document tree
3290 *
3291 * Validate a document tree in memory.
3292 *
3293 * Returns 0 if the document is valid, a positive error code
3294 * number otherwise and -1 in case of internal or API error.
3295 */
3296int
3297xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
3298 int ret;
3299
3300 if ((ctxt == NULL) || (doc == NULL))
3301 return(-1);
3302
3303 ctxt->doc = doc;
3304
3305 ret = xmlRelaxNGValidateDocument(ctxt, doc);
3306 return(ret);
3307}
3308
3309#endif /* LIBXML_SCHEMAS_ENABLED */
3310