blob: 9b553b133272f03be28eae020e534bf868b4219c [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
Daniel Veillardd41f4f42003-01-29 21:07:52 +00009/**
10 * TODO:
11 * - <interleave></interleave><element>...
12 * - error reporting
13 * - module
14 * - fixing ref/def cross grammar contexts
15 */
16
Daniel Veillard6eadf632003-01-23 18:29:16 +000017#define IN_LIBXML
18#include "libxml.h"
19
20#ifdef LIBXML_SCHEMAS_ENABLED
21
22#include <string.h>
23#include <stdio.h>
24#include <libxml/xmlmemory.h>
25#include <libxml/parser.h>
26#include <libxml/parserInternals.h>
27#include <libxml/hash.h>
28#include <libxml/uri.h>
29
30#include <libxml/relaxng.h>
31
32#include <libxml/xmlschemastypes.h>
33#include <libxml/xmlautomata.h>
34#include <libxml/xmlregexp.h>
Daniel Veillardc6e997c2003-01-27 12:35:42 +000035#include <libxml/xmlschemastypes.h>
Daniel Veillard6eadf632003-01-23 18:29:16 +000036
37/*
38 * The Relax-NG namespace
39 */
40static const xmlChar *xmlRelaxNGNs = (const xmlChar *)
41 "http://relaxng.org/ns/structure/1.0";
42
43#define IS_RELAXNG(node, type) \
44 ((node != NULL) && (node->ns != NULL) && \
45 (xmlStrEqual(node->name, (const xmlChar *) type)) && \
46 (xmlStrEqual(node->ns->href, xmlRelaxNGNs)))
47
48
49#define DEBUG 1 /* very verbose output */
50#define DEBUG_CONTENT 1
51#define DEBUG_TYPE 1
Daniel Veillard276be4a2003-01-24 01:03:34 +000052#define DEBUG_VALID 1
Daniel Veillardb08c9812003-01-28 23:09:49 +000053#define DEBUG_INTERLEAVE 1 */
Daniel Veillard6eadf632003-01-23 18:29:16 +000054
55#define UNBOUNDED (1 << 30)
56#define TODO \
57 xmlGenericError(xmlGenericErrorContext, \
58 "Unimplemented block at %s:%d\n", \
59 __FILE__, __LINE__);
60
61typedef struct _xmlRelaxNGSchema xmlRelaxNGSchema;
62typedef xmlRelaxNGSchema *xmlRelaxNGSchemaPtr;
63
64typedef struct _xmlRelaxNGDefine xmlRelaxNGDefine;
65typedef xmlRelaxNGDefine *xmlRelaxNGDefinePtr;
66
Daniel Veillardd41f4f42003-01-29 21:07:52 +000067typedef struct _xmlRelaxNGDocument xmlRelaxNGDocument;
68typedef xmlRelaxNGDocument *xmlRelaxNGDocumentPtr;
69
Daniel Veillard6eadf632003-01-23 18:29:16 +000070typedef enum {
71 XML_RELAXNG_COMBINE_UNDEFINED = 0, /* undefined */
72 XML_RELAXNG_COMBINE_CHOICE, /* choice */
73 XML_RELAXNG_COMBINE_INTERLEAVE /* interleave */
74} xmlRelaxNGCombine;
75
76typedef struct _xmlRelaxNGGrammar xmlRelaxNGGrammar;
77typedef xmlRelaxNGGrammar *xmlRelaxNGGrammarPtr;
78
79struct _xmlRelaxNGGrammar {
80 xmlRelaxNGGrammarPtr parent;/* the parent grammar if any */
81 xmlRelaxNGGrammarPtr children;/* the children grammar if any */
82 xmlRelaxNGGrammarPtr next; /* the next grammar if any */
83 xmlRelaxNGDefinePtr start; /* <start> content */
84 xmlRelaxNGCombine combine; /* the default combine value */
Daniel Veillardd41f4f42003-01-29 21:07:52 +000085 xmlRelaxNGDefinePtr startList;/* list of <start> definitions */
Daniel Veillard6eadf632003-01-23 18:29:16 +000086 xmlHashTablePtr defs; /* define* */
87 xmlHashTablePtr refs; /* references */
88};
89
90
Daniel Veillard6eadf632003-01-23 18:29:16 +000091typedef enum {
92 XML_RELAXNG_EMPTY = 0, /* an empty pattern */
93 XML_RELAXNG_NOT_ALLOWED, /* not allowed top */
94 XML_RELAXNG_TEXT, /* textual content */
95 XML_RELAXNG_ELEMENT, /* an element */
96 XML_RELAXNG_DATATYPE, /* extenal data type definition */
97 XML_RELAXNG_VALUE, /* value from an extenal data type definition */
98 XML_RELAXNG_LIST, /* a list of patterns */
99 XML_RELAXNG_ATTRIBUTE, /* an attrbute following a pattern */
100 XML_RELAXNG_DEF, /* a definition */
101 XML_RELAXNG_REF, /* reference to a definition */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000102 XML_RELAXNG_EXTERNALREF, /* reference to an external def */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000103 XML_RELAXNG_OPTIONAL, /* optional patterns */
104 XML_RELAXNG_ZEROORMORE, /* zero or more non empty patterns */
105 XML_RELAXNG_ONEORMORE, /* one or more non empty patterns */
106 XML_RELAXNG_CHOICE, /* a choice between non empty patterns */
107 XML_RELAXNG_GROUP, /* a pair/group of non empty patterns */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000108 XML_RELAXNG_INTERLEAVE, /* interleaving choice of non-empty patterns */
109 XML_RELAXNG_START /* Used to keep track of starts on grammars */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000110} xmlRelaxNGType;
111
112struct _xmlRelaxNGDefine {
113 xmlRelaxNGType type; /* the type of definition */
114 xmlNodePtr node; /* the node in the source */
115 xmlChar *name; /* the element local name if present */
116 xmlChar *ns; /* the namespace local name if present */
Daniel Veillardedc91922003-01-26 00:52:04 +0000117 xmlChar *value; /* value when available */
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000118 void *data; /* data lib or specific pointer */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000119 xmlRelaxNGDefinePtr content;/* the expected content */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000120 xmlRelaxNGDefinePtr parent; /* the parent definition, if any */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000121 xmlRelaxNGDefinePtr next; /* list within grouping sequences */
122 xmlRelaxNGDefinePtr attrs; /* list of attributes for elements */
123 xmlRelaxNGDefinePtr nextHash;/* next define in defs/refs hash tables */
124};
125
126/**
127 * _xmlRelaxNG:
128 *
129 * A RelaxNGs definition
130 */
131struct _xmlRelaxNG {
132 xmlRelaxNGGrammarPtr topgrammar;
133 xmlDocPtr doc;
134
135 xmlHashTablePtr defs; /* define */
136 xmlHashTablePtr refs; /* references */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000137 xmlHashTablePtr documents; /* all the documents loaded */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000138 void *_private; /* unused by the library for users or bindings */
139};
140
141typedef enum {
142 XML_RELAXNG_ERR_OK = 0,
143 XML_RELAXNG_ERR_NOROOT = 1,
144 XML_RELAXNG_ERR_
145} xmlRelaxNGValidError;
146
147#define XML_RELAXNG_IN_ATTRIBUTE 1
148
149struct _xmlRelaxNGParserCtxt {
150 void *userData; /* user specific data block */
151 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
152 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
153 xmlRelaxNGValidError err;
154
155 xmlRelaxNGPtr schema; /* The schema in use */
156 xmlRelaxNGGrammarPtr grammar; /* the current grammar */
157 int flags; /* parser flags */
158 int nbErrors; /* number of errors at parse time */
159 int nbWarnings; /* number of warnings at parse time */
Daniel Veillard276be4a2003-01-24 01:03:34 +0000160 const xmlChar *define; /* the current define scope */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000161 xmlRelaxNGDefinePtr def; /* the current define */
162
163 int nbInterleaves;
164 xmlHashTablePtr interleaves; /* keep track of all the interleaves */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000165
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000166 xmlHashTablePtr documents; /* all the documents loaded */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000167 xmlChar *URL;
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000168 xmlDocPtr document;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000169
170 const char *buffer;
171 int size;
172
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000173 /* the document stack */
174 xmlRelaxNGDocumentPtr doc; /* Current parsed Node */
175 int docNr; /* Depth of the parsing stack */
176 int docMax; /* Max depth of the parsing stack */
177 xmlRelaxNGDocumentPtr *docTab; /* array of docs */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000178};
179
180#define FLAGS_IGNORABLE 1
181#define FLAGS_NEGATIVE 2
182
183/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000184 * xmlRelaxNGInterleaveGroup:
185 *
186 * A RelaxNGs partition set associated to lists of definitions
187 */
188typedef struct _xmlRelaxNGInterleaveGroup xmlRelaxNGInterleaveGroup;
189typedef xmlRelaxNGInterleaveGroup *xmlRelaxNGInterleaveGroupPtr;
190struct _xmlRelaxNGInterleaveGroup {
191 xmlRelaxNGDefinePtr rule; /* the rule to satisfy */
192 xmlRelaxNGDefinePtr *defs; /* the array of element definitions */
193};
194
195/**
196 * xmlRelaxNGPartitions:
197 *
198 * A RelaxNGs partition associated to an interleave group
199 */
200typedef struct _xmlRelaxNGPartition xmlRelaxNGPartition;
201typedef xmlRelaxNGPartition *xmlRelaxNGPartitionPtr;
202struct _xmlRelaxNGPartition {
203 int nbgroups; /* number of groups in the partitions */
204 xmlRelaxNGInterleaveGroupPtr *groups;
205};
206
207/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000208 * xmlRelaxNGValidState:
209 *
210 * A RelaxNGs validation state
211 */
212#define MAX_ATTR 20
213typedef struct _xmlRelaxNGValidState xmlRelaxNGValidState;
214typedef xmlRelaxNGValidState *xmlRelaxNGValidStatePtr;
215struct _xmlRelaxNGValidState {
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000216 xmlNodePtr node; /* the current node */
217 xmlNodePtr seq; /* the sequence of children left to validate */
218 int nbAttrs; /* the number of attributes */
219 xmlChar *value; /* the value when operating on string */
220 xmlChar *endvalue; /* the end value when operating on string */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000221 xmlAttrPtr attrs[1]; /* the array of attributes */
222};
223
224/**
225 * xmlRelaxNGValidCtxt:
226 *
227 * A RelaxNGs validation context
228 */
229
230struct _xmlRelaxNGValidCtxt {
231 void *userData; /* user specific data block */
232 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
233 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
234
235 xmlRelaxNGPtr schema; /* The schema in use */
236 xmlDocPtr doc; /* the document being validated */
237 xmlRelaxNGValidStatePtr state; /* the current validation state */
238 int flags; /* validation flags */
239};
240
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000241/**
242 * xmlRelaxNGDocument:
243 *
244 * Structure associated to a RelaxNGs document element
245 */
246struct _xmlRelaxNGDocument {
247 xmlChar *href; /* the normalized href value */
248 xmlDocPtr doc; /* the associated XML document */
249 xmlRelaxNGDefinePtr content;/* the definitions */
250 xmlRelaxNGPtr schema; /* the schema */
251};
252
Daniel Veillard6eadf632003-01-23 18:29:16 +0000253/************************************************************************
254 * *
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000255 * Preliminary type checking interfaces *
256 * *
257 ************************************************************************/
258/**
259 * xmlRelaxNGTypeHave:
260 * @data: data needed for the library
261 * @type: the type name
262 * @value: the value to check
263 *
264 * Function provided by a type library to check if a type is exported
265 *
266 * Returns 1 if yes, 0 if no and -1 in case of error.
267 */
268typedef int (*xmlRelaxNGTypeHave) (void *data, const xmlChar *type);
269
270/**
271 * xmlRelaxNGTypeCheck:
272 * @data: data needed for the library
273 * @type: the type name
274 * @value: the value to check
275 *
276 * Function provided by a type library to check if a value match a type
277 *
278 * Returns 1 if yes, 0 if no and -1 in case of error.
279 */
280typedef int (*xmlRelaxNGTypeCheck) (void *data, const xmlChar *type,
281 const xmlChar *value);
282
283/**
284 * xmlRelaxNGTypeCompare:
285 * @data: data needed for the library
286 * @type: the type name
287 * @value1: the first value
288 * @value2: the second value
289 *
290 * Function provided by a type library to compare two values accordingly
291 * to a type.
292 *
293 * Returns 1 if yes, 0 if no and -1 in case of error.
294 */
295typedef int (*xmlRelaxNGTypeCompare) (void *data, const xmlChar *type,
296 const xmlChar *value1,
297 const xmlChar *value2);
298typedef struct _xmlRelaxNGTypeLibrary xmlRelaxNGTypeLibrary;
299typedef xmlRelaxNGTypeLibrary *xmlRelaxNGTypeLibraryPtr;
300struct _xmlRelaxNGTypeLibrary {
301 const xmlChar *namespace; /* the datatypeLibrary value */
302 void *data; /* data needed for the library */
303 xmlRelaxNGTypeHave have; /* the export function */
304 xmlRelaxNGTypeCheck check; /* the checking function */
305 xmlRelaxNGTypeCompare comp; /* the compare function */
306};
307
308/************************************************************************
309 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +0000310 * Allocation functions *
311 * *
312 ************************************************************************/
313static void xmlRelaxNGFreeDefineList(xmlRelaxNGDefinePtr defines);
314static void xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar);
315static void xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define);
316
317/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000318 * xmlRelaxNGFreeDocument:
319 * @docu: a document structure
320 *
321 * Deallocate a RelaxNG document structure.
322 */
323static void
324xmlRelaxNGFreeDocument(xmlRelaxNGDocumentPtr docu)
325{
326 if (docu == NULL)
327 return;
328
329 if (docu->href != NULL)
330 xmlFree(docu->href);
331 if (docu->doc != NULL)
332 xmlFreeDoc(docu->doc);
333 if (docu->schema != NULL)
334 xmlRelaxNGFree(docu->schema);
335 xmlFree(docu);
336}
337
338/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000339 * xmlRelaxNGNewRelaxNG:
340 * @ctxt: a Relax-NG validation context (optional)
341 *
342 * Allocate a new RelaxNG structure.
343 *
344 * Returns the newly allocated structure or NULL in case or error
345 */
346static xmlRelaxNGPtr
347xmlRelaxNGNewRelaxNG(xmlRelaxNGParserCtxtPtr ctxt)
348{
349 xmlRelaxNGPtr ret;
350
351 ret = (xmlRelaxNGPtr) xmlMalloc(sizeof(xmlRelaxNG));
352 if (ret == NULL) {
353 if ((ctxt != NULL) && (ctxt->error != NULL))
354 ctxt->error(ctxt->userData, "Out of memory\n");
355 ctxt->nbErrors++;
356 return (NULL);
357 }
358 memset(ret, 0, sizeof(xmlRelaxNG));
359
360 return (ret);
361}
362
363/**
364 * xmlRelaxNGFree:
365 * @schema: a schema structure
366 *
367 * Deallocate a RelaxNG structure.
368 */
369void
370xmlRelaxNGFree(xmlRelaxNGPtr schema)
371{
372 if (schema == NULL)
373 return;
374
Daniel Veillard6eadf632003-01-23 18:29:16 +0000375 if (schema->topgrammar != NULL)
376 xmlRelaxNGFreeGrammar(schema->topgrammar);
377 if (schema->doc != NULL)
378 xmlFreeDoc(schema->doc);
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000379 if (schema->documents != NULL)
380 xmlHashFree(schema->documents, (xmlHashDeallocator)
381 xmlRelaxNGFreeDocument);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000382
383 xmlFree(schema);
384}
385
386/**
387 * xmlRelaxNGNewGrammar:
388 * @ctxt: a Relax-NG validation context (optional)
389 *
390 * Allocate a new RelaxNG grammar.
391 *
392 * Returns the newly allocated structure or NULL in case or error
393 */
394static xmlRelaxNGGrammarPtr
395xmlRelaxNGNewGrammar(xmlRelaxNGParserCtxtPtr ctxt)
396{
397 xmlRelaxNGGrammarPtr ret;
398
399 ret = (xmlRelaxNGGrammarPtr) xmlMalloc(sizeof(xmlRelaxNGGrammar));
400 if (ret == NULL) {
401 if ((ctxt != NULL) && (ctxt->error != NULL))
402 ctxt->error(ctxt->userData, "Out of memory\n");
403 ctxt->nbErrors++;
404 return (NULL);
405 }
406 memset(ret, 0, sizeof(xmlRelaxNGGrammar));
407
408 return (ret);
409}
410
411/**
Daniel Veillard276be4a2003-01-24 01:03:34 +0000412 * xmlRelaxNGFreeDefineHash:
413 * @defines: a list of define structures
414 *
415 * Deallocate a RelaxNG definition in the hash table
416 */
417static void
418xmlRelaxNGFreeDefineHash(xmlRelaxNGDefinePtr defines)
419{
420 xmlRelaxNGDefinePtr next;
421
422 while (defines != NULL) {
423 next = defines->nextHash;
424 xmlRelaxNGFreeDefine(defines);
425 defines = next;
426 }
427}
428
429/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000430 * xmlRelaxNGFreeGrammar:
431 * @grammar: a grammar structure
432 *
433 * Deallocate a RelaxNG grammar structure.
434 */
435static void
436xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar)
437{
438 if (grammar == NULL)
439 return;
440
441 if (grammar->start != NULL)
442 xmlRelaxNGFreeDefine(grammar->start);
443 if (grammar->refs != NULL) {
444 xmlHashFree(grammar->refs, NULL);
445 }
446 if (grammar->defs != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +0000447 xmlHashFree(grammar->defs, (xmlHashDeallocator)
448 xmlRelaxNGFreeDefineHash);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000449 }
450
451 xmlFree(grammar);
452}
453
454/**
455 * xmlRelaxNGNewDefine:
456 * @ctxt: a Relax-NG validation context
457 * @node: the node in the input document.
458 *
459 * Allocate a new RelaxNG define.
460 *
461 * Returns the newly allocated structure or NULL in case or error
462 */
463static xmlRelaxNGDefinePtr
464xmlRelaxNGNewDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
465{
466 xmlRelaxNGDefinePtr ret;
467
468 ret = (xmlRelaxNGDefinePtr) xmlMalloc(sizeof(xmlRelaxNGDefine));
469 if (ret == NULL) {
470 if ((ctxt != NULL) && (ctxt->error != NULL))
471 ctxt->error(ctxt->userData, "Out of memory\n");
472 ctxt->nbErrors++;
473 return (NULL);
474 }
475 memset(ret, 0, sizeof(xmlRelaxNGDefine));
476 ret->node = node;
477
478 return (ret);
479}
480
481/**
482 * xmlRelaxNGFreeDefineList:
483 * @defines: a list of define structures
484 *
485 * Deallocate a RelaxNG define structures.
486 */
487static void
488xmlRelaxNGFreeDefineList(xmlRelaxNGDefinePtr defines)
489{
490 xmlRelaxNGDefinePtr next;
491
492 while (defines != NULL) {
493 next = defines->next;
494 xmlRelaxNGFreeDefine(defines);
495 defines = next;
496 }
497}
498
499/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000500 * xmlRelaxNGFreePartition:
501 * @partitions: a partition set structure
502 *
503 * Deallocate RelaxNG partition set structures.
504 */
505static void
506xmlRelaxNGFreePartition(xmlRelaxNGPartitionPtr partitions) {
507 xmlRelaxNGInterleaveGroupPtr group;
508 int j;
509
510 if (partitions != NULL) {
511 if (partitions->groups != NULL) {
512 for (j = 0;j < partitions->nbgroups;j++) {
513 group = partitions->groups[j];
514 if (group != NULL) {
515 if (group->defs != NULL)
516 xmlFree(group->defs);
517 xmlFree(group);
518 }
519 }
520 xmlFree(partitions->groups);
521 }
522 xmlFree(partitions);
523 }
524}
525/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000526 * xmlRelaxNGFreeDefine:
527 * @define: a define structure
528 *
529 * Deallocate a RelaxNG define structure.
530 */
531static void
532xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define)
533{
534 if (define == NULL)
535 return;
536
537 if (define->name != NULL)
538 xmlFree(define->name);
539 if (define->ns != NULL)
540 xmlFree(define->ns);
Daniel Veillardedc91922003-01-26 00:52:04 +0000541 if (define->value != NULL)
542 xmlFree(define->value);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000543 if (define->attrs != NULL)
544 xmlRelaxNGFreeDefineList(define->attrs);
Daniel Veillard276be4a2003-01-24 01:03:34 +0000545 if ((define->content != NULL) &&
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000546 (define->type != XML_RELAXNG_REF) &&
547 (define->type != XML_RELAXNG_EXTERNALREF))
Daniel Veillard6eadf632003-01-23 18:29:16 +0000548 xmlRelaxNGFreeDefineList(define->content);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000549 if ((define->data != NULL) &&
550 (define->type == XML_RELAXNG_INTERLEAVE))
551 xmlRelaxNGFreePartition((xmlRelaxNGPartitionPtr) define->data);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000552 xmlFree(define);
553}
554
555/**
556 * xmlRelaxNGNewValidState:
557 * @ctxt: a Relax-NG validation context
558 * @node: the current node or NULL for the document
559 *
560 * Allocate a new RelaxNG validation state
561 *
562 * Returns the newly allocated structure or NULL in case or error
563 */
564static xmlRelaxNGValidStatePtr
565xmlRelaxNGNewValidState(xmlRelaxNGValidCtxtPtr ctxt, xmlNodePtr node)
566{
567 xmlRelaxNGValidStatePtr ret;
568 xmlAttrPtr attr;
569 xmlAttrPtr attrs[MAX_ATTR];
570 int nbAttrs = 0;
571 xmlNodePtr root = NULL;
572
573 if (node == NULL) {
574 root = xmlDocGetRootElement(ctxt->doc);
575 if (root == NULL)
576 return(NULL);
577 } else {
578 attr = node->properties;
579 while (attr != NULL) {
580 if (nbAttrs < MAX_ATTR)
581 attrs[nbAttrs++] = attr;
582 else
583 nbAttrs++;
584 attr = attr->next;
585 }
586 }
587
588 if (nbAttrs < MAX_ATTR)
589 attrs[nbAttrs] = NULL;
590 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(sizeof(xmlRelaxNGValidState) +
591 nbAttrs * sizeof(xmlAttrPtr));
592 if (ret == NULL) {
593 if ((ctxt != NULL) && (ctxt->error != NULL))
594 ctxt->error(ctxt->userData, "Out of memory\n");
595 return (NULL);
596 }
597 if (node == NULL) {
598 ret->node = (xmlNodePtr) ctxt->doc;
599 ret->seq = root;
600 ret->nbAttrs = 0;
601 } else {
602 ret->node = node;
603 ret->seq = node->children;
604 ret->nbAttrs = nbAttrs;
605 if (nbAttrs > 0) {
606 if (nbAttrs < MAX_ATTR) {
607 memcpy(&(ret->attrs[0]), attrs,
608 sizeof(xmlAttrPtr) * (nbAttrs + 1));
609 } else {
610 attr = node->properties;
611 nbAttrs = 0;
612 while (attr != NULL) {
613 ret->attrs[nbAttrs++] = attr;
614 attr = attr->next;
615 }
616 ret->attrs[nbAttrs] = NULL;
617 }
618 }
619 }
620 return (ret);
621}
622
623/**
624 * xmlRelaxNGCopyValidState:
625 * @ctxt: a Relax-NG validation context
626 * @state: a validation state
627 *
628 * Copy the validation state
629 *
630 * Returns the newly allocated structure or NULL in case or error
631 */
632static xmlRelaxNGValidStatePtr
633xmlRelaxNGCopyValidState(xmlRelaxNGValidCtxtPtr ctxt,
634 xmlRelaxNGValidStatePtr state)
635{
636 xmlRelaxNGValidStatePtr ret;
637 unsigned int size;
638
639 if (state == NULL)
640 return(NULL);
641
642 size = sizeof(xmlRelaxNGValidState) +
643 state->nbAttrs * sizeof(xmlAttrPtr);
644 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(size);
645 if (ret == NULL) {
646 if ((ctxt != NULL) && (ctxt->error != NULL))
647 ctxt->error(ctxt->userData, "Out of memory\n");
648 return (NULL);
649 }
650 memcpy(ret, state, size);
651 return(ret);
652}
653
654/**
655 * xmlRelaxNGFreeValidState:
656 * @state: a validation state structure
657 *
658 * Deallocate a RelaxNG validation state structure.
659 */
660static void
661xmlRelaxNGFreeValidState(xmlRelaxNGValidStatePtr state)
662{
663 if (state == NULL)
664 return;
665
666 xmlFree(state);
667}
668
669/************************************************************************
670 * *
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000671 * Document functions *
672 * *
673 ************************************************************************/
674static xmlDocPtr xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt,
675 xmlDocPtr doc);
676
677/**
678 * xmlRelaxNGDocumentPush:
679 * @ctxt: the parser context
680 * @value: the element doc
681 *
682 * Pushes a new doc on top of the doc stack
683 *
684 * Returns 0 in case of error, the index in the stack otherwise
685 */
686static int
687xmlRelaxNGDocumentPush(xmlRelaxNGParserCtxtPtr ctxt,
688 xmlRelaxNGDocumentPtr value)
689{
690 if (ctxt->docTab == NULL) {
691 ctxt->docMax = 4;
692 ctxt->docNr = 0;
693 ctxt->docTab = (xmlRelaxNGDocumentPtr *) xmlMalloc(
694 ctxt->docMax * sizeof(ctxt->docTab[0]));
695 if (ctxt->docTab == NULL) {
696 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
697 return (0);
698 }
699 }
700 if (ctxt->docNr >= ctxt->docMax) {
701 ctxt->docMax *= 2;
702 ctxt->docTab =
703 (xmlRelaxNGDocumentPtr *) xmlRealloc(ctxt->docTab,
704 ctxt->docMax *
705 sizeof(ctxt->docTab[0]));
706 if (ctxt->docTab == NULL) {
707 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
708 return (0);
709 }
710 }
711 ctxt->docTab[ctxt->docNr] = value;
712 ctxt->doc = value;
713 return (ctxt->docNr++);
714}
715
716/**
717 * xmlRelaxNGDocumentPop:
718 * @ctxt: the parser context
719 *
720 * Pops the top doc from the doc stack
721 *
722 * Returns the doc just removed
723 */
724static xmlRelaxNGDocumentPtr
725xmlRelaxNGDocumentPop(xmlRelaxNGParserCtxtPtr ctxt)
726{
727 xmlRelaxNGDocumentPtr ret;
728
729 if (ctxt->docNr <= 0)
730 return (0);
731 ctxt->docNr--;
732 if (ctxt->docNr > 0)
733 ctxt->doc = ctxt->docTab[ctxt->docNr - 1];
734 else
735 ctxt->doc = NULL;
736 ret = ctxt->docTab[ctxt->docNr];
737 ctxt->docTab[ctxt->docNr] = 0;
738 return (ret);
739}
740
741/**
742 * xmlRelaxNGLoadocument:
743 * @ctxt: the parser context
744 * @URL: the normalized URL
745 * @ns: the inherited ns if any
746 *
747 * First lookup if the document is already loaded into the parser context,
748 * check against recursion. If not found the resource is loaded and
749 * the content is preprocessed before being returned back to the caller.
750 *
751 * Returns the xmlRelaxNGDocumentPtr or NULL in case of error
752 */
753static xmlRelaxNGDocumentPtr
754xmlRelaxNGLoaddocument(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
755 const xmlChar *ns) {
756 xmlRelaxNGDocumentPtr ret = NULL;
757 xmlDocPtr doc;
758 xmlNodePtr root;
759 int i;
760
761 /*
762 * check against recursion in the stack
763 */
764 for (i = 0;i < ctxt->docNr;i++) {
765 if (xmlStrEqual(ctxt->docTab[i]->href, URL)) {
766 if (ctxt->error != NULL)
767 ctxt->error(ctxt->userData,
768 "Detected an externalRef recursion for %s\n",
769 URL);
770 ctxt->nbErrors++;
771 return(NULL);
772 }
773 }
774
775 /*
776 * Lookup in the hash table
777 */
778 if (ctxt->documents == NULL) {
779 ctxt->documents = xmlHashCreate(10);
780 if (ctxt->documents == NULL) {
781 if (ctxt->error != NULL)
782 ctxt->error(ctxt->userData,
783 "Failed to allocate hash table for document\n");
784 ctxt->nbErrors++;
785 return(NULL);
786 }
787 } else {
788 if (ns == NULL)
789 ret = xmlHashLookup2(ctxt->documents, BAD_CAST "", URL);
790 else
791 ret = xmlHashLookup2(ctxt->documents, ns, URL);
792 if (ret != NULL)
793 return(ret);
794 }
795
796
797 /*
798 * load the document
799 */
800 doc = xmlParseFile((const char *) URL);
801 if (doc == NULL) {
802 if (ctxt->error != NULL)
803 ctxt->error(ctxt->userData,
804 "xmlRelaxNG: could not load %s\n", URL);
805 ctxt->nbErrors++;
806 return (NULL);
807 }
808
809 /*
810 * Allocate the document structures and register it first.
811 */
812 ret = (xmlRelaxNGDocumentPtr) xmlMalloc(sizeof(xmlRelaxNGDocument));
813 if (ret == NULL) {
814 if (ctxt->error != NULL)
815 ctxt->error(ctxt->userData,
816 "xmlRelaxNG: allocate memory for doc %s\n", URL);
817 ctxt->nbErrors++;
818 xmlFreeDoc(doc);
819 return (NULL);
820 }
821 memset(ret, 0, sizeof(xmlRelaxNGDocument));
822 ret->doc = doc;
823 ret->href = xmlStrdup(URL);
824
825 /*
826 * transmit the ns if needed
827 */
828 if (ns != NULL) {
829 root = xmlDocGetRootElement(doc);
830 if (root != NULL) {
831 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
832 xmlSetProp(root, BAD_CAST"ns", ns);
833 }
834 }
835 }
836
837 /*
838 * push it on the stack and register it in the hash table
839 */
840 if (ns == NULL)
841 xmlHashAddEntry2(ctxt->documents, BAD_CAST "", URL, ret);
842 else
843 xmlHashAddEntry2(ctxt->documents, ns, URL, ret);
844 xmlRelaxNGDocumentPush(ctxt, ret);
845
846 /*
847 * Some preprocessing of the document content
848 */
849 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
850 if (doc == NULL) {
851 xmlFreeDoc(ctxt->document);
852 ctxt->doc = NULL;
853 return(NULL);
854 }
855
856 xmlRelaxNGDocumentPop(ctxt);
857
858 return(ret);
859}
860
861/************************************************************************
862 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +0000863 * Error functions *
864 * *
865 ************************************************************************/
866
867#define VALID_CTXT() \
868 if (ctxt->flags == 0) xmlGenericError(xmlGenericErrorContext, \
869 "error detected at %s:%d\n", \
870 __FILE__, __LINE__);
871#define VALID_ERROR if (ctxt->flags == 0) printf
872
873#if 0
874/**
875 * xmlRelaxNGErrorContext:
876 * @ctxt: the parsing context
877 * @schema: the schema being built
878 * @node: the node being processed
879 * @child: the child being processed
880 *
881 * Dump a RelaxNGType structure
882 */
883static void
884xmlRelaxNGErrorContext(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGPtr schema,
885 xmlNodePtr node, xmlNodePtr child)
886{
887 int line = 0;
888 const xmlChar *file = NULL;
889 const xmlChar *name = NULL;
890 const char *type = "error";
891
892 if ((ctxt == NULL) || (ctxt->error == NULL))
893 return;
894
895 if (child != NULL)
896 node = child;
897
898 if (node != NULL) {
899 if ((node->type == XML_DOCUMENT_NODE) ||
900 (node->type == XML_HTML_DOCUMENT_NODE)) {
901 xmlDocPtr doc = (xmlDocPtr) node;
902
903 file = doc->URL;
904 } else {
905 /*
906 * Try to find contextual informations to report
907 */
908 if (node->type == XML_ELEMENT_NODE) {
909 line = (int) node->content;
910 } else if ((node->prev != NULL) &&
911 (node->prev->type == XML_ELEMENT_NODE)) {
912 line = (int) node->prev->content;
913 } else if ((node->parent != NULL) &&
914 (node->parent->type == XML_ELEMENT_NODE)) {
915 line = (int) node->parent->content;
916 }
917 if ((node->doc != NULL) && (node->doc->URL != NULL))
918 file = node->doc->URL;
919 if (node->name != NULL)
920 name = node->name;
921 }
922 }
923
924 if (ctxt != NULL)
925 type = "compilation error";
926 else if (schema != NULL)
927 type = "runtime error";
928
929 if ((file != NULL) && (line != 0) && (name != NULL))
930 ctxt->error(ctxt->userData, "%s: file %s line %d element %s\n",
931 type, file, line, name);
932 else if ((file != NULL) && (name != NULL))
933 ctxt->error(ctxt->userData, "%s: file %s element %s\n",
934 type, file, name);
935 else if ((file != NULL) && (line != 0))
936 ctxt->error(ctxt->userData, "%s: file %s line %d\n", type, file, line);
937 else if (file != NULL)
938 ctxt->error(ctxt->userData, "%s: file %s\n", type, file);
939 else if (name != NULL)
940 ctxt->error(ctxt->userData, "%s: element %s\n", type, name);
941 else
942 ctxt->error(ctxt->userData, "%s\n", type);
943}
944#endif
945
946/************************************************************************
947 * *
948 * Type library hooks *
949 * *
950 ************************************************************************/
Daniel Veillardea3f3982003-01-26 19:45:18 +0000951static xmlChar *xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt,
952 const xmlChar *str);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000953
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000954/**
955 * xmlRelaxNGSchemaTypeHave:
956 * @data: data needed for the library
957 * @type: the type name
958 *
959 * Check if the given type is provided by
960 * the W3C XMLSchema Datatype library.
961 *
962 * Returns 1 if yes, 0 if no and -1 in case of error.
963 */
964static int
965xmlRelaxNGSchemaTypeHave(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000966 const xmlChar *type) {
967 xmlSchemaTypePtr typ;
968
969 if (type == NULL)
970 return(-1);
971 typ = xmlSchemaGetPredefinedType(type,
972 BAD_CAST "http://www.w3.org/2001/XMLSchema");
973 if (typ == NULL)
974 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000975 return(1);
976}
977
978/**
979 * xmlRelaxNGSchemaTypeCheck:
980 * @data: data needed for the library
981 * @type: the type name
982 * @value: the value to check
983 *
984 * Check if the given type and value are validated by
985 * the W3C XMLSchema Datatype library.
986 *
987 * Returns 1 if yes, 0 if no and -1 in case of error.
988 */
989static int
990xmlRelaxNGSchemaTypeCheck(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000991 const xmlChar *type,
992 const xmlChar *value) {
993 xmlSchemaTypePtr typ;
994 int ret;
995
996 /*
997 * TODO: the type should be cached ab provided back, interface subject
998 * to changes.
999 * TODO: handle facets, may require an additional interface and keep
1000 * the value returned from the validation.
1001 */
1002 if ((type == NULL) || (value == NULL))
1003 return(-1);
1004 typ = xmlSchemaGetPredefinedType(type,
1005 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1006 if (typ == NULL)
1007 return(-1);
1008 ret = xmlSchemaValidatePredefinedType(typ, value, NULL);
1009 if (ret == 0)
1010 return(1);
1011 if (ret > 0)
1012 return(0);
1013 return(-1);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001014}
1015
1016/**
1017 * xmlRelaxNGSchemaTypeCompare:
1018 * @data: data needed for the library
1019 * @type: the type name
1020 * @value1: the first value
1021 * @value2: the second value
1022 *
1023 * Compare two values accordingly a type from the W3C XMLSchema
1024 * Datatype library.
1025 *
1026 * Returns 1 if yes, 0 if no and -1 in case of error.
1027 */
1028static int
1029xmlRelaxNGSchemaTypeCompare(void *data ATTRIBUTE_UNUSED,
1030 const xmlChar *type ATTRIBUTE_UNUSED,
1031 const xmlChar *value1 ATTRIBUTE_UNUSED,
1032 const xmlChar *value2 ATTRIBUTE_UNUSED) {
1033 TODO
1034 return(1);
1035}
1036
1037/**
1038 * xmlRelaxNGDefaultTypeHave:
1039 * @data: data needed for the library
1040 * @type: the type name
1041 *
1042 * Check if the given type is provided by
1043 * the default datatype library.
1044 *
1045 * Returns 1 if yes, 0 if no and -1 in case of error.
1046 */
1047static int
1048xmlRelaxNGDefaultTypeHave(void *data ATTRIBUTE_UNUSED, const xmlChar *type) {
1049 if (type == NULL)
1050 return(-1);
1051 if (xmlStrEqual(type, BAD_CAST "string"))
1052 return(1);
1053 if (xmlStrEqual(type, BAD_CAST "token"))
1054 return(1);
1055 return(0);
1056}
1057
1058/**
1059 * xmlRelaxNGDefaultTypeCheck:
1060 * @data: data needed for the library
1061 * @type: the type name
1062 * @value: the value to check
1063 *
1064 * Check if the given type and value are validated by
1065 * the default datatype library.
1066 *
1067 * Returns 1 if yes, 0 if no and -1 in case of error.
1068 */
1069static int
1070xmlRelaxNGDefaultTypeCheck(void *data ATTRIBUTE_UNUSED,
1071 const xmlChar *type ATTRIBUTE_UNUSED,
1072 const xmlChar *value ATTRIBUTE_UNUSED) {
1073 return(1);
1074}
1075
1076/**
1077 * xmlRelaxNGDefaultTypeCompare:
1078 * @data: data needed for the library
1079 * @type: the type name
1080 * @value1: the first value
1081 * @value2: the second value
1082 *
1083 * Compare two values accordingly a type from the default
1084 * datatype library.
1085 *
1086 * Returns 1 if yes, 0 if no and -1 in case of error.
1087 */
1088static int
1089xmlRelaxNGDefaultTypeCompare(void *data ATTRIBUTE_UNUSED,
1090 const xmlChar *type ATTRIBUTE_UNUSED,
1091 const xmlChar *value1 ATTRIBUTE_UNUSED,
1092 const xmlChar *value2 ATTRIBUTE_UNUSED) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00001093 int ret = -1;
1094
1095 if (xmlStrEqual(type, BAD_CAST "string")) {
1096 ret = xmlStrEqual(value1, value2);
1097 } else if (xmlStrEqual(type, BAD_CAST "token")) {
1098 if (!xmlStrEqual(value1, value2)) {
1099 xmlChar *nval, *nvalue;
1100
1101 /*
1102 * TODO: trivial optimizations are possible by
1103 * computing at compile-time
1104 */
1105 nval = xmlRelaxNGNormalize(NULL, value1);
1106 nvalue = xmlRelaxNGNormalize(NULL, value2);
1107
1108 if ((nval == NULL) || (nvalue == NULL) ||
1109 (!xmlStrEqual(nval, nvalue)))
1110 ret = -1;
1111 if (nval != NULL)
1112 xmlFree(nval);
1113 if (nvalue != NULL)
1114 xmlFree(nvalue);
1115 }
1116 }
1117 return(ret);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001118}
1119
1120static int xmlRelaxNGTypeInitialized = 0;
1121static xmlHashTablePtr xmlRelaxNGRegisteredTypes = NULL;
1122
1123/**
1124 * xmlRelaxNGFreeTypeLibrary:
1125 * @lib: the type library structure
1126 * @namespace: the URI bound to the library
1127 *
1128 * Free the structure associated to the type library
1129 */
Daniel Veillard6eadf632003-01-23 18:29:16 +00001130static void
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001131xmlRelaxNGFreeTypeLibrary(xmlRelaxNGTypeLibraryPtr lib,
1132 const xmlChar *namespace ATTRIBUTE_UNUSED) {
1133 if (lib == NULL)
1134 return;
1135 if (lib->namespace != NULL)
1136 xmlFree((xmlChar *)lib->namespace);
1137 xmlFree(lib);
1138}
1139
1140/**
1141 * xmlRelaxNGRegisterTypeLibrary:
1142 * @namespace: the URI bound to the library
1143 * @data: data associated to the library
1144 * @have: the provide function
1145 * @check: the checking function
1146 * @comp: the comparison function
1147 *
1148 * Register a new type library
1149 *
1150 * Returns 0 in case of success and -1 in case of error.
1151 */
1152static int
1153xmlRelaxNGRegisterTypeLibrary(const xmlChar *namespace, void *data,
1154 xmlRelaxNGTypeHave have, xmlRelaxNGTypeCheck check,
1155 xmlRelaxNGTypeCompare comp) {
1156 xmlRelaxNGTypeLibraryPtr lib;
1157 int ret;
1158
1159 if ((xmlRelaxNGRegisteredTypes == NULL) || (namespace == NULL) ||
1160 (check == NULL) || (comp == NULL))
1161 return(-1);
1162 if (xmlHashLookup(xmlRelaxNGRegisteredTypes, namespace) != NULL) {
1163 xmlGenericError(xmlGenericErrorContext,
1164 "Relax-NG types library '%s' already registered\n",
1165 namespace);
1166 return(-1);
1167 }
1168 lib = (xmlRelaxNGTypeLibraryPtr) xmlMalloc(sizeof(xmlRelaxNGTypeLibrary));
1169 if (lib == NULL) {
1170 xmlGenericError(xmlGenericErrorContext,
1171 "Relax-NG types library '%s' malloc() failed\n",
1172 namespace);
1173 return (-1);
1174 }
1175 memset(lib, 0, sizeof(xmlRelaxNGTypeLibrary));
1176 lib->namespace = xmlStrdup(namespace);
1177 lib->data = data;
1178 lib->have = have;
1179 lib->comp = comp;
1180 lib->check = check;
1181 ret = xmlHashAddEntry(xmlRelaxNGRegisteredTypes, namespace, lib);
1182 if (ret < 0) {
1183 xmlGenericError(xmlGenericErrorContext,
1184 "Relax-NG types library failed to register '%s'\n",
1185 namespace);
1186 xmlRelaxNGFreeTypeLibrary(lib, namespace);
1187 return(-1);
1188 }
1189 return(0);
1190}
1191
1192/**
1193 * xmlRelaxNGInitTypes:
1194 *
1195 * Initilize the default type libraries.
1196 *
1197 * Returns 0 in case of success and -1 in case of error.
1198 */
1199static int
Daniel Veillard6eadf632003-01-23 18:29:16 +00001200xmlRelaxNGInitTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001201 if (xmlRelaxNGTypeInitialized != 0)
1202 return(0);
1203 xmlRelaxNGRegisteredTypes = xmlHashCreate(10);
1204 if (xmlRelaxNGRegisteredTypes == NULL) {
1205 xmlGenericError(xmlGenericErrorContext,
1206 "Failed to allocate sh table for Relax-NG types\n");
1207 return(-1);
1208 }
1209 xmlRelaxNGRegisterTypeLibrary(
1210 BAD_CAST "http://www.w3.org/2001/XMLSchema-datatypes",
1211 NULL,
1212 xmlRelaxNGSchemaTypeHave,
1213 xmlRelaxNGSchemaTypeCheck,
1214 xmlRelaxNGSchemaTypeCompare);
1215 xmlRelaxNGRegisterTypeLibrary(
1216 xmlRelaxNGNs,
1217 NULL,
1218 xmlRelaxNGDefaultTypeHave,
1219 xmlRelaxNGDefaultTypeCheck,
1220 xmlRelaxNGDefaultTypeCompare);
1221 xmlRelaxNGTypeInitialized = 1;
1222 return(0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001223}
1224
1225/**
1226 * xmlRelaxNGCleanupTypes:
1227 *
1228 * Cleanup the default Schemas type library associated to RelaxNG
1229 */
1230void
1231xmlRelaxNGCleanupTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001232 if (xmlRelaxNGTypeInitialized == 0)
1233 return;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001234 xmlSchemaCleanupTypes();
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001235 xmlHashFree(xmlRelaxNGRegisteredTypes, (xmlHashDeallocator)
1236 xmlRelaxNGFreeTypeLibrary);
1237 xmlRelaxNGTypeInitialized = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001238}
1239
1240/************************************************************************
1241 * *
1242 * Parsing functions *
1243 * *
1244 ************************************************************************/
1245
1246static xmlRelaxNGDefinePtr xmlRelaxNGParseAttribute(
1247 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1248static xmlRelaxNGDefinePtr xmlRelaxNGParseElement(
1249 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1250static xmlRelaxNGDefinePtr xmlRelaxNGParsePatterns(
1251 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001252static xmlRelaxNGDefinePtr xmlRelaxNGParsePattern(
1253 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001254static xmlRelaxNGPtr xmlRelaxNGParseDocument(
1255 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001256
1257
1258#define IS_BLANK_NODE(n) \
1259 (((n)->type == XML_TEXT_NODE) && (xmlRelaxNGIsBlank((n)->content)))
1260
1261/**
1262 * xmlRelaxNGIsBlank:
1263 * @str: a string
1264 *
1265 * Check if a string is ignorable c.f. 4.2. Whitespace
1266 *
1267 * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
1268 */
1269static int
1270xmlRelaxNGIsBlank(xmlChar *str) {
1271 if (str == NULL)
1272 return(1);
1273 while (*str != 0) {
1274 if (!(IS_BLANK(*str))) return(0);
1275 str++;
1276 }
1277 return(1);
1278}
1279
Daniel Veillard6eadf632003-01-23 18:29:16 +00001280/**
1281 * xmlRelaxNGGetDataTypeLibrary:
1282 * @ctxt: a Relax-NG parser context
1283 * @node: the current data or value element
1284 *
1285 * Applies algorithm from 4.3. datatypeLibrary attribute
1286 *
1287 * Returns the datatypeLibary value or NULL if not found
1288 */
1289static xmlChar *
1290xmlRelaxNGGetDataTypeLibrary(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1291 xmlNodePtr node) {
1292 xmlChar *ret, *escape;
1293
Daniel Veillard6eadf632003-01-23 18:29:16 +00001294 if ((IS_RELAXNG(node, "data")) || (IS_RELAXNG(node, "value"))) {
1295 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1296 if (ret != NULL) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001297 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +00001298 if (escape == NULL) {
1299 return(ret);
1300 }
1301 xmlFree(ret);
1302 return(escape);
1303 }
1304 }
1305 node = node->parent;
1306 while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) {
1307 if (IS_RELAXNG(node, "element")) {
1308 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1309 if (ret != NULL) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001310 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +00001311 if (escape == NULL) {
1312 return(ret);
1313 }
1314 xmlFree(ret);
1315 return(escape);
1316 }
1317 }
1318 node = node->parent;
1319 }
1320 return(NULL);
1321}
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001322
1323/**
Daniel Veillardedc91922003-01-26 00:52:04 +00001324 * xmlRelaxNGParseValue:
1325 * @ctxt: a Relax-NG parser context
1326 * @node: the data node.
1327 *
1328 * parse the content of a RelaxNG value node.
1329 *
1330 * Returns the definition pointer or NULL in case of error
1331 */
1332static xmlRelaxNGDefinePtr
1333xmlRelaxNGParseValue(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1334 xmlRelaxNGDefinePtr def = NULL;
1335 xmlRelaxNGTypeLibraryPtr lib;
1336 xmlChar *type;
1337 xmlChar *library;
1338 int tmp;
1339
1340 def = xmlRelaxNGNewDefine(ctxt, node);
1341 if (def == NULL)
1342 return(NULL);
1343 def->type = XML_RELAXNG_VALUE;
1344
1345 type = xmlGetProp(node, BAD_CAST "type");
1346 if (type != NULL) {
1347 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1348 if (library == NULL)
1349 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1350
1351 def->name = type;
1352 def->ns = library;
1353
1354 lib = (xmlRelaxNGTypeLibraryPtr)
1355 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1356 if (lib == NULL) {
1357 if (ctxt->error != NULL)
1358 ctxt->error(ctxt->userData,
1359 "Use of unregistered type library '%s'\n",
1360 library);
1361 ctxt->nbErrors++;
1362 def->data = NULL;
1363 } else {
1364 def->data = lib;
1365 if (lib->have == NULL) {
1366 ctxt->error(ctxt->userData,
1367 "Internal error with type library '%s': no 'have'\n",
1368 library);
1369 ctxt->nbErrors++;
1370 } else {
1371 tmp = lib->have(lib->data, def->name);
1372 if (tmp != 1) {
1373 ctxt->error(ctxt->userData,
1374 "Error type '%s' is not exported by type library '%s'\n",
1375 def->name, library);
1376 ctxt->nbErrors++;
1377 }
1378 }
1379 }
1380 }
1381 if (node->children == NULL) {
1382 if (ctxt->error != NULL)
1383 ctxt->error(ctxt->userData,
1384 "Element <value> has no content\n");
1385 ctxt->nbErrors++;
1386 } else if ((node->children->type != XML_TEXT_NODE) ||
1387 (node->children->next != NULL)) {
1388 if (ctxt->error != NULL)
1389 ctxt->error(ctxt->userData,
1390 "Expecting a single text value for <value>content\n");
1391 ctxt->nbErrors++;
1392 } else {
1393 def->value = xmlNodeGetContent(node);
1394 if (def->value == NULL) {
1395 if (ctxt->error != NULL)
1396 ctxt->error(ctxt->userData,
1397 "Element <value> has no content\n");
1398 ctxt->nbErrors++;
1399 }
1400 }
1401 /* TODO check ahead of time that the value is okay per the type */
1402 return(def);
1403}
1404
1405/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001406 * xmlRelaxNGParseData:
1407 * @ctxt: a Relax-NG parser context
1408 * @node: the data node.
1409 *
1410 * parse the content of a RelaxNG data node.
1411 *
1412 * Returns the definition pointer or NULL in case of error
1413 */
1414static xmlRelaxNGDefinePtr
1415xmlRelaxNGParseData(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1416 xmlRelaxNGDefinePtr def = NULL;
1417 xmlRelaxNGTypeLibraryPtr lib;
1418 xmlChar *type;
1419 xmlChar *library;
1420 xmlNodePtr content;
1421 int tmp;
1422
1423 type = xmlGetProp(node, BAD_CAST "type");
1424 if (type == NULL) {
1425 if (ctxt->error != NULL)
1426 ctxt->error(ctxt->userData,
1427 "data has no type\n");
1428 ctxt->nbErrors++;
1429 return(NULL);
1430 }
1431 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1432 if (library == NULL)
1433 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1434
1435 def = xmlRelaxNGNewDefine(ctxt, node);
1436 if (def == NULL) {
1437 xmlFree(type);
1438 return(NULL);
1439 }
1440 def->type = XML_RELAXNG_DATATYPE;
1441 def->name = type;
1442 def->ns = library;
1443
1444 lib = (xmlRelaxNGTypeLibraryPtr)
1445 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1446 if (lib == NULL) {
1447 if (ctxt->error != NULL)
1448 ctxt->error(ctxt->userData,
1449 "Use of unregistered type library '%s'\n",
1450 library);
1451 ctxt->nbErrors++;
1452 def->data = NULL;
1453 } else {
1454 def->data = lib;
1455 if (lib->have == NULL) {
1456 ctxt->error(ctxt->userData,
1457 "Internal error with type library '%s': no 'have'\n",
1458 library);
1459 ctxt->nbErrors++;
1460 } else {
1461 tmp = lib->have(lib->data, def->name);
1462 if (tmp != 1) {
1463 ctxt->error(ctxt->userData,
1464 "Error type '%s' is not exported by type library '%s'\n",
1465 def->name, library);
1466 ctxt->nbErrors++;
1467 }
1468 }
1469 }
1470 content = node->children;
1471 while (content != NULL) {
1472 TODO
1473 content = content->next;
1474 }
1475
1476 return(def);
1477}
1478
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001479/**
1480 * xmlRelaxNGCompareElemDefLists:
1481 * @ctxt: a Relax-NG parser context
1482 * @defs1: the first list of element defs
1483 * @defs2: the second list of element defs
1484 *
1485 * Compare the 2 lists of element definitions. The comparison is
1486 * that if both lists do not accept the same QNames, it returns 1
1487 * If the 2 lists can accept the same QName the comparison returns 0
1488 *
1489 * Returns 1 disttinct, 0 if equal
1490 */
1491static int
1492xmlRelaxNGCompareElemDefLists(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1493 xmlRelaxNGDefinePtr *def1,
1494 xmlRelaxNGDefinePtr *def2) {
1495 xmlRelaxNGDefinePtr *basedef2 = def2;
1496
1497 if ((*def1 == NULL) || (*def2 == NULL))
1498 return(1);
1499 while (*def1 != NULL) {
1500 while ((*def2) != NULL) {
1501 if ((*def1)->name == NULL) {
1502 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1503 return(0);
1504 } else if ((*def2)->name == NULL) {
1505 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1506 return(0);
1507 } else if (xmlStrEqual((*def1)->name, (*def2)->name)) {
1508 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1509 return(0);
1510 }
1511 def2++;
1512 }
1513 def2 = basedef2;
1514 def1++;
1515 }
1516 return(1);
1517}
1518
1519/**
1520 * xmlRelaxNGGetElements:
1521 * @ctxt: a Relax-NG parser context
1522 * @def: the interleave definition
1523 *
1524 * Compute the list of top elements a definition can generate
1525 *
1526 * Returns a list of elements or NULL if none was found.
1527 */
1528static xmlRelaxNGDefinePtr *
1529xmlRelaxNGGetElements(xmlRelaxNGParserCtxtPtr ctxt,
1530 xmlRelaxNGDefinePtr def) {
1531 xmlRelaxNGDefinePtr *ret = NULL, parent, cur, tmp;
1532 int len = 0;
1533 int max = 0;
1534
1535 parent = NULL;
1536 cur = def;
1537 while (cur != NULL) {
Daniel Veillardb08c9812003-01-28 23:09:49 +00001538 if ((cur->type == XML_RELAXNG_ELEMENT) ||
1539 (cur->type == XML_RELAXNG_TEXT)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001540 if (ret == NULL) {
1541 max = 10;
1542 ret = (xmlRelaxNGDefinePtr *)
1543 xmlMalloc((max + 1) * sizeof(xmlRelaxNGDefinePtr));
1544 if (ret == NULL) {
1545 if (ctxt->error != NULL)
1546 ctxt->error(ctxt->userData,
1547 "Out of memory in element search\n");
1548 ctxt->nbErrors++;
1549 return(NULL);
1550 }
1551 } else if (max <= len) {
1552 max *= 2;
1553 ret = xmlRealloc(ret, (max + 1) * sizeof(xmlRelaxNGDefinePtr));
1554 if (ret == NULL) {
1555 if (ctxt->error != NULL)
1556 ctxt->error(ctxt->userData,
1557 "Out of memory in element search\n");
1558 ctxt->nbErrors++;
1559 return(NULL);
1560 }
1561 }
Daniel Veillardb08c9812003-01-28 23:09:49 +00001562 ret[len++] = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001563 ret[len] = NULL;
1564 } else if ((cur->type == XML_RELAXNG_CHOICE) ||
1565 (cur->type == XML_RELAXNG_INTERLEAVE) ||
1566 (cur->type == XML_RELAXNG_GROUP) ||
1567 (cur->type == XML_RELAXNG_ONEORMORE) ||
Daniel Veillardb08c9812003-01-28 23:09:49 +00001568 (cur->type == XML_RELAXNG_ZEROORMORE) ||
1569 (cur->type == XML_RELAXNG_OPTIONAL) ||
1570 (cur->type == XML_RELAXNG_REF) ||
1571 (cur->type == XML_RELAXNG_DEF)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001572 /*
1573 * Don't go within elements or attributes or string values.
1574 * Just gather the element top list
1575 */
1576 if (cur->content != NULL) {
1577 parent = cur;
1578 cur = cur->content;
1579 tmp = cur;
1580 while (tmp != NULL) {
1581 tmp->parent = parent;
1582 tmp = tmp->next;
1583 }
1584 continue;
1585 }
1586 }
1587 if (cur == def) return(ret);
1588 if (cur->next != NULL) {
1589 cur = cur->next;
1590 continue;
1591 }
1592 do {
1593 cur = cur->parent;
1594 if (cur == NULL) break;
1595 if (cur == def) return(ret);
1596 if (cur->next != NULL) {
1597 cur = cur->next;
1598 break;
1599 }
1600 } while (cur != NULL);
1601 }
1602 return(ret);
1603}
1604
1605/**
1606 * xmlRelaxNGComputeInterleaves:
1607 * @def: the interleave definition
1608 * @ctxt: a Relax-NG parser context
1609 * @node: the data node.
1610 *
1611 * A lot of work for preprocessing interleave definitions
1612 * is potentially needed to get a decent execution speed at runtime
1613 * - trying to get a total order on the element nodes generated
1614 * by the interleaves, order the list of interleave definitions
1615 * following that order.
1616 * - if <text/> is used to handle mixed content, it is better to
1617 * flag this in the define and simplify the runtime checking
1618 * algorithm
1619 */
1620static void
1621xmlRelaxNGComputeInterleaves(xmlRelaxNGDefinePtr def,
1622 xmlRelaxNGParserCtxtPtr ctxt,
1623 xmlChar *name ATTRIBUTE_UNUSED) {
1624 xmlRelaxNGDefinePtr cur;
1625
1626 xmlRelaxNGDefinePtr *list = NULL;
1627 xmlRelaxNGPartitionPtr partitions = NULL;
1628 xmlRelaxNGInterleaveGroupPtr *groups = NULL;
1629 xmlRelaxNGInterleaveGroupPtr group;
1630 int i,j,ret;
1631 int nbgroups = 0;
1632 int nbchild = 0;
1633
1634#ifdef DEBUG_INTERLEAVE
1635 xmlGenericError(xmlGenericErrorContext,
1636 "xmlRelaxNGComputeInterleaves(%s)\n",
1637 name);
1638#endif
1639 cur = def->content;
1640 while (cur != NULL) {
1641 nbchild++;
1642 cur = cur->next;
1643 }
1644
1645#ifdef DEBUG_INTERLEAVE
1646 xmlGenericError(xmlGenericErrorContext, " %d child\n", nbchild);
1647#endif
1648 groups = (xmlRelaxNGInterleaveGroupPtr *)
1649 xmlMalloc(nbchild * sizeof(xmlRelaxNGInterleaveGroupPtr));
1650 if (groups == NULL)
1651 goto error;
1652 cur = def->content;
1653 while (cur != NULL) {
1654 list = xmlRelaxNGGetElements(ctxt, cur);
1655 if (list != NULL) {
1656 groups[nbgroups] = (xmlRelaxNGInterleaveGroupPtr)
1657 xmlMalloc(sizeof(xmlRelaxNGInterleaveGroup));
1658 if (groups[nbgroups] == NULL)
1659 goto error;
1660 groups[nbgroups]->rule = cur;
1661 groups[nbgroups]->defs = list;
1662 nbgroups++;
1663 }
1664 cur = cur->next;
1665 }
1666 list = NULL;
1667#ifdef DEBUG_INTERLEAVE
1668 xmlGenericError(xmlGenericErrorContext, " %d groups\n", nbgroups);
1669#endif
1670
1671 /*
1672 * Let's check that all rules makes a partitions according to 7.4
1673 */
1674 partitions = (xmlRelaxNGPartitionPtr)
1675 xmlMalloc(sizeof(xmlRelaxNGPartition));
1676 if (partitions == NULL)
1677 goto error;
1678 partitions->nbgroups = nbgroups;
1679 for (i = 0;i < nbgroups;i++) {
1680 group = groups[i];
1681 for (j = i+1;j < nbgroups;j++) {
1682 if (groups[j] == NULL)
1683 continue;
1684 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->defs,
1685 groups[j]->defs);
1686 if (ret == 0) {
1687 if (ctxt->error != NULL)
1688 ctxt->error(ctxt->userData,
1689 "Element or text conflicts in interleave\n");
1690 ctxt->nbErrors++;
1691 }
1692 }
1693 }
1694 partitions->groups = groups;
1695
1696 /*
1697 * Free Up the child list, and save the partition list back in the def
1698 */
1699 def->data = partitions;
1700 return;
1701
1702error:
1703 if (ctxt->error != NULL)
1704 ctxt->error(ctxt->userData,
1705 "Out of memory in interleave computation\n");
1706 ctxt->nbErrors++;
1707 if (list == NULL)
1708 xmlFree(list);
1709 if (groups != NULL) {
1710 for (i = 0;i < nbgroups;i++)
1711 if (groups[i] != NULL) {
1712 if (groups[i]->defs != NULL)
1713 xmlFree(groups[i]->defs);
1714 xmlFree(groups[i]);
1715 }
1716 xmlFree(groups);
1717 }
1718 xmlRelaxNGFreePartition(partitions);
1719}
1720
1721/**
1722 * xmlRelaxNGParseInterleave:
1723 * @ctxt: a Relax-NG parser context
1724 * @node: the data node.
1725 *
1726 * parse the content of a RelaxNG interleave node.
1727 *
1728 * Returns the definition pointer or NULL in case of error
1729 */
1730static xmlRelaxNGDefinePtr
1731xmlRelaxNGParseInterleave(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1732 xmlRelaxNGDefinePtr def = NULL;
1733 xmlRelaxNGDefinePtr last = NULL, cur;
1734 xmlNodePtr child;
1735
1736 def = xmlRelaxNGNewDefine(ctxt, node);
1737 if (def == NULL) {
1738 return(NULL);
1739 }
1740 def->type = XML_RELAXNG_INTERLEAVE;
1741
1742 if (ctxt->interleaves == NULL)
1743 ctxt->interleaves = xmlHashCreate(10);
1744 if (ctxt->interleaves == NULL) {
1745 if (ctxt->error != NULL)
1746 ctxt->error(ctxt->userData,
1747 "Failed to create interleaves hash table\n");
1748 ctxt->nbErrors++;
1749 } else {
1750 char name[32];
1751
1752 snprintf(name, 32, "interleave%d", ctxt->nbInterleaves++);
1753 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST name, def) < 0) {
1754 if (ctxt->error != NULL)
1755 ctxt->error(ctxt->userData,
1756 "Failed to add %s to hash table\n", name);
1757 ctxt->nbErrors++;
1758 }
1759 }
1760 child = node->children;
1761 while (child != NULL) {
1762 if (IS_RELAXNG(child, "element")) {
1763 cur = xmlRelaxNGParseElement(ctxt, child);
1764 } else {
1765 cur = xmlRelaxNGParsePattern(ctxt, child);
1766 }
1767 if (cur != NULL) {
1768 cur->parent = def;
1769 if (last == NULL) {
1770 def->content = last = cur;
1771 } else {
1772 last->next = cur;
1773 last = cur;
1774 }
1775 }
1776 child = child->next;
1777 }
1778
1779 return(def);
1780}
Daniel Veillard6eadf632003-01-23 18:29:16 +00001781
1782/**
Daniel Veillard276be4a2003-01-24 01:03:34 +00001783 * xmlRelaxNGParseDefine:
1784 * @ctxt: a Relax-NG parser context
1785 * @node: the define node
1786 *
1787 * parse the content of a RelaxNG define element node.
1788 *
1789 * Returns the definition pointer or NULL in case of error.
1790 */
1791static int
1792xmlRelaxNGParseDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1793 xmlChar *name;
1794 int ret = 0, tmp;
1795 xmlRelaxNGDefinePtr def;
1796 const xmlChar *olddefine;
1797
1798 name = xmlGetProp(node, BAD_CAST "name");
1799 if (name == NULL) {
1800 if (ctxt->error != NULL)
1801 ctxt->error(ctxt->userData,
1802 "define has no name\n");
1803 ctxt->nbErrors++;
1804 } else {
1805 def = xmlRelaxNGNewDefine(ctxt, node);
1806 if (def == NULL) {
1807 xmlFree(name);
1808 return(-1);
1809 }
1810 def->type = XML_RELAXNG_DEF;
1811 def->name = name;
1812 if (node->children == NULL) {
1813 if (ctxt->error != NULL)
1814 ctxt->error(ctxt->userData,
1815 "define has no children\n");
1816 ctxt->nbErrors++;
1817 } else {
1818 olddefine = ctxt->define;
1819 ctxt->define = name;
1820 def->content = xmlRelaxNGParsePatterns(ctxt,
1821 node->children);
1822 ctxt->define = olddefine;
1823 }
1824 if (ctxt->grammar->defs == NULL)
1825 ctxt->grammar->defs = xmlHashCreate(10);
1826 if (ctxt->grammar->defs == NULL) {
1827 if (ctxt->error != NULL)
1828 ctxt->error(ctxt->userData,
1829 "Could not create definition hash\n");
1830 ctxt->nbErrors++;
1831 ret = -1;
1832 xmlRelaxNGFreeDefine(def);
1833 } else {
1834 tmp = xmlHashAddEntry(ctxt->grammar->defs, name, def);
1835 if (tmp < 0) {
1836 TODO
1837 /* store and implement 4.17 on combining */
1838 ctxt->nbErrors++;
1839 ret = -1;
1840 xmlRelaxNGFreeDefine(def);
1841 }
1842 }
1843 }
1844 return(ret);
1845}
1846
1847/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00001848 * xmlRelaxNGParsePattern:
1849 * @ctxt: a Relax-NG parser context
1850 * @node: the pattern node.
1851 *
1852 * parse the content of a RelaxNG pattern node.
1853 *
Daniel Veillard276be4a2003-01-24 01:03:34 +00001854 * Returns the definition pointer or NULL in case of error or if no
1855 * pattern is generated.
Daniel Veillard6eadf632003-01-23 18:29:16 +00001856 */
1857static xmlRelaxNGDefinePtr
1858xmlRelaxNGParsePattern(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1859 xmlRelaxNGDefinePtr def = NULL;
1860
1861 if (IS_RELAXNG(node, "element")) {
1862 def = xmlRelaxNGParseElement(ctxt, node);
1863 } else if (IS_RELAXNG(node, "attribute")) {
1864 def = xmlRelaxNGParseAttribute(ctxt, node);
1865 } else if (IS_RELAXNG(node, "empty")) {
1866 def = xmlRelaxNGNewDefine(ctxt, node);
1867 if (def == NULL)
1868 return(NULL);
1869 def->type = XML_RELAXNG_EMPTY;
1870 } else if (IS_RELAXNG(node, "text")) {
1871 def = xmlRelaxNGNewDefine(ctxt, node);
1872 if (def == NULL)
1873 return(NULL);
1874 def->type = XML_RELAXNG_TEXT;
1875 if (node->children != NULL) {
1876 if (ctxt->error != NULL)
1877 ctxt->error(ctxt->userData, "text: had a child node\n");
1878 ctxt->nbErrors++;
1879 }
1880 } else if (IS_RELAXNG(node, "zeroOrMore")) {
1881 def = xmlRelaxNGNewDefine(ctxt, node);
1882 if (def == NULL)
1883 return(NULL);
1884 def->type = XML_RELAXNG_ZEROORMORE;
1885 def->content = xmlRelaxNGParsePatterns(ctxt, node->children);
1886 } else if (IS_RELAXNG(node, "oneOrMore")) {
1887 def = xmlRelaxNGNewDefine(ctxt, node);
1888 if (def == NULL)
1889 return(NULL);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001890 def->type = XML_RELAXNG_ONEORMORE;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001891 def->content = xmlRelaxNGParsePatterns(ctxt, node->children);
1892 } else if (IS_RELAXNG(node, "optional")) {
1893 def = xmlRelaxNGNewDefine(ctxt, node);
1894 if (def == NULL)
1895 return(NULL);
1896 def->type = XML_RELAXNG_OPTIONAL;
1897 def->content = xmlRelaxNGParsePatterns(ctxt, node->children);
1898 } else if (IS_RELAXNG(node, "choice")) {
1899 def = xmlRelaxNGNewDefine(ctxt, node);
1900 if (def == NULL)
1901 return(NULL);
1902 def->type = XML_RELAXNG_CHOICE;
1903 def->content = xmlRelaxNGParsePatterns(ctxt, node->children);
1904 } else if (IS_RELAXNG(node, "group")) {
1905 def = xmlRelaxNGNewDefine(ctxt, node);
1906 if (def == NULL)
1907 return(NULL);
1908 def->type = XML_RELAXNG_GROUP;
1909 def->content = xmlRelaxNGParsePatterns(ctxt, node->children);
1910 } else if (IS_RELAXNG(node, "ref")) {
1911 def = xmlRelaxNGNewDefine(ctxt, node);
1912 if (def == NULL)
1913 return(NULL);
1914 def->type = XML_RELAXNG_REF;
1915 def->name = xmlGetProp(node, BAD_CAST "name");
1916 if (def->name == NULL) {
1917 if (ctxt->error != NULL)
1918 ctxt->error(ctxt->userData,
1919 "ref has no name\n");
1920 ctxt->nbErrors++;
Daniel Veillard276be4a2003-01-24 01:03:34 +00001921 } else {
1922 if ((ctxt->define != NULL) &&
1923 (xmlStrEqual(ctxt->define, def->name))) {
1924 if (ctxt->error != NULL)
1925 ctxt->error(ctxt->userData,
1926 "Recursive reference to %s not in an element\n",
1927 def->name);
1928 ctxt->nbErrors++;
1929 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00001930 }
1931 if (node->children != NULL) {
1932 if (ctxt->error != NULL)
1933 ctxt->error(ctxt->userData,
1934 "ref is not empty\n");
1935 ctxt->nbErrors++;
1936 }
1937 if (ctxt->grammar->refs == NULL)
1938 ctxt->grammar->refs = xmlHashCreate(10);
1939 if (ctxt->grammar->refs == NULL) {
1940 if (ctxt->error != NULL)
1941 ctxt->error(ctxt->userData,
1942 "Could not create references hash\n");
1943 ctxt->nbErrors++;
1944 xmlRelaxNGFreeDefine(def);
1945 def = NULL;
1946 } else {
1947 int tmp;
1948
1949 tmp = xmlHashAddEntry(ctxt->grammar->refs, def->name, def);
1950 if (tmp < 0) {
1951 xmlRelaxNGDefinePtr prev;
1952
1953 prev = (xmlRelaxNGDefinePtr)
1954 xmlHashLookup(ctxt->grammar->refs, def->name);
1955 if (prev == NULL) {
1956 if (ctxt->error != NULL)
1957 ctxt->error(ctxt->userData,
1958 "Internal error refs definitions '%s'\n",
1959 def->name);
1960 ctxt->nbErrors++;
1961 xmlRelaxNGFreeDefine(def);
1962 def = NULL;
1963 } else {
1964 def->nextHash = prev->nextHash;
1965 prev->nextHash = def;
1966 }
1967 }
1968 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001969 } else if (IS_RELAXNG(node, "data")) {
1970 def = xmlRelaxNGParseData(ctxt, node);
Daniel Veillard276be4a2003-01-24 01:03:34 +00001971 } else if (IS_RELAXNG(node, "define")) {
1972 xmlRelaxNGParseDefine(ctxt, node);
1973 def = NULL;
Daniel Veillardedc91922003-01-26 00:52:04 +00001974 } else if (IS_RELAXNG(node, "value")) {
1975 def = xmlRelaxNGParseValue(ctxt, node);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001976 } else if (IS_RELAXNG(node, "list")) {
1977 def = xmlRelaxNGNewDefine(ctxt, node);
1978 if (def == NULL)
1979 return(NULL);
1980 def->type = XML_RELAXNG_LIST;
1981 def->content = xmlRelaxNGParsePatterns(ctxt, node->children);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001982 } else if (IS_RELAXNG(node, "interleave")) {
1983 def = xmlRelaxNGParseInterleave(ctxt, node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001984 } else if (IS_RELAXNG(node, "externalRef")) {
1985 xmlRelaxNGDocumentPtr docu;
1986 xmlNodePtr root;
1987
1988 docu = node->_private;
1989 if (docu != NULL) {
1990 def = xmlRelaxNGNewDefine(ctxt, node);
1991 if (def == NULL)
1992 return(NULL);
1993 def->type = XML_RELAXNG_EXTERNALREF;
1994
1995 if (docu->content == NULL) {
1996 /*
1997 * Then do the parsing for good
1998 */
1999 root = xmlDocGetRootElement(docu->doc);
2000 if (root == NULL) {
2001 if (ctxt->error != NULL)
2002 ctxt->error(ctxt->userData,
2003 "xmlRelaxNGParse: %s is empty\n",
2004 ctxt->URL);
2005 ctxt->nbErrors++;
2006 return (NULL);
2007 }
2008 docu->schema = xmlRelaxNGParseDocument(ctxt, root);
2009 if ((docu->schema != NULL) &&
2010 (docu->schema->topgrammar != NULL)) {
2011 docu->content = docu->schema->topgrammar->start;
2012 }
2013 }
2014 def->content = docu->content;
2015 } else {
2016 def = NULL;
2017 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002018 } else {
2019 TODO
2020 }
2021 return(def);
2022}
2023
2024/**
2025 * xmlRelaxNGParseAttribute:
2026 * @ctxt: a Relax-NG parser context
2027 * @node: the element node
2028 *
2029 * parse the content of a RelaxNG attribute node.
2030 *
2031 * Returns the definition pointer or NULL in case of error.
2032 */
2033static xmlRelaxNGDefinePtr
2034xmlRelaxNGParseAttribute(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2035 xmlRelaxNGDefinePtr ret, cur, last;
2036 xmlNodePtr child;
2037 xmlChar *val;
2038 int old_flags;
2039
2040 ret = xmlRelaxNGNewDefine(ctxt, node);
2041 if (ret == NULL)
2042 return(NULL);
2043 ret->type = XML_RELAXNG_ATTRIBUTE;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002044 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002045 child = node->children;
2046 if (child == NULL) {
2047 if (ctxt->error != NULL)
2048 ctxt->error(ctxt->userData,
2049 "xmlRelaxNGParseattribute: attribute has no children\n");
2050 ctxt->nbErrors++;
2051 return(ret);
2052 }
2053 old_flags = ctxt->flags;
2054 ctxt->flags |= XML_RELAXNG_IN_ATTRIBUTE;
2055 if (IS_RELAXNG(child, "name")) {
2056 val = xmlNodeGetContent(child);
2057 ret->name = val;
2058 val = xmlGetProp(child, BAD_CAST "ns");
2059 ret->ns = val;
2060 } else if (IS_RELAXNG(child, "anyName")) {
2061 TODO
2062 } else if (IS_RELAXNG(child, "nsName")) {
2063 TODO
2064 } else if (IS_RELAXNG(child, "choice")) {
2065 TODO
2066 } else {
2067 if (ctxt->error != NULL)
2068 ctxt->error(ctxt->userData,
2069 "element: expecting name, anyName, nsName or choice : got %s\n",
2070 child->name);
2071 ctxt->nbErrors++;
2072 ctxt->flags = old_flags;
2073 return(ret);
2074 }
2075 child = child->next;
2076 last = NULL;
2077 while (child != NULL) {
2078 cur = xmlRelaxNGParsePattern(ctxt, child);
2079 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002080 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002081 switch (cur->type) {
2082 case XML_RELAXNG_EMPTY:
2083 case XML_RELAXNG_NOT_ALLOWED:
2084 case XML_RELAXNG_TEXT:
2085 case XML_RELAXNG_ELEMENT:
2086 case XML_RELAXNG_DATATYPE:
2087 case XML_RELAXNG_VALUE:
2088 case XML_RELAXNG_LIST:
2089 case XML_RELAXNG_REF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002090 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00002091 case XML_RELAXNG_DEF:
2092 case XML_RELAXNG_ONEORMORE:
2093 case XML_RELAXNG_ZEROORMORE:
2094 case XML_RELAXNG_OPTIONAL:
2095 case XML_RELAXNG_CHOICE:
2096 case XML_RELAXNG_GROUP:
2097 case XML_RELAXNG_INTERLEAVE:
2098 if (last == NULL) {
2099 ret->content = last = cur;
2100 } else {
2101 if ((last->type == XML_RELAXNG_ELEMENT) &&
2102 (ret->content == last)) {
2103 ret->content = xmlRelaxNGNewDefine(ctxt, node);
2104 if (ret->content != NULL) {
2105 ret->content->type = XML_RELAXNG_GROUP;
2106 ret->content->content = last;
2107 } else {
2108 ret->content = last;
2109 }
2110 }
2111 last->next = cur;
2112 last = cur;
2113 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002114 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002115 break;
2116 case XML_RELAXNG_ATTRIBUTE:
2117 cur->next = ret->attrs;
2118 ret->attrs = cur;
2119 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002120 case XML_RELAXNG_START:
2121 TODO
2122 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002123 }
2124 }
2125 child = child->next;
2126 }
2127 ctxt->flags = old_flags;
2128 return(ret);
2129}
2130
2131/**
2132 * xmlRelaxNGParseElement:
2133 * @ctxt: a Relax-NG parser context
2134 * @node: the element node
2135 *
2136 * parse the content of a RelaxNG element node.
2137 *
2138 * Returns the definition pointer or NULL in case of error.
2139 */
2140static xmlRelaxNGDefinePtr
2141xmlRelaxNGParseElement(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2142 xmlRelaxNGDefinePtr ret, cur, last;
2143 xmlNodePtr child;
2144 xmlChar *val;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002145 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002146
2147 ret = xmlRelaxNGNewDefine(ctxt, node);
2148 if (ret == NULL)
2149 return(NULL);
2150 ret->type = XML_RELAXNG_ELEMENT;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002151 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002152 child = node->children;
2153 if (child == NULL) {
2154 if (ctxt->error != NULL)
2155 ctxt->error(ctxt->userData,
2156 "xmlRelaxNGParseElement: element has no children\n");
2157 ctxt->nbErrors++;
2158 return(ret);
2159 }
2160 if (IS_RELAXNG(child, "name")) {
2161 val = xmlNodeGetContent(child);
2162 ret->name = val;
2163 val = xmlGetProp(child, BAD_CAST "ns");
2164 ret->ns = val;
2165 } else if (IS_RELAXNG(child, "anyName")) {
2166 TODO
2167 } else if (IS_RELAXNG(child, "nsName")) {
2168 TODO
2169 } else if (IS_RELAXNG(child, "choice")) {
2170 TODO
2171 } else {
2172 if (ctxt->error != NULL)
2173 ctxt->error(ctxt->userData,
2174 "element: expecting name, anyName, nsName or choice : got %s\n",
2175 child->name);
2176 ctxt->nbErrors++;
2177 return(ret);
2178 }
2179 child = child->next;
2180 if (child == NULL) {
2181 if (ctxt->error != NULL)
2182 ctxt->error(ctxt->userData,
2183 "xmlRelaxNGParseElement: element has no content\n");
2184 ctxt->nbErrors++;
2185 return(ret);
2186 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002187 olddefine = ctxt->define;
2188 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002189 last = NULL;
2190 while (child != NULL) {
2191 cur = xmlRelaxNGParsePattern(ctxt, child);
2192 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002193 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002194 switch (cur->type) {
2195 case XML_RELAXNG_EMPTY:
2196 case XML_RELAXNG_NOT_ALLOWED:
2197 case XML_RELAXNG_TEXT:
2198 case XML_RELAXNG_ELEMENT:
2199 case XML_RELAXNG_DATATYPE:
2200 case XML_RELAXNG_VALUE:
2201 case XML_RELAXNG_LIST:
2202 case XML_RELAXNG_REF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002203 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00002204 case XML_RELAXNG_DEF:
2205 case XML_RELAXNG_ZEROORMORE:
2206 case XML_RELAXNG_ONEORMORE:
2207 case XML_RELAXNG_OPTIONAL:
2208 case XML_RELAXNG_CHOICE:
2209 case XML_RELAXNG_GROUP:
2210 case XML_RELAXNG_INTERLEAVE:
2211 if (last == NULL) {
2212 ret->content = last = cur;
2213 } else {
2214 if ((last->type == XML_RELAXNG_ELEMENT) &&
2215 (ret->content == last)) {
2216 ret->content = xmlRelaxNGNewDefine(ctxt, node);
2217 if (ret->content != NULL) {
2218 ret->content->type = XML_RELAXNG_GROUP;
2219 ret->content->content = last;
2220 } else {
2221 ret->content = last;
2222 }
2223 }
2224 last->next = cur;
2225 last = cur;
2226 }
2227 break;
2228 case XML_RELAXNG_ATTRIBUTE:
2229 cur->next = ret->attrs;
2230 ret->attrs = cur;
2231 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002232 case XML_RELAXNG_START:
2233 TODO
2234 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002235 }
2236 }
2237 child = child->next;
2238 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002239 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002240 return(ret);
2241}
2242
2243/**
2244 * xmlRelaxNGParsePatterns:
2245 * @ctxt: a Relax-NG parser context
2246 * @nodes: list of nodes
2247 *
2248 * parse the content of a RelaxNG start node.
2249 *
2250 * Returns the definition pointer or NULL in case of error.
2251 */
2252static xmlRelaxNGDefinePtr
2253xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002254 xmlRelaxNGDefinePtr def = NULL, last = NULL, cur, parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002255
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002256 parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002257 while (nodes != NULL) {
2258 if (IS_RELAXNG(nodes, "element")) {
2259 cur = xmlRelaxNGParseElement(ctxt, nodes);
2260 if (def == NULL) {
2261 def = last = cur;
2262 } else {
2263 if ((def->type == XML_RELAXNG_ELEMENT) && (def == last)) {
2264 def = xmlRelaxNGNewDefine(ctxt, nodes);
2265 def->type = XML_RELAXNG_GROUP;
2266 def->content = last;
2267 }
2268 last->next = cur;
2269 last = cur;
2270 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002271 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002272 } else {
2273 cur = xmlRelaxNGParsePattern(ctxt, nodes);
2274 if (def == NULL) {
2275 def = last = cur;
2276 } else {
2277 last->next = cur;
2278 last = cur;
2279 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002280 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002281 }
2282 nodes = nodes->next;
2283 }
2284 return(def);
2285}
2286
2287/**
2288 * xmlRelaxNGParseStart:
2289 * @ctxt: a Relax-NG parser context
2290 * @nodes: start children nodes
2291 *
2292 * parse the content of a RelaxNG start node.
2293 *
2294 * Returns 0 in case of success, -1 in case of error
2295 */
2296static int
2297xmlRelaxNGParseStart(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
2298 int ret = 0;
2299 xmlRelaxNGDefinePtr def = NULL;
2300
2301 while (nodes != NULL) {
2302 if (IS_RELAXNG(nodes, "empty")) {
2303 TODO
2304 xmlElemDump(stdout, nodes->doc, nodes);
2305 } else if (IS_RELAXNG(nodes, "notAllowed")) {
2306 TODO
2307 xmlElemDump(stdout, nodes->doc, nodes);
2308 } else {
2309 def = xmlRelaxNGParsePatterns(ctxt, nodes);
2310 ctxt->grammar->start = def;
2311 }
2312 nodes = nodes->next;
2313 }
2314 return(ret);
2315}
2316
2317/**
2318 * xmlRelaxNGParseGrammarContent:
2319 * @ctxt: a Relax-NG parser context
2320 * @nodes: grammar children nodes
2321 *
2322 * parse the content of a RelaxNG grammar node.
2323 *
2324 * Returns 0 in case of success, -1 in case of error
2325 */
2326static int
2327xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt
2328 ATTRIBUTE_UNUSED, xmlNodePtr nodes)
2329{
Daniel Veillard276be4a2003-01-24 01:03:34 +00002330 int ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002331
2332 if (nodes == NULL) {
2333 if (ctxt->error != NULL)
2334 ctxt->error(ctxt->userData,
2335 "grammar has no children\n");
2336 ctxt->nbErrors++;
2337 return(-1);
2338 }
2339 if (IS_RELAXNG(nodes, "start")) {
2340 if (nodes->children == NULL) {
2341 if (ctxt->error != NULL)
2342 ctxt->error(ctxt->userData,
2343 "grammar has no children\n");
2344 ctxt->nbErrors++;
2345 } else {
2346 xmlRelaxNGParseStart(ctxt, nodes->children);
2347 }
2348 nodes = nodes->next;
2349 } else {
2350 if (ctxt->error != NULL)
2351 ctxt->error(ctxt->userData,
2352 "grammar first child must be a <start>\n");
2353 ctxt->nbErrors++;
2354 return(-1);
2355 }
2356 while (nodes != NULL) {
2357 if (IS_RELAXNG(nodes, "define")) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00002358 ret = xmlRelaxNGParseDefine(ctxt, nodes);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002359 } else {
2360 if (ctxt->error != NULL)
2361 ctxt->error(ctxt->userData,
2362 "grammar allows onlys <define> child after <start>\n");
2363 ctxt->nbErrors++;
2364 ret = -1;
2365 }
2366 nodes = nodes->next;
2367 }
2368 return (ret);
2369}
2370
2371/**
2372 * xmlRelaxNGCheckReference:
2373 * @ref: the ref
2374 * @ctxt: a Relax-NG parser context
2375 * @name: the name associated to the defines
2376 *
2377 * Applies the 4.17. combine attribute rule for all the define
2378 * element of a given grammar using the same name.
2379 */
2380static void
2381xmlRelaxNGCheckReference(xmlRelaxNGDefinePtr ref,
2382 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
2383 xmlRelaxNGGrammarPtr grammar;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002384 xmlRelaxNGDefinePtr def, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002385
2386 grammar = ctxt->grammar;
2387 if (grammar == NULL) {
2388 if (ctxt->error != NULL)
2389 ctxt->error(ctxt->userData,
2390 "Internal error: no grammar in CheckReference %s\n",
2391 name);
2392 ctxt->nbErrors++;
2393 return;
2394 }
2395 if (ref->content != NULL) {
2396 if (ctxt->error != NULL)
2397 ctxt->error(ctxt->userData,
2398 "Internal error: reference has content in CheckReference %s\n",
2399 name);
2400 ctxt->nbErrors++;
2401 return;
2402 }
2403 if (grammar->defs != NULL) {
2404 def = xmlHashLookup(grammar->defs, name);
2405 if (def != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00002406 cur = ref;
2407 while (cur != NULL) {
2408 cur->content = def;
2409 cur = cur->nextHash;
2410 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002411 } else {
2412 TODO
2413 }
2414 }
2415 /*
2416 * TODO: make a closure and verify there is no loop !
2417 */
2418}
2419
2420/**
2421 * xmlRelaxNGCheckCombine:
2422 * @define: the define(s) list
2423 * @ctxt: a Relax-NG parser context
2424 * @name: the name associated to the defines
2425 *
2426 * Applies the 4.17. combine attribute rule for all the define
2427 * element of a given grammar using the same name.
2428 */
2429static void
2430xmlRelaxNGCheckCombine(xmlRelaxNGDefinePtr define,
2431 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
2432 xmlChar *combine;
2433 int choiceOrInterleave = -1;
2434 int missing = 0;
2435 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
2436
2437 if (define->nextHash == NULL)
2438 return;
2439 cur = define;
2440 while (cur != NULL) {
2441 combine = xmlGetProp(cur->node, BAD_CAST "combine");
2442 if (combine != NULL) {
2443 if (xmlStrEqual(combine, BAD_CAST "choice")) {
2444 if (choiceOrInterleave == -1)
2445 choiceOrInterleave = 1;
2446 else if (choiceOrInterleave == 0) {
2447 if (ctxt->error != NULL)
2448 ctxt->error(ctxt->userData,
2449 "Defines for %s use both 'choice' and 'interleave'\n",
2450 name);
2451 ctxt->nbErrors++;
2452 }
2453 } else if (xmlStrEqual(combine, BAD_CAST "choice")) {
2454 if (choiceOrInterleave == -1)
2455 choiceOrInterleave = 0;
2456 else if (choiceOrInterleave == 1) {
2457 if (ctxt->error != NULL)
2458 ctxt->error(ctxt->userData,
2459 "Defines for %s use both 'choice' and 'interleave'\n",
2460 name);
2461 ctxt->nbErrors++;
2462 }
2463 } else {
2464 if (ctxt->error != NULL)
2465 ctxt->error(ctxt->userData,
2466 "Defines for %s use unknown combine value '%s''\n",
2467 name, combine);
2468 ctxt->nbErrors++;
2469 }
2470 xmlFree(combine);
2471 } else {
2472 if (missing == 0)
2473 missing = 1;
2474 else {
2475 if (ctxt->error != NULL)
2476 ctxt->error(ctxt->userData,
2477 "Some defines for %s lacks the combine attribute\n",
2478 name);
2479 ctxt->nbErrors++;
2480 }
2481 }
2482
2483 cur = cur->nextHash;
2484 }
2485#ifdef DEBUG
2486 xmlGenericError(xmlGenericErrorContext,
2487 "xmlRelaxNGCheckCombine(): merging %s defines: %d\n",
2488 name, choiceOrInterleave);
2489#endif
2490 if (choiceOrInterleave == -1)
2491 choiceOrInterleave = 0;
2492 cur = xmlRelaxNGNewDefine(ctxt, define->node);
2493 if (cur == NULL)
2494 return;
2495 if (choiceOrInterleave == 0)
2496 cur->type = XML_RELAXNG_CHOICE;
2497 else
2498 cur->type = XML_RELAXNG_INTERLEAVE;
2499 tmp = define;
2500 last = NULL;
2501 while (tmp != NULL) {
2502 if (tmp->content != NULL) {
2503 if (tmp->content->next != NULL) {
2504 /*
2505 * we need first to create a wrapper.
2506 */
2507 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
2508 if (tmp2 == NULL)
2509 break;
2510 tmp2->type = XML_RELAXNG_GROUP;
2511 tmp2->content = tmp->content;
2512 } else {
2513 tmp2 = tmp->content;
2514 }
2515 if (last == NULL) {
2516 cur->content = tmp2;
2517 } else {
2518 last->next = tmp2;
2519 }
2520 last = tmp2;
2521 tmp->content = NULL;
2522 }
2523 tmp = tmp->nextHash;
2524 }
2525 define->content = cur;
2526}
2527
2528/**
2529 * xmlRelaxNGCombineStart:
2530 * @ctxt: a Relax-NG parser context
2531 * @grammar: the grammar
2532 *
2533 * Applies the 4.17. combine rule for all the start
2534 * element of a given grammar.
2535 */
2536static void
2537xmlRelaxNGCombineStart(xmlRelaxNGParserCtxtPtr ctxt,
2538 xmlRelaxNGGrammarPtr grammar) {
2539 xmlRelaxNGDefinePtr starts;
2540 xmlChar *combine;
2541 int choiceOrInterleave = -1;
2542 int missing = 0;
2543 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
2544
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002545 starts = grammar->startList;
2546 if ((starts == NULL) || (starts->nextHash == NULL))
Daniel Veillard6eadf632003-01-23 18:29:16 +00002547 return;
2548 cur = starts;
2549 while (cur != NULL) {
2550 combine = xmlGetProp(cur->node, BAD_CAST "combine");
2551 if (combine != NULL) {
2552 if (xmlStrEqual(combine, BAD_CAST "choice")) {
2553 if (choiceOrInterleave == -1)
2554 choiceOrInterleave = 1;
2555 else if (choiceOrInterleave == 0) {
2556 if (ctxt->error != NULL)
2557 ctxt->error(ctxt->userData,
2558 "<start> use both 'choice' and 'interleave'\n");
2559 ctxt->nbErrors++;
2560 }
2561 } else if (xmlStrEqual(combine, BAD_CAST "choice")) {
2562 if (choiceOrInterleave == -1)
2563 choiceOrInterleave = 0;
2564 else if (choiceOrInterleave == 1) {
2565 if (ctxt->error != NULL)
2566 ctxt->error(ctxt->userData,
2567 "<start> use both 'choice' and 'interleave'\n");
2568 ctxt->nbErrors++;
2569 }
2570 } else {
2571 if (ctxt->error != NULL)
2572 ctxt->error(ctxt->userData,
2573 "<start> uses unknown combine value '%s''\n", combine);
2574 ctxt->nbErrors++;
2575 }
2576 xmlFree(combine);
2577 } else {
2578 if (missing == 0)
2579 missing = 1;
2580 else {
2581 if (ctxt->error != NULL)
2582 ctxt->error(ctxt->userData,
2583 "Some <start> elements lacks the combine attribute\n");
2584 ctxt->nbErrors++;
2585 }
2586 }
2587
2588 cur = cur->nextHash;
2589 }
2590#ifdef DEBUG
2591 xmlGenericError(xmlGenericErrorContext,
2592 "xmlRelaxNGCombineStart(): merging <start>: %d\n",
2593 choiceOrInterleave);
2594#endif
2595 if (choiceOrInterleave == -1)
2596 choiceOrInterleave = 0;
2597 cur = xmlRelaxNGNewDefine(ctxt, starts->node);
2598 if (cur == NULL)
2599 return;
2600 if (choiceOrInterleave == 0)
2601 cur->type = XML_RELAXNG_CHOICE;
2602 else
2603 cur->type = XML_RELAXNG_INTERLEAVE;
2604 tmp = starts;
2605 last = NULL;
2606 while (tmp != NULL) {
2607 if (tmp->content != NULL) {
2608 if (tmp->content->next != NULL) {
2609 /*
2610 * we need first to create a wrapper.
2611 */
2612 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
2613 if (tmp2 == NULL)
2614 break;
2615 tmp2->type = XML_RELAXNG_GROUP;
2616 tmp2->content = tmp->content;
2617 } else {
2618 tmp2 = tmp->content;
2619 }
2620 if (last == NULL) {
2621 cur->content = tmp2;
2622 } else {
2623 last->next = tmp2;
2624 }
2625 last = tmp2;
2626 tmp->content = NULL;
2627 }
2628 tmp = tmp->nextHash;
2629 }
2630 starts->content = cur;
2631}
2632
2633/**
2634 * xmlRelaxNGParseGrammar:
2635 * @ctxt: a Relax-NG parser context
2636 * @nodes: grammar children nodes
2637 *
2638 * parse a Relax-NG <grammar> node
2639 *
2640 * Returns the internal xmlRelaxNGGrammarPtr built or
2641 * NULL in case of error
2642 */
2643static xmlRelaxNGGrammarPtr
2644xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
2645 xmlRelaxNGGrammarPtr ret, tmp, old;
2646
Daniel Veillard6eadf632003-01-23 18:29:16 +00002647 ret = xmlRelaxNGNewGrammar(ctxt);
2648 if (ret == NULL)
2649 return(NULL);
2650
2651 /*
2652 * Link the new grammar in the tree
2653 */
2654 ret->parent = ctxt->grammar;
2655 if (ctxt->grammar != NULL) {
2656 tmp = ctxt->grammar->children;
2657 if (tmp == NULL) {
2658 ctxt->grammar->children = ret;
2659 } else {
2660 while (tmp->next != NULL)
2661 tmp = tmp->next;
2662 tmp->next = ret;
2663 }
2664 }
2665
2666 old = ctxt->grammar;
2667 ctxt->grammar = ret;
2668 xmlRelaxNGParseGrammarContent(ctxt, nodes);
2669 ctxt->grammar = ret;
2670
2671 /*
2672 * Apply 4.17 mergingd rules to defines and starts
2673 */
2674 xmlRelaxNGCombineStart(ctxt, ret);
2675 if (ret->defs != NULL) {
2676 xmlHashScan(ret->defs, (xmlHashScanner) xmlRelaxNGCheckCombine,
2677 ctxt);
2678 }
2679
2680 /*
2681 * link together defines and refs in this grammar
2682 */
2683 if (ret->refs != NULL) {
2684 xmlHashScan(ret->refs, (xmlHashScanner) xmlRelaxNGCheckReference,
2685 ctxt);
2686 }
2687 ctxt->grammar = old;
2688 return(ret);
2689}
2690
2691/**
2692 * xmlRelaxNGParseDocument:
2693 * @ctxt: a Relax-NG parser context
2694 * @node: the root node of the RelaxNG schema
2695 *
2696 * parse a Relax-NG definition resource and build an internal
2697 * xmlRelaxNG struture which can be used to validate instances.
2698 *
2699 * Returns the internal XML RelaxNG structure built or
2700 * NULL in case of error
2701 */
2702static xmlRelaxNGPtr
2703xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2704 xmlRelaxNGPtr schema = NULL;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002705 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002706
2707 if ((ctxt == NULL) || (node == NULL))
2708 return (NULL);
2709
2710 schema = xmlRelaxNGNewRelaxNG(ctxt);
2711 if (schema == NULL)
2712 return(NULL);
2713
Daniel Veillard276be4a2003-01-24 01:03:34 +00002714 olddefine = ctxt->define;
2715 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002716 if (IS_RELAXNG(node, "grammar")) {
2717 schema->topgrammar = xmlRelaxNGParseGrammar(ctxt, node->children);
2718 } else {
2719 schema->topgrammar = xmlRelaxNGNewGrammar(ctxt);
2720 if (schema->topgrammar == NULL) {
2721 return(schema);
2722 }
2723 schema->topgrammar->parent = NULL;
2724 ctxt->grammar = schema->topgrammar;
2725 xmlRelaxNGParseStart(ctxt, node);
2726 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002727 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002728
2729#ifdef DEBUG
2730 if (schema == NULL)
2731 xmlGenericError(xmlGenericErrorContext,
2732 "xmlRelaxNGParseDocument() failed\n");
2733#endif
2734
2735 return (schema);
2736}
2737
2738/************************************************************************
2739 * *
2740 * Reading RelaxNGs *
2741 * *
2742 ************************************************************************/
2743
2744/**
2745 * xmlRelaxNGNewParserCtxt:
2746 * @URL: the location of the schema
2747 *
2748 * Create an XML RelaxNGs parse context for that file/resource expected
2749 * to contain an XML RelaxNGs file.
2750 *
2751 * Returns the parser context or NULL in case of error
2752 */
2753xmlRelaxNGParserCtxtPtr
2754xmlRelaxNGNewParserCtxt(const char *URL) {
2755 xmlRelaxNGParserCtxtPtr ret;
2756
2757 if (URL == NULL)
2758 return(NULL);
2759
2760 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
2761 if (ret == NULL) {
2762 xmlGenericError(xmlGenericErrorContext,
2763 "Failed to allocate new schama parser context for %s\n", URL);
2764 return (NULL);
2765 }
2766 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
2767 ret->URL = xmlStrdup((const xmlChar *)URL);
2768 return (ret);
2769}
2770
2771/**
2772 * xmlRelaxNGNewMemParserCtxt:
2773 * @buffer: a pointer to a char array containing the schemas
2774 * @size: the size of the array
2775 *
2776 * Create an XML RelaxNGs parse context for that memory buffer expected
2777 * to contain an XML RelaxNGs file.
2778 *
2779 * Returns the parser context or NULL in case of error
2780 */
2781xmlRelaxNGParserCtxtPtr
2782xmlRelaxNGNewMemParserCtxt(const char *buffer, int size) {
2783 xmlRelaxNGParserCtxtPtr ret;
2784
2785 if ((buffer == NULL) || (size <= 0))
2786 return(NULL);
2787
2788 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
2789 if (ret == NULL) {
2790 xmlGenericError(xmlGenericErrorContext,
2791 "Failed to allocate new schama parser context\n");
2792 return (NULL);
2793 }
2794 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
2795 ret->buffer = buffer;
2796 ret->size = size;
2797 return (ret);
2798}
2799
2800/**
2801 * xmlRelaxNGFreeParserCtxt:
2802 * @ctxt: the schema parser context
2803 *
2804 * Free the resources associated to the schema parser context
2805 */
2806void
2807xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxtPtr ctxt) {
2808 if (ctxt == NULL)
2809 return;
2810 if (ctxt->URL != NULL)
2811 xmlFree(ctxt->URL);
2812 if (ctxt->doc != NULL)
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002813 xmlFreeDoc(ctxt->document);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002814 if (ctxt->interleaves != NULL)
2815 xmlHashFree(ctxt->interleaves, NULL);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002816 if (ctxt->documents != NULL)
2817 xmlHashFree(ctxt->documents, (xmlHashDeallocator)
2818 xmlRelaxNGFreeDocument);
2819 if (ctxt->docTab != NULL)
2820 xmlFree(ctxt->docTab);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002821 xmlFree(ctxt);
2822}
2823
Daniel Veillard6eadf632003-01-23 18:29:16 +00002824/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002825 * xmlRelaxNGCleanupDoc:
2826 * @ctxt: a Relax-NG parser context
2827 * @doc: an xmldocPtr document pointer
Daniel Veillard6eadf632003-01-23 18:29:16 +00002828 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002829 * Cleanup the document from unwanted nodes for parsing, resolve
2830 * Include and externalRef lookups.
Daniel Veillard6eadf632003-01-23 18:29:16 +00002831 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002832 * Returns the cleaned up document or NULL in case of error
Daniel Veillard6eadf632003-01-23 18:29:16 +00002833 */
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002834static xmlDocPtr
2835xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt, xmlDocPtr doc) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00002836 xmlNodePtr root, cur, delete;
2837
Daniel Veillard6eadf632003-01-23 18:29:16 +00002838 /*
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002839 * Extract the root
Daniel Veillard6eadf632003-01-23 18:29:16 +00002840 */
2841 root = xmlDocGetRootElement(doc);
2842 if (root == NULL) {
2843 if (ctxt->error != NULL)
2844 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
2845 ctxt->URL);
2846 ctxt->nbErrors++;
2847 return (NULL);
2848 }
2849
2850 /*
2851 * Remove all the blank text nodes
2852 */
2853 delete = NULL;
2854 cur = root;
2855 while (cur != NULL) {
2856 if (delete != NULL) {
2857 xmlUnlinkNode(delete);
2858 xmlFreeNode(delete);
2859 delete = NULL;
2860 }
2861 if (cur->type == XML_ELEMENT_NODE) {
2862 /*
2863 * Simplification 4.1. Annotations
2864 */
2865 if ((cur->ns == NULL) ||
2866 (!xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
2867 delete = cur;
2868 goto skip_children;
2869 } else {
2870 if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002871 xmlChar *href, *ns, *base, *URL;
2872 xmlRelaxNGDocumentPtr docu;
2873
2874 ns = xmlGetProp(cur, BAD_CAST "ns");
2875 href = xmlGetProp(cur, BAD_CAST "href");
2876 if (href == NULL) {
2877 if (ctxt->error != NULL)
2878 ctxt->error(ctxt->userData,
2879 "xmlRelaxNGParse: externalRef has no href attribute\n");
2880 ctxt->nbErrors++;
2881 delete = cur;
2882 goto skip_children;
2883 }
2884 base = xmlNodeGetBase(cur->doc, cur);
2885 URL = xmlBuildURI(href, base);
2886 if (URL == NULL) {
2887 if (ctxt->error != NULL)
2888 ctxt->error(ctxt->userData,
2889 "Failed to compute URL for externalRef %s\n", href);
2890 ctxt->nbErrors++;
2891 if (href != NULL)
2892 xmlFree(href);
2893 if (base != NULL)
2894 xmlFree(base);
2895 delete = cur;
2896 goto skip_children;
2897 }
2898 if (href != NULL)
2899 xmlFree(href);
2900 if (base != NULL)
2901 xmlFree(base);
2902 docu = xmlRelaxNGLoaddocument(ctxt, URL, ns);
2903 if (docu == NULL) {
2904 if (ctxt->error != NULL)
2905 ctxt->error(ctxt->userData,
2906 "Failed to load externalRef %s\n", URL);
2907 ctxt->nbErrors++;
2908 xmlFree(URL);
2909 delete = cur;
2910 goto skip_children;
2911 }
2912 xmlFree(URL);
2913 cur->_private = docu;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002914 } else if (xmlStrEqual(cur->name, BAD_CAST "include")) {
2915 TODO
2916 } else if ((xmlStrEqual(cur->name, BAD_CAST "element")) ||
2917 (xmlStrEqual(cur->name, BAD_CAST "attribute"))) {
2918 xmlChar *name;
2919 xmlNodePtr text = NULL;
2920
2921 /*
2922 * Simplification 4.8. name attribute of element
2923 * and attribute elements
2924 */
2925 name = xmlGetProp(cur, BAD_CAST "name");
2926 if (name != NULL) {
2927 if (cur->children == NULL) {
2928 text = xmlNewChild(cur, cur->ns, BAD_CAST "name",
2929 name);
2930 } else {
2931 xmlNodePtr node;
2932 node = xmlNewNode(cur->ns, BAD_CAST "name");
2933 if (node != NULL) {
2934 xmlAddPrevSibling(cur->children, node);
2935 text = xmlNewText(name);
2936 xmlAddChild(node, text);
2937 text = node;
2938 }
2939 }
2940 xmlUnsetProp(cur, BAD_CAST "name");
2941 xmlFree(name);
2942 }
2943 if (xmlStrEqual(cur->name, BAD_CAST "attribute")) {
2944 if (text == NULL) {
2945 text = cur->children;
2946 while (text != NULL) {
2947 if ((text->type == XML_ELEMENT_NODE) &&
2948 (xmlStrEqual(text->name, BAD_CAST "name")))
2949 break;
2950 text = text->next;
2951 }
2952 }
2953 if (text == NULL) {
2954 if (ctxt->error != NULL)
2955 ctxt->error(ctxt->userData,
2956 "xmlRelaxNGParse: attribute without name\n");
2957 ctxt->nbErrors++;
2958 } else {
2959 xmlSetProp(text, BAD_CAST "ns", BAD_CAST "");
2960 }
2961 }
2962 } else if ((xmlStrEqual(cur->name, BAD_CAST "name")) ||
2963 (xmlStrEqual(cur->name, BAD_CAST "nsName")) ||
2964 (xmlStrEqual(cur->name, BAD_CAST "value"))) {
2965 /*
2966 * Simplification 4.8. name attribute of element
2967 * and attribute elements
2968 */
2969 if (xmlHasProp(cur, BAD_CAST "ns") == NULL) {
2970 xmlNodePtr node;
2971 xmlChar *ns = NULL;
2972
2973 node = cur->parent;
2974 while ((node != NULL) &&
2975 (node->type == XML_ELEMENT_NODE)) {
2976 ns = xmlGetProp(node, BAD_CAST "ns");
2977 if (ns != NULL) {
2978 break;
2979 }
2980 node = node->parent;
2981 }
2982 if (ns == NULL) {
2983 xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
2984 } else {
2985 xmlSetProp(cur, BAD_CAST "ns", ns);
2986 xmlFree(ns);
2987 }
2988 }
2989 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
2990 xmlChar *name, *local, *prefix;
2991
2992 /*
2993 * Simplification: 4.10. QNames
2994 */
2995 name = xmlNodeGetContent(cur);
2996 if (name != NULL) {
2997 local = xmlSplitQName2(name, &prefix);
2998 if (local != NULL) {
2999 xmlNsPtr ns;
3000
3001 ns = xmlSearchNs(cur->doc, cur, prefix);
3002 if (ns == NULL) {
3003 if (ctxt->error != NULL)
3004 ctxt->error(ctxt->userData,
3005 "xmlRelaxNGParse: no namespace for prefix %s\n", prefix);
3006 ctxt->nbErrors++;
3007 } else {
3008 xmlSetProp(cur, BAD_CAST "ns", ns->href);
3009 xmlNodeSetContent(cur, local);
3010 }
3011 xmlFree(local);
3012 xmlFree(prefix);
3013 }
3014 xmlFree(name);
3015 }
3016 }
3017 }
3018 }
3019 }
3020 /*
3021 * Simplification 4.2 whitespaces
3022 */
3023 else if (cur->type == XML_TEXT_NODE) {
3024 if (IS_BLANK_NODE(cur)) {
3025 if (cur->parent->type == XML_ELEMENT_NODE) {
3026 if ((!xmlStrEqual(cur->parent->name, BAD_CAST "value")) &&
3027 (!xmlStrEqual(cur->parent->name, BAD_CAST "param")))
3028 delete = cur;
3029 } else {
3030 delete = cur;
3031 goto skip_children;
3032 }
3033 }
3034 } else if (cur->type != XML_CDATA_SECTION_NODE) {
3035 delete = cur;
3036 goto skip_children;
3037 }
3038
3039 /*
3040 * Skip to next node
3041 */
3042 if (cur->children != NULL) {
3043 if ((cur->children->type != XML_ENTITY_DECL) &&
3044 (cur->children->type != XML_ENTITY_REF_NODE) &&
3045 (cur->children->type != XML_ENTITY_NODE)) {
3046 cur = cur->children;
3047 continue;
3048 }
3049 }
3050skip_children:
3051 if (cur->next != NULL) {
3052 cur = cur->next;
3053 continue;
3054 }
3055
3056 do {
3057 cur = cur->parent;
3058 if (cur == NULL)
3059 break;
3060 if (cur == root) {
3061 cur = NULL;
3062 break;
3063 }
3064 if (cur->next != NULL) {
3065 cur = cur->next;
3066 break;
3067 }
3068 } while (cur != NULL);
3069 }
3070 if (delete != NULL) {
3071 xmlUnlinkNode(delete);
3072 xmlFreeNode(delete);
3073 delete = NULL;
3074 }
3075
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003076 return(doc);
3077}
3078
3079/**
3080 * xmlRelaxNGParse:
3081 * @ctxt: a Relax-NG parser context
3082 *
3083 * parse a schema definition resource and build an internal
3084 * XML Shema struture which can be used to validate instances.
3085 * *WARNING* this interface is highly subject to change
3086 *
3087 * Returns the internal XML RelaxNG structure built from the resource or
3088 * NULL in case of error
3089 */
3090xmlRelaxNGPtr
3091xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt)
3092{
3093 xmlRelaxNGPtr ret = NULL;
3094 xmlDocPtr doc;
3095 xmlNodePtr root;
3096
3097 xmlRelaxNGInitTypes();
3098
3099 if (ctxt == NULL)
3100 return (NULL);
3101
3102 /*
3103 * First step is to parse the input document into an DOM/Infoset
3104 */
3105 if (ctxt->URL != NULL) {
3106 doc = xmlParseFile((const char *) ctxt->URL);
3107 if (doc == NULL) {
3108 if (ctxt->error != NULL)
3109 ctxt->error(ctxt->userData,
3110 "xmlRelaxNGParse: could not load %s\n", ctxt->URL);
3111 ctxt->nbErrors++;
3112 return (NULL);
3113 }
3114 } else if (ctxt->buffer != NULL) {
3115 doc = xmlParseMemory(ctxt->buffer, ctxt->size);
3116 if (doc == NULL) {
3117 if (ctxt->error != NULL)
3118 ctxt->error(ctxt->userData,
3119 "xmlRelaxNGParse: could not parse schemas\n");
3120 ctxt->nbErrors++;
3121 return (NULL);
3122 }
3123 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
3124 ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
3125 } else {
3126 if (ctxt->error != NULL)
3127 ctxt->error(ctxt->userData,
3128 "xmlRelaxNGParse: nothing to parse\n");
3129 ctxt->nbErrors++;
3130 return (NULL);
3131 }
3132 ctxt->document = doc;
3133
3134 /*
3135 * Some preprocessing of the document content
3136 */
3137 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
3138 if (doc == NULL) {
3139 xmlFreeDoc(ctxt->document);
3140 ctxt->document = NULL;
3141 return(NULL);
3142 }
3143
Daniel Veillard6eadf632003-01-23 18:29:16 +00003144 /*
3145 * Then do the parsing for good
3146 */
3147 root = xmlDocGetRootElement(doc);
3148 if (root == NULL) {
3149 if (ctxt->error != NULL)
3150 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
3151 ctxt->URL);
3152 ctxt->nbErrors++;
3153 return (NULL);
3154 }
3155 ret = xmlRelaxNGParseDocument(ctxt, root);
3156 if (ret == NULL)
3157 return(NULL);
3158
3159 /*
3160 * Check the ref/defines links
3161 */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003162 /*
3163 * try to preprocess interleaves
3164 */
3165 if (ctxt->interleaves != NULL) {
3166 xmlHashScan(ctxt->interleaves,
3167 (xmlHashScanner)xmlRelaxNGComputeInterleaves, ctxt);
3168 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003169
3170 /*
3171 * if there was a parsing error return NULL
3172 */
3173 if (ctxt->nbErrors > 0) {
3174 xmlRelaxNGFree(ret);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003175 ctxt->document = NULL;
3176 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003177 return(NULL);
3178 }
3179
3180 /*
3181 * Transfer the pointer for cleanup at the schema level.
3182 */
3183 ret->doc = doc;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003184 ctxt->document = NULL;
3185 ret->documents = ctxt->documents;
3186 ctxt->documents = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003187
3188 return (ret);
3189}
3190
3191/**
3192 * xmlRelaxNGSetParserErrors:
3193 * @ctxt: a Relax-NG validation context
3194 * @err: the error callback
3195 * @warn: the warning callback
3196 * @ctx: contextual data for the callbacks
3197 *
3198 * Set the callback functions used to handle errors for a validation context
3199 */
3200void
3201xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
3202 xmlRelaxNGValidityErrorFunc err,
3203 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
3204 if (ctxt == NULL)
3205 return;
3206 ctxt->error = err;
3207 ctxt->warning = warn;
3208 ctxt->userData = ctx;
3209}
3210/************************************************************************
3211 * *
3212 * Dump back a compiled form *
3213 * *
3214 ************************************************************************/
3215static void xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define);
3216
3217/**
3218 * xmlRelaxNGDumpDefines:
3219 * @output: the file output
3220 * @defines: a list of define structures
3221 *
3222 * Dump a RelaxNG structure back
3223 */
3224static void
3225xmlRelaxNGDumpDefines(FILE * output, xmlRelaxNGDefinePtr defines) {
3226 while (defines != NULL) {
3227 xmlRelaxNGDumpDefine(output, defines);
3228 defines = defines->next;
3229 }
3230}
3231
3232/**
3233 * xmlRelaxNGDumpDefine:
3234 * @output: the file output
3235 * @define: a define structure
3236 *
3237 * Dump a RelaxNG structure back
3238 */
3239static void
3240xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define) {
3241 if (define == NULL)
3242 return;
3243 switch(define->type) {
3244 case XML_RELAXNG_EMPTY:
3245 fprintf(output, "<empty/>\n");
3246 break;
3247 case XML_RELAXNG_NOT_ALLOWED:
3248 fprintf(output, "<notAllowed/>\n");
3249 break;
3250 case XML_RELAXNG_TEXT:
3251 fprintf(output, "<text/>\n");
3252 break;
3253 case XML_RELAXNG_ELEMENT:
3254 fprintf(output, "<element>\n");
3255 if (define->name != NULL) {
3256 fprintf(output, "<name");
3257 if (define->ns != NULL)
3258 fprintf(output, " ns=\"%s\"", define->ns);
3259 fprintf(output, ">%s</name>\n", define->name);
3260 }
3261 xmlRelaxNGDumpDefines(output, define->attrs);
3262 xmlRelaxNGDumpDefines(output, define->content);
3263 fprintf(output, "</element>\n");
3264 break;
3265 case XML_RELAXNG_LIST:
3266 fprintf(output, "<list>\n");
3267 xmlRelaxNGDumpDefines(output, define->content);
3268 fprintf(output, "</list>\n");
3269 break;
3270 case XML_RELAXNG_ONEORMORE:
3271 fprintf(output, "<oneOrMore>\n");
3272 xmlRelaxNGDumpDefines(output, define->content);
3273 fprintf(output, "</oneOrMore>\n");
3274 break;
3275 case XML_RELAXNG_ZEROORMORE:
3276 fprintf(output, "<zeroOrMore>\n");
3277 xmlRelaxNGDumpDefines(output, define->content);
3278 fprintf(output, "</zeroOrMore>\n");
3279 break;
3280 case XML_RELAXNG_CHOICE:
3281 fprintf(output, "<choice>\n");
3282 xmlRelaxNGDumpDefines(output, define->content);
3283 fprintf(output, "</choice>\n");
3284 break;
3285 case XML_RELAXNG_GROUP:
3286 fprintf(output, "<group>\n");
3287 xmlRelaxNGDumpDefines(output, define->content);
3288 fprintf(output, "</group>\n");
3289 break;
3290 case XML_RELAXNG_INTERLEAVE:
3291 fprintf(output, "<interleave>\n");
3292 xmlRelaxNGDumpDefines(output, define->content);
3293 fprintf(output, "</interleave>\n");
3294 break;
3295 case XML_RELAXNG_OPTIONAL:
3296 fprintf(output, "<optional>\n");
3297 xmlRelaxNGDumpDefines(output, define->content);
3298 fprintf(output, "</optional>\n");
3299 break;
3300 case XML_RELAXNG_ATTRIBUTE:
3301 fprintf(output, "<attribute>\n");
3302 xmlRelaxNGDumpDefines(output, define->content);
3303 fprintf(output, "</attribute>\n");
3304 break;
3305 case XML_RELAXNG_DEF:
3306 fprintf(output, "<define");
3307 if (define->name != NULL)
3308 fprintf(output, " name=\"%s\"", define->name);
3309 fprintf(output, ">\n");
3310 xmlRelaxNGDumpDefines(output, define->content);
3311 fprintf(output, "</define>\n");
3312 break;
3313 case XML_RELAXNG_REF:
3314 fprintf(output, "<ref");
3315 if (define->name != NULL)
3316 fprintf(output, " name=\"%s\"", define->name);
3317 fprintf(output, ">\n");
3318 xmlRelaxNGDumpDefines(output, define->content);
3319 fprintf(output, "</ref>\n");
3320 break;
3321 case XML_RELAXNG_DATATYPE:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003322 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00003323 case XML_RELAXNG_VALUE:
3324 TODO
3325 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003326 case XML_RELAXNG_START:
3327 TODO
3328 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003329 }
3330}
3331
3332/**
3333 * xmlRelaxNGDumpGrammar:
3334 * @output: the file output
3335 * @grammar: a grammar structure
3336 * @top: is this a top grammar
3337 *
3338 * Dump a RelaxNG structure back
3339 */
3340static void
3341xmlRelaxNGDumpGrammar(FILE * output, xmlRelaxNGGrammarPtr grammar, int top)
3342{
3343 if (grammar == NULL)
3344 return;
3345
3346 fprintf(output, "<grammar");
3347 if (top)
3348 fprintf(output,
3349 " xmlns=\"http://relaxng.org/ns/structure/1.0\"");
3350 switch(grammar->combine) {
3351 case XML_RELAXNG_COMBINE_UNDEFINED:
3352 break;
3353 case XML_RELAXNG_COMBINE_CHOICE:
3354 fprintf(output, " combine=\"choice\"");
3355 break;
3356 case XML_RELAXNG_COMBINE_INTERLEAVE:
3357 fprintf(output, " combine=\"interleave\"");
3358 break;
3359 default:
3360 fprintf(output, " <!-- invalid combine value -->");
3361 }
3362 fprintf(output, ">\n");
3363 if (grammar->start == NULL) {
3364 fprintf(output, " <!-- grammar had no start -->");
3365 } else {
3366 fprintf(output, "<start>\n");
3367 xmlRelaxNGDumpDefine(output, grammar->start);
3368 fprintf(output, "</start>\n");
3369 }
3370 /* TODO ? Dump the defines ? */
3371 fprintf(output, "</grammar>\n");
3372}
3373
3374/**
3375 * xmlRelaxNGDump:
3376 * @output: the file output
3377 * @schema: a schema structure
3378 *
3379 * Dump a RelaxNG structure back
3380 */
3381void
3382xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema)
3383{
3384 if (schema == NULL) {
3385 fprintf(output, "RelaxNG empty or failed to compile\n");
3386 return;
3387 }
3388 fprintf(output, "RelaxNG: ");
3389 if (schema->doc == NULL) {
3390 fprintf(output, "no document\n");
3391 } else if (schema->doc->URL != NULL) {
3392 fprintf(output, "%s\n", schema->doc->URL);
3393 } else {
3394 fprintf(output, "\n");
3395 }
3396 if (schema->topgrammar == NULL) {
3397 fprintf(output, "RelaxNG has no top grammar\n");
3398 return;
3399 }
3400 xmlRelaxNGDumpGrammar(output, schema->topgrammar, 1);
3401}
3402
3403/************************************************************************
3404 * *
3405 * Validation implementation *
3406 * *
3407 ************************************************************************/
3408static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
3409 xmlRelaxNGDefinePtr define);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00003410static int xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
3411 xmlRelaxNGDefinePtr define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003412
3413/**
3414 * xmlRelaxNGSkipIgnored:
3415 * @ctxt: a schema validation context
3416 * @node: the top node.
3417 *
3418 * Skip ignorable nodes in that context
3419 *
3420 * Returns the new sibling or NULL in case of error.
3421 */
3422static xmlNodePtr
3423xmlRelaxNGSkipIgnored(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
3424 xmlNodePtr node) {
3425 /*
3426 * TODO complete and handle entities
3427 */
3428 while ((node != NULL) &&
3429 ((node->type == XML_COMMENT_NODE) ||
3430 ((node->type == XML_TEXT_NODE) &&
3431 (IS_BLANK_NODE(node))))) {
3432 node = node->next;
3433 }
3434 return(node);
3435}
3436
3437/**
Daniel Veillardedc91922003-01-26 00:52:04 +00003438 * xmlRelaxNGNormalize:
3439 * @ctxt: a schema validation context
3440 * @str: the string to normalize
3441 *
3442 * Implements the normalizeWhiteSpace( s ) function from
3443 * section 6.2.9 of the spec
3444 *
3445 * Returns the new string or NULL in case of error.
3446 */
3447static xmlChar *
3448xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *str) {
3449 xmlChar *ret, *p;
3450 const xmlChar *tmp;
3451 int len;
3452
3453 if (str == NULL)
3454 return(NULL);
3455 tmp = str;
3456 while (*tmp != 0) tmp++;
3457 len = tmp - str;
3458
3459 ret = (xmlChar *) xmlMalloc((len + 1) * sizeof(xmlChar));
3460 if (ret == NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00003461 if (ctxt != NULL) {
3462 VALID_CTXT();
3463 VALID_ERROR("xmlRelaxNGNormalize: out of memory\n");
3464 } else {
3465 xmlGenericError(xmlGenericErrorContext,
3466 "xmlRelaxNGNormalize: out of memory\n");
3467 }
Daniel Veillardedc91922003-01-26 00:52:04 +00003468 return(NULL);
3469 }
3470 p = ret;
3471 while (IS_BLANK(*str)) str++;
3472 while (*str != 0) {
3473 if (IS_BLANK(*str)) {
3474 while (IS_BLANK(*str)) str++;
3475 if (*str == 0)
3476 break;
3477 *p++ = ' ';
3478 } else
3479 *p++ = *str++;
3480 }
3481 *p = 0;
3482 return(ret);
3483}
3484
3485/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00003486 * xmlRelaxNGValidateDatatype:
3487 * @ctxt: a Relax-NG validation context
3488 * @value: the string value
3489 * @type: the datatype definition
3490 *
3491 * Validate the given value against the dataype
3492 *
3493 * Returns 0 if the validation succeeded or an error code.
3494 */
3495static int
3496xmlRelaxNGValidateDatatype(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *value,
3497 xmlRelaxNGDefinePtr define) {
3498 int ret;
3499 xmlRelaxNGTypeLibraryPtr lib;
3500
3501 if ((define == NULL) || (define->data == NULL)) {
3502 return(-1);
3503 }
3504 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
3505 if (lib->check != NULL)
3506 ret = lib->check(lib->data, define->name, value);
3507 else
3508 ret = -1;
3509 if (ret < 0) {
3510 VALID_CTXT();
3511 VALID_ERROR("Internal: failed to validate type %s\n", define->name);
3512 return(-1);
3513 } else if (ret == 1) {
3514 ret = 0;
3515 } else {
3516 VALID_CTXT();
3517 VALID_ERROR("Type %s doesn't allow value %s\n", define->name, value);
3518 return(-1);
3519 ret = -1;
3520 }
3521 return(ret);
3522}
3523
3524/**
Daniel Veillardc6e997c2003-01-27 12:35:42 +00003525 * xmlRelaxNGNextValue:
3526 * @ctxt: a Relax-NG validation context
3527 *
3528 * Skip to the next value when validating within a list
3529 *
3530 * Returns 0 if the operation succeeded or an error code.
3531 */
3532static int
3533xmlRelaxNGNextValue(xmlRelaxNGValidCtxtPtr ctxt) {
3534 xmlChar *cur;
3535
3536 cur = ctxt->state->value;
3537 if ((cur == NULL) || (ctxt->state->endvalue == NULL)) {
3538 ctxt->state->value = NULL;
3539 return(0);
3540 }
3541 while (*cur != 0) cur++;
3542 while ((cur != ctxt->state->endvalue) && (*cur == 0)) cur++;
3543 if (cur == ctxt->state->endvalue)
3544 ctxt->state->value = NULL;
3545 else
3546 ctxt->state->value = cur;
3547 return(0);
3548}
3549
3550/**
3551 * xmlRelaxNGValidateValueList:
3552 * @ctxt: a Relax-NG validation context
3553 * @defines: the list of definitions to verify
3554 *
3555 * Validate the given set of definitions for the current value
3556 *
3557 * Returns 0 if the validation succeeded or an error code.
3558 */
3559static int
3560xmlRelaxNGValidateValueList(xmlRelaxNGValidCtxtPtr ctxt,
3561 xmlRelaxNGDefinePtr defines) {
3562 int ret = 0;
3563
3564 while (defines != NULL) {
3565 ret = xmlRelaxNGValidateValue(ctxt, defines);
3566 if (ret != 0)
3567 break;
3568 defines = defines->next;
3569 }
3570 return(ret);
3571}
3572
3573/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00003574 * xmlRelaxNGValidateValue:
3575 * @ctxt: a Relax-NG validation context
3576 * @define: the definition to verify
3577 *
3578 * Validate the given definition for the current value
3579 *
3580 * Returns 0 if the validation succeeded or an error code.
3581 */
3582static int
3583xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
3584 xmlRelaxNGDefinePtr define) {
Daniel Veillardedc91922003-01-26 00:52:04 +00003585 int ret = 0, oldflags;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003586 xmlChar *value;
3587
3588 value = ctxt->state->value;
3589 switch (define->type) {
3590 case XML_RELAXNG_EMPTY:
3591 if ((value != NULL) && (value[0] != '0'))
3592 ret = -1;
3593 break;
3594 case XML_RELAXNG_TEXT:
3595 break;
Daniel Veillardedc91922003-01-26 00:52:04 +00003596 case XML_RELAXNG_VALUE: {
3597 if (!xmlStrEqual(value, define->value)) {
3598 if (define->name != NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00003599 xmlRelaxNGTypeLibraryPtr lib;
3600
3601 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
3602 if ((lib != NULL) && (lib->comp != NULL))
3603 ret = lib->comp(lib->data, define->name, value,
3604 define->value);
3605 else
3606 ret = -1;
3607 if (ret < 0) {
3608 VALID_CTXT();
3609 VALID_ERROR("Internal: failed to compare type %s\n",
3610 define->name);
3611 return(-1);
3612 } else if (ret == 1) {
3613 ret = 0;
3614 } else {
3615 ret = -1;
3616 }
Daniel Veillardedc91922003-01-26 00:52:04 +00003617 } else {
3618 xmlChar *nval, *nvalue;
3619
3620 /*
3621 * TODO: trivial optimizations are possible by
3622 * computing at compile-time
3623 */
3624 nval = xmlRelaxNGNormalize(ctxt, define->value);
3625 nvalue = xmlRelaxNGNormalize(ctxt, value);
3626
Daniel Veillardea3f3982003-01-26 19:45:18 +00003627 if ((nval == NULL) || (nvalue == NULL) ||
3628 (!xmlStrEqual(nval, nvalue)))
Daniel Veillardedc91922003-01-26 00:52:04 +00003629 ret = -1;
3630 if (nval != NULL)
3631 xmlFree(nval);
3632 if (nvalue != NULL)
3633 xmlFree(nvalue);
3634 }
3635 }
3636 break;
3637 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00003638 case XML_RELAXNG_DATATYPE: {
3639 ret = xmlRelaxNGValidateDatatype(ctxt, value, define);
3640 if (ret == 0)
3641 xmlRelaxNGNextValue(ctxt);
3642
3643 break;
3644 }
3645 case XML_RELAXNG_CHOICE: {
3646 xmlRelaxNGDefinePtr list = define->content;
3647 xmlChar *oldvalue;
3648
3649 oldflags = ctxt->flags;
3650 ctxt->flags |= FLAGS_IGNORABLE;
3651
3652 oldvalue = ctxt->state->value;
3653 while (list != NULL) {
3654 ret = xmlRelaxNGValidateValue(ctxt, list);
3655 if (ret == 0) {
3656 break;
3657 }
3658 ctxt->state->value = oldvalue;
3659 list = list->next;
3660 }
3661 ctxt->flags = oldflags;
3662 break;
3663 }
3664 case XML_RELAXNG_LIST: {
3665 xmlRelaxNGDefinePtr list = define->content;
3666 xmlChar *oldvalue, *oldend, *val, *cur;
3667
3668 oldvalue = ctxt->state->value;
3669 oldend = ctxt->state->endvalue;
3670
3671 val = xmlStrdup(oldvalue);
3672 if (val == NULL) {
3673 VALID_CTXT();
3674 VALID_ERROR("Internal: no state\n");
3675 return(-1);
3676 }
3677 cur = val;
3678 while (*cur != 0) {
3679 if (IS_BLANK(*cur))
3680 *cur = 0;
3681 cur++;
3682 }
3683 ctxt->state->endvalue = cur;
3684 cur = val;
3685 while ((*cur == 0) && (cur != ctxt->state->endvalue)) cur++;
3686
3687 ctxt->state->value = cur;
3688
3689 while (list != NULL) {
3690 ret = xmlRelaxNGValidateValue(ctxt, list);
3691 if (ret != 0) {
3692 break;
3693 }
3694 list = list->next;
3695 }
3696 if ((ret == 0) && (ctxt->state->value != NULL) &&
3697 (ctxt->state->value != ctxt->state->endvalue)) {
3698 VALID_CTXT();
3699 VALID_ERROR("Extra data in list: %s\n", ctxt->state->value);
3700 ret = -1;
3701 }
3702 xmlFree(val);
3703 ctxt->state->value = oldvalue;
3704 ctxt->state->endvalue = oldend;
3705 break;
3706 }
3707 case XML_RELAXNG_ONEORMORE:
3708 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
3709 if (ret != 0) {
3710 break;
3711 }
3712 /* no break on purpose */
3713 case XML_RELAXNG_ZEROORMORE: {
3714 xmlChar *cur, *temp;
3715
3716 oldflags = ctxt->flags;
3717 ctxt->flags |= FLAGS_IGNORABLE;
3718 cur = ctxt->state->value;
3719 temp = NULL;
3720 while ((cur != NULL) && (cur != ctxt->state->endvalue) &&
3721 (temp != cur)) {
3722 temp = cur;
3723 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
3724 if (ret != 0) {
3725 ctxt->state->value = temp;
3726 ret = 0;
3727 break;
3728 }
3729 cur = ctxt->state->value;
3730 }
3731 ctxt->flags = oldflags;
3732 break;
3733 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003734 default:
3735 TODO
3736 ret = -1;
3737 }
3738 return(ret);
3739}
3740
3741/**
3742 * xmlRelaxNGValidateValueContent:
3743 * @ctxt: a Relax-NG validation context
3744 * @defines: the list of definitions to verify
3745 *
3746 * Validate the given definitions for the current value
3747 *
3748 * Returns 0 if the validation succeeded or an error code.
3749 */
3750static int
3751xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt,
3752 xmlRelaxNGDefinePtr defines) {
3753 int ret = 0;
3754
3755 while (defines != NULL) {
3756 ret = xmlRelaxNGValidateValue(ctxt, defines);
3757 if (ret != 0)
3758 break;
3759 defines = defines->next;
3760 }
3761 return(ret);
3762}
3763
3764/**
3765 * xmlRelaxNGValidateAttribute:
3766 * @ctxt: a Relax-NG validation context
3767 * @define: the definition to verify
3768 *
3769 * Validate the given attribute definition for that node
3770 *
3771 * Returns 0 if the validation succeeded or an error code.
3772 */
3773static int
3774xmlRelaxNGValidateAttribute(xmlRelaxNGValidCtxtPtr ctxt,
3775 xmlRelaxNGDefinePtr define) {
3776 int ret = 0, i;
3777 xmlChar *value, *oldvalue;
3778 xmlAttrPtr prop = NULL, tmp;
3779
3780 if (define->name != NULL) {
3781 for (i = 0;i < ctxt->state->nbAttrs;i++) {
3782 tmp = ctxt->state->attrs[i];
3783 if ((tmp != NULL) && (xmlStrEqual(define->name, tmp->name))) {
3784 if ((((define->ns == NULL) || (define->ns[0] == 0)) &&
3785 (tmp->ns == NULL)) ||
3786 ((tmp->ns != NULL) &&
3787 (xmlStrEqual(define->ns, tmp->ns->href)))) {
3788 prop = tmp;
3789 break;
3790 }
3791 }
3792 }
3793 if (prop != NULL) {
3794 value = xmlNodeListGetString(prop->doc, prop->children, 1);
3795 oldvalue = ctxt->state->value;
3796 ctxt->state->value = value;
3797 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
3798 value = ctxt->state->value;
3799 ctxt->state->value = oldvalue;
3800 if (value != NULL)
3801 xmlFree(value);
3802 if (ret == 0) {
3803 /*
3804 * flag the attribute as processed
3805 */
3806 ctxt->state->attrs[i] = NULL;
3807 }
3808 } else {
3809 ret = -1;
3810 }
3811#ifdef DEBUG
3812 xmlGenericError(xmlGenericErrorContext,
3813 "xmlRelaxNGValidateAttribute(%s): %d\n", define->name, ret);
3814#endif
3815 } else {
3816 TODO
3817 }
3818
3819 return(ret);
3820}
3821
3822/**
3823 * xmlRelaxNGValidateAttributeList:
3824 * @ctxt: a Relax-NG validation context
3825 * @define: the list of definition to verify
3826 *
3827 * Validate the given node against the list of attribute definitions
3828 *
3829 * Returns 0 if the validation succeeded or an error code.
3830 */
3831static int
3832xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt,
3833 xmlRelaxNGDefinePtr defines) {
3834 int ret = 0;
3835 while (defines != NULL) {
3836 if (xmlRelaxNGValidateAttribute(ctxt, defines) != 0)
3837 ret = -1;
3838 defines = defines->next;
3839 }
3840 return(ret);
3841}
3842
3843/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003844 * xmlRelaxNGValidateTryPermutation:
3845 * @ctxt: a Relax-NG validation context
3846 * @groups: the array of groups
3847 * @nbgroups: the number of groups in the array
3848 * @array: the permutation to try
3849 * @len: the size of the set
3850 *
3851 * Try to validate a permutation for the group of definitions.
3852 *
3853 * Returns 0 if the validation succeeded or an error code.
3854 */
3855static int
3856xmlRelaxNGValidateTryPermutation(xmlRelaxNGValidCtxtPtr ctxt,
3857 xmlRelaxNGDefinePtr rule,
3858 xmlNodePtr *array, int len) {
3859 int i, ret;
3860
3861 if (len > 0) {
3862 /*
3863 * One only need the next pointer set-up to do the validation
3864 */
3865 for (i = 0;i < (len - 1);i++)
3866 array[i]->next = array[i + 1];
3867 array[i]->next = NULL;
3868
3869 /*
3870 * Now try to validate the sequence
3871 */
3872 ctxt->state->seq = array[0];
3873 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
3874 } else {
3875 ctxt->state->seq = NULL;
3876 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
3877 }
3878
3879 /*
3880 * the sequence must be fully consumed
3881 */
3882 if (ctxt->state->seq != NULL)
3883 return(-1);
3884
3885 return(ret);
3886}
3887
3888/**
3889 * xmlRelaxNGValidateWalkPermutations:
3890 * @ctxt: a Relax-NG validation context
3891 * @groups: the array of groups
3892 * @nbgroups: the number of groups in the array
3893 * @nodes: the set of nodes
3894 * @array: the current state of the parmutation
3895 * @len: the size of the set
3896 * @level: a pointer to the level variable
3897 * @k: the index in the array to fill
3898 *
3899 * Validate a set of nodes for a groups of definitions, will try the
3900 * full set of permutations
3901 *
3902 * Returns 0 if the validation succeeded or an error code.
3903 */
3904static int
3905xmlRelaxNGValidateWalkPermutations(xmlRelaxNGValidCtxtPtr ctxt,
3906 xmlRelaxNGDefinePtr rule, xmlNodePtr *nodes,
3907 xmlNodePtr *array, int len,
3908 int *level, int k) {
3909 int i, ret;
3910
3911 if ((k >= 0) && (k < len))
3912 array[k] = nodes[*level];
3913 *level = *level + 1;
3914 if (*level == len) {
3915 ret = xmlRelaxNGValidateTryPermutation(ctxt, rule, array, len);
3916 if (ret == 0)
3917 return(0);
3918 } else {
3919 for (i = 0;i < len;i++) {
3920 if (array[i] == NULL) {
3921 ret = xmlRelaxNGValidateWalkPermutations(ctxt, rule,
3922 nodes, array, len, level, i);
3923 if (ret == 0)
3924 return(0);
3925 }
3926 }
3927 }
3928 *level = *level - 1;
3929 array[k] = NULL;
3930 return(-1);
3931}
3932
3933/**
3934 * xmlRelaxNGNodeMatchesList:
3935 * @node: the node
3936 * @list: a NULL terminated array of definitions
3937 *
3938 * Check if a node can be matched by one of the definitions
3939 *
3940 * Returns 1 if matches 0 otherwise
3941 */
3942static int
3943xmlRelaxNGNodeMatchesList(xmlNodePtr node, xmlRelaxNGDefinePtr *list) {
3944 xmlRelaxNGDefinePtr cur;
3945 int i = 0;
3946
3947 if ((node == NULL) || (list == NULL))
3948 return(0);
3949
3950 cur = list[i++];
3951 while (cur != NULL) {
3952 if ((node->type == XML_ELEMENT_NODE) &&
3953 (cur->type == XML_RELAXNG_ELEMENT)) {
3954 if (cur->name == NULL) {
3955 if ((node->ns != NULL) &&
3956 (xmlStrEqual(node->ns->href, cur->ns)))
3957 return(1);
3958 } else if (xmlStrEqual(cur->name, node->name)) {
3959 if ((cur->ns == NULL) || (cur->ns[0] == 0)) {
3960 if (node->ns == NULL)
3961 return(1);
3962 } else {
3963 if ((node->ns != NULL) &&
3964 (xmlStrEqual(node->ns->href, cur->ns)))
3965 return(1);
3966 }
3967 }
3968 } else if ((node->type == XML_TEXT_NODE) &&
3969 (cur->type == XML_RELAXNG_TEXT)) {
3970 return(1);
3971 }
3972 cur = list[i++];
3973 }
3974 return(0);
3975}
3976
3977/**
3978 * xmlRelaxNGValidatePartGroup:
3979 * @ctxt: a Relax-NG validation context
3980 * @groups: the array of groups
3981 * @nbgroups: the number of groups in the array
3982 * @nodes: the set of nodes
3983 * @len: the size of the set of nodes
3984 *
3985 * Validate a set of nodes for a groups of definitions
3986 *
3987 * Returns 0 if the validation succeeded or an error code.
3988 */
3989static int
3990xmlRelaxNGValidatePartGroup(xmlRelaxNGValidCtxtPtr ctxt,
3991 xmlRelaxNGInterleaveGroupPtr *groups,
3992 int nbgroups, xmlNodePtr *nodes, int len) {
Daniel Veillardb08c9812003-01-28 23:09:49 +00003993 int level, ret = -1, i, j, k;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003994 xmlNodePtr *array = NULL, *list, oldseq;
3995 xmlRelaxNGInterleaveGroupPtr group;
3996
3997 list = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
3998 if (list == NULL) {
3999 return(-1);
4000 }
4001 array = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
4002 if (array == NULL) {
4003 xmlFree(list);
4004 return(-1);
4005 }
4006 memset(array, 0, len * sizeof(xmlNodePtr));
4007
4008 /*
4009 * Partition the elements and validate the subsets.
4010 */
4011 oldseq = ctxt->state->seq;
4012 for (i = 0;i < nbgroups;i++) {
4013 group = groups[i];
4014 if (group == NULL)
4015 continue;
4016 k = 0;
4017 for (j = 0;j < len;j++) {
4018 if (nodes[j] == NULL)
4019 continue;
4020 if (xmlRelaxNGNodeMatchesList(nodes[j], group->defs)) {
4021 list[k++] = nodes[j];
4022 nodes[j] = NULL;
4023 }
4024 }
4025 ctxt->state->seq = oldseq;
4026 if (k > 1) {
4027 memset(array, 0, k * sizeof(xmlNodePtr));
Daniel Veillardb08c9812003-01-28 23:09:49 +00004028 level = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004029 ret = xmlRelaxNGValidateWalkPermutations(ctxt, group->rule,
4030 list, array, k, &level, -1);
4031 } else {
4032 ret = xmlRelaxNGValidateTryPermutation(ctxt, group->rule, list, k);
4033 }
4034 if (ret != 0) {
4035 ctxt->state->seq = oldseq;
4036 break;
4037 }
4038 }
4039
4040 xmlFree(list);
4041 xmlFree(array);
4042 return(ret);
4043}
4044
4045/**
4046 * xmlRelaxNGValidateInterleave:
4047 * @ctxt: a Relax-NG validation context
4048 * @define: the definition to verify
4049 *
4050 * Validate an interleave definition for a node.
4051 *
4052 * Returns 0 if the validation succeeded or an error code.
4053 */
4054static int
4055xmlRelaxNGValidateInterleave(xmlRelaxNGValidCtxtPtr ctxt,
4056 xmlRelaxNGDefinePtr define) {
4057 int ret = 0, nbchildren, nbtot, i, j;
4058 xmlRelaxNGPartitionPtr partitions;
4059 xmlNodePtr *children = NULL;
4060 xmlNodePtr *order = NULL;
4061 xmlNodePtr cur;
4062
4063 if (define->data != NULL) {
4064 partitions = (xmlRelaxNGPartitionPtr) define->data;
4065 } else {
4066 VALID_CTXT();
4067 VALID_ERROR("Internal: interleave block has no data\n");
4068 return(-1);
4069 }
4070
4071 /*
4072 * Build the sequence of child and an array preserving the children
4073 * initial order.
4074 */
4075 cur = ctxt->state->seq;
4076 nbchildren = 0;
4077 nbtot = 0;
4078 while (cur != NULL) {
4079 if ((cur->type == XML_COMMENT_NODE) ||
4080 (cur->type == XML_PI_NODE) ||
4081 ((cur->type == XML_TEXT_NODE) &&
4082 (IS_BLANK_NODE(cur)))) {
4083 nbtot++;
4084 } else {
4085 nbchildren++;
4086 nbtot++;
4087 }
4088 cur = cur->next;
4089 }
4090 children = (xmlNodePtr *) xmlMalloc(nbchildren * sizeof(xmlNodePtr));
4091 if (children == NULL)
4092 goto error;
4093 order = (xmlNodePtr *) xmlMalloc(nbtot * sizeof(xmlNodePtr));
4094 if (order == NULL)
4095 goto error;
4096 cur = ctxt->state->seq;
4097 i = 0;
4098 j = 0;
4099 while (cur != NULL) {
4100 if ((cur->type == XML_COMMENT_NODE) ||
4101 (cur->type == XML_PI_NODE) ||
4102 ((cur->type == XML_TEXT_NODE) &&
4103 (IS_BLANK_NODE(cur)))) {
4104 order[j++] = cur;
4105 } else {
4106 order[j++] = cur;
4107 children[i++] = cur;
4108 }
4109 cur = cur->next;
4110 }
4111
4112 /* TODO: retry with a maller set of child if there is a next... */
4113 ret = xmlRelaxNGValidatePartGroup(ctxt, partitions->groups,
4114 partitions->nbgroups, children, nbchildren);
4115 if (ret == 0) {
4116 ctxt->state->seq = NULL;
4117 }
4118
4119 /*
4120 * Cleanup: rebuid the child sequence and free the structure
4121 */
4122 if (order != NULL) {
4123 for (i = 0;i < nbtot;i++) {
4124 if (i == 0)
4125 order[i]->prev = NULL;
4126 else
4127 order[i]->prev = order[i - 1];
4128 if (i == nbtot - 1)
4129 order[i]->next = NULL;
4130 else
4131 order[i]->next = order[i + 1];
4132 }
4133 xmlFree(order);
4134 }
4135 if (children != NULL)
4136 xmlFree(children);
4137
4138 return(ret);
4139
4140error:
4141 if (order != NULL) {
4142 for (i = 0;i < nbtot;i++) {
4143 if (i == 0)
4144 order[i]->prev = NULL;
4145 else
4146 order[i]->prev = order[i - 1];
4147 if (i == nbtot - 1)
4148 order[i]->next = NULL;
4149 else
4150 order[i]->next = order[i + 1];
4151 }
4152 xmlFree(order);
4153 }
4154 if (children != NULL)
4155 xmlFree(children);
4156 return(-1);
4157}
4158
4159/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004160 * xmlRelaxNGValidateElementContent:
4161 * @ctxt: a Relax-NG validation context
4162 * @define: the list of definition to verify
4163 *
4164 * Validate the given node content against the (list) of definitions
4165 *
4166 * Returns 0 if the validation succeeded or an error code.
4167 */
4168static int
4169xmlRelaxNGValidateElementContent(xmlRelaxNGValidCtxtPtr ctxt,
4170 xmlRelaxNGDefinePtr defines) {
4171 int ret = 0, res;
4172
4173 if (ctxt->state == NULL) {
4174 VALID_CTXT();
4175 VALID_ERROR("Internal: no state\n");
4176 return(-1);
4177 }
4178 while (defines != NULL) {
4179 res = xmlRelaxNGValidateDefinition(ctxt, defines);
4180 if (res < 0)
4181 ret = -1;
4182 defines = defines->next;
4183 }
4184
4185 return(ret);
4186}
4187
4188/**
4189 * xmlRelaxNGValidateDefinition:
4190 * @ctxt: a Relax-NG validation context
4191 * @define: the definition to verify
4192 *
4193 * Validate the current node against the definition
4194 *
4195 * Returns 0 if the validation succeeded or an error code.
4196 */
4197static int
4198xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
4199 xmlRelaxNGDefinePtr define) {
4200 xmlNodePtr node;
4201 int ret = 0, i, tmp, oldflags;
4202 xmlRelaxNGValidStatePtr oldstate, state;
4203
4204 if (define == NULL) {
4205 VALID_CTXT();
4206 VALID_ERROR("internal error: define == NULL\n");
4207 return(-1);
4208 }
4209 if (ctxt->state != NULL) {
4210 node = ctxt->state->seq;
4211 } else {
4212 node = NULL;
4213 }
4214 switch (define->type) {
4215 case XML_RELAXNG_EMPTY:
4216 if (node != NULL) {
4217 VALID_CTXT();
4218 VALID_ERROR("Expecting an empty element\n");
4219 return(-1);
4220 }
4221#ifdef DEBUG
4222 xmlGenericError(xmlGenericErrorContext,
4223 "xmlRelaxNGValidateDefinition(): validated empty\n");
4224#endif
4225 return(0);
4226 case XML_RELAXNG_NOT_ALLOWED:
4227 TODO
4228 break;
4229 case XML_RELAXNG_TEXT:
4230 if (node == NULL)
4231 return(0);
4232 while ((node != NULL) &&
4233 ((node->type == XML_TEXT_NODE) ||
4234 (node->type == XML_CDATA_SECTION_NODE)))
4235 node = node->next;
Daniel Veillard276be4a2003-01-24 01:03:34 +00004236 if (node == ctxt->state->seq) {
4237 VALID_CTXT();
4238 VALID_ERROR("Expecting text content\n");
4239 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004240 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00004241 ctxt->state->seq = node;
4242 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004243 case XML_RELAXNG_ELEMENT:
4244 node = xmlRelaxNGSkipIgnored(ctxt, node);
4245 if ((node == NULL) || (node->type != XML_ELEMENT_NODE)) {
4246 VALID_CTXT();
4247 VALID_ERROR("Expecting an element\n");
4248 return(-1);
4249 }
4250 if (define->name != NULL) {
4251 if (!xmlStrEqual(node->name, define->name)) {
4252 VALID_CTXT();
4253 VALID_ERROR("Expecting element %s, got %s\n",
4254 define->name, node->name);
4255 return(-1);
4256 }
4257 }
4258 if ((define->ns != NULL) && (define->ns[0] != 0)) {
4259 if (node->ns == NULL) {
4260 VALID_CTXT();
4261 VALID_ERROR("Expecting a namespace for element %s\n",
4262 node->name);
4263 return(-1);
4264 } else if (!xmlStrEqual(node->ns->href, define->ns)) {
4265 VALID_CTXT();
4266 VALID_ERROR("Expecting element %s has wrong namespace: expecting %s\n",
4267 node->name, define->ns);
4268 return(-1);
4269 }
4270 } else {
4271 if (node->ns != NULL) {
4272 VALID_CTXT();
4273 VALID_ERROR("Expecting no namespace for element %s\n",
4274 node->name);
4275 return(-1);
4276 }
4277 }
4278
4279 state = xmlRelaxNGNewValidState(ctxt, node);
4280 if (state == NULL) {
4281 return(-1);
4282 }
4283
4284 oldstate = ctxt->state;
4285 ctxt->state = state;
4286 if (define->attrs != NULL) {
4287 tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
4288 if (tmp != 0)
4289 ret = -1;
4290 }
4291 if (define->content != NULL) {
4292 tmp = xmlRelaxNGValidateElementContent(ctxt, define->content);
4293 if (tmp != 0)
4294 ret = -1;
4295 }
4296 state = ctxt->state;
4297 if (state->seq != NULL) {
4298 state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq);
4299 if (state->seq != NULL) {
4300 VALID_CTXT();
4301 VALID_ERROR("Extra content for element %s\n",
4302 node->name);
4303 ret = -1;
4304 }
4305 }
4306 for (i = 0;i < state->nbAttrs;i++) {
4307 if (state->attrs[i] != NULL) {
4308 VALID_CTXT();
Daniel Veillardea3f3982003-01-26 19:45:18 +00004309 VALID_ERROR("Invalid attribute %s for element %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00004310 state->attrs[i]->name, node->name);
4311 ret = -1;
4312 }
4313 }
4314 ctxt->state = oldstate;
4315 xmlRelaxNGFreeValidState(state);
4316 if (oldstate != NULL)
4317 oldstate->seq = node->next;
4318
4319
4320#ifdef DEBUG
4321 xmlGenericError(xmlGenericErrorContext,
4322 "xmlRelaxNGValidateDefinition(): validated %s : %d\n",
4323 node->name, ret);
4324#endif
4325 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004326 case XML_RELAXNG_OPTIONAL:
4327 oldflags = ctxt->flags;
4328 ctxt->flags |= FLAGS_IGNORABLE;
4329 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
4330 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
4331 if (ret != 0) {
4332 xmlRelaxNGFreeValidState(ctxt->state);
4333 ctxt->state = oldstate;
4334 ret = 0;
4335 break;
4336 }
4337 xmlRelaxNGFreeValidState(oldstate);
4338 ctxt->flags = oldflags;
4339 ret = 0;
4340 break;
4341 case XML_RELAXNG_ONEORMORE:
4342 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
4343 if (ret != 0) {
4344 break;
4345 }
4346 /* no break on purpose */
Daniel Veillard276be4a2003-01-24 01:03:34 +00004347 case XML_RELAXNG_ZEROORMORE: {
4348 xmlNodePtr cur, temp;
4349
Daniel Veillard6eadf632003-01-23 18:29:16 +00004350 oldflags = ctxt->flags;
4351 ctxt->flags |= FLAGS_IGNORABLE;
Daniel Veillard276be4a2003-01-24 01:03:34 +00004352 cur = ctxt->state->seq;
4353 temp = NULL;
4354 while ((cur != NULL) && (temp != cur)) {
4355 temp = cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004356 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
4357 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
4358 if (ret != 0) {
4359 xmlRelaxNGFreeValidState(ctxt->state);
4360 ctxt->state = oldstate;
4361 ret = 0;
4362 break;
4363 }
4364 xmlRelaxNGFreeValidState(oldstate);
Daniel Veillard276be4a2003-01-24 01:03:34 +00004365 cur = ctxt->state->seq;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004366 }
4367 ctxt->flags = oldflags;
4368 break;
Daniel Veillard276be4a2003-01-24 01:03:34 +00004369 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004370 case XML_RELAXNG_CHOICE: {
4371 xmlRelaxNGDefinePtr list = define->content;
4372
4373 oldflags = ctxt->flags;
4374 ctxt->flags |= FLAGS_IGNORABLE;
4375
4376 while (list != NULL) {
4377 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
4378 ret = xmlRelaxNGValidateDefinition(ctxt, list);
4379 if (ret == 0) {
4380 xmlRelaxNGFreeValidState(oldstate);
4381 break;
4382 }
4383 xmlRelaxNGFreeValidState(ctxt->state);
4384 ctxt->state = oldstate;
4385 list = list->next;
4386 }
4387 ctxt->flags = oldflags;
4388 break;
4389 }
4390 case XML_RELAXNG_GROUP: {
4391 xmlRelaxNGDefinePtr list = define->content;
4392
4393 while (list != NULL) {
4394 ret = xmlRelaxNGValidateDefinition(ctxt, list);
4395 if (ret != 0)
4396 break;
4397 list = list->next;
4398 }
4399 break;
4400 }
4401 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004402 ret = xmlRelaxNGValidateInterleave(ctxt, define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004403 break;
4404 case XML_RELAXNG_ATTRIBUTE:
4405 ret = xmlRelaxNGValidateAttribute(ctxt, define);
4406 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004407 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00004408 case XML_RELAXNG_REF:
4409 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
4410 break;
4411 case XML_RELAXNG_DEF:
4412 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
4413 break;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004414 case XML_RELAXNG_DATATYPE: {
4415 xmlChar *content;
4416
4417 content = xmlNodeGetContent(node);
4418 ret = xmlRelaxNGValidateDatatype(ctxt, content, define);
4419 if (ret == -1) {
4420 VALID_CTXT();
4421 VALID_ERROR("internal error validating %s\n", define->name);
4422 } else if (ret == 0) {
4423 ctxt->state->seq = node->next;
4424 }
4425 /*
4426 * TODO cover the problems with
4427 * <p>12<!-- comment -->34</p>
4428 * TODO detect full element coverage at compilation time.
4429 */
4430 if ((node != NULL) && (node->next != NULL)) {
4431 VALID_CTXT();
4432 VALID_ERROR("The data does not cover the full element %s\n",
4433 node->parent->name);
4434 ret = -1;
4435 }
4436 if (content != NULL)
4437 xmlFree(content);
4438 break;
4439 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00004440 case XML_RELAXNG_VALUE: {
4441 xmlChar *content;
4442 xmlChar *oldvalue;
4443
4444 content = xmlNodeGetContent(node);
4445 oldvalue = ctxt->state->value;
4446 ctxt->state->value = content;
4447 ret = xmlRelaxNGValidateValue(ctxt, define);
4448 ctxt->state->value = oldvalue;
4449 if (ret == -1) {
4450 VALID_CTXT();
4451 VALID_ERROR("internal error validating %s\n", define->name);
4452 } else if (ret == 0) {
4453 ctxt->state->seq = node->next;
4454 }
4455 /*
4456 * TODO cover the problems with
4457 * <p>12<!-- comment -->34</p>
4458 * TODO detect full element coverage at compilation time.
4459 */
4460 if ((node != NULL) && (node->next != NULL)) {
4461 VALID_CTXT();
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004462 VALID_ERROR("The value does not cover the full element %s\n",
Daniel Veillardea3f3982003-01-26 19:45:18 +00004463 node->parent->name);
4464 ret = -1;
4465 }
4466 if (content != NULL)
4467 xmlFree(content);
4468 break;
4469 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004470 case XML_RELAXNG_LIST: {
4471 xmlChar *content;
4472 xmlChar *oldvalue, *oldendvalue;
4473 int len;
Daniel Veillardea3f3982003-01-26 19:45:18 +00004474
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004475 content = xmlNodeGetContent(node);
4476 len = xmlStrlen(content);
4477 oldvalue = ctxt->state->value;
4478 oldendvalue = ctxt->state->endvalue;
4479 ctxt->state->value = content;
4480 ctxt->state->endvalue = content + len;
4481 ret = xmlRelaxNGValidateValue(ctxt, define);
4482 ctxt->state->value = oldvalue;
4483 ctxt->state->endvalue = oldendvalue;
4484 if (ret == -1) {
4485 VALID_CTXT();
4486 VALID_ERROR("internal error validating list\n");
4487 } else if (ret == 0) {
4488 ctxt->state->seq = node->next;
4489 }
4490 /*
4491 * TODO cover the problems with
4492 * <p>12<!-- comment -->34</p>
4493 * TODO detect full element coverage at compilation time.
4494 */
4495 if ((node != NULL) && (node->next != NULL)) {
4496 VALID_CTXT();
4497 VALID_ERROR("The list does not cover the full element %s\n",
4498 node->parent->name);
4499 ret = -1;
4500 }
4501 if (content != NULL)
4502 xmlFree(content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004503 break;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004504 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004505 case XML_RELAXNG_START:
4506 TODO
4507 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004508 }
4509 return(ret);
4510}
4511
4512/**
4513 * xmlRelaxNGValidateDocument:
4514 * @ctxt: a Relax-NG validation context
4515 * @doc: the document
4516 *
4517 * Validate the given document
4518 *
4519 * Returns 0 if the validation succeeded or an error code.
4520 */
4521static int
4522xmlRelaxNGValidateDocument(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
4523 int ret;
4524 xmlRelaxNGPtr schema;
4525 xmlRelaxNGGrammarPtr grammar;
4526 xmlRelaxNGValidStatePtr state;
4527
4528 if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL))
4529 return(-1);
4530
4531 schema = ctxt->schema;
4532 grammar = schema->topgrammar;
4533 if (grammar == NULL) {
4534 VALID_CTXT();
4535 VALID_ERROR("No top grammar defined\n");
4536 return(-1);
4537 }
4538 state = xmlRelaxNGNewValidState(ctxt, NULL);
4539 ctxt->state = state;
4540 ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start);
4541 state = ctxt->state;
4542 if ((state != NULL) && (state->seq != NULL)) {
4543 xmlNodePtr node;
4544
4545 node = state->seq;
4546 node = xmlRelaxNGSkipIgnored(ctxt, node);
4547 if (node != NULL) {
4548 VALID_CTXT();
4549 VALID_ERROR("extra data on the document\n");
4550 ret = -1;
4551 }
4552 }
4553 xmlRelaxNGFreeValidState(state);
4554
4555 return(ret);
4556}
4557
4558/************************************************************************
4559 * *
4560 * Validation interfaces *
4561 * *
4562 ************************************************************************/
4563/**
4564 * xmlRelaxNGNewValidCtxt:
4565 * @schema: a precompiled XML RelaxNGs
4566 *
4567 * Create an XML RelaxNGs validation context based on the given schema
4568 *
4569 * Returns the validation context or NULL in case of error
4570 */
4571xmlRelaxNGValidCtxtPtr
4572xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema) {
4573 xmlRelaxNGValidCtxtPtr ret;
4574
4575 ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt));
4576 if (ret == NULL) {
4577 xmlGenericError(xmlGenericErrorContext,
4578 "Failed to allocate new schama validation context\n");
4579 return (NULL);
4580 }
4581 memset(ret, 0, sizeof(xmlRelaxNGValidCtxt));
4582 ret->schema = schema;
4583 return (ret);
4584}
4585
4586/**
4587 * xmlRelaxNGFreeValidCtxt:
4588 * @ctxt: the schema validation context
4589 *
4590 * Free the resources associated to the schema validation context
4591 */
4592void
4593xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt) {
4594 if (ctxt == NULL)
4595 return;
4596 xmlFree(ctxt);
4597}
4598
4599/**
4600 * xmlRelaxNGSetValidErrors:
4601 * @ctxt: a Relax-NG validation context
4602 * @err: the error function
4603 * @warn: the warning function
4604 * @ctx: the functions context
4605 *
4606 * Set the error and warning callback informations
4607 */
4608void
4609xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
4610 xmlRelaxNGValidityErrorFunc err,
4611 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
4612 if (ctxt == NULL)
4613 return;
4614 ctxt->error = err;
4615 ctxt->warning = warn;
4616 ctxt->userData = ctx;
4617}
4618
4619/**
4620 * xmlRelaxNGValidateDoc:
4621 * @ctxt: a Relax-NG validation context
4622 * @doc: a parsed document tree
4623 *
4624 * Validate a document tree in memory.
4625 *
4626 * Returns 0 if the document is valid, a positive error code
4627 * number otherwise and -1 in case of internal or API error.
4628 */
4629int
4630xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
4631 int ret;
4632
4633 if ((ctxt == NULL) || (doc == NULL))
4634 return(-1);
4635
4636 ctxt->doc = doc;
4637
4638 ret = xmlRelaxNGValidateDocument(ctxt, doc);
4639 return(ret);
4640}
4641
4642#endif /* LIBXML_SCHEMAS_ENABLED */
4643