blob: 85c49a07b662d1772c3c88f4d00504f7822258cf [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 Veillarde431a272003-01-29 23:02:33 +00002706 xmlRelaxNGGrammarPtr old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002707
2708 if ((ctxt == NULL) || (node == NULL))
2709 return (NULL);
2710
2711 schema = xmlRelaxNGNewRelaxNG(ctxt);
2712 if (schema == NULL)
2713 return(NULL);
2714
Daniel Veillard276be4a2003-01-24 01:03:34 +00002715 olddefine = ctxt->define;
2716 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002717 if (IS_RELAXNG(node, "grammar")) {
2718 schema->topgrammar = xmlRelaxNGParseGrammar(ctxt, node->children);
2719 } else {
2720 schema->topgrammar = xmlRelaxNGNewGrammar(ctxt);
2721 if (schema->topgrammar == NULL) {
2722 return(schema);
2723 }
2724 schema->topgrammar->parent = NULL;
Daniel Veillarde431a272003-01-29 23:02:33 +00002725 old = ctxt->grammar;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002726 ctxt->grammar = schema->topgrammar;
2727 xmlRelaxNGParseStart(ctxt, node);
Daniel Veillarde431a272003-01-29 23:02:33 +00002728 if (old != NULL)
2729 ctxt->grammar = old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002730 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002731 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002732
2733#ifdef DEBUG
2734 if (schema == NULL)
2735 xmlGenericError(xmlGenericErrorContext,
2736 "xmlRelaxNGParseDocument() failed\n");
2737#endif
2738
2739 return (schema);
2740}
2741
2742/************************************************************************
2743 * *
2744 * Reading RelaxNGs *
2745 * *
2746 ************************************************************************/
2747
2748/**
2749 * xmlRelaxNGNewParserCtxt:
2750 * @URL: the location of the schema
2751 *
2752 * Create an XML RelaxNGs parse context for that file/resource expected
2753 * to contain an XML RelaxNGs file.
2754 *
2755 * Returns the parser context or NULL in case of error
2756 */
2757xmlRelaxNGParserCtxtPtr
2758xmlRelaxNGNewParserCtxt(const char *URL) {
2759 xmlRelaxNGParserCtxtPtr ret;
2760
2761 if (URL == NULL)
2762 return(NULL);
2763
2764 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
2765 if (ret == NULL) {
2766 xmlGenericError(xmlGenericErrorContext,
2767 "Failed to allocate new schama parser context for %s\n", URL);
2768 return (NULL);
2769 }
2770 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
2771 ret->URL = xmlStrdup((const xmlChar *)URL);
2772 return (ret);
2773}
2774
2775/**
2776 * xmlRelaxNGNewMemParserCtxt:
2777 * @buffer: a pointer to a char array containing the schemas
2778 * @size: the size of the array
2779 *
2780 * Create an XML RelaxNGs parse context for that memory buffer expected
2781 * to contain an XML RelaxNGs file.
2782 *
2783 * Returns the parser context or NULL in case of error
2784 */
2785xmlRelaxNGParserCtxtPtr
2786xmlRelaxNGNewMemParserCtxt(const char *buffer, int size) {
2787 xmlRelaxNGParserCtxtPtr ret;
2788
2789 if ((buffer == NULL) || (size <= 0))
2790 return(NULL);
2791
2792 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
2793 if (ret == NULL) {
2794 xmlGenericError(xmlGenericErrorContext,
2795 "Failed to allocate new schama parser context\n");
2796 return (NULL);
2797 }
2798 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
2799 ret->buffer = buffer;
2800 ret->size = size;
2801 return (ret);
2802}
2803
2804/**
2805 * xmlRelaxNGFreeParserCtxt:
2806 * @ctxt: the schema parser context
2807 *
2808 * Free the resources associated to the schema parser context
2809 */
2810void
2811xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxtPtr ctxt) {
2812 if (ctxt == NULL)
2813 return;
2814 if (ctxt->URL != NULL)
2815 xmlFree(ctxt->URL);
2816 if (ctxt->doc != NULL)
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002817 xmlFreeDoc(ctxt->document);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002818 if (ctxt->interleaves != NULL)
2819 xmlHashFree(ctxt->interleaves, NULL);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002820 if (ctxt->documents != NULL)
2821 xmlHashFree(ctxt->documents, (xmlHashDeallocator)
2822 xmlRelaxNGFreeDocument);
2823 if (ctxt->docTab != NULL)
2824 xmlFree(ctxt->docTab);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002825 xmlFree(ctxt);
2826}
2827
Daniel Veillard6eadf632003-01-23 18:29:16 +00002828/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002829 * xmlRelaxNGCleanupDoc:
2830 * @ctxt: a Relax-NG parser context
2831 * @doc: an xmldocPtr document pointer
Daniel Veillard6eadf632003-01-23 18:29:16 +00002832 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002833 * Cleanup the document from unwanted nodes for parsing, resolve
2834 * Include and externalRef lookups.
Daniel Veillard6eadf632003-01-23 18:29:16 +00002835 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002836 * Returns the cleaned up document or NULL in case of error
Daniel Veillard6eadf632003-01-23 18:29:16 +00002837 */
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002838static xmlDocPtr
2839xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt, xmlDocPtr doc) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00002840 xmlNodePtr root, cur, delete;
2841
Daniel Veillard6eadf632003-01-23 18:29:16 +00002842 /*
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002843 * Extract the root
Daniel Veillard6eadf632003-01-23 18:29:16 +00002844 */
2845 root = xmlDocGetRootElement(doc);
2846 if (root == NULL) {
2847 if (ctxt->error != NULL)
2848 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
2849 ctxt->URL);
2850 ctxt->nbErrors++;
2851 return (NULL);
2852 }
2853
2854 /*
2855 * Remove all the blank text nodes
2856 */
2857 delete = NULL;
2858 cur = root;
2859 while (cur != NULL) {
2860 if (delete != NULL) {
2861 xmlUnlinkNode(delete);
2862 xmlFreeNode(delete);
2863 delete = NULL;
2864 }
2865 if (cur->type == XML_ELEMENT_NODE) {
2866 /*
2867 * Simplification 4.1. Annotations
2868 */
2869 if ((cur->ns == NULL) ||
2870 (!xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
2871 delete = cur;
2872 goto skip_children;
2873 } else {
2874 if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002875 xmlChar *href, *ns, *base, *URL;
2876 xmlRelaxNGDocumentPtr docu;
2877
2878 ns = xmlGetProp(cur, BAD_CAST "ns");
2879 href = xmlGetProp(cur, BAD_CAST "href");
2880 if (href == NULL) {
2881 if (ctxt->error != NULL)
2882 ctxt->error(ctxt->userData,
2883 "xmlRelaxNGParse: externalRef has no href attribute\n");
2884 ctxt->nbErrors++;
2885 delete = cur;
2886 goto skip_children;
2887 }
2888 base = xmlNodeGetBase(cur->doc, cur);
2889 URL = xmlBuildURI(href, base);
2890 if (URL == NULL) {
2891 if (ctxt->error != NULL)
2892 ctxt->error(ctxt->userData,
2893 "Failed to compute URL for externalRef %s\n", href);
2894 ctxt->nbErrors++;
2895 if (href != NULL)
2896 xmlFree(href);
2897 if (base != NULL)
2898 xmlFree(base);
2899 delete = cur;
2900 goto skip_children;
2901 }
2902 if (href != NULL)
2903 xmlFree(href);
2904 if (base != NULL)
2905 xmlFree(base);
2906 docu = xmlRelaxNGLoaddocument(ctxt, URL, ns);
2907 if (docu == NULL) {
2908 if (ctxt->error != NULL)
2909 ctxt->error(ctxt->userData,
2910 "Failed to load externalRef %s\n", URL);
2911 ctxt->nbErrors++;
2912 xmlFree(URL);
2913 delete = cur;
2914 goto skip_children;
2915 }
2916 xmlFree(URL);
2917 cur->_private = docu;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002918 } else if (xmlStrEqual(cur->name, BAD_CAST "include")) {
2919 TODO
2920 } else if ((xmlStrEqual(cur->name, BAD_CAST "element")) ||
2921 (xmlStrEqual(cur->name, BAD_CAST "attribute"))) {
2922 xmlChar *name;
2923 xmlNodePtr text = NULL;
2924
2925 /*
2926 * Simplification 4.8. name attribute of element
2927 * and attribute elements
2928 */
2929 name = xmlGetProp(cur, BAD_CAST "name");
2930 if (name != NULL) {
2931 if (cur->children == NULL) {
2932 text = xmlNewChild(cur, cur->ns, BAD_CAST "name",
2933 name);
2934 } else {
2935 xmlNodePtr node;
2936 node = xmlNewNode(cur->ns, BAD_CAST "name");
2937 if (node != NULL) {
2938 xmlAddPrevSibling(cur->children, node);
2939 text = xmlNewText(name);
2940 xmlAddChild(node, text);
2941 text = node;
2942 }
2943 }
2944 xmlUnsetProp(cur, BAD_CAST "name");
2945 xmlFree(name);
2946 }
2947 if (xmlStrEqual(cur->name, BAD_CAST "attribute")) {
2948 if (text == NULL) {
2949 text = cur->children;
2950 while (text != NULL) {
2951 if ((text->type == XML_ELEMENT_NODE) &&
2952 (xmlStrEqual(text->name, BAD_CAST "name")))
2953 break;
2954 text = text->next;
2955 }
2956 }
2957 if (text == NULL) {
2958 if (ctxt->error != NULL)
2959 ctxt->error(ctxt->userData,
2960 "xmlRelaxNGParse: attribute without name\n");
2961 ctxt->nbErrors++;
2962 } else {
2963 xmlSetProp(text, BAD_CAST "ns", BAD_CAST "");
2964 }
2965 }
2966 } else if ((xmlStrEqual(cur->name, BAD_CAST "name")) ||
2967 (xmlStrEqual(cur->name, BAD_CAST "nsName")) ||
2968 (xmlStrEqual(cur->name, BAD_CAST "value"))) {
2969 /*
2970 * Simplification 4.8. name attribute of element
2971 * and attribute elements
2972 */
2973 if (xmlHasProp(cur, BAD_CAST "ns") == NULL) {
2974 xmlNodePtr node;
2975 xmlChar *ns = NULL;
2976
2977 node = cur->parent;
2978 while ((node != NULL) &&
2979 (node->type == XML_ELEMENT_NODE)) {
2980 ns = xmlGetProp(node, BAD_CAST "ns");
2981 if (ns != NULL) {
2982 break;
2983 }
2984 node = node->parent;
2985 }
2986 if (ns == NULL) {
2987 xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
2988 } else {
2989 xmlSetProp(cur, BAD_CAST "ns", ns);
2990 xmlFree(ns);
2991 }
2992 }
2993 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
2994 xmlChar *name, *local, *prefix;
2995
2996 /*
2997 * Simplification: 4.10. QNames
2998 */
2999 name = xmlNodeGetContent(cur);
3000 if (name != NULL) {
3001 local = xmlSplitQName2(name, &prefix);
3002 if (local != NULL) {
3003 xmlNsPtr ns;
3004
3005 ns = xmlSearchNs(cur->doc, cur, prefix);
3006 if (ns == NULL) {
3007 if (ctxt->error != NULL)
3008 ctxt->error(ctxt->userData,
3009 "xmlRelaxNGParse: no namespace for prefix %s\n", prefix);
3010 ctxt->nbErrors++;
3011 } else {
3012 xmlSetProp(cur, BAD_CAST "ns", ns->href);
3013 xmlNodeSetContent(cur, local);
3014 }
3015 xmlFree(local);
3016 xmlFree(prefix);
3017 }
3018 xmlFree(name);
3019 }
3020 }
3021 }
3022 }
3023 }
3024 /*
3025 * Simplification 4.2 whitespaces
3026 */
3027 else if (cur->type == XML_TEXT_NODE) {
3028 if (IS_BLANK_NODE(cur)) {
3029 if (cur->parent->type == XML_ELEMENT_NODE) {
3030 if ((!xmlStrEqual(cur->parent->name, BAD_CAST "value")) &&
3031 (!xmlStrEqual(cur->parent->name, BAD_CAST "param")))
3032 delete = cur;
3033 } else {
3034 delete = cur;
3035 goto skip_children;
3036 }
3037 }
3038 } else if (cur->type != XML_CDATA_SECTION_NODE) {
3039 delete = cur;
3040 goto skip_children;
3041 }
3042
3043 /*
3044 * Skip to next node
3045 */
3046 if (cur->children != NULL) {
3047 if ((cur->children->type != XML_ENTITY_DECL) &&
3048 (cur->children->type != XML_ENTITY_REF_NODE) &&
3049 (cur->children->type != XML_ENTITY_NODE)) {
3050 cur = cur->children;
3051 continue;
3052 }
3053 }
3054skip_children:
3055 if (cur->next != NULL) {
3056 cur = cur->next;
3057 continue;
3058 }
3059
3060 do {
3061 cur = cur->parent;
3062 if (cur == NULL)
3063 break;
3064 if (cur == root) {
3065 cur = NULL;
3066 break;
3067 }
3068 if (cur->next != NULL) {
3069 cur = cur->next;
3070 break;
3071 }
3072 } while (cur != NULL);
3073 }
3074 if (delete != NULL) {
3075 xmlUnlinkNode(delete);
3076 xmlFreeNode(delete);
3077 delete = NULL;
3078 }
3079
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003080 return(doc);
3081}
3082
3083/**
3084 * xmlRelaxNGParse:
3085 * @ctxt: a Relax-NG parser context
3086 *
3087 * parse a schema definition resource and build an internal
3088 * XML Shema struture which can be used to validate instances.
3089 * *WARNING* this interface is highly subject to change
3090 *
3091 * Returns the internal XML RelaxNG structure built from the resource or
3092 * NULL in case of error
3093 */
3094xmlRelaxNGPtr
3095xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt)
3096{
3097 xmlRelaxNGPtr ret = NULL;
3098 xmlDocPtr doc;
3099 xmlNodePtr root;
3100
3101 xmlRelaxNGInitTypes();
3102
3103 if (ctxt == NULL)
3104 return (NULL);
3105
3106 /*
3107 * First step is to parse the input document into an DOM/Infoset
3108 */
3109 if (ctxt->URL != NULL) {
3110 doc = xmlParseFile((const char *) ctxt->URL);
3111 if (doc == NULL) {
3112 if (ctxt->error != NULL)
3113 ctxt->error(ctxt->userData,
3114 "xmlRelaxNGParse: could not load %s\n", ctxt->URL);
3115 ctxt->nbErrors++;
3116 return (NULL);
3117 }
3118 } else if (ctxt->buffer != NULL) {
3119 doc = xmlParseMemory(ctxt->buffer, ctxt->size);
3120 if (doc == NULL) {
3121 if (ctxt->error != NULL)
3122 ctxt->error(ctxt->userData,
3123 "xmlRelaxNGParse: could not parse schemas\n");
3124 ctxt->nbErrors++;
3125 return (NULL);
3126 }
3127 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
3128 ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
3129 } else {
3130 if (ctxt->error != NULL)
3131 ctxt->error(ctxt->userData,
3132 "xmlRelaxNGParse: nothing to parse\n");
3133 ctxt->nbErrors++;
3134 return (NULL);
3135 }
3136 ctxt->document = doc;
3137
3138 /*
3139 * Some preprocessing of the document content
3140 */
3141 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
3142 if (doc == NULL) {
3143 xmlFreeDoc(ctxt->document);
3144 ctxt->document = NULL;
3145 return(NULL);
3146 }
3147
Daniel Veillard6eadf632003-01-23 18:29:16 +00003148 /*
3149 * Then do the parsing for good
3150 */
3151 root = xmlDocGetRootElement(doc);
3152 if (root == NULL) {
3153 if (ctxt->error != NULL)
3154 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
3155 ctxt->URL);
3156 ctxt->nbErrors++;
3157 return (NULL);
3158 }
3159 ret = xmlRelaxNGParseDocument(ctxt, root);
3160 if (ret == NULL)
3161 return(NULL);
3162
3163 /*
3164 * Check the ref/defines links
3165 */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003166 /*
3167 * try to preprocess interleaves
3168 */
3169 if (ctxt->interleaves != NULL) {
3170 xmlHashScan(ctxt->interleaves,
3171 (xmlHashScanner)xmlRelaxNGComputeInterleaves, ctxt);
3172 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003173
3174 /*
3175 * if there was a parsing error return NULL
3176 */
3177 if (ctxt->nbErrors > 0) {
3178 xmlRelaxNGFree(ret);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003179 ctxt->document = NULL;
3180 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003181 return(NULL);
3182 }
3183
3184 /*
3185 * Transfer the pointer for cleanup at the schema level.
3186 */
3187 ret->doc = doc;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003188 ctxt->document = NULL;
3189 ret->documents = ctxt->documents;
3190 ctxt->documents = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003191
3192 return (ret);
3193}
3194
3195/**
3196 * xmlRelaxNGSetParserErrors:
3197 * @ctxt: a Relax-NG validation context
3198 * @err: the error callback
3199 * @warn: the warning callback
3200 * @ctx: contextual data for the callbacks
3201 *
3202 * Set the callback functions used to handle errors for a validation context
3203 */
3204void
3205xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
3206 xmlRelaxNGValidityErrorFunc err,
3207 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
3208 if (ctxt == NULL)
3209 return;
3210 ctxt->error = err;
3211 ctxt->warning = warn;
3212 ctxt->userData = ctx;
3213}
3214/************************************************************************
3215 * *
3216 * Dump back a compiled form *
3217 * *
3218 ************************************************************************/
3219static void xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define);
3220
3221/**
3222 * xmlRelaxNGDumpDefines:
3223 * @output: the file output
3224 * @defines: a list of define structures
3225 *
3226 * Dump a RelaxNG structure back
3227 */
3228static void
3229xmlRelaxNGDumpDefines(FILE * output, xmlRelaxNGDefinePtr defines) {
3230 while (defines != NULL) {
3231 xmlRelaxNGDumpDefine(output, defines);
3232 defines = defines->next;
3233 }
3234}
3235
3236/**
3237 * xmlRelaxNGDumpDefine:
3238 * @output: the file output
3239 * @define: a define structure
3240 *
3241 * Dump a RelaxNG structure back
3242 */
3243static void
3244xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define) {
3245 if (define == NULL)
3246 return;
3247 switch(define->type) {
3248 case XML_RELAXNG_EMPTY:
3249 fprintf(output, "<empty/>\n");
3250 break;
3251 case XML_RELAXNG_NOT_ALLOWED:
3252 fprintf(output, "<notAllowed/>\n");
3253 break;
3254 case XML_RELAXNG_TEXT:
3255 fprintf(output, "<text/>\n");
3256 break;
3257 case XML_RELAXNG_ELEMENT:
3258 fprintf(output, "<element>\n");
3259 if (define->name != NULL) {
3260 fprintf(output, "<name");
3261 if (define->ns != NULL)
3262 fprintf(output, " ns=\"%s\"", define->ns);
3263 fprintf(output, ">%s</name>\n", define->name);
3264 }
3265 xmlRelaxNGDumpDefines(output, define->attrs);
3266 xmlRelaxNGDumpDefines(output, define->content);
3267 fprintf(output, "</element>\n");
3268 break;
3269 case XML_RELAXNG_LIST:
3270 fprintf(output, "<list>\n");
3271 xmlRelaxNGDumpDefines(output, define->content);
3272 fprintf(output, "</list>\n");
3273 break;
3274 case XML_RELAXNG_ONEORMORE:
3275 fprintf(output, "<oneOrMore>\n");
3276 xmlRelaxNGDumpDefines(output, define->content);
3277 fprintf(output, "</oneOrMore>\n");
3278 break;
3279 case XML_RELAXNG_ZEROORMORE:
3280 fprintf(output, "<zeroOrMore>\n");
3281 xmlRelaxNGDumpDefines(output, define->content);
3282 fprintf(output, "</zeroOrMore>\n");
3283 break;
3284 case XML_RELAXNG_CHOICE:
3285 fprintf(output, "<choice>\n");
3286 xmlRelaxNGDumpDefines(output, define->content);
3287 fprintf(output, "</choice>\n");
3288 break;
3289 case XML_RELAXNG_GROUP:
3290 fprintf(output, "<group>\n");
3291 xmlRelaxNGDumpDefines(output, define->content);
3292 fprintf(output, "</group>\n");
3293 break;
3294 case XML_RELAXNG_INTERLEAVE:
3295 fprintf(output, "<interleave>\n");
3296 xmlRelaxNGDumpDefines(output, define->content);
3297 fprintf(output, "</interleave>\n");
3298 break;
3299 case XML_RELAXNG_OPTIONAL:
3300 fprintf(output, "<optional>\n");
3301 xmlRelaxNGDumpDefines(output, define->content);
3302 fprintf(output, "</optional>\n");
3303 break;
3304 case XML_RELAXNG_ATTRIBUTE:
3305 fprintf(output, "<attribute>\n");
3306 xmlRelaxNGDumpDefines(output, define->content);
3307 fprintf(output, "</attribute>\n");
3308 break;
3309 case XML_RELAXNG_DEF:
3310 fprintf(output, "<define");
3311 if (define->name != NULL)
3312 fprintf(output, " name=\"%s\"", define->name);
3313 fprintf(output, ">\n");
3314 xmlRelaxNGDumpDefines(output, define->content);
3315 fprintf(output, "</define>\n");
3316 break;
3317 case XML_RELAXNG_REF:
3318 fprintf(output, "<ref");
3319 if (define->name != NULL)
3320 fprintf(output, " name=\"%s\"", define->name);
3321 fprintf(output, ">\n");
3322 xmlRelaxNGDumpDefines(output, define->content);
3323 fprintf(output, "</ref>\n");
3324 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003325 case XML_RELAXNG_EXTERNALREF:
Daniel Veillarde431a272003-01-29 23:02:33 +00003326 fprintf(output, "<externalRef");
3327 xmlRelaxNGDumpDefines(output, define->content);
3328 fprintf(output, "</externalRef>\n");
3329 break;
3330 case XML_RELAXNG_DATATYPE:
Daniel Veillard6eadf632003-01-23 18:29:16 +00003331 case XML_RELAXNG_VALUE:
3332 TODO
3333 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003334 case XML_RELAXNG_START:
3335 TODO
3336 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003337 }
3338}
3339
3340/**
3341 * xmlRelaxNGDumpGrammar:
3342 * @output: the file output
3343 * @grammar: a grammar structure
3344 * @top: is this a top grammar
3345 *
3346 * Dump a RelaxNG structure back
3347 */
3348static void
3349xmlRelaxNGDumpGrammar(FILE * output, xmlRelaxNGGrammarPtr grammar, int top)
3350{
3351 if (grammar == NULL)
3352 return;
3353
3354 fprintf(output, "<grammar");
3355 if (top)
3356 fprintf(output,
3357 " xmlns=\"http://relaxng.org/ns/structure/1.0\"");
3358 switch(grammar->combine) {
3359 case XML_RELAXNG_COMBINE_UNDEFINED:
3360 break;
3361 case XML_RELAXNG_COMBINE_CHOICE:
3362 fprintf(output, " combine=\"choice\"");
3363 break;
3364 case XML_RELAXNG_COMBINE_INTERLEAVE:
3365 fprintf(output, " combine=\"interleave\"");
3366 break;
3367 default:
3368 fprintf(output, " <!-- invalid combine value -->");
3369 }
3370 fprintf(output, ">\n");
3371 if (grammar->start == NULL) {
3372 fprintf(output, " <!-- grammar had no start -->");
3373 } else {
3374 fprintf(output, "<start>\n");
3375 xmlRelaxNGDumpDefine(output, grammar->start);
3376 fprintf(output, "</start>\n");
3377 }
3378 /* TODO ? Dump the defines ? */
3379 fprintf(output, "</grammar>\n");
3380}
3381
3382/**
3383 * xmlRelaxNGDump:
3384 * @output: the file output
3385 * @schema: a schema structure
3386 *
3387 * Dump a RelaxNG structure back
3388 */
3389void
3390xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema)
3391{
3392 if (schema == NULL) {
3393 fprintf(output, "RelaxNG empty or failed to compile\n");
3394 return;
3395 }
3396 fprintf(output, "RelaxNG: ");
3397 if (schema->doc == NULL) {
3398 fprintf(output, "no document\n");
3399 } else if (schema->doc->URL != NULL) {
3400 fprintf(output, "%s\n", schema->doc->URL);
3401 } else {
3402 fprintf(output, "\n");
3403 }
3404 if (schema->topgrammar == NULL) {
3405 fprintf(output, "RelaxNG has no top grammar\n");
3406 return;
3407 }
3408 xmlRelaxNGDumpGrammar(output, schema->topgrammar, 1);
3409}
3410
3411/************************************************************************
3412 * *
3413 * Validation implementation *
3414 * *
3415 ************************************************************************/
3416static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
3417 xmlRelaxNGDefinePtr define);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00003418static int xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
3419 xmlRelaxNGDefinePtr define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003420
3421/**
3422 * xmlRelaxNGSkipIgnored:
3423 * @ctxt: a schema validation context
3424 * @node: the top node.
3425 *
3426 * Skip ignorable nodes in that context
3427 *
3428 * Returns the new sibling or NULL in case of error.
3429 */
3430static xmlNodePtr
3431xmlRelaxNGSkipIgnored(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
3432 xmlNodePtr node) {
3433 /*
3434 * TODO complete and handle entities
3435 */
3436 while ((node != NULL) &&
3437 ((node->type == XML_COMMENT_NODE) ||
3438 ((node->type == XML_TEXT_NODE) &&
3439 (IS_BLANK_NODE(node))))) {
3440 node = node->next;
3441 }
3442 return(node);
3443}
3444
3445/**
Daniel Veillardedc91922003-01-26 00:52:04 +00003446 * xmlRelaxNGNormalize:
3447 * @ctxt: a schema validation context
3448 * @str: the string to normalize
3449 *
3450 * Implements the normalizeWhiteSpace( s ) function from
3451 * section 6.2.9 of the spec
3452 *
3453 * Returns the new string or NULL in case of error.
3454 */
3455static xmlChar *
3456xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *str) {
3457 xmlChar *ret, *p;
3458 const xmlChar *tmp;
3459 int len;
3460
3461 if (str == NULL)
3462 return(NULL);
3463 tmp = str;
3464 while (*tmp != 0) tmp++;
3465 len = tmp - str;
3466
3467 ret = (xmlChar *) xmlMalloc((len + 1) * sizeof(xmlChar));
3468 if (ret == NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00003469 if (ctxt != NULL) {
3470 VALID_CTXT();
3471 VALID_ERROR("xmlRelaxNGNormalize: out of memory\n");
3472 } else {
3473 xmlGenericError(xmlGenericErrorContext,
3474 "xmlRelaxNGNormalize: out of memory\n");
3475 }
Daniel Veillardedc91922003-01-26 00:52:04 +00003476 return(NULL);
3477 }
3478 p = ret;
3479 while (IS_BLANK(*str)) str++;
3480 while (*str != 0) {
3481 if (IS_BLANK(*str)) {
3482 while (IS_BLANK(*str)) str++;
3483 if (*str == 0)
3484 break;
3485 *p++ = ' ';
3486 } else
3487 *p++ = *str++;
3488 }
3489 *p = 0;
3490 return(ret);
3491}
3492
3493/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00003494 * xmlRelaxNGValidateDatatype:
3495 * @ctxt: a Relax-NG validation context
3496 * @value: the string value
3497 * @type: the datatype definition
3498 *
3499 * Validate the given value against the dataype
3500 *
3501 * Returns 0 if the validation succeeded or an error code.
3502 */
3503static int
3504xmlRelaxNGValidateDatatype(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *value,
3505 xmlRelaxNGDefinePtr define) {
3506 int ret;
3507 xmlRelaxNGTypeLibraryPtr lib;
3508
3509 if ((define == NULL) || (define->data == NULL)) {
3510 return(-1);
3511 }
3512 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
3513 if (lib->check != NULL)
3514 ret = lib->check(lib->data, define->name, value);
3515 else
3516 ret = -1;
3517 if (ret < 0) {
3518 VALID_CTXT();
3519 VALID_ERROR("Internal: failed to validate type %s\n", define->name);
3520 return(-1);
3521 } else if (ret == 1) {
3522 ret = 0;
3523 } else {
3524 VALID_CTXT();
3525 VALID_ERROR("Type %s doesn't allow value %s\n", define->name, value);
3526 return(-1);
3527 ret = -1;
3528 }
3529 return(ret);
3530}
3531
3532/**
Daniel Veillardc6e997c2003-01-27 12:35:42 +00003533 * xmlRelaxNGNextValue:
3534 * @ctxt: a Relax-NG validation context
3535 *
3536 * Skip to the next value when validating within a list
3537 *
3538 * Returns 0 if the operation succeeded or an error code.
3539 */
3540static int
3541xmlRelaxNGNextValue(xmlRelaxNGValidCtxtPtr ctxt) {
3542 xmlChar *cur;
3543
3544 cur = ctxt->state->value;
3545 if ((cur == NULL) || (ctxt->state->endvalue == NULL)) {
3546 ctxt->state->value = NULL;
3547 return(0);
3548 }
3549 while (*cur != 0) cur++;
3550 while ((cur != ctxt->state->endvalue) && (*cur == 0)) cur++;
3551 if (cur == ctxt->state->endvalue)
3552 ctxt->state->value = NULL;
3553 else
3554 ctxt->state->value = cur;
3555 return(0);
3556}
3557
3558/**
3559 * xmlRelaxNGValidateValueList:
3560 * @ctxt: a Relax-NG validation context
3561 * @defines: the list of definitions to verify
3562 *
3563 * Validate the given set of definitions for the current value
3564 *
3565 * Returns 0 if the validation succeeded or an error code.
3566 */
3567static int
3568xmlRelaxNGValidateValueList(xmlRelaxNGValidCtxtPtr ctxt,
3569 xmlRelaxNGDefinePtr defines) {
3570 int ret = 0;
3571
3572 while (defines != NULL) {
3573 ret = xmlRelaxNGValidateValue(ctxt, defines);
3574 if (ret != 0)
3575 break;
3576 defines = defines->next;
3577 }
3578 return(ret);
3579}
3580
3581/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00003582 * xmlRelaxNGValidateValue:
3583 * @ctxt: a Relax-NG validation context
3584 * @define: the definition to verify
3585 *
3586 * Validate the given definition for the current value
3587 *
3588 * Returns 0 if the validation succeeded or an error code.
3589 */
3590static int
3591xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
3592 xmlRelaxNGDefinePtr define) {
Daniel Veillardedc91922003-01-26 00:52:04 +00003593 int ret = 0, oldflags;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003594 xmlChar *value;
3595
3596 value = ctxt->state->value;
3597 switch (define->type) {
3598 case XML_RELAXNG_EMPTY:
3599 if ((value != NULL) && (value[0] != '0'))
3600 ret = -1;
3601 break;
3602 case XML_RELAXNG_TEXT:
3603 break;
Daniel Veillardedc91922003-01-26 00:52:04 +00003604 case XML_RELAXNG_VALUE: {
3605 if (!xmlStrEqual(value, define->value)) {
3606 if (define->name != NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00003607 xmlRelaxNGTypeLibraryPtr lib;
3608
3609 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
3610 if ((lib != NULL) && (lib->comp != NULL))
3611 ret = lib->comp(lib->data, define->name, value,
3612 define->value);
3613 else
3614 ret = -1;
3615 if (ret < 0) {
3616 VALID_CTXT();
3617 VALID_ERROR("Internal: failed to compare type %s\n",
3618 define->name);
3619 return(-1);
3620 } else if (ret == 1) {
3621 ret = 0;
3622 } else {
3623 ret = -1;
3624 }
Daniel Veillardedc91922003-01-26 00:52:04 +00003625 } else {
3626 xmlChar *nval, *nvalue;
3627
3628 /*
3629 * TODO: trivial optimizations are possible by
3630 * computing at compile-time
3631 */
3632 nval = xmlRelaxNGNormalize(ctxt, define->value);
3633 nvalue = xmlRelaxNGNormalize(ctxt, value);
3634
Daniel Veillardea3f3982003-01-26 19:45:18 +00003635 if ((nval == NULL) || (nvalue == NULL) ||
3636 (!xmlStrEqual(nval, nvalue)))
Daniel Veillardedc91922003-01-26 00:52:04 +00003637 ret = -1;
3638 if (nval != NULL)
3639 xmlFree(nval);
3640 if (nvalue != NULL)
3641 xmlFree(nvalue);
3642 }
3643 }
3644 break;
3645 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00003646 case XML_RELAXNG_DATATYPE: {
3647 ret = xmlRelaxNGValidateDatatype(ctxt, value, define);
3648 if (ret == 0)
3649 xmlRelaxNGNextValue(ctxt);
3650
3651 break;
3652 }
3653 case XML_RELAXNG_CHOICE: {
3654 xmlRelaxNGDefinePtr list = define->content;
3655 xmlChar *oldvalue;
3656
3657 oldflags = ctxt->flags;
3658 ctxt->flags |= FLAGS_IGNORABLE;
3659
3660 oldvalue = ctxt->state->value;
3661 while (list != NULL) {
3662 ret = xmlRelaxNGValidateValue(ctxt, list);
3663 if (ret == 0) {
3664 break;
3665 }
3666 ctxt->state->value = oldvalue;
3667 list = list->next;
3668 }
3669 ctxt->flags = oldflags;
3670 break;
3671 }
3672 case XML_RELAXNG_LIST: {
3673 xmlRelaxNGDefinePtr list = define->content;
3674 xmlChar *oldvalue, *oldend, *val, *cur;
3675
3676 oldvalue = ctxt->state->value;
3677 oldend = ctxt->state->endvalue;
3678
3679 val = xmlStrdup(oldvalue);
3680 if (val == NULL) {
3681 VALID_CTXT();
3682 VALID_ERROR("Internal: no state\n");
3683 return(-1);
3684 }
3685 cur = val;
3686 while (*cur != 0) {
3687 if (IS_BLANK(*cur))
3688 *cur = 0;
3689 cur++;
3690 }
3691 ctxt->state->endvalue = cur;
3692 cur = val;
3693 while ((*cur == 0) && (cur != ctxt->state->endvalue)) cur++;
3694
3695 ctxt->state->value = cur;
3696
3697 while (list != NULL) {
3698 ret = xmlRelaxNGValidateValue(ctxt, list);
3699 if (ret != 0) {
3700 break;
3701 }
3702 list = list->next;
3703 }
3704 if ((ret == 0) && (ctxt->state->value != NULL) &&
3705 (ctxt->state->value != ctxt->state->endvalue)) {
3706 VALID_CTXT();
3707 VALID_ERROR("Extra data in list: %s\n", ctxt->state->value);
3708 ret = -1;
3709 }
3710 xmlFree(val);
3711 ctxt->state->value = oldvalue;
3712 ctxt->state->endvalue = oldend;
3713 break;
3714 }
3715 case XML_RELAXNG_ONEORMORE:
3716 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
3717 if (ret != 0) {
3718 break;
3719 }
3720 /* no break on purpose */
3721 case XML_RELAXNG_ZEROORMORE: {
3722 xmlChar *cur, *temp;
3723
3724 oldflags = ctxt->flags;
3725 ctxt->flags |= FLAGS_IGNORABLE;
3726 cur = ctxt->state->value;
3727 temp = NULL;
3728 while ((cur != NULL) && (cur != ctxt->state->endvalue) &&
3729 (temp != cur)) {
3730 temp = cur;
3731 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
3732 if (ret != 0) {
3733 ctxt->state->value = temp;
3734 ret = 0;
3735 break;
3736 }
3737 cur = ctxt->state->value;
3738 }
3739 ctxt->flags = oldflags;
3740 break;
3741 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003742 default:
3743 TODO
3744 ret = -1;
3745 }
3746 return(ret);
3747}
3748
3749/**
3750 * xmlRelaxNGValidateValueContent:
3751 * @ctxt: a Relax-NG validation context
3752 * @defines: the list of definitions to verify
3753 *
3754 * Validate the given definitions for the current value
3755 *
3756 * Returns 0 if the validation succeeded or an error code.
3757 */
3758static int
3759xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt,
3760 xmlRelaxNGDefinePtr defines) {
3761 int ret = 0;
3762
3763 while (defines != NULL) {
3764 ret = xmlRelaxNGValidateValue(ctxt, defines);
3765 if (ret != 0)
3766 break;
3767 defines = defines->next;
3768 }
3769 return(ret);
3770}
3771
3772/**
3773 * xmlRelaxNGValidateAttribute:
3774 * @ctxt: a Relax-NG validation context
3775 * @define: the definition to verify
3776 *
3777 * Validate the given attribute definition for that node
3778 *
3779 * Returns 0 if the validation succeeded or an error code.
3780 */
3781static int
3782xmlRelaxNGValidateAttribute(xmlRelaxNGValidCtxtPtr ctxt,
3783 xmlRelaxNGDefinePtr define) {
3784 int ret = 0, i;
3785 xmlChar *value, *oldvalue;
3786 xmlAttrPtr prop = NULL, tmp;
3787
3788 if (define->name != NULL) {
3789 for (i = 0;i < ctxt->state->nbAttrs;i++) {
3790 tmp = ctxt->state->attrs[i];
3791 if ((tmp != NULL) && (xmlStrEqual(define->name, tmp->name))) {
3792 if ((((define->ns == NULL) || (define->ns[0] == 0)) &&
3793 (tmp->ns == NULL)) ||
3794 ((tmp->ns != NULL) &&
3795 (xmlStrEqual(define->ns, tmp->ns->href)))) {
3796 prop = tmp;
3797 break;
3798 }
3799 }
3800 }
3801 if (prop != NULL) {
3802 value = xmlNodeListGetString(prop->doc, prop->children, 1);
3803 oldvalue = ctxt->state->value;
3804 ctxt->state->value = value;
3805 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
3806 value = ctxt->state->value;
3807 ctxt->state->value = oldvalue;
3808 if (value != NULL)
3809 xmlFree(value);
3810 if (ret == 0) {
3811 /*
3812 * flag the attribute as processed
3813 */
3814 ctxt->state->attrs[i] = NULL;
3815 }
3816 } else {
3817 ret = -1;
3818 }
3819#ifdef DEBUG
3820 xmlGenericError(xmlGenericErrorContext,
3821 "xmlRelaxNGValidateAttribute(%s): %d\n", define->name, ret);
3822#endif
3823 } else {
3824 TODO
3825 }
3826
3827 return(ret);
3828}
3829
3830/**
3831 * xmlRelaxNGValidateAttributeList:
3832 * @ctxt: a Relax-NG validation context
3833 * @define: the list of definition to verify
3834 *
3835 * Validate the given node against the list of attribute definitions
3836 *
3837 * Returns 0 if the validation succeeded or an error code.
3838 */
3839static int
3840xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt,
3841 xmlRelaxNGDefinePtr defines) {
3842 int ret = 0;
3843 while (defines != NULL) {
3844 if (xmlRelaxNGValidateAttribute(ctxt, defines) != 0)
3845 ret = -1;
3846 defines = defines->next;
3847 }
3848 return(ret);
3849}
3850
3851/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003852 * xmlRelaxNGValidateTryPermutation:
3853 * @ctxt: a Relax-NG validation context
3854 * @groups: the array of groups
3855 * @nbgroups: the number of groups in the array
3856 * @array: the permutation to try
3857 * @len: the size of the set
3858 *
3859 * Try to validate a permutation for the group of definitions.
3860 *
3861 * Returns 0 if the validation succeeded or an error code.
3862 */
3863static int
3864xmlRelaxNGValidateTryPermutation(xmlRelaxNGValidCtxtPtr ctxt,
3865 xmlRelaxNGDefinePtr rule,
3866 xmlNodePtr *array, int len) {
3867 int i, ret;
3868
3869 if (len > 0) {
3870 /*
3871 * One only need the next pointer set-up to do the validation
3872 */
3873 for (i = 0;i < (len - 1);i++)
3874 array[i]->next = array[i + 1];
3875 array[i]->next = NULL;
3876
3877 /*
3878 * Now try to validate the sequence
3879 */
3880 ctxt->state->seq = array[0];
3881 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
3882 } else {
3883 ctxt->state->seq = NULL;
3884 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
3885 }
3886
3887 /*
3888 * the sequence must be fully consumed
3889 */
3890 if (ctxt->state->seq != NULL)
3891 return(-1);
3892
3893 return(ret);
3894}
3895
3896/**
3897 * xmlRelaxNGValidateWalkPermutations:
3898 * @ctxt: a Relax-NG validation context
3899 * @groups: the array of groups
3900 * @nbgroups: the number of groups in the array
3901 * @nodes: the set of nodes
3902 * @array: the current state of the parmutation
3903 * @len: the size of the set
3904 * @level: a pointer to the level variable
3905 * @k: the index in the array to fill
3906 *
3907 * Validate a set of nodes for a groups of definitions, will try the
3908 * full set of permutations
3909 *
3910 * Returns 0 if the validation succeeded or an error code.
3911 */
3912static int
3913xmlRelaxNGValidateWalkPermutations(xmlRelaxNGValidCtxtPtr ctxt,
3914 xmlRelaxNGDefinePtr rule, xmlNodePtr *nodes,
3915 xmlNodePtr *array, int len,
3916 int *level, int k) {
3917 int i, ret;
3918
3919 if ((k >= 0) && (k < len))
3920 array[k] = nodes[*level];
3921 *level = *level + 1;
3922 if (*level == len) {
3923 ret = xmlRelaxNGValidateTryPermutation(ctxt, rule, array, len);
3924 if (ret == 0)
3925 return(0);
3926 } else {
3927 for (i = 0;i < len;i++) {
3928 if (array[i] == NULL) {
3929 ret = xmlRelaxNGValidateWalkPermutations(ctxt, rule,
3930 nodes, array, len, level, i);
3931 if (ret == 0)
3932 return(0);
3933 }
3934 }
3935 }
3936 *level = *level - 1;
3937 array[k] = NULL;
3938 return(-1);
3939}
3940
3941/**
3942 * xmlRelaxNGNodeMatchesList:
3943 * @node: the node
3944 * @list: a NULL terminated array of definitions
3945 *
3946 * Check if a node can be matched by one of the definitions
3947 *
3948 * Returns 1 if matches 0 otherwise
3949 */
3950static int
3951xmlRelaxNGNodeMatchesList(xmlNodePtr node, xmlRelaxNGDefinePtr *list) {
3952 xmlRelaxNGDefinePtr cur;
3953 int i = 0;
3954
3955 if ((node == NULL) || (list == NULL))
3956 return(0);
3957
3958 cur = list[i++];
3959 while (cur != NULL) {
3960 if ((node->type == XML_ELEMENT_NODE) &&
3961 (cur->type == XML_RELAXNG_ELEMENT)) {
3962 if (cur->name == NULL) {
3963 if ((node->ns != NULL) &&
3964 (xmlStrEqual(node->ns->href, cur->ns)))
3965 return(1);
3966 } else if (xmlStrEqual(cur->name, node->name)) {
3967 if ((cur->ns == NULL) || (cur->ns[0] == 0)) {
3968 if (node->ns == NULL)
3969 return(1);
3970 } else {
3971 if ((node->ns != NULL) &&
3972 (xmlStrEqual(node->ns->href, cur->ns)))
3973 return(1);
3974 }
3975 }
3976 } else if ((node->type == XML_TEXT_NODE) &&
3977 (cur->type == XML_RELAXNG_TEXT)) {
3978 return(1);
3979 }
3980 cur = list[i++];
3981 }
3982 return(0);
3983}
3984
3985/**
3986 * xmlRelaxNGValidatePartGroup:
3987 * @ctxt: a Relax-NG validation context
3988 * @groups: the array of groups
3989 * @nbgroups: the number of groups in the array
3990 * @nodes: the set of nodes
3991 * @len: the size of the set of nodes
3992 *
3993 * Validate a set of nodes for a groups of definitions
3994 *
3995 * Returns 0 if the validation succeeded or an error code.
3996 */
3997static int
3998xmlRelaxNGValidatePartGroup(xmlRelaxNGValidCtxtPtr ctxt,
3999 xmlRelaxNGInterleaveGroupPtr *groups,
4000 int nbgroups, xmlNodePtr *nodes, int len) {
Daniel Veillardb08c9812003-01-28 23:09:49 +00004001 int level, ret = -1, i, j, k;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004002 xmlNodePtr *array = NULL, *list, oldseq;
4003 xmlRelaxNGInterleaveGroupPtr group;
4004
4005 list = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
4006 if (list == NULL) {
4007 return(-1);
4008 }
4009 array = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
4010 if (array == NULL) {
4011 xmlFree(list);
4012 return(-1);
4013 }
4014 memset(array, 0, len * sizeof(xmlNodePtr));
4015
4016 /*
4017 * Partition the elements and validate the subsets.
4018 */
4019 oldseq = ctxt->state->seq;
4020 for (i = 0;i < nbgroups;i++) {
4021 group = groups[i];
4022 if (group == NULL)
4023 continue;
4024 k = 0;
4025 for (j = 0;j < len;j++) {
4026 if (nodes[j] == NULL)
4027 continue;
4028 if (xmlRelaxNGNodeMatchesList(nodes[j], group->defs)) {
4029 list[k++] = nodes[j];
4030 nodes[j] = NULL;
4031 }
4032 }
4033 ctxt->state->seq = oldseq;
4034 if (k > 1) {
4035 memset(array, 0, k * sizeof(xmlNodePtr));
Daniel Veillardb08c9812003-01-28 23:09:49 +00004036 level = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004037 ret = xmlRelaxNGValidateWalkPermutations(ctxt, group->rule,
4038 list, array, k, &level, -1);
4039 } else {
4040 ret = xmlRelaxNGValidateTryPermutation(ctxt, group->rule, list, k);
4041 }
4042 if (ret != 0) {
4043 ctxt->state->seq = oldseq;
4044 break;
4045 }
4046 }
4047
4048 xmlFree(list);
4049 xmlFree(array);
4050 return(ret);
4051}
4052
4053/**
4054 * xmlRelaxNGValidateInterleave:
4055 * @ctxt: a Relax-NG validation context
4056 * @define: the definition to verify
4057 *
4058 * Validate an interleave definition for a node.
4059 *
4060 * Returns 0 if the validation succeeded or an error code.
4061 */
4062static int
4063xmlRelaxNGValidateInterleave(xmlRelaxNGValidCtxtPtr ctxt,
4064 xmlRelaxNGDefinePtr define) {
4065 int ret = 0, nbchildren, nbtot, i, j;
4066 xmlRelaxNGPartitionPtr partitions;
4067 xmlNodePtr *children = NULL;
4068 xmlNodePtr *order = NULL;
4069 xmlNodePtr cur;
4070
4071 if (define->data != NULL) {
4072 partitions = (xmlRelaxNGPartitionPtr) define->data;
4073 } else {
4074 VALID_CTXT();
4075 VALID_ERROR("Internal: interleave block has no data\n");
4076 return(-1);
4077 }
4078
4079 /*
4080 * Build the sequence of child and an array preserving the children
4081 * initial order.
4082 */
4083 cur = ctxt->state->seq;
4084 nbchildren = 0;
4085 nbtot = 0;
4086 while (cur != NULL) {
4087 if ((cur->type == XML_COMMENT_NODE) ||
4088 (cur->type == XML_PI_NODE) ||
4089 ((cur->type == XML_TEXT_NODE) &&
4090 (IS_BLANK_NODE(cur)))) {
4091 nbtot++;
4092 } else {
4093 nbchildren++;
4094 nbtot++;
4095 }
4096 cur = cur->next;
4097 }
4098 children = (xmlNodePtr *) xmlMalloc(nbchildren * sizeof(xmlNodePtr));
4099 if (children == NULL)
4100 goto error;
4101 order = (xmlNodePtr *) xmlMalloc(nbtot * sizeof(xmlNodePtr));
4102 if (order == NULL)
4103 goto error;
4104 cur = ctxt->state->seq;
4105 i = 0;
4106 j = 0;
4107 while (cur != NULL) {
4108 if ((cur->type == XML_COMMENT_NODE) ||
4109 (cur->type == XML_PI_NODE) ||
4110 ((cur->type == XML_TEXT_NODE) &&
4111 (IS_BLANK_NODE(cur)))) {
4112 order[j++] = cur;
4113 } else {
4114 order[j++] = cur;
4115 children[i++] = cur;
4116 }
4117 cur = cur->next;
4118 }
4119
4120 /* TODO: retry with a maller set of child if there is a next... */
4121 ret = xmlRelaxNGValidatePartGroup(ctxt, partitions->groups,
4122 partitions->nbgroups, children, nbchildren);
4123 if (ret == 0) {
4124 ctxt->state->seq = NULL;
4125 }
4126
4127 /*
4128 * Cleanup: rebuid the child sequence and free the structure
4129 */
4130 if (order != NULL) {
4131 for (i = 0;i < nbtot;i++) {
4132 if (i == 0)
4133 order[i]->prev = NULL;
4134 else
4135 order[i]->prev = order[i - 1];
4136 if (i == nbtot - 1)
4137 order[i]->next = NULL;
4138 else
4139 order[i]->next = order[i + 1];
4140 }
4141 xmlFree(order);
4142 }
4143 if (children != NULL)
4144 xmlFree(children);
4145
4146 return(ret);
4147
4148error:
4149 if (order != NULL) {
4150 for (i = 0;i < nbtot;i++) {
4151 if (i == 0)
4152 order[i]->prev = NULL;
4153 else
4154 order[i]->prev = order[i - 1];
4155 if (i == nbtot - 1)
4156 order[i]->next = NULL;
4157 else
4158 order[i]->next = order[i + 1];
4159 }
4160 xmlFree(order);
4161 }
4162 if (children != NULL)
4163 xmlFree(children);
4164 return(-1);
4165}
4166
4167/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004168 * xmlRelaxNGValidateElementContent:
4169 * @ctxt: a Relax-NG validation context
4170 * @define: the list of definition to verify
4171 *
4172 * Validate the given node content against the (list) of definitions
4173 *
4174 * Returns 0 if the validation succeeded or an error code.
4175 */
4176static int
4177xmlRelaxNGValidateElementContent(xmlRelaxNGValidCtxtPtr ctxt,
4178 xmlRelaxNGDefinePtr defines) {
4179 int ret = 0, res;
4180
4181 if (ctxt->state == NULL) {
4182 VALID_CTXT();
4183 VALID_ERROR("Internal: no state\n");
4184 return(-1);
4185 }
4186 while (defines != NULL) {
4187 res = xmlRelaxNGValidateDefinition(ctxt, defines);
4188 if (res < 0)
4189 ret = -1;
4190 defines = defines->next;
4191 }
4192
4193 return(ret);
4194}
4195
4196/**
4197 * xmlRelaxNGValidateDefinition:
4198 * @ctxt: a Relax-NG validation context
4199 * @define: the definition to verify
4200 *
4201 * Validate the current node against the definition
4202 *
4203 * Returns 0 if the validation succeeded or an error code.
4204 */
4205static int
4206xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
4207 xmlRelaxNGDefinePtr define) {
4208 xmlNodePtr node;
4209 int ret = 0, i, tmp, oldflags;
4210 xmlRelaxNGValidStatePtr oldstate, state;
4211
4212 if (define == NULL) {
4213 VALID_CTXT();
4214 VALID_ERROR("internal error: define == NULL\n");
4215 return(-1);
4216 }
4217 if (ctxt->state != NULL) {
4218 node = ctxt->state->seq;
4219 } else {
4220 node = NULL;
4221 }
4222 switch (define->type) {
4223 case XML_RELAXNG_EMPTY:
4224 if (node != NULL) {
4225 VALID_CTXT();
4226 VALID_ERROR("Expecting an empty element\n");
4227 return(-1);
4228 }
4229#ifdef DEBUG
4230 xmlGenericError(xmlGenericErrorContext,
4231 "xmlRelaxNGValidateDefinition(): validated empty\n");
4232#endif
4233 return(0);
4234 case XML_RELAXNG_NOT_ALLOWED:
4235 TODO
4236 break;
4237 case XML_RELAXNG_TEXT:
4238 if (node == NULL)
4239 return(0);
4240 while ((node != NULL) &&
4241 ((node->type == XML_TEXT_NODE) ||
4242 (node->type == XML_CDATA_SECTION_NODE)))
4243 node = node->next;
Daniel Veillard276be4a2003-01-24 01:03:34 +00004244 if (node == ctxt->state->seq) {
4245 VALID_CTXT();
4246 VALID_ERROR("Expecting text content\n");
4247 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004248 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00004249 ctxt->state->seq = node;
4250 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004251 case XML_RELAXNG_ELEMENT:
4252 node = xmlRelaxNGSkipIgnored(ctxt, node);
4253 if ((node == NULL) || (node->type != XML_ELEMENT_NODE)) {
4254 VALID_CTXT();
4255 VALID_ERROR("Expecting an element\n");
4256 return(-1);
4257 }
4258 if (define->name != NULL) {
4259 if (!xmlStrEqual(node->name, define->name)) {
4260 VALID_CTXT();
4261 VALID_ERROR("Expecting element %s, got %s\n",
4262 define->name, node->name);
4263 return(-1);
4264 }
4265 }
4266 if ((define->ns != NULL) && (define->ns[0] != 0)) {
4267 if (node->ns == NULL) {
4268 VALID_CTXT();
4269 VALID_ERROR("Expecting a namespace for element %s\n",
4270 node->name);
4271 return(-1);
4272 } else if (!xmlStrEqual(node->ns->href, define->ns)) {
4273 VALID_CTXT();
4274 VALID_ERROR("Expecting element %s has wrong namespace: expecting %s\n",
4275 node->name, define->ns);
4276 return(-1);
4277 }
4278 } else {
4279 if (node->ns != NULL) {
4280 VALID_CTXT();
4281 VALID_ERROR("Expecting no namespace for element %s\n",
4282 node->name);
4283 return(-1);
4284 }
4285 }
4286
4287 state = xmlRelaxNGNewValidState(ctxt, node);
4288 if (state == NULL) {
4289 return(-1);
4290 }
4291
4292 oldstate = ctxt->state;
4293 ctxt->state = state;
4294 if (define->attrs != NULL) {
4295 tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
4296 if (tmp != 0)
4297 ret = -1;
4298 }
4299 if (define->content != NULL) {
4300 tmp = xmlRelaxNGValidateElementContent(ctxt, define->content);
4301 if (tmp != 0)
4302 ret = -1;
4303 }
4304 state = ctxt->state;
4305 if (state->seq != NULL) {
4306 state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq);
4307 if (state->seq != NULL) {
4308 VALID_CTXT();
4309 VALID_ERROR("Extra content for element %s\n",
4310 node->name);
4311 ret = -1;
4312 }
4313 }
4314 for (i = 0;i < state->nbAttrs;i++) {
4315 if (state->attrs[i] != NULL) {
4316 VALID_CTXT();
Daniel Veillardea3f3982003-01-26 19:45:18 +00004317 VALID_ERROR("Invalid attribute %s for element %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00004318 state->attrs[i]->name, node->name);
4319 ret = -1;
4320 }
4321 }
4322 ctxt->state = oldstate;
4323 xmlRelaxNGFreeValidState(state);
4324 if (oldstate != NULL)
4325 oldstate->seq = node->next;
4326
4327
4328#ifdef DEBUG
4329 xmlGenericError(xmlGenericErrorContext,
4330 "xmlRelaxNGValidateDefinition(): validated %s : %d\n",
4331 node->name, ret);
4332#endif
4333 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004334 case XML_RELAXNG_OPTIONAL:
4335 oldflags = ctxt->flags;
4336 ctxt->flags |= FLAGS_IGNORABLE;
4337 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
4338 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
4339 if (ret != 0) {
4340 xmlRelaxNGFreeValidState(ctxt->state);
4341 ctxt->state = oldstate;
4342 ret = 0;
4343 break;
4344 }
4345 xmlRelaxNGFreeValidState(oldstate);
4346 ctxt->flags = oldflags;
4347 ret = 0;
4348 break;
4349 case XML_RELAXNG_ONEORMORE:
4350 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
4351 if (ret != 0) {
4352 break;
4353 }
4354 /* no break on purpose */
Daniel Veillard276be4a2003-01-24 01:03:34 +00004355 case XML_RELAXNG_ZEROORMORE: {
4356 xmlNodePtr cur, temp;
4357
Daniel Veillard6eadf632003-01-23 18:29:16 +00004358 oldflags = ctxt->flags;
4359 ctxt->flags |= FLAGS_IGNORABLE;
Daniel Veillard276be4a2003-01-24 01:03:34 +00004360 cur = ctxt->state->seq;
4361 temp = NULL;
4362 while ((cur != NULL) && (temp != cur)) {
4363 temp = cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004364 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
4365 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
4366 if (ret != 0) {
4367 xmlRelaxNGFreeValidState(ctxt->state);
4368 ctxt->state = oldstate;
4369 ret = 0;
4370 break;
4371 }
4372 xmlRelaxNGFreeValidState(oldstate);
Daniel Veillard276be4a2003-01-24 01:03:34 +00004373 cur = ctxt->state->seq;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004374 }
4375 ctxt->flags = oldflags;
4376 break;
Daniel Veillard276be4a2003-01-24 01:03:34 +00004377 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004378 case XML_RELAXNG_CHOICE: {
4379 xmlRelaxNGDefinePtr list = define->content;
4380
4381 oldflags = ctxt->flags;
4382 ctxt->flags |= FLAGS_IGNORABLE;
4383
4384 while (list != NULL) {
4385 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
4386 ret = xmlRelaxNGValidateDefinition(ctxt, list);
4387 if (ret == 0) {
4388 xmlRelaxNGFreeValidState(oldstate);
4389 break;
4390 }
4391 xmlRelaxNGFreeValidState(ctxt->state);
4392 ctxt->state = oldstate;
4393 list = list->next;
4394 }
4395 ctxt->flags = oldflags;
4396 break;
4397 }
4398 case XML_RELAXNG_GROUP: {
4399 xmlRelaxNGDefinePtr list = define->content;
4400
4401 while (list != NULL) {
4402 ret = xmlRelaxNGValidateDefinition(ctxt, list);
4403 if (ret != 0)
4404 break;
4405 list = list->next;
4406 }
4407 break;
4408 }
4409 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004410 ret = xmlRelaxNGValidateInterleave(ctxt, define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004411 break;
4412 case XML_RELAXNG_ATTRIBUTE:
4413 ret = xmlRelaxNGValidateAttribute(ctxt, define);
4414 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004415 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00004416 case XML_RELAXNG_REF:
4417 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
4418 break;
4419 case XML_RELAXNG_DEF:
4420 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
4421 break;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004422 case XML_RELAXNG_DATATYPE: {
4423 xmlChar *content;
4424
4425 content = xmlNodeGetContent(node);
4426 ret = xmlRelaxNGValidateDatatype(ctxt, content, define);
4427 if (ret == -1) {
4428 VALID_CTXT();
4429 VALID_ERROR("internal error validating %s\n", define->name);
4430 } else if (ret == 0) {
4431 ctxt->state->seq = node->next;
4432 }
4433 /*
4434 * TODO cover the problems with
4435 * <p>12<!-- comment -->34</p>
4436 * TODO detect full element coverage at compilation time.
4437 */
4438 if ((node != NULL) && (node->next != NULL)) {
4439 VALID_CTXT();
4440 VALID_ERROR("The data does not cover the full element %s\n",
4441 node->parent->name);
4442 ret = -1;
4443 }
4444 if (content != NULL)
4445 xmlFree(content);
4446 break;
4447 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00004448 case XML_RELAXNG_VALUE: {
4449 xmlChar *content;
4450 xmlChar *oldvalue;
4451
4452 content = xmlNodeGetContent(node);
4453 oldvalue = ctxt->state->value;
4454 ctxt->state->value = content;
4455 ret = xmlRelaxNGValidateValue(ctxt, define);
4456 ctxt->state->value = oldvalue;
4457 if (ret == -1) {
4458 VALID_CTXT();
4459 VALID_ERROR("internal error validating %s\n", define->name);
4460 } else if (ret == 0) {
4461 ctxt->state->seq = node->next;
4462 }
4463 /*
4464 * TODO cover the problems with
4465 * <p>12<!-- comment -->34</p>
4466 * TODO detect full element coverage at compilation time.
4467 */
4468 if ((node != NULL) && (node->next != NULL)) {
4469 VALID_CTXT();
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004470 VALID_ERROR("The value does not cover the full element %s\n",
Daniel Veillardea3f3982003-01-26 19:45:18 +00004471 node->parent->name);
4472 ret = -1;
4473 }
4474 if (content != NULL)
4475 xmlFree(content);
4476 break;
4477 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004478 case XML_RELAXNG_LIST: {
4479 xmlChar *content;
4480 xmlChar *oldvalue, *oldendvalue;
4481 int len;
Daniel Veillardea3f3982003-01-26 19:45:18 +00004482
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004483 content = xmlNodeGetContent(node);
4484 len = xmlStrlen(content);
4485 oldvalue = ctxt->state->value;
4486 oldendvalue = ctxt->state->endvalue;
4487 ctxt->state->value = content;
4488 ctxt->state->endvalue = content + len;
4489 ret = xmlRelaxNGValidateValue(ctxt, define);
4490 ctxt->state->value = oldvalue;
4491 ctxt->state->endvalue = oldendvalue;
4492 if (ret == -1) {
4493 VALID_CTXT();
4494 VALID_ERROR("internal error validating list\n");
4495 } else if (ret == 0) {
4496 ctxt->state->seq = node->next;
4497 }
4498 /*
4499 * TODO cover the problems with
4500 * <p>12<!-- comment -->34</p>
4501 * TODO detect full element coverage at compilation time.
4502 */
4503 if ((node != NULL) && (node->next != NULL)) {
4504 VALID_CTXT();
4505 VALID_ERROR("The list does not cover the full element %s\n",
4506 node->parent->name);
4507 ret = -1;
4508 }
4509 if (content != NULL)
4510 xmlFree(content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004511 break;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004512 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004513 case XML_RELAXNG_START:
4514 TODO
4515 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004516 }
4517 return(ret);
4518}
4519
4520/**
4521 * xmlRelaxNGValidateDocument:
4522 * @ctxt: a Relax-NG validation context
4523 * @doc: the document
4524 *
4525 * Validate the given document
4526 *
4527 * Returns 0 if the validation succeeded or an error code.
4528 */
4529static int
4530xmlRelaxNGValidateDocument(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
4531 int ret;
4532 xmlRelaxNGPtr schema;
4533 xmlRelaxNGGrammarPtr grammar;
4534 xmlRelaxNGValidStatePtr state;
4535
4536 if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL))
4537 return(-1);
4538
4539 schema = ctxt->schema;
4540 grammar = schema->topgrammar;
4541 if (grammar == NULL) {
4542 VALID_CTXT();
4543 VALID_ERROR("No top grammar defined\n");
4544 return(-1);
4545 }
4546 state = xmlRelaxNGNewValidState(ctxt, NULL);
4547 ctxt->state = state;
4548 ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start);
4549 state = ctxt->state;
4550 if ((state != NULL) && (state->seq != NULL)) {
4551 xmlNodePtr node;
4552
4553 node = state->seq;
4554 node = xmlRelaxNGSkipIgnored(ctxt, node);
4555 if (node != NULL) {
4556 VALID_CTXT();
4557 VALID_ERROR("extra data on the document\n");
4558 ret = -1;
4559 }
4560 }
4561 xmlRelaxNGFreeValidState(state);
4562
4563 return(ret);
4564}
4565
4566/************************************************************************
4567 * *
4568 * Validation interfaces *
4569 * *
4570 ************************************************************************/
4571/**
4572 * xmlRelaxNGNewValidCtxt:
4573 * @schema: a precompiled XML RelaxNGs
4574 *
4575 * Create an XML RelaxNGs validation context based on the given schema
4576 *
4577 * Returns the validation context or NULL in case of error
4578 */
4579xmlRelaxNGValidCtxtPtr
4580xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema) {
4581 xmlRelaxNGValidCtxtPtr ret;
4582
4583 ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt));
4584 if (ret == NULL) {
4585 xmlGenericError(xmlGenericErrorContext,
4586 "Failed to allocate new schama validation context\n");
4587 return (NULL);
4588 }
4589 memset(ret, 0, sizeof(xmlRelaxNGValidCtxt));
4590 ret->schema = schema;
4591 return (ret);
4592}
4593
4594/**
4595 * xmlRelaxNGFreeValidCtxt:
4596 * @ctxt: the schema validation context
4597 *
4598 * Free the resources associated to the schema validation context
4599 */
4600void
4601xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt) {
4602 if (ctxt == NULL)
4603 return;
4604 xmlFree(ctxt);
4605}
4606
4607/**
4608 * xmlRelaxNGSetValidErrors:
4609 * @ctxt: a Relax-NG validation context
4610 * @err: the error function
4611 * @warn: the warning function
4612 * @ctx: the functions context
4613 *
4614 * Set the error and warning callback informations
4615 */
4616void
4617xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
4618 xmlRelaxNGValidityErrorFunc err,
4619 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
4620 if (ctxt == NULL)
4621 return;
4622 ctxt->error = err;
4623 ctxt->warning = warn;
4624 ctxt->userData = ctx;
4625}
4626
4627/**
4628 * xmlRelaxNGValidateDoc:
4629 * @ctxt: a Relax-NG validation context
4630 * @doc: a parsed document tree
4631 *
4632 * Validate a document tree in memory.
4633 *
4634 * Returns 0 if the document is valid, a positive error code
4635 * number otherwise and -1 in case of internal or API error.
4636 */
4637int
4638xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
4639 int ret;
4640
4641 if ((ctxt == NULL) || (doc == NULL))
4642 return(-1);
4643
4644 ctxt->doc = doc;
4645
4646 ret = xmlRelaxNGValidateDocument(ctxt, doc);
4647 return(ret);
4648}
4649
4650#endif /* LIBXML_SCHEMAS_ENABLED */
4651