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