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