blob: 114167d60fb1378dd567ecbab6c75b47c005c5d3 [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:
Daniel Veillardd41f4f42003-01-29 21:07:52 +000011 * - error reporting
Daniel Veillarde2a5a082003-02-02 14:35:17 +000012 * - simplification of the resulting compiled trees:
13 * - NOT_ALLOWED
14 * - EMPTY
Daniel Veillard1ed7f362003-02-03 10:57:45 +000015 * - handle namespace declarations as attributes.
Daniel Veillardd41f4f42003-01-29 21:07:52 +000016 */
17
Daniel Veillard6eadf632003-01-23 18:29:16 +000018#define IN_LIBXML
19#include "libxml.h"
20
21#ifdef LIBXML_SCHEMAS_ENABLED
22
23#include <string.h>
24#include <stdio.h>
25#include <libxml/xmlmemory.h>
26#include <libxml/parser.h>
27#include <libxml/parserInternals.h>
28#include <libxml/hash.h>
29#include <libxml/uri.h>
30
31#include <libxml/relaxng.h>
32
33#include <libxml/xmlschemastypes.h>
34#include <libxml/xmlautomata.h>
35#include <libxml/xmlregexp.h>
Daniel Veillardc6e997c2003-01-27 12:35:42 +000036#include <libxml/xmlschemastypes.h>
Daniel Veillard6eadf632003-01-23 18:29:16 +000037
38/*
39 * The Relax-NG namespace
40 */
41static const xmlChar *xmlRelaxNGNs = (const xmlChar *)
42 "http://relaxng.org/ns/structure/1.0";
43
44#define IS_RELAXNG(node, type) \
45 ((node != NULL) && (node->ns != NULL) && \
46 (xmlStrEqual(node->name, (const xmlChar *) type)) && \
47 (xmlStrEqual(node->ns->href, xmlRelaxNGNs)))
48
49
Daniel Veillard71531f32003-02-05 13:19:53 +000050/* #define DEBUG 1 */ /* very verbose output */
51/* #define DEBUG_CONTENT 1 */
52/* #define DEBUG_TYPE 1 */
53/* #define DEBUG_VALID 1 */
Daniel Veillarde5b110b2003-02-04 14:43:39 +000054/* #define DEBUG_INTERLEAVE 1 */
Daniel Veillard6eadf632003-01-23 18:29:16 +000055
56#define UNBOUNDED (1 << 30)
57#define TODO \
58 xmlGenericError(xmlGenericErrorContext, \
59 "Unimplemented block at %s:%d\n", \
60 __FILE__, __LINE__);
61
62typedef struct _xmlRelaxNGSchema xmlRelaxNGSchema;
63typedef xmlRelaxNGSchema *xmlRelaxNGSchemaPtr;
64
65typedef struct _xmlRelaxNGDefine xmlRelaxNGDefine;
66typedef xmlRelaxNGDefine *xmlRelaxNGDefinePtr;
67
Daniel Veillardd41f4f42003-01-29 21:07:52 +000068typedef struct _xmlRelaxNGDocument xmlRelaxNGDocument;
69typedef xmlRelaxNGDocument *xmlRelaxNGDocumentPtr;
70
Daniel Veillarda9d912d2003-02-01 17:43:10 +000071typedef struct _xmlRelaxNGInclude xmlRelaxNGInclude;
72typedef xmlRelaxNGInclude *xmlRelaxNGIncludePtr;
73
Daniel Veillard6eadf632003-01-23 18:29:16 +000074typedef enum {
75 XML_RELAXNG_COMBINE_UNDEFINED = 0, /* undefined */
76 XML_RELAXNG_COMBINE_CHOICE, /* choice */
77 XML_RELAXNG_COMBINE_INTERLEAVE /* interleave */
78} xmlRelaxNGCombine;
79
80typedef struct _xmlRelaxNGGrammar xmlRelaxNGGrammar;
81typedef xmlRelaxNGGrammar *xmlRelaxNGGrammarPtr;
82
83struct _xmlRelaxNGGrammar {
84 xmlRelaxNGGrammarPtr parent;/* the parent grammar if any */
85 xmlRelaxNGGrammarPtr children;/* the children grammar if any */
86 xmlRelaxNGGrammarPtr next; /* the next grammar if any */
87 xmlRelaxNGDefinePtr start; /* <start> content */
88 xmlRelaxNGCombine combine; /* the default combine value */
Daniel Veillardd41f4f42003-01-29 21:07:52 +000089 xmlRelaxNGDefinePtr startList;/* list of <start> definitions */
Daniel Veillard6eadf632003-01-23 18:29:16 +000090 xmlHashTablePtr defs; /* define* */
91 xmlHashTablePtr refs; /* references */
92};
93
94
Daniel Veillard6eadf632003-01-23 18:29:16 +000095typedef enum {
96 XML_RELAXNG_EMPTY = 0, /* an empty pattern */
97 XML_RELAXNG_NOT_ALLOWED, /* not allowed top */
Daniel Veillard144fae12003-02-03 13:17:57 +000098 XML_RELAXNG_EXCEPT, /* except present in nameclass defs */
Daniel Veillard6eadf632003-01-23 18:29:16 +000099 XML_RELAXNG_TEXT, /* textual content */
100 XML_RELAXNG_ELEMENT, /* an element */
101 XML_RELAXNG_DATATYPE, /* extenal data type definition */
102 XML_RELAXNG_VALUE, /* value from an extenal data type definition */
103 XML_RELAXNG_LIST, /* a list of patterns */
104 XML_RELAXNG_ATTRIBUTE, /* an attrbute following a pattern */
105 XML_RELAXNG_DEF, /* a definition */
106 XML_RELAXNG_REF, /* reference to a definition */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000107 XML_RELAXNG_EXTERNALREF, /* reference to an external def */
Daniel Veillard419a7682003-02-03 23:22:49 +0000108 XML_RELAXNG_PARENTREF, /* reference to a def in the parent grammar */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000109 XML_RELAXNG_OPTIONAL, /* optional patterns */
110 XML_RELAXNG_ZEROORMORE, /* zero or more non empty patterns */
111 XML_RELAXNG_ONEORMORE, /* one or more non empty patterns */
112 XML_RELAXNG_CHOICE, /* a choice between non empty patterns */
113 XML_RELAXNG_GROUP, /* a pair/group of non empty patterns */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000114 XML_RELAXNG_INTERLEAVE, /* interleaving choice of non-empty patterns */
115 XML_RELAXNG_START /* Used to keep track of starts on grammars */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000116} xmlRelaxNGType;
117
118struct _xmlRelaxNGDefine {
119 xmlRelaxNGType type; /* the type of definition */
120 xmlNodePtr node; /* the node in the source */
121 xmlChar *name; /* the element local name if present */
122 xmlChar *ns; /* the namespace local name if present */
Daniel Veillardedc91922003-01-26 00:52:04 +0000123 xmlChar *value; /* value when available */
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000124 void *data; /* data lib or specific pointer */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000125 xmlRelaxNGDefinePtr content;/* the expected content */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000126 xmlRelaxNGDefinePtr parent; /* the parent definition, if any */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000127 xmlRelaxNGDefinePtr next; /* list within grouping sequences */
128 xmlRelaxNGDefinePtr attrs; /* list of attributes for elements */
Daniel Veillard3b2e4e12003-02-03 08:52:58 +0000129 xmlRelaxNGDefinePtr nameClass;/* the nameClass definition if any */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000130 xmlRelaxNGDefinePtr nextHash;/* next define in defs/refs hash tables */
131};
132
133/**
134 * _xmlRelaxNG:
135 *
136 * A RelaxNGs definition
137 */
138struct _xmlRelaxNG {
139 xmlRelaxNGGrammarPtr topgrammar;
140 xmlDocPtr doc;
141
142 xmlHashTablePtr defs; /* define */
143 xmlHashTablePtr refs; /* references */
Daniel Veillard419a7682003-02-03 23:22:49 +0000144 xmlHashTablePtr documents; /* all the documents loaded */
145 xmlHashTablePtr includes; /* all the includes loaded */
146 int defNr; /* number of defines used */
147 xmlRelaxNGDefinePtr *defTab;/* pointer to the allocated definitions */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000148 void *_private; /* unused by the library for users or bindings */
149};
150
151typedef enum {
152 XML_RELAXNG_ERR_OK = 0,
153 XML_RELAXNG_ERR_NOROOT = 1,
154 XML_RELAXNG_ERR_
155} xmlRelaxNGValidError;
156
157#define XML_RELAXNG_IN_ATTRIBUTE 1
158
159struct _xmlRelaxNGParserCtxt {
160 void *userData; /* user specific data block */
161 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
162 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
163 xmlRelaxNGValidError err;
164
165 xmlRelaxNGPtr schema; /* The schema in use */
166 xmlRelaxNGGrammarPtr grammar; /* the current grammar */
Daniel Veillard419a7682003-02-03 23:22:49 +0000167 xmlRelaxNGGrammarPtr parentgrammar;/* the parent grammar */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000168 int flags; /* parser flags */
169 int nbErrors; /* number of errors at parse time */
170 int nbWarnings; /* number of warnings at parse time */
Daniel Veillard276be4a2003-01-24 01:03:34 +0000171 const xmlChar *define; /* the current define scope */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000172 xmlRelaxNGDefinePtr def; /* the current define */
173
174 int nbInterleaves;
175 xmlHashTablePtr interleaves; /* keep track of all the interleaves */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000176
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000177 xmlHashTablePtr documents; /* all the documents loaded */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000178 xmlHashTablePtr includes; /* all the includes loaded */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000179 xmlChar *URL;
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000180 xmlDocPtr document;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000181
Daniel Veillard419a7682003-02-03 23:22:49 +0000182 int defNr; /* number of defines used */
183 int defMax; /* number of defines aloocated */
184 xmlRelaxNGDefinePtr *defTab; /* pointer to the allocated definitions */
185
Daniel Veillard6eadf632003-01-23 18:29:16 +0000186 const char *buffer;
187 int size;
188
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000189 /* the document stack */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000190 xmlRelaxNGDocumentPtr doc; /* Current parsed external ref */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000191 int docNr; /* Depth of the parsing stack */
192 int docMax; /* Max depth of the parsing stack */
193 xmlRelaxNGDocumentPtr *docTab; /* array of docs */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000194
195 /* the include stack */
196 xmlRelaxNGIncludePtr inc; /* Current parsed include */
197 int incNr; /* Depth of the include parsing stack */
198 int incMax; /* Max depth of the parsing stack */
199 xmlRelaxNGIncludePtr *incTab; /* array of incs */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000200};
201
202#define FLAGS_IGNORABLE 1
203#define FLAGS_NEGATIVE 2
204
205/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000206 * xmlRelaxNGInterleaveGroup:
207 *
208 * A RelaxNGs partition set associated to lists of definitions
209 */
210typedef struct _xmlRelaxNGInterleaveGroup xmlRelaxNGInterleaveGroup;
211typedef xmlRelaxNGInterleaveGroup *xmlRelaxNGInterleaveGroupPtr;
212struct _xmlRelaxNGInterleaveGroup {
213 xmlRelaxNGDefinePtr rule; /* the rule to satisfy */
214 xmlRelaxNGDefinePtr *defs; /* the array of element definitions */
215};
216
217/**
218 * xmlRelaxNGPartitions:
219 *
220 * A RelaxNGs partition associated to an interleave group
221 */
222typedef struct _xmlRelaxNGPartition xmlRelaxNGPartition;
223typedef xmlRelaxNGPartition *xmlRelaxNGPartitionPtr;
224struct _xmlRelaxNGPartition {
225 int nbgroups; /* number of groups in the partitions */
226 xmlRelaxNGInterleaveGroupPtr *groups;
227};
228
229/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000230 * xmlRelaxNGValidState:
231 *
232 * A RelaxNGs validation state
233 */
234#define MAX_ATTR 20
235typedef struct _xmlRelaxNGValidState xmlRelaxNGValidState;
236typedef xmlRelaxNGValidState *xmlRelaxNGValidStatePtr;
237struct _xmlRelaxNGValidState {
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000238 xmlNodePtr node; /* the current node */
239 xmlNodePtr seq; /* the sequence of children left to validate */
240 int nbAttrs; /* the number of attributes */
Daniel Veillard1ed7f362003-02-03 10:57:45 +0000241 int nbAttrLeft; /* the number of attributes left to validate */
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000242 xmlChar *value; /* the value when operating on string */
243 xmlChar *endvalue; /* the end value when operating on string */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000244 xmlAttrPtr attrs[1]; /* the array of attributes */
245};
246
247/**
248 * xmlRelaxNGValidCtxt:
249 *
250 * A RelaxNGs validation context
251 */
252
253struct _xmlRelaxNGValidCtxt {
254 void *userData; /* user specific data block */
255 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
256 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
257
258 xmlRelaxNGPtr schema; /* The schema in use */
259 xmlDocPtr doc; /* the document being validated */
260 xmlRelaxNGValidStatePtr state; /* the current validation state */
261 int flags; /* validation flags */
262};
263
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000264/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000265 * xmlRelaxNGInclude:
266 *
267 * Structure associated to a RelaxNGs document element
268 */
269struct _xmlRelaxNGInclude {
270 xmlChar *href; /* the normalized href value */
271 xmlDocPtr doc; /* the associated XML document */
272 xmlRelaxNGDefinePtr content;/* the definitions */
273 xmlRelaxNGPtr schema; /* the schema */
274};
275
276/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000277 * xmlRelaxNGDocument:
278 *
279 * Structure associated to a RelaxNGs document element
280 */
281struct _xmlRelaxNGDocument {
282 xmlChar *href; /* the normalized href value */
283 xmlDocPtr doc; /* the associated XML document */
284 xmlRelaxNGDefinePtr content;/* the definitions */
285 xmlRelaxNGPtr schema; /* the schema */
286};
287
Daniel Veillard6eadf632003-01-23 18:29:16 +0000288/************************************************************************
289 * *
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000290 * Preliminary type checking interfaces *
291 * *
292 ************************************************************************/
293/**
294 * xmlRelaxNGTypeHave:
295 * @data: data needed for the library
296 * @type: the type name
297 * @value: the value to check
298 *
299 * Function provided by a type library to check if a type is exported
300 *
301 * Returns 1 if yes, 0 if no and -1 in case of error.
302 */
303typedef int (*xmlRelaxNGTypeHave) (void *data, const xmlChar *type);
304
305/**
306 * xmlRelaxNGTypeCheck:
307 * @data: data needed for the library
308 * @type: the type name
309 * @value: the value to check
310 *
311 * Function provided by a type library to check if a value match a type
312 *
313 * Returns 1 if yes, 0 if no and -1 in case of error.
314 */
315typedef int (*xmlRelaxNGTypeCheck) (void *data, const xmlChar *type,
316 const xmlChar *value);
317
318/**
319 * xmlRelaxNGTypeCompare:
320 * @data: data needed for the library
321 * @type: the type name
322 * @value1: the first value
323 * @value2: the second value
324 *
325 * Function provided by a type library to compare two values accordingly
326 * to a type.
327 *
328 * Returns 1 if yes, 0 if no and -1 in case of error.
329 */
330typedef int (*xmlRelaxNGTypeCompare) (void *data, const xmlChar *type,
331 const xmlChar *value1,
332 const xmlChar *value2);
333typedef struct _xmlRelaxNGTypeLibrary xmlRelaxNGTypeLibrary;
334typedef xmlRelaxNGTypeLibrary *xmlRelaxNGTypeLibraryPtr;
335struct _xmlRelaxNGTypeLibrary {
336 const xmlChar *namespace; /* the datatypeLibrary value */
337 void *data; /* data needed for the library */
338 xmlRelaxNGTypeHave have; /* the export function */
339 xmlRelaxNGTypeCheck check; /* the checking function */
340 xmlRelaxNGTypeCompare comp; /* the compare function */
341};
342
343/************************************************************************
344 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +0000345 * Allocation functions *
346 * *
347 ************************************************************************/
Daniel Veillard6eadf632003-01-23 18:29:16 +0000348static void xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar);
349static void xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define);
350
351/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000352 * xmlRelaxNGFreeDocument:
353 * @docu: a document structure
354 *
355 * Deallocate a RelaxNG document structure.
356 */
357static void
358xmlRelaxNGFreeDocument(xmlRelaxNGDocumentPtr docu)
359{
360 if (docu == NULL)
361 return;
362
363 if (docu->href != NULL)
364 xmlFree(docu->href);
365 if (docu->doc != NULL)
366 xmlFreeDoc(docu->doc);
367 if (docu->schema != NULL)
368 xmlRelaxNGFree(docu->schema);
369 xmlFree(docu);
370}
371
372/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000373 * xmlRelaxNGFreeInclude:
374 * @incl: a include structure
375 *
376 * Deallocate a RelaxNG include structure.
377 */
378static void
379xmlRelaxNGFreeInclude(xmlRelaxNGIncludePtr incl)
380{
381 if (incl == NULL)
382 return;
383
384 if (incl->href != NULL)
385 xmlFree(incl->href);
386 if (incl->doc != NULL)
387 xmlFreeDoc(incl->doc);
388 if (incl->schema != NULL)
389 xmlRelaxNGFree(incl->schema);
390 xmlFree(incl);
391}
392
393/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000394 * xmlRelaxNGNewRelaxNG:
395 * @ctxt: a Relax-NG validation context (optional)
396 *
397 * Allocate a new RelaxNG structure.
398 *
399 * Returns the newly allocated structure or NULL in case or error
400 */
401static xmlRelaxNGPtr
402xmlRelaxNGNewRelaxNG(xmlRelaxNGParserCtxtPtr ctxt)
403{
404 xmlRelaxNGPtr ret;
405
406 ret = (xmlRelaxNGPtr) xmlMalloc(sizeof(xmlRelaxNG));
407 if (ret == NULL) {
408 if ((ctxt != NULL) && (ctxt->error != NULL))
409 ctxt->error(ctxt->userData, "Out of memory\n");
410 ctxt->nbErrors++;
411 return (NULL);
412 }
413 memset(ret, 0, sizeof(xmlRelaxNG));
414
415 return (ret);
416}
417
418/**
419 * xmlRelaxNGFree:
420 * @schema: a schema structure
421 *
422 * Deallocate a RelaxNG structure.
423 */
424void
425xmlRelaxNGFree(xmlRelaxNGPtr schema)
426{
427 if (schema == NULL)
428 return;
429
Daniel Veillard6eadf632003-01-23 18:29:16 +0000430 if (schema->topgrammar != NULL)
431 xmlRelaxNGFreeGrammar(schema->topgrammar);
432 if (schema->doc != NULL)
433 xmlFreeDoc(schema->doc);
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000434 if (schema->documents != NULL)
435 xmlHashFree(schema->documents, (xmlHashDeallocator)
436 xmlRelaxNGFreeDocument);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000437 if (schema->includes != NULL)
438 xmlHashFree(schema->includes, (xmlHashDeallocator)
439 xmlRelaxNGFreeInclude);
Daniel Veillard419a7682003-02-03 23:22:49 +0000440 if (schema->defTab != NULL) {
441 int i;
442
443 for (i = 0;i < schema->defNr;i++)
444 xmlRelaxNGFreeDefine(schema->defTab[i]);
445 xmlFree(schema->defTab);
446 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000447
448 xmlFree(schema);
449}
450
451/**
452 * xmlRelaxNGNewGrammar:
453 * @ctxt: a Relax-NG validation context (optional)
454 *
455 * Allocate a new RelaxNG grammar.
456 *
457 * Returns the newly allocated structure or NULL in case or error
458 */
459static xmlRelaxNGGrammarPtr
460xmlRelaxNGNewGrammar(xmlRelaxNGParserCtxtPtr ctxt)
461{
462 xmlRelaxNGGrammarPtr ret;
463
464 ret = (xmlRelaxNGGrammarPtr) xmlMalloc(sizeof(xmlRelaxNGGrammar));
465 if (ret == NULL) {
466 if ((ctxt != NULL) && (ctxt->error != NULL))
467 ctxt->error(ctxt->userData, "Out of memory\n");
468 ctxt->nbErrors++;
469 return (NULL);
470 }
471 memset(ret, 0, sizeof(xmlRelaxNGGrammar));
472
473 return (ret);
474}
475
476/**
477 * xmlRelaxNGFreeGrammar:
478 * @grammar: a grammar structure
479 *
480 * Deallocate a RelaxNG grammar structure.
481 */
482static void
483xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar)
484{
485 if (grammar == NULL)
486 return;
487
Daniel Veillard419a7682003-02-03 23:22:49 +0000488 if (grammar->next != NULL) {
489 xmlRelaxNGFreeGrammar(grammar->next);
490 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000491 if (grammar->refs != NULL) {
492 xmlHashFree(grammar->refs, NULL);
493 }
494 if (grammar->defs != NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000495 xmlHashFree(grammar->defs, NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000496 }
497
498 xmlFree(grammar);
499}
500
501/**
502 * xmlRelaxNGNewDefine:
503 * @ctxt: a Relax-NG validation context
504 * @node: the node in the input document.
505 *
506 * Allocate a new RelaxNG define.
507 *
508 * Returns the newly allocated structure or NULL in case or error
509 */
510static xmlRelaxNGDefinePtr
511xmlRelaxNGNewDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
512{
513 xmlRelaxNGDefinePtr ret;
514
Daniel Veillard419a7682003-02-03 23:22:49 +0000515 if (ctxt->defMax == 0) {
516 ctxt->defMax = 16;
517 ctxt->defNr = 0;
518 ctxt->defTab = (xmlRelaxNGDefinePtr *)
519 xmlMalloc(ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
520 if (ctxt->defTab == NULL) {
521 if ((ctxt != NULL) && (ctxt->error != NULL))
522 ctxt->error(ctxt->userData, "Out of memory\n");
523 ctxt->nbErrors++;
524 return (NULL);
525 }
526 } else if (ctxt->defMax <= ctxt->defNr) {
527 xmlRelaxNGDefinePtr *tmp;
528 ctxt->defMax *= 2;
529 tmp = (xmlRelaxNGDefinePtr *) xmlRealloc(ctxt->defTab,
530 ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
531 if (tmp == NULL) {
532 if ((ctxt != NULL) && (ctxt->error != NULL))
533 ctxt->error(ctxt->userData, "Out of memory\n");
534 ctxt->nbErrors++;
535 return (NULL);
536 }
537 ctxt->defTab = tmp;
538 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000539 ret = (xmlRelaxNGDefinePtr) xmlMalloc(sizeof(xmlRelaxNGDefine));
540 if (ret == NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000541 if ((ctxt != NULL) && (ctxt->error != NULL))
542 ctxt->error(ctxt->userData, "Out of memory\n");
Daniel Veillard6eadf632003-01-23 18:29:16 +0000543 ctxt->nbErrors++;
Daniel Veillard419a7682003-02-03 23:22:49 +0000544 return(NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000545 }
546 memset(ret, 0, sizeof(xmlRelaxNGDefine));
Daniel Veillard419a7682003-02-03 23:22:49 +0000547 ctxt->defTab[ctxt->defNr++] = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000548 ret->node = node;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000549 return (ret);
550}
551
552/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000553 * xmlRelaxNGFreePartition:
554 * @partitions: a partition set structure
555 *
556 * Deallocate RelaxNG partition set structures.
557 */
558static void
559xmlRelaxNGFreePartition(xmlRelaxNGPartitionPtr partitions) {
560 xmlRelaxNGInterleaveGroupPtr group;
561 int j;
562
563 if (partitions != NULL) {
564 if (partitions->groups != NULL) {
565 for (j = 0;j < partitions->nbgroups;j++) {
566 group = partitions->groups[j];
567 if (group != NULL) {
568 if (group->defs != NULL)
569 xmlFree(group->defs);
570 xmlFree(group);
571 }
572 }
573 xmlFree(partitions->groups);
574 }
575 xmlFree(partitions);
576 }
577}
578/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000579 * xmlRelaxNGFreeDefine:
580 * @define: a define structure
581 *
582 * Deallocate a RelaxNG define structure.
583 */
584static void
585xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define)
586{
587 if (define == NULL)
588 return;
589
Daniel Veillard419a7682003-02-03 23:22:49 +0000590 if ((define->data != NULL) &&
591 (define->type == XML_RELAXNG_INTERLEAVE))
592 xmlRelaxNGFreePartition((xmlRelaxNGPartitionPtr) define->data);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000593 if (define->name != NULL)
594 xmlFree(define->name);
595 if (define->ns != NULL)
596 xmlFree(define->ns);
Daniel Veillardedc91922003-01-26 00:52:04 +0000597 if (define->value != NULL)
598 xmlFree(define->value);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000599 xmlFree(define);
600}
601
602/**
603 * xmlRelaxNGNewValidState:
604 * @ctxt: a Relax-NG validation context
605 * @node: the current node or NULL for the document
606 *
607 * Allocate a new RelaxNG validation state
608 *
609 * Returns the newly allocated structure or NULL in case or error
610 */
611static xmlRelaxNGValidStatePtr
612xmlRelaxNGNewValidState(xmlRelaxNGValidCtxtPtr ctxt, xmlNodePtr node)
613{
614 xmlRelaxNGValidStatePtr ret;
615 xmlAttrPtr attr;
616 xmlAttrPtr attrs[MAX_ATTR];
617 int nbAttrs = 0;
618 xmlNodePtr root = NULL;
619
620 if (node == NULL) {
621 root = xmlDocGetRootElement(ctxt->doc);
622 if (root == NULL)
623 return(NULL);
624 } else {
625 attr = node->properties;
626 while (attr != NULL) {
627 if (nbAttrs < MAX_ATTR)
628 attrs[nbAttrs++] = attr;
629 else
630 nbAttrs++;
631 attr = attr->next;
632 }
633 }
634
635 if (nbAttrs < MAX_ATTR)
636 attrs[nbAttrs] = NULL;
637 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(sizeof(xmlRelaxNGValidState) +
638 nbAttrs * sizeof(xmlAttrPtr));
639 if (ret == NULL) {
640 if ((ctxt != NULL) && (ctxt->error != NULL))
641 ctxt->error(ctxt->userData, "Out of memory\n");
642 return (NULL);
643 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +0000644 ret->value = NULL;
645 ret->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000646 if (node == NULL) {
647 ret->node = (xmlNodePtr) ctxt->doc;
648 ret->seq = root;
649 ret->nbAttrs = 0;
650 } else {
651 ret->node = node;
652 ret->seq = node->children;
653 ret->nbAttrs = nbAttrs;
654 if (nbAttrs > 0) {
655 if (nbAttrs < MAX_ATTR) {
656 memcpy(&(ret->attrs[0]), attrs,
657 sizeof(xmlAttrPtr) * (nbAttrs + 1));
658 } else {
659 attr = node->properties;
660 nbAttrs = 0;
661 while (attr != NULL) {
662 ret->attrs[nbAttrs++] = attr;
663 attr = attr->next;
664 }
665 ret->attrs[nbAttrs] = NULL;
666 }
667 }
668 }
Daniel Veillard1ed7f362003-02-03 10:57:45 +0000669 ret->nbAttrLeft = ret->nbAttrs;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000670 return (ret);
671}
672
673/**
674 * xmlRelaxNGCopyValidState:
675 * @ctxt: a Relax-NG validation context
676 * @state: a validation state
677 *
678 * Copy the validation state
679 *
680 * Returns the newly allocated structure or NULL in case or error
681 */
682static xmlRelaxNGValidStatePtr
683xmlRelaxNGCopyValidState(xmlRelaxNGValidCtxtPtr ctxt,
684 xmlRelaxNGValidStatePtr state)
685{
686 xmlRelaxNGValidStatePtr ret;
687 unsigned int size;
688
689 if (state == NULL)
690 return(NULL);
691
692 size = sizeof(xmlRelaxNGValidState) +
693 state->nbAttrs * sizeof(xmlAttrPtr);
694 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(size);
695 if (ret == NULL) {
696 if ((ctxt != NULL) && (ctxt->error != NULL))
697 ctxt->error(ctxt->userData, "Out of memory\n");
698 return (NULL);
699 }
700 memcpy(ret, state, size);
701 return(ret);
702}
703
704/**
705 * xmlRelaxNGFreeValidState:
706 * @state: a validation state structure
707 *
708 * Deallocate a RelaxNG validation state structure.
709 */
710static void
711xmlRelaxNGFreeValidState(xmlRelaxNGValidStatePtr state)
712{
713 if (state == NULL)
714 return;
715
716 xmlFree(state);
717}
718
719/************************************************************************
720 * *
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000721 * Document functions *
722 * *
723 ************************************************************************/
724static xmlDocPtr xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt,
725 xmlDocPtr doc);
726
727/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000728 * xmlRelaxNGIncludePush:
729 * @ctxt: the parser context
730 * @value: the element doc
731 *
732 * Pushes a new include on top of the include stack
733 *
734 * Returns 0 in case of error, the index in the stack otherwise
735 */
736static int
737xmlRelaxNGIncludePush(xmlRelaxNGParserCtxtPtr ctxt,
738 xmlRelaxNGIncludePtr value)
739{
740 if (ctxt->incTab == NULL) {
741 ctxt->incMax = 4;
742 ctxt->incNr = 0;
743 ctxt->incTab = (xmlRelaxNGIncludePtr *) xmlMalloc(
744 ctxt->incMax * sizeof(ctxt->incTab[0]));
745 if (ctxt->incTab == NULL) {
746 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
747 return (0);
748 }
749 }
750 if (ctxt->incNr >= ctxt->incMax) {
751 ctxt->incMax *= 2;
752 ctxt->incTab =
753 (xmlRelaxNGIncludePtr *) xmlRealloc(ctxt->incTab,
754 ctxt->incMax *
755 sizeof(ctxt->incTab[0]));
756 if (ctxt->incTab == NULL) {
757 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
758 return (0);
759 }
760 }
761 ctxt->incTab[ctxt->incNr] = value;
762 ctxt->inc = value;
763 return (ctxt->incNr++);
764}
765
766/**
767 * xmlRelaxNGIncludePop:
768 * @ctxt: the parser context
769 *
770 * Pops the top include from the include stack
771 *
772 * Returns the include just removed
773 */
774static xmlRelaxNGIncludePtr
775xmlRelaxNGIncludePop(xmlRelaxNGParserCtxtPtr ctxt)
776{
777 xmlRelaxNGIncludePtr ret;
778
779 if (ctxt->incNr <= 0)
780 return (0);
781 ctxt->incNr--;
782 if (ctxt->incNr > 0)
783 ctxt->inc = ctxt->incTab[ctxt->incNr - 1];
784 else
785 ctxt->inc = NULL;
786 ret = ctxt->incTab[ctxt->incNr];
787 ctxt->incTab[ctxt->incNr] = 0;
788 return (ret);
789}
790
791/**
792 * xmlRelaxNGLoadInclude:
793 * @ctxt: the parser context
794 * @URL: the normalized URL
795 * @node: the include node.
796 *
797 * First lookup if the document is already loaded into the parser context,
798 * check against recursion. If not found the resource is loaded and
799 * the content is preprocessed before being returned back to the caller.
800 *
801 * Returns the xmlRelaxNGIncludePtr or NULL in case of error
802 */
803static xmlRelaxNGIncludePtr
804xmlRelaxNGLoadInclude(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
805 xmlNodePtr node) {
806 xmlRelaxNGIncludePtr ret = NULL;
807 xmlDocPtr doc;
808 int i;
809 xmlNodePtr root, tmp, tmp2, cur;
810
811 /*
812 * check against recursion in the stack
813 */
814 for (i = 0;i < ctxt->incNr;i++) {
815 if (xmlStrEqual(ctxt->incTab[i]->href, URL)) {
816 if (ctxt->error != NULL)
817 ctxt->error(ctxt->userData,
818 "Detected an externalRef recursion for %s\n",
819 URL);
820 ctxt->nbErrors++;
821 return(NULL);
822 }
823 }
824
825 /*
826 * Lookup in the hash table
827 */
828 if (ctxt->includes == NULL) {
829 ctxt->includes = xmlHashCreate(10);
830 if (ctxt->includes == NULL) {
831 if (ctxt->error != NULL)
832 ctxt->error(ctxt->userData,
833 "Failed to allocate hash table for document\n");
834 ctxt->nbErrors++;
835 return(NULL);
836 }
837 } else {
838 ret = xmlHashLookup(ctxt->includes, URL);
839 if (ret != NULL)
840 return(ret);
841 }
842
843
844 /*
845 * load the document
846 */
847 doc = xmlParseFile((const char *) URL);
848 if (doc == NULL) {
849 if (ctxt->error != NULL)
850 ctxt->error(ctxt->userData,
851 "xmlRelaxNG: could not load %s\n", URL);
852 ctxt->nbErrors++;
853 return (NULL);
854 }
855
856 /*
857 * Allocate the document structures and register it first.
858 */
859 ret = (xmlRelaxNGIncludePtr) xmlMalloc(sizeof(xmlRelaxNGInclude));
860 if (ret == NULL) {
861 if (ctxt->error != NULL)
862 ctxt->error(ctxt->userData,
863 "xmlRelaxNG: allocate memory for doc %s\n", URL);
864 ctxt->nbErrors++;
865 xmlFreeDoc(doc);
866 return (NULL);
867 }
868 memset(ret, 0, sizeof(xmlRelaxNGInclude));
869 ret->doc = doc;
870 ret->href = xmlStrdup(URL);
871
872 /*
873 * push it on the stack and register it in the hash table
874 */
875 xmlHashAddEntry(ctxt->includes, URL, ret);
876 xmlRelaxNGIncludePush(ctxt, ret);
877
878 /*
879 * Some preprocessing of the document content, this include recursing
880 * in the include stack.
881 */
882 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
883 if (doc == NULL) {
884 /* xmlFreeDoc(ctxt->include); */
885 ctxt->inc = NULL;
886 return(NULL);
887 }
888
889 /*
890 * Pop up the include from the stack
891 */
892 xmlRelaxNGIncludePop(ctxt);
893
894 /*
895 * Check that the top element is a grammar
896 */
897 root = xmlDocGetRootElement(doc);
898 if (root == NULL) {
899 if (ctxt->error != NULL)
900 ctxt->error(ctxt->userData,
901 "xmlRelaxNG: included document is empty %s\n", URL);
902 ctxt->nbErrors++;
903 xmlFreeDoc(doc);
904 return (NULL);
905 }
906 if (!IS_RELAXNG(root, "grammar")) {
907 if (ctxt->error != NULL)
908 ctxt->error(ctxt->userData,
909 "xmlRelaxNG: included document %s root is not a grammar\n",
910 URL);
911 ctxt->nbErrors++;
912 xmlFreeDoc(doc);
913 return (NULL);
914 }
915
916 /*
917 * Elimination of redefined rules in the include.
918 */
919 cur = node->children;
920 while (cur != NULL) {
921 if (IS_RELAXNG(cur, "start")) {
922 int found = 0;
923
924 tmp = root->children;
925 while (tmp != NULL) {
926 tmp2 = tmp->next;
927 if (IS_RELAXNG(tmp, "start")) {
928 found = 1;
929 xmlUnlinkNode(tmp);
930 xmlFreeNode(tmp);
931 }
932 tmp = tmp2;
933 }
934 if (!found) {
935 if (ctxt->error != NULL)
936 ctxt->error(ctxt->userData,
937 "xmlRelaxNG: include %s has a start but not the included grammar\n",
938 URL);
939 ctxt->nbErrors++;
940 }
941 } else if (IS_RELAXNG(cur, "define")) {
942 xmlChar *name, *name2;
943
944 name = xmlGetProp(cur, BAD_CAST "name");
945 if (name == NULL) {
946 if (ctxt->error != NULL)
947 ctxt->error(ctxt->userData,
948 "xmlRelaxNG: include %s has define without name\n",
949 URL);
950 ctxt->nbErrors++;
951 } else {
952 int found = 0;
953
954 tmp = root->children;
955 while (tmp != NULL) {
956 tmp2 = tmp->next;
957 if (IS_RELAXNG(tmp, "define")) {
958 name2 = xmlGetProp(tmp, BAD_CAST "name");
959 if (name2 != NULL) {
960 if (xmlStrEqual(name, name2)) {
961 found = 1;
962 xmlUnlinkNode(tmp);
963 xmlFreeNode(tmp);
964 }
965 xmlFree(name2);
966 }
967 }
968 tmp = tmp2;
969 }
970 if (!found) {
971 if (ctxt->error != NULL)
972 ctxt->error(ctxt->userData,
973 "xmlRelaxNG: include %s has a define %s but not the included grammar\n",
974 URL, name);
975 ctxt->nbErrors++;
976 }
977 xmlFree(name);
978 }
979 }
980 cur = cur->next;
981 }
982
983
984 return(ret);
985}
986
987/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000988 * xmlRelaxNGDocumentPush:
989 * @ctxt: the parser context
990 * @value: the element doc
991 *
992 * Pushes a new doc on top of the doc stack
993 *
994 * Returns 0 in case of error, the index in the stack otherwise
995 */
996static int
997xmlRelaxNGDocumentPush(xmlRelaxNGParserCtxtPtr ctxt,
998 xmlRelaxNGDocumentPtr value)
999{
1000 if (ctxt->docTab == NULL) {
1001 ctxt->docMax = 4;
1002 ctxt->docNr = 0;
1003 ctxt->docTab = (xmlRelaxNGDocumentPtr *) xmlMalloc(
1004 ctxt->docMax * sizeof(ctxt->docTab[0]));
1005 if (ctxt->docTab == NULL) {
1006 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
1007 return (0);
1008 }
1009 }
1010 if (ctxt->docNr >= ctxt->docMax) {
1011 ctxt->docMax *= 2;
1012 ctxt->docTab =
1013 (xmlRelaxNGDocumentPtr *) xmlRealloc(ctxt->docTab,
1014 ctxt->docMax *
1015 sizeof(ctxt->docTab[0]));
1016 if (ctxt->docTab == NULL) {
1017 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
1018 return (0);
1019 }
1020 }
1021 ctxt->docTab[ctxt->docNr] = value;
1022 ctxt->doc = value;
1023 return (ctxt->docNr++);
1024}
1025
1026/**
1027 * xmlRelaxNGDocumentPop:
1028 * @ctxt: the parser context
1029 *
1030 * Pops the top doc from the doc stack
1031 *
1032 * Returns the doc just removed
1033 */
1034static xmlRelaxNGDocumentPtr
1035xmlRelaxNGDocumentPop(xmlRelaxNGParserCtxtPtr ctxt)
1036{
1037 xmlRelaxNGDocumentPtr ret;
1038
1039 if (ctxt->docNr <= 0)
1040 return (0);
1041 ctxt->docNr--;
1042 if (ctxt->docNr > 0)
1043 ctxt->doc = ctxt->docTab[ctxt->docNr - 1];
1044 else
1045 ctxt->doc = NULL;
1046 ret = ctxt->docTab[ctxt->docNr];
1047 ctxt->docTab[ctxt->docNr] = 0;
1048 return (ret);
1049}
1050
1051/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001052 * xmlRelaxNGLoadExternalRef:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001053 * @ctxt: the parser context
1054 * @URL: the normalized URL
1055 * @ns: the inherited ns if any
1056 *
1057 * First lookup if the document is already loaded into the parser context,
1058 * check against recursion. If not found the resource is loaded and
1059 * the content is preprocessed before being returned back to the caller.
1060 *
1061 * Returns the xmlRelaxNGDocumentPtr or NULL in case of error
1062 */
1063static xmlRelaxNGDocumentPtr
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001064xmlRelaxNGLoadExternalRef(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001065 const xmlChar *ns) {
1066 xmlRelaxNGDocumentPtr ret = NULL;
1067 xmlDocPtr doc;
1068 xmlNodePtr root;
1069 int i;
1070
1071 /*
1072 * check against recursion in the stack
1073 */
1074 for (i = 0;i < ctxt->docNr;i++) {
1075 if (xmlStrEqual(ctxt->docTab[i]->href, URL)) {
1076 if (ctxt->error != NULL)
1077 ctxt->error(ctxt->userData,
1078 "Detected an externalRef recursion for %s\n",
1079 URL);
1080 ctxt->nbErrors++;
1081 return(NULL);
1082 }
1083 }
1084
1085 /*
1086 * Lookup in the hash table
1087 */
1088 if (ctxt->documents == NULL) {
1089 ctxt->documents = xmlHashCreate(10);
1090 if (ctxt->documents == NULL) {
1091 if (ctxt->error != NULL)
1092 ctxt->error(ctxt->userData,
1093 "Failed to allocate hash table for document\n");
1094 ctxt->nbErrors++;
1095 return(NULL);
1096 }
1097 } else {
1098 if (ns == NULL)
1099 ret = xmlHashLookup2(ctxt->documents, BAD_CAST "", URL);
1100 else
1101 ret = xmlHashLookup2(ctxt->documents, ns, URL);
1102 if (ret != NULL)
1103 return(ret);
1104 }
1105
1106
1107 /*
1108 * load the document
1109 */
1110 doc = xmlParseFile((const char *) URL);
1111 if (doc == NULL) {
1112 if (ctxt->error != NULL)
1113 ctxt->error(ctxt->userData,
1114 "xmlRelaxNG: could not load %s\n", URL);
1115 ctxt->nbErrors++;
1116 return (NULL);
1117 }
1118
1119 /*
1120 * Allocate the document structures and register it first.
1121 */
1122 ret = (xmlRelaxNGDocumentPtr) xmlMalloc(sizeof(xmlRelaxNGDocument));
1123 if (ret == NULL) {
1124 if (ctxt->error != NULL)
1125 ctxt->error(ctxt->userData,
1126 "xmlRelaxNG: allocate memory for doc %s\n", URL);
1127 ctxt->nbErrors++;
1128 xmlFreeDoc(doc);
1129 return (NULL);
1130 }
1131 memset(ret, 0, sizeof(xmlRelaxNGDocument));
1132 ret->doc = doc;
1133 ret->href = xmlStrdup(URL);
1134
1135 /*
1136 * transmit the ns if needed
1137 */
1138 if (ns != NULL) {
1139 root = xmlDocGetRootElement(doc);
1140 if (root != NULL) {
1141 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
1142 xmlSetProp(root, BAD_CAST"ns", ns);
1143 }
1144 }
1145 }
1146
1147 /*
1148 * push it on the stack and register it in the hash table
1149 */
1150 if (ns == NULL)
1151 xmlHashAddEntry2(ctxt->documents, BAD_CAST "", URL, ret);
1152 else
1153 xmlHashAddEntry2(ctxt->documents, ns, URL, ret);
1154 xmlRelaxNGDocumentPush(ctxt, ret);
1155
1156 /*
1157 * Some preprocessing of the document content
1158 */
1159 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
1160 if (doc == NULL) {
1161 xmlFreeDoc(ctxt->document);
1162 ctxt->doc = NULL;
1163 return(NULL);
1164 }
1165
1166 xmlRelaxNGDocumentPop(ctxt);
1167
1168 return(ret);
1169}
1170
1171/************************************************************************
1172 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +00001173 * Error functions *
1174 * *
1175 ************************************************************************/
1176
1177#define VALID_CTXT() \
1178 if (ctxt->flags == 0) xmlGenericError(xmlGenericErrorContext, \
1179 "error detected at %s:%d\n", \
1180 __FILE__, __LINE__);
1181#define VALID_ERROR if (ctxt->flags == 0) printf
1182
1183#if 0
1184/**
1185 * xmlRelaxNGErrorContext:
1186 * @ctxt: the parsing context
1187 * @schema: the schema being built
1188 * @node: the node being processed
1189 * @child: the child being processed
1190 *
1191 * Dump a RelaxNGType structure
1192 */
1193static void
1194xmlRelaxNGErrorContext(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGPtr schema,
1195 xmlNodePtr node, xmlNodePtr child)
1196{
1197 int line = 0;
1198 const xmlChar *file = NULL;
1199 const xmlChar *name = NULL;
1200 const char *type = "error";
1201
1202 if ((ctxt == NULL) || (ctxt->error == NULL))
1203 return;
1204
1205 if (child != NULL)
1206 node = child;
1207
1208 if (node != NULL) {
1209 if ((node->type == XML_DOCUMENT_NODE) ||
1210 (node->type == XML_HTML_DOCUMENT_NODE)) {
1211 xmlDocPtr doc = (xmlDocPtr) node;
1212
1213 file = doc->URL;
1214 } else {
1215 /*
1216 * Try to find contextual informations to report
1217 */
1218 if (node->type == XML_ELEMENT_NODE) {
1219 line = (int) node->content;
1220 } else if ((node->prev != NULL) &&
1221 (node->prev->type == XML_ELEMENT_NODE)) {
1222 line = (int) node->prev->content;
1223 } else if ((node->parent != NULL) &&
1224 (node->parent->type == XML_ELEMENT_NODE)) {
1225 line = (int) node->parent->content;
1226 }
1227 if ((node->doc != NULL) && (node->doc->URL != NULL))
1228 file = node->doc->URL;
1229 if (node->name != NULL)
1230 name = node->name;
1231 }
1232 }
1233
1234 if (ctxt != NULL)
1235 type = "compilation error";
1236 else if (schema != NULL)
1237 type = "runtime error";
1238
1239 if ((file != NULL) && (line != 0) && (name != NULL))
1240 ctxt->error(ctxt->userData, "%s: file %s line %d element %s\n",
1241 type, file, line, name);
1242 else if ((file != NULL) && (name != NULL))
1243 ctxt->error(ctxt->userData, "%s: file %s element %s\n",
1244 type, file, name);
1245 else if ((file != NULL) && (line != 0))
1246 ctxt->error(ctxt->userData, "%s: file %s line %d\n", type, file, line);
1247 else if (file != NULL)
1248 ctxt->error(ctxt->userData, "%s: file %s\n", type, file);
1249 else if (name != NULL)
1250 ctxt->error(ctxt->userData, "%s: element %s\n", type, name);
1251 else
1252 ctxt->error(ctxt->userData, "%s\n", type);
1253}
1254#endif
1255
1256/************************************************************************
1257 * *
1258 * Type library hooks *
1259 * *
1260 ************************************************************************/
Daniel Veillardea3f3982003-01-26 19:45:18 +00001261static xmlChar *xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt,
1262 const xmlChar *str);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001263
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001264/**
1265 * xmlRelaxNGSchemaTypeHave:
1266 * @data: data needed for the library
1267 * @type: the type name
1268 *
1269 * Check if the given type is provided by
1270 * the W3C XMLSchema Datatype library.
1271 *
1272 * Returns 1 if yes, 0 if no and -1 in case of error.
1273 */
1274static int
1275xmlRelaxNGSchemaTypeHave(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001276 const xmlChar *type) {
1277 xmlSchemaTypePtr typ;
1278
1279 if (type == NULL)
1280 return(-1);
1281 typ = xmlSchemaGetPredefinedType(type,
1282 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1283 if (typ == NULL)
1284 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001285 return(1);
1286}
1287
1288/**
1289 * xmlRelaxNGSchemaTypeCheck:
1290 * @data: data needed for the library
1291 * @type: the type name
1292 * @value: the value to check
1293 *
1294 * Check if the given type and value are validated by
1295 * the W3C XMLSchema Datatype library.
1296 *
1297 * Returns 1 if yes, 0 if no and -1 in case of error.
1298 */
1299static int
1300xmlRelaxNGSchemaTypeCheck(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001301 const xmlChar *type,
1302 const xmlChar *value) {
1303 xmlSchemaTypePtr typ;
1304 int ret;
1305
1306 /*
1307 * TODO: the type should be cached ab provided back, interface subject
1308 * to changes.
1309 * TODO: handle facets, may require an additional interface and keep
1310 * the value returned from the validation.
1311 */
1312 if ((type == NULL) || (value == NULL))
1313 return(-1);
1314 typ = xmlSchemaGetPredefinedType(type,
1315 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1316 if (typ == NULL)
1317 return(-1);
1318 ret = xmlSchemaValidatePredefinedType(typ, value, NULL);
1319 if (ret == 0)
1320 return(1);
1321 if (ret > 0)
1322 return(0);
1323 return(-1);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001324}
1325
1326/**
1327 * xmlRelaxNGSchemaTypeCompare:
1328 * @data: data needed for the library
1329 * @type: the type name
1330 * @value1: the first value
1331 * @value2: the second value
1332 *
1333 * Compare two values accordingly a type from the W3C XMLSchema
1334 * Datatype library.
1335 *
1336 * Returns 1 if yes, 0 if no and -1 in case of error.
1337 */
1338static int
1339xmlRelaxNGSchemaTypeCompare(void *data ATTRIBUTE_UNUSED,
1340 const xmlChar *type ATTRIBUTE_UNUSED,
1341 const xmlChar *value1 ATTRIBUTE_UNUSED,
1342 const xmlChar *value2 ATTRIBUTE_UNUSED) {
1343 TODO
1344 return(1);
1345}
1346
1347/**
1348 * xmlRelaxNGDefaultTypeHave:
1349 * @data: data needed for the library
1350 * @type: the type name
1351 *
1352 * Check if the given type is provided by
1353 * the default datatype library.
1354 *
1355 * Returns 1 if yes, 0 if no and -1 in case of error.
1356 */
1357static int
1358xmlRelaxNGDefaultTypeHave(void *data ATTRIBUTE_UNUSED, const xmlChar *type) {
1359 if (type == NULL)
1360 return(-1);
1361 if (xmlStrEqual(type, BAD_CAST "string"))
1362 return(1);
1363 if (xmlStrEqual(type, BAD_CAST "token"))
1364 return(1);
1365 return(0);
1366}
1367
1368/**
1369 * xmlRelaxNGDefaultTypeCheck:
1370 * @data: data needed for the library
1371 * @type: the type name
1372 * @value: the value to check
1373 *
1374 * Check if the given type and value are validated by
1375 * the default datatype library.
1376 *
1377 * Returns 1 if yes, 0 if no and -1 in case of error.
1378 */
1379static int
1380xmlRelaxNGDefaultTypeCheck(void *data ATTRIBUTE_UNUSED,
1381 const xmlChar *type ATTRIBUTE_UNUSED,
1382 const xmlChar *value ATTRIBUTE_UNUSED) {
1383 return(1);
1384}
1385
1386/**
1387 * xmlRelaxNGDefaultTypeCompare:
1388 * @data: data needed for the library
1389 * @type: the type name
1390 * @value1: the first value
1391 * @value2: the second value
1392 *
1393 * Compare two values accordingly a type from the default
1394 * datatype library.
1395 *
1396 * Returns 1 if yes, 0 if no and -1 in case of error.
1397 */
1398static int
1399xmlRelaxNGDefaultTypeCompare(void *data ATTRIBUTE_UNUSED,
1400 const xmlChar *type ATTRIBUTE_UNUSED,
1401 const xmlChar *value1 ATTRIBUTE_UNUSED,
1402 const xmlChar *value2 ATTRIBUTE_UNUSED) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00001403 int ret = -1;
1404
1405 if (xmlStrEqual(type, BAD_CAST "string")) {
1406 ret = xmlStrEqual(value1, value2);
1407 } else if (xmlStrEqual(type, BAD_CAST "token")) {
1408 if (!xmlStrEqual(value1, value2)) {
1409 xmlChar *nval, *nvalue;
1410
1411 /*
1412 * TODO: trivial optimizations are possible by
1413 * computing at compile-time
1414 */
1415 nval = xmlRelaxNGNormalize(NULL, value1);
1416 nvalue = xmlRelaxNGNormalize(NULL, value2);
1417
1418 if ((nval == NULL) || (nvalue == NULL) ||
1419 (!xmlStrEqual(nval, nvalue)))
1420 ret = -1;
1421 if (nval != NULL)
1422 xmlFree(nval);
1423 if (nvalue != NULL)
1424 xmlFree(nvalue);
1425 }
1426 }
1427 return(ret);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001428}
1429
1430static int xmlRelaxNGTypeInitialized = 0;
1431static xmlHashTablePtr xmlRelaxNGRegisteredTypes = NULL;
1432
1433/**
1434 * xmlRelaxNGFreeTypeLibrary:
1435 * @lib: the type library structure
1436 * @namespace: the URI bound to the library
1437 *
1438 * Free the structure associated to the type library
1439 */
Daniel Veillard6eadf632003-01-23 18:29:16 +00001440static void
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001441xmlRelaxNGFreeTypeLibrary(xmlRelaxNGTypeLibraryPtr lib,
1442 const xmlChar *namespace ATTRIBUTE_UNUSED) {
1443 if (lib == NULL)
1444 return;
1445 if (lib->namespace != NULL)
1446 xmlFree((xmlChar *)lib->namespace);
1447 xmlFree(lib);
1448}
1449
1450/**
1451 * xmlRelaxNGRegisterTypeLibrary:
1452 * @namespace: the URI bound to the library
1453 * @data: data associated to the library
1454 * @have: the provide function
1455 * @check: the checking function
1456 * @comp: the comparison function
1457 *
1458 * Register a new type library
1459 *
1460 * Returns 0 in case of success and -1 in case of error.
1461 */
1462static int
1463xmlRelaxNGRegisterTypeLibrary(const xmlChar *namespace, void *data,
1464 xmlRelaxNGTypeHave have, xmlRelaxNGTypeCheck check,
1465 xmlRelaxNGTypeCompare comp) {
1466 xmlRelaxNGTypeLibraryPtr lib;
1467 int ret;
1468
1469 if ((xmlRelaxNGRegisteredTypes == NULL) || (namespace == NULL) ||
1470 (check == NULL) || (comp == NULL))
1471 return(-1);
1472 if (xmlHashLookup(xmlRelaxNGRegisteredTypes, namespace) != NULL) {
1473 xmlGenericError(xmlGenericErrorContext,
1474 "Relax-NG types library '%s' already registered\n",
1475 namespace);
1476 return(-1);
1477 }
1478 lib = (xmlRelaxNGTypeLibraryPtr) xmlMalloc(sizeof(xmlRelaxNGTypeLibrary));
1479 if (lib == NULL) {
1480 xmlGenericError(xmlGenericErrorContext,
1481 "Relax-NG types library '%s' malloc() failed\n",
1482 namespace);
1483 return (-1);
1484 }
1485 memset(lib, 0, sizeof(xmlRelaxNGTypeLibrary));
1486 lib->namespace = xmlStrdup(namespace);
1487 lib->data = data;
1488 lib->have = have;
1489 lib->comp = comp;
1490 lib->check = check;
1491 ret = xmlHashAddEntry(xmlRelaxNGRegisteredTypes, namespace, lib);
1492 if (ret < 0) {
1493 xmlGenericError(xmlGenericErrorContext,
1494 "Relax-NG types library failed to register '%s'\n",
1495 namespace);
1496 xmlRelaxNGFreeTypeLibrary(lib, namespace);
1497 return(-1);
1498 }
1499 return(0);
1500}
1501
1502/**
1503 * xmlRelaxNGInitTypes:
1504 *
1505 * Initilize the default type libraries.
1506 *
1507 * Returns 0 in case of success and -1 in case of error.
1508 */
1509static int
Daniel Veillard6eadf632003-01-23 18:29:16 +00001510xmlRelaxNGInitTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001511 if (xmlRelaxNGTypeInitialized != 0)
1512 return(0);
1513 xmlRelaxNGRegisteredTypes = xmlHashCreate(10);
1514 if (xmlRelaxNGRegisteredTypes == NULL) {
1515 xmlGenericError(xmlGenericErrorContext,
1516 "Failed to allocate sh table for Relax-NG types\n");
1517 return(-1);
1518 }
1519 xmlRelaxNGRegisterTypeLibrary(
1520 BAD_CAST "http://www.w3.org/2001/XMLSchema-datatypes",
1521 NULL,
1522 xmlRelaxNGSchemaTypeHave,
1523 xmlRelaxNGSchemaTypeCheck,
1524 xmlRelaxNGSchemaTypeCompare);
1525 xmlRelaxNGRegisterTypeLibrary(
1526 xmlRelaxNGNs,
1527 NULL,
1528 xmlRelaxNGDefaultTypeHave,
1529 xmlRelaxNGDefaultTypeCheck,
1530 xmlRelaxNGDefaultTypeCompare);
1531 xmlRelaxNGTypeInitialized = 1;
1532 return(0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001533}
1534
1535/**
1536 * xmlRelaxNGCleanupTypes:
1537 *
1538 * Cleanup the default Schemas type library associated to RelaxNG
1539 */
1540void
1541xmlRelaxNGCleanupTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001542 if (xmlRelaxNGTypeInitialized == 0)
1543 return;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001544 xmlSchemaCleanupTypes();
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001545 xmlHashFree(xmlRelaxNGRegisteredTypes, (xmlHashDeallocator)
1546 xmlRelaxNGFreeTypeLibrary);
1547 xmlRelaxNGTypeInitialized = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001548}
1549
1550/************************************************************************
1551 * *
1552 * Parsing functions *
1553 * *
1554 ************************************************************************/
1555
1556static xmlRelaxNGDefinePtr xmlRelaxNGParseAttribute(
1557 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1558static xmlRelaxNGDefinePtr xmlRelaxNGParseElement(
1559 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1560static xmlRelaxNGDefinePtr xmlRelaxNGParsePatterns(
Daniel Veillard154877e2003-01-30 12:17:05 +00001561 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes, int group);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001562static xmlRelaxNGDefinePtr xmlRelaxNGParsePattern(
1563 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001564static xmlRelaxNGPtr xmlRelaxNGParseDocument(
1565 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00001566static int xmlRelaxNGParseGrammarContent(
1567 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00001568static xmlRelaxNGDefinePtr xmlRelaxNGParseNameClass(
1569 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
1570 xmlRelaxNGDefinePtr def);
Daniel Veillard419a7682003-02-03 23:22:49 +00001571static xmlRelaxNGGrammarPtr xmlRelaxNGParseGrammar(
1572 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001573
1574
1575#define IS_BLANK_NODE(n) \
1576 (((n)->type == XML_TEXT_NODE) && (xmlRelaxNGIsBlank((n)->content)))
1577
1578/**
1579 * xmlRelaxNGIsBlank:
1580 * @str: a string
1581 *
1582 * Check if a string is ignorable c.f. 4.2. Whitespace
1583 *
1584 * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
1585 */
1586static int
1587xmlRelaxNGIsBlank(xmlChar *str) {
1588 if (str == NULL)
1589 return(1);
1590 while (*str != 0) {
1591 if (!(IS_BLANK(*str))) return(0);
1592 str++;
1593 }
1594 return(1);
1595}
1596
Daniel Veillard6eadf632003-01-23 18:29:16 +00001597/**
1598 * xmlRelaxNGGetDataTypeLibrary:
1599 * @ctxt: a Relax-NG parser context
1600 * @node: the current data or value element
1601 *
1602 * Applies algorithm from 4.3. datatypeLibrary attribute
1603 *
1604 * Returns the datatypeLibary value or NULL if not found
1605 */
1606static xmlChar *
1607xmlRelaxNGGetDataTypeLibrary(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1608 xmlNodePtr node) {
1609 xmlChar *ret, *escape;
1610
Daniel Veillard6eadf632003-01-23 18:29:16 +00001611 if ((IS_RELAXNG(node, "data")) || (IS_RELAXNG(node, "value"))) {
1612 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1613 if (ret != NULL) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001614 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +00001615 if (escape == NULL) {
1616 return(ret);
1617 }
1618 xmlFree(ret);
1619 return(escape);
1620 }
1621 }
1622 node = node->parent;
1623 while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) {
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001624 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1625 if (ret != NULL) {
1626 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
1627 if (escape == NULL) {
1628 return(ret);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001629 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001630 xmlFree(ret);
1631 return(escape);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001632 }
1633 node = node->parent;
1634 }
1635 return(NULL);
1636}
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001637
1638/**
Daniel Veillardedc91922003-01-26 00:52:04 +00001639 * xmlRelaxNGParseValue:
1640 * @ctxt: a Relax-NG parser context
1641 * @node: the data node.
1642 *
1643 * parse the content of a RelaxNG value node.
1644 *
1645 * Returns the definition pointer or NULL in case of error
1646 */
1647static xmlRelaxNGDefinePtr
1648xmlRelaxNGParseValue(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1649 xmlRelaxNGDefinePtr def = NULL;
1650 xmlRelaxNGTypeLibraryPtr lib;
1651 xmlChar *type;
1652 xmlChar *library;
1653 int tmp;
1654
1655 def = xmlRelaxNGNewDefine(ctxt, node);
1656 if (def == NULL)
1657 return(NULL);
1658 def->type = XML_RELAXNG_VALUE;
1659
1660 type = xmlGetProp(node, BAD_CAST "type");
1661 if (type != NULL) {
1662 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1663 if (library == NULL)
1664 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1665
1666 def->name = type;
1667 def->ns = library;
1668
1669 lib = (xmlRelaxNGTypeLibraryPtr)
1670 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1671 if (lib == NULL) {
1672 if (ctxt->error != NULL)
1673 ctxt->error(ctxt->userData,
1674 "Use of unregistered type library '%s'\n",
1675 library);
1676 ctxt->nbErrors++;
1677 def->data = NULL;
1678 } else {
1679 def->data = lib;
1680 if (lib->have == NULL) {
1681 ctxt->error(ctxt->userData,
1682 "Internal error with type library '%s': no 'have'\n",
1683 library);
1684 ctxt->nbErrors++;
1685 } else {
1686 tmp = lib->have(lib->data, def->name);
1687 if (tmp != 1) {
1688 ctxt->error(ctxt->userData,
1689 "Error type '%s' is not exported by type library '%s'\n",
1690 def->name, library);
1691 ctxt->nbErrors++;
1692 }
1693 }
1694 }
1695 }
1696 if (node->children == NULL) {
1697 if (ctxt->error != NULL)
1698 ctxt->error(ctxt->userData,
1699 "Element <value> has no content\n");
1700 ctxt->nbErrors++;
1701 } else if ((node->children->type != XML_TEXT_NODE) ||
1702 (node->children->next != NULL)) {
1703 if (ctxt->error != NULL)
1704 ctxt->error(ctxt->userData,
1705 "Expecting a single text value for <value>content\n");
1706 ctxt->nbErrors++;
1707 } else {
1708 def->value = xmlNodeGetContent(node);
1709 if (def->value == NULL) {
1710 if (ctxt->error != NULL)
1711 ctxt->error(ctxt->userData,
1712 "Element <value> has no content\n");
1713 ctxt->nbErrors++;
1714 }
1715 }
1716 /* TODO check ahead of time that the value is okay per the type */
1717 return(def);
1718}
1719
1720/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001721 * xmlRelaxNGParseData:
1722 * @ctxt: a Relax-NG parser context
1723 * @node: the data node.
1724 *
1725 * parse the content of a RelaxNG data node.
1726 *
1727 * Returns the definition pointer or NULL in case of error
1728 */
1729static xmlRelaxNGDefinePtr
1730xmlRelaxNGParseData(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1731 xmlRelaxNGDefinePtr def = NULL;
1732 xmlRelaxNGTypeLibraryPtr lib;
1733 xmlChar *type;
1734 xmlChar *library;
1735 xmlNodePtr content;
1736 int tmp;
1737
1738 type = xmlGetProp(node, BAD_CAST "type");
1739 if (type == NULL) {
1740 if (ctxt->error != NULL)
1741 ctxt->error(ctxt->userData,
1742 "data has no type\n");
1743 ctxt->nbErrors++;
1744 return(NULL);
1745 }
1746 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1747 if (library == NULL)
1748 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1749
1750 def = xmlRelaxNGNewDefine(ctxt, node);
1751 if (def == NULL) {
1752 xmlFree(type);
1753 return(NULL);
1754 }
1755 def->type = XML_RELAXNG_DATATYPE;
1756 def->name = type;
1757 def->ns = library;
1758
1759 lib = (xmlRelaxNGTypeLibraryPtr)
1760 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1761 if (lib == NULL) {
1762 if (ctxt->error != NULL)
1763 ctxt->error(ctxt->userData,
1764 "Use of unregistered type library '%s'\n",
1765 library);
1766 ctxt->nbErrors++;
1767 def->data = NULL;
1768 } else {
1769 def->data = lib;
1770 if (lib->have == NULL) {
1771 ctxt->error(ctxt->userData,
1772 "Internal error with type library '%s': no 'have'\n",
1773 library);
1774 ctxt->nbErrors++;
1775 } else {
1776 tmp = lib->have(lib->data, def->name);
1777 if (tmp != 1) {
1778 ctxt->error(ctxt->userData,
1779 "Error type '%s' is not exported by type library '%s'\n",
1780 def->name, library);
1781 ctxt->nbErrors++;
1782 }
1783 }
1784 }
1785 content = node->children;
1786 while (content != NULL) {
1787 TODO
1788 content = content->next;
1789 }
1790
1791 return(def);
1792}
1793
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001794/**
1795 * xmlRelaxNGCompareElemDefLists:
1796 * @ctxt: a Relax-NG parser context
1797 * @defs1: the first list of element defs
1798 * @defs2: the second list of element defs
1799 *
1800 * Compare the 2 lists of element definitions. The comparison is
1801 * that if both lists do not accept the same QNames, it returns 1
1802 * If the 2 lists can accept the same QName the comparison returns 0
1803 *
1804 * Returns 1 disttinct, 0 if equal
1805 */
1806static int
1807xmlRelaxNGCompareElemDefLists(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1808 xmlRelaxNGDefinePtr *def1,
1809 xmlRelaxNGDefinePtr *def2) {
1810 xmlRelaxNGDefinePtr *basedef2 = def2;
1811
Daniel Veillard154877e2003-01-30 12:17:05 +00001812 if ((def1 == NULL) || (def2 == NULL))
1813 return(1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001814 if ((*def1 == NULL) || (*def2 == NULL))
1815 return(1);
1816 while (*def1 != NULL) {
1817 while ((*def2) != NULL) {
1818 if ((*def1)->name == NULL) {
1819 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1820 return(0);
1821 } else if ((*def2)->name == NULL) {
1822 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1823 return(0);
1824 } else if (xmlStrEqual((*def1)->name, (*def2)->name)) {
1825 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1826 return(0);
1827 }
1828 def2++;
1829 }
1830 def2 = basedef2;
1831 def1++;
1832 }
1833 return(1);
1834}
1835
1836/**
1837 * xmlRelaxNGGetElements:
1838 * @ctxt: a Relax-NG parser context
1839 * @def: the interleave definition
1840 *
1841 * Compute the list of top elements a definition can generate
1842 *
1843 * Returns a list of elements or NULL if none was found.
1844 */
1845static xmlRelaxNGDefinePtr *
1846xmlRelaxNGGetElements(xmlRelaxNGParserCtxtPtr ctxt,
1847 xmlRelaxNGDefinePtr def) {
1848 xmlRelaxNGDefinePtr *ret = NULL, parent, cur, tmp;
1849 int len = 0;
1850 int max = 0;
1851
1852 parent = NULL;
1853 cur = def;
1854 while (cur != NULL) {
Daniel Veillardb08c9812003-01-28 23:09:49 +00001855 if ((cur->type == XML_RELAXNG_ELEMENT) ||
1856 (cur->type == XML_RELAXNG_TEXT)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001857 if (ret == NULL) {
1858 max = 10;
1859 ret = (xmlRelaxNGDefinePtr *)
1860 xmlMalloc((max + 1) * sizeof(xmlRelaxNGDefinePtr));
1861 if (ret == NULL) {
1862 if (ctxt->error != NULL)
1863 ctxt->error(ctxt->userData,
1864 "Out of memory in element search\n");
1865 ctxt->nbErrors++;
1866 return(NULL);
1867 }
1868 } else if (max <= len) {
1869 max *= 2;
1870 ret = xmlRealloc(ret, (max + 1) * sizeof(xmlRelaxNGDefinePtr));
1871 if (ret == NULL) {
1872 if (ctxt->error != NULL)
1873 ctxt->error(ctxt->userData,
1874 "Out of memory in element search\n");
1875 ctxt->nbErrors++;
1876 return(NULL);
1877 }
1878 }
Daniel Veillardb08c9812003-01-28 23:09:49 +00001879 ret[len++] = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001880 ret[len] = NULL;
1881 } else if ((cur->type == XML_RELAXNG_CHOICE) ||
1882 (cur->type == XML_RELAXNG_INTERLEAVE) ||
1883 (cur->type == XML_RELAXNG_GROUP) ||
1884 (cur->type == XML_RELAXNG_ONEORMORE) ||
Daniel Veillardb08c9812003-01-28 23:09:49 +00001885 (cur->type == XML_RELAXNG_ZEROORMORE) ||
1886 (cur->type == XML_RELAXNG_OPTIONAL) ||
1887 (cur->type == XML_RELAXNG_REF) ||
1888 (cur->type == XML_RELAXNG_DEF)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001889 /*
1890 * Don't go within elements or attributes or string values.
1891 * Just gather the element top list
1892 */
1893 if (cur->content != NULL) {
1894 parent = cur;
1895 cur = cur->content;
1896 tmp = cur;
1897 while (tmp != NULL) {
1898 tmp->parent = parent;
1899 tmp = tmp->next;
1900 }
1901 continue;
1902 }
1903 }
Daniel Veillard154877e2003-01-30 12:17:05 +00001904 if (cur == def)
1905 return(ret);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001906 if (cur->next != NULL) {
1907 cur = cur->next;
1908 continue;
1909 }
1910 do {
1911 cur = cur->parent;
1912 if (cur == NULL) break;
1913 if (cur == def) return(ret);
1914 if (cur->next != NULL) {
1915 cur = cur->next;
1916 break;
1917 }
1918 } while (cur != NULL);
1919 }
1920 return(ret);
1921}
1922
1923/**
1924 * xmlRelaxNGComputeInterleaves:
1925 * @def: the interleave definition
1926 * @ctxt: a Relax-NG parser context
1927 * @node: the data node.
1928 *
1929 * A lot of work for preprocessing interleave definitions
1930 * is potentially needed to get a decent execution speed at runtime
1931 * - trying to get a total order on the element nodes generated
1932 * by the interleaves, order the list of interleave definitions
1933 * following that order.
1934 * - if <text/> is used to handle mixed content, it is better to
1935 * flag this in the define and simplify the runtime checking
1936 * algorithm
1937 */
1938static void
1939xmlRelaxNGComputeInterleaves(xmlRelaxNGDefinePtr def,
1940 xmlRelaxNGParserCtxtPtr ctxt,
1941 xmlChar *name ATTRIBUTE_UNUSED) {
1942 xmlRelaxNGDefinePtr cur;
1943
1944 xmlRelaxNGDefinePtr *list = NULL;
1945 xmlRelaxNGPartitionPtr partitions = NULL;
1946 xmlRelaxNGInterleaveGroupPtr *groups = NULL;
1947 xmlRelaxNGInterleaveGroupPtr group;
1948 int i,j,ret;
1949 int nbgroups = 0;
1950 int nbchild = 0;
1951
1952#ifdef DEBUG_INTERLEAVE
1953 xmlGenericError(xmlGenericErrorContext,
1954 "xmlRelaxNGComputeInterleaves(%s)\n",
1955 name);
1956#endif
1957 cur = def->content;
1958 while (cur != NULL) {
1959 nbchild++;
1960 cur = cur->next;
1961 }
1962
1963#ifdef DEBUG_INTERLEAVE
1964 xmlGenericError(xmlGenericErrorContext, " %d child\n", nbchild);
1965#endif
1966 groups = (xmlRelaxNGInterleaveGroupPtr *)
1967 xmlMalloc(nbchild * sizeof(xmlRelaxNGInterleaveGroupPtr));
1968 if (groups == NULL)
1969 goto error;
1970 cur = def->content;
1971 while (cur != NULL) {
Daniel Veillard154877e2003-01-30 12:17:05 +00001972 groups[nbgroups] = (xmlRelaxNGInterleaveGroupPtr)
1973 xmlMalloc(sizeof(xmlRelaxNGInterleaveGroup));
1974 if (groups[nbgroups] == NULL)
1975 goto error;
1976 groups[nbgroups]->rule = cur;
1977 groups[nbgroups]->defs = xmlRelaxNGGetElements(ctxt, cur);
1978 nbgroups++;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001979 cur = cur->next;
1980 }
1981 list = NULL;
1982#ifdef DEBUG_INTERLEAVE
1983 xmlGenericError(xmlGenericErrorContext, " %d groups\n", nbgroups);
1984#endif
1985
1986 /*
1987 * Let's check that all rules makes a partitions according to 7.4
1988 */
1989 partitions = (xmlRelaxNGPartitionPtr)
1990 xmlMalloc(sizeof(xmlRelaxNGPartition));
1991 if (partitions == NULL)
1992 goto error;
1993 partitions->nbgroups = nbgroups;
1994 for (i = 0;i < nbgroups;i++) {
1995 group = groups[i];
1996 for (j = i+1;j < nbgroups;j++) {
1997 if (groups[j] == NULL)
1998 continue;
1999 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->defs,
2000 groups[j]->defs);
2001 if (ret == 0) {
2002 if (ctxt->error != NULL)
2003 ctxt->error(ctxt->userData,
2004 "Element or text conflicts in interleave\n");
2005 ctxt->nbErrors++;
2006 }
2007 }
2008 }
2009 partitions->groups = groups;
2010
2011 /*
2012 * Free Up the child list, and save the partition list back in the def
2013 */
2014 def->data = partitions;
2015 return;
2016
2017error:
2018 if (ctxt->error != NULL)
2019 ctxt->error(ctxt->userData,
2020 "Out of memory in interleave computation\n");
2021 ctxt->nbErrors++;
2022 if (list == NULL)
2023 xmlFree(list);
2024 if (groups != NULL) {
2025 for (i = 0;i < nbgroups;i++)
2026 if (groups[i] != NULL) {
2027 if (groups[i]->defs != NULL)
2028 xmlFree(groups[i]->defs);
2029 xmlFree(groups[i]);
2030 }
2031 xmlFree(groups);
2032 }
2033 xmlRelaxNGFreePartition(partitions);
2034}
2035
2036/**
2037 * xmlRelaxNGParseInterleave:
2038 * @ctxt: a Relax-NG parser context
2039 * @node: the data node.
2040 *
2041 * parse the content of a RelaxNG interleave node.
2042 *
2043 * Returns the definition pointer or NULL in case of error
2044 */
2045static xmlRelaxNGDefinePtr
2046xmlRelaxNGParseInterleave(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2047 xmlRelaxNGDefinePtr def = NULL;
2048 xmlRelaxNGDefinePtr last = NULL, cur;
2049 xmlNodePtr child;
2050
2051 def = xmlRelaxNGNewDefine(ctxt, node);
2052 if (def == NULL) {
2053 return(NULL);
2054 }
2055 def->type = XML_RELAXNG_INTERLEAVE;
2056
2057 if (ctxt->interleaves == NULL)
2058 ctxt->interleaves = xmlHashCreate(10);
2059 if (ctxt->interleaves == NULL) {
2060 if (ctxt->error != NULL)
2061 ctxt->error(ctxt->userData,
2062 "Failed to create interleaves hash table\n");
2063 ctxt->nbErrors++;
2064 } else {
2065 char name[32];
2066
2067 snprintf(name, 32, "interleave%d", ctxt->nbInterleaves++);
2068 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST name, def) < 0) {
2069 if (ctxt->error != NULL)
2070 ctxt->error(ctxt->userData,
2071 "Failed to add %s to hash table\n", name);
2072 ctxt->nbErrors++;
2073 }
2074 }
2075 child = node->children;
2076 while (child != NULL) {
2077 if (IS_RELAXNG(child, "element")) {
2078 cur = xmlRelaxNGParseElement(ctxt, child);
2079 } else {
2080 cur = xmlRelaxNGParsePattern(ctxt, child);
2081 }
2082 if (cur != NULL) {
2083 cur->parent = def;
2084 if (last == NULL) {
2085 def->content = last = cur;
2086 } else {
2087 last->next = cur;
2088 last = cur;
2089 }
2090 }
2091 child = child->next;
2092 }
2093
2094 return(def);
2095}
Daniel Veillard6eadf632003-01-23 18:29:16 +00002096
2097/**
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002098 * xmlRelaxNGParseInclude:
2099 * @ctxt: a Relax-NG parser context
2100 * @node: the include node
2101 *
2102 * Integrate the content of an include node in the current grammar
2103 *
2104 * Returns 0 in case of success or -1 in case of error
2105 */
2106static int
2107xmlRelaxNGParseInclude(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2108 xmlRelaxNGIncludePtr incl;
2109 xmlNodePtr root;
2110 int ret = 0, tmp;
2111
2112 incl = node->_private;
2113 if (incl == NULL) {
2114 if (ctxt->error != NULL)
2115 ctxt->error(ctxt->userData,
2116 "Include node has no data\n");
2117 ctxt->nbErrors++;
2118 return(-1);
2119 }
2120 root = xmlDocGetRootElement(incl->doc);
2121 if (root == NULL) {
2122 if (ctxt->error != NULL)
2123 ctxt->error(ctxt->userData,
2124 "Include document is empty\n");
2125 ctxt->nbErrors++;
2126 return(-1);
2127 }
2128 if (!xmlStrEqual(root->name, BAD_CAST "grammar")) {
2129 if (ctxt->error != NULL)
2130 ctxt->error(ctxt->userData,
2131 "Include document root is not a grammar\n");
2132 ctxt->nbErrors++;
2133 return(-1);
2134 }
2135
2136 /*
2137 * Merge the definition from both the include and the internal list
2138 */
2139 if (root->children != NULL) {
2140 tmp = xmlRelaxNGParseGrammarContent(ctxt, root->children);
2141 if (tmp != 0)
2142 ret = -1;
2143 }
2144 if (node->children != NULL) {
2145 tmp = xmlRelaxNGParseGrammarContent(ctxt, node->children);
2146 if (tmp != 0)
2147 ret = -1;
2148 }
2149 return(ret);
2150}
2151
2152/**
Daniel Veillard276be4a2003-01-24 01:03:34 +00002153 * xmlRelaxNGParseDefine:
2154 * @ctxt: a Relax-NG parser context
2155 * @node: the define node
2156 *
2157 * parse the content of a RelaxNG define element node.
2158 *
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002159 * Returns 0 in case of success or -1 in case of error
Daniel Veillard276be4a2003-01-24 01:03:34 +00002160 */
2161static int
2162xmlRelaxNGParseDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2163 xmlChar *name;
2164 int ret = 0, tmp;
2165 xmlRelaxNGDefinePtr def;
2166 const xmlChar *olddefine;
2167
2168 name = xmlGetProp(node, BAD_CAST "name");
2169 if (name == NULL) {
2170 if (ctxt->error != NULL)
2171 ctxt->error(ctxt->userData,
2172 "define has no name\n");
2173 ctxt->nbErrors++;
2174 } else {
2175 def = xmlRelaxNGNewDefine(ctxt, node);
2176 if (def == NULL) {
2177 xmlFree(name);
2178 return(-1);
2179 }
2180 def->type = XML_RELAXNG_DEF;
2181 def->name = name;
2182 if (node->children == NULL) {
2183 if (ctxt->error != NULL)
2184 ctxt->error(ctxt->userData,
2185 "define has no children\n");
2186 ctxt->nbErrors++;
2187 } else {
2188 olddefine = ctxt->define;
2189 ctxt->define = name;
Daniel Veillard154877e2003-01-30 12:17:05 +00002190 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard276be4a2003-01-24 01:03:34 +00002191 ctxt->define = olddefine;
2192 }
2193 if (ctxt->grammar->defs == NULL)
2194 ctxt->grammar->defs = xmlHashCreate(10);
2195 if (ctxt->grammar->defs == NULL) {
2196 if (ctxt->error != NULL)
2197 ctxt->error(ctxt->userData,
2198 "Could not create definition hash\n");
2199 ctxt->nbErrors++;
2200 ret = -1;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002201 } else {
2202 tmp = xmlHashAddEntry(ctxt->grammar->defs, name, def);
2203 if (tmp < 0) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002204 xmlRelaxNGDefinePtr prev;
2205
2206 prev = xmlHashLookup(ctxt->grammar->defs, name);
2207 if (prev == NULL) {
2208 if (ctxt->error != NULL)
2209 ctxt->error(ctxt->userData,
2210 "Internal error on define aggregation of %s\n",
2211 name);
2212 ctxt->nbErrors++;
2213 ret = -1;
Daniel Veillard154877e2003-01-30 12:17:05 +00002214 } else {
2215 while (prev->nextHash != NULL)
2216 prev = prev->nextHash;
2217 prev->nextHash = def;
2218 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002219 }
2220 }
2221 }
2222 return(ret);
2223}
2224
2225/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00002226 * xmlRelaxNGParsePattern:
2227 * @ctxt: a Relax-NG parser context
2228 * @node: the pattern node.
2229 *
2230 * parse the content of a RelaxNG pattern node.
2231 *
Daniel Veillard276be4a2003-01-24 01:03:34 +00002232 * Returns the definition pointer or NULL in case of error or if no
2233 * pattern is generated.
Daniel Veillard6eadf632003-01-23 18:29:16 +00002234 */
2235static xmlRelaxNGDefinePtr
2236xmlRelaxNGParsePattern(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2237 xmlRelaxNGDefinePtr def = NULL;
2238
2239 if (IS_RELAXNG(node, "element")) {
2240 def = xmlRelaxNGParseElement(ctxt, node);
2241 } else if (IS_RELAXNG(node, "attribute")) {
2242 def = xmlRelaxNGParseAttribute(ctxt, node);
2243 } else if (IS_RELAXNG(node, "empty")) {
2244 def = xmlRelaxNGNewDefine(ctxt, node);
2245 if (def == NULL)
2246 return(NULL);
2247 def->type = XML_RELAXNG_EMPTY;
2248 } else if (IS_RELAXNG(node, "text")) {
2249 def = xmlRelaxNGNewDefine(ctxt, node);
2250 if (def == NULL)
2251 return(NULL);
2252 def->type = XML_RELAXNG_TEXT;
2253 if (node->children != NULL) {
2254 if (ctxt->error != NULL)
2255 ctxt->error(ctxt->userData, "text: had a child node\n");
2256 ctxt->nbErrors++;
2257 }
2258 } else if (IS_RELAXNG(node, "zeroOrMore")) {
2259 def = xmlRelaxNGNewDefine(ctxt, node);
2260 if (def == NULL)
2261 return(NULL);
2262 def->type = XML_RELAXNG_ZEROORMORE;
Daniel Veillard154877e2003-01-30 12:17:05 +00002263 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002264 } else if (IS_RELAXNG(node, "oneOrMore")) {
2265 def = xmlRelaxNGNewDefine(ctxt, node);
2266 if (def == NULL)
2267 return(NULL);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002268 def->type = XML_RELAXNG_ONEORMORE;
Daniel Veillard154877e2003-01-30 12:17:05 +00002269 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002270 } else if (IS_RELAXNG(node, "optional")) {
2271 def = xmlRelaxNGNewDefine(ctxt, node);
2272 if (def == NULL)
2273 return(NULL);
2274 def->type = XML_RELAXNG_OPTIONAL;
Daniel Veillard154877e2003-01-30 12:17:05 +00002275 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002276 } else if (IS_RELAXNG(node, "choice")) {
2277 def = xmlRelaxNGNewDefine(ctxt, node);
2278 if (def == NULL)
2279 return(NULL);
2280 def->type = XML_RELAXNG_CHOICE;
Daniel Veillard154877e2003-01-30 12:17:05 +00002281 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002282 } else if (IS_RELAXNG(node, "group")) {
2283 def = xmlRelaxNGNewDefine(ctxt, node);
2284 if (def == NULL)
2285 return(NULL);
2286 def->type = XML_RELAXNG_GROUP;
Daniel Veillard154877e2003-01-30 12:17:05 +00002287 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002288 } else if (IS_RELAXNG(node, "ref")) {
2289 def = xmlRelaxNGNewDefine(ctxt, node);
2290 if (def == NULL)
2291 return(NULL);
2292 def->type = XML_RELAXNG_REF;
2293 def->name = xmlGetProp(node, BAD_CAST "name");
2294 if (def->name == NULL) {
2295 if (ctxt->error != NULL)
2296 ctxt->error(ctxt->userData,
2297 "ref has no name\n");
2298 ctxt->nbErrors++;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002299 } else {
2300 if ((ctxt->define != NULL) &&
2301 (xmlStrEqual(ctxt->define, def->name))) {
2302 if (ctxt->error != NULL)
2303 ctxt->error(ctxt->userData,
2304 "Recursive reference to %s not in an element\n",
2305 def->name);
2306 ctxt->nbErrors++;
2307 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002308 }
2309 if (node->children != NULL) {
2310 if (ctxt->error != NULL)
2311 ctxt->error(ctxt->userData,
2312 "ref is not empty\n");
2313 ctxt->nbErrors++;
2314 }
2315 if (ctxt->grammar->refs == NULL)
2316 ctxt->grammar->refs = xmlHashCreate(10);
2317 if (ctxt->grammar->refs == NULL) {
2318 if (ctxt->error != NULL)
2319 ctxt->error(ctxt->userData,
2320 "Could not create references hash\n");
2321 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002322 def = NULL;
2323 } else {
2324 int tmp;
2325
2326 tmp = xmlHashAddEntry(ctxt->grammar->refs, def->name, def);
2327 if (tmp < 0) {
2328 xmlRelaxNGDefinePtr prev;
2329
2330 prev = (xmlRelaxNGDefinePtr)
2331 xmlHashLookup(ctxt->grammar->refs, def->name);
2332 if (prev == NULL) {
2333 if (ctxt->error != NULL)
2334 ctxt->error(ctxt->userData,
2335 "Internal error refs definitions '%s'\n",
2336 def->name);
2337 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002338 def = NULL;
2339 } else {
2340 def->nextHash = prev->nextHash;
2341 prev->nextHash = def;
2342 }
2343 }
2344 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002345 } else if (IS_RELAXNG(node, "data")) {
2346 def = xmlRelaxNGParseData(ctxt, node);
Daniel Veillard276be4a2003-01-24 01:03:34 +00002347 } else if (IS_RELAXNG(node, "define")) {
2348 xmlRelaxNGParseDefine(ctxt, node);
2349 def = NULL;
Daniel Veillardedc91922003-01-26 00:52:04 +00002350 } else if (IS_RELAXNG(node, "value")) {
2351 def = xmlRelaxNGParseValue(ctxt, node);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002352 } else if (IS_RELAXNG(node, "list")) {
2353 def = xmlRelaxNGNewDefine(ctxt, node);
2354 if (def == NULL)
2355 return(NULL);
2356 def->type = XML_RELAXNG_LIST;
Daniel Veillard154877e2003-01-30 12:17:05 +00002357 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002358 } else if (IS_RELAXNG(node, "interleave")) {
2359 def = xmlRelaxNGParseInterleave(ctxt, node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002360 } else if (IS_RELAXNG(node, "externalRef")) {
2361 xmlRelaxNGDocumentPtr docu;
2362 xmlNodePtr root;
2363
2364 docu = node->_private;
2365 if (docu != NULL) {
2366 def = xmlRelaxNGNewDefine(ctxt, node);
2367 if (def == NULL)
2368 return(NULL);
2369 def->type = XML_RELAXNG_EXTERNALREF;
2370
2371 if (docu->content == NULL) {
2372 /*
2373 * Then do the parsing for good
2374 */
2375 root = xmlDocGetRootElement(docu->doc);
2376 if (root == NULL) {
2377 if (ctxt->error != NULL)
2378 ctxt->error(ctxt->userData,
2379 "xmlRelaxNGParse: %s is empty\n",
2380 ctxt->URL);
2381 ctxt->nbErrors++;
2382 return (NULL);
2383 }
2384 docu->schema = xmlRelaxNGParseDocument(ctxt, root);
2385 if ((docu->schema != NULL) &&
2386 (docu->schema->topgrammar != NULL)) {
2387 docu->content = docu->schema->topgrammar->start;
2388 }
2389 }
2390 def->content = docu->content;
2391 } else {
2392 def = NULL;
2393 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002394 } else if (IS_RELAXNG(node, "notAllowed")) {
2395 def = xmlRelaxNGNewDefine(ctxt, node);
2396 if (def == NULL)
2397 return(NULL);
2398 def->type = XML_RELAXNG_NOT_ALLOWED;
2399 if (node->children != NULL) {
2400 if (ctxt->error != NULL)
2401 ctxt->error(ctxt->userData,
2402 "xmlRelaxNGParse: notAllowed element is not empty\n");
2403 ctxt->nbErrors++;
2404 }
Daniel Veillard419a7682003-02-03 23:22:49 +00002405 } else if (IS_RELAXNG(node, "grammar")) {
2406 xmlRelaxNGGrammarPtr grammar, old;
2407 xmlRelaxNGGrammarPtr oldparent;
2408
2409 oldparent = ctxt->parentgrammar;
2410 old = ctxt->grammar;
2411 ctxt->parentgrammar = old;
2412 grammar = xmlRelaxNGParseGrammar(ctxt, node->children);
2413 if (old != NULL) {
2414 ctxt->grammar = old;
2415 ctxt->parentgrammar = oldparent;
2416 if (grammar != NULL) {
2417 grammar->next = old->next;
2418 old->next = grammar;
2419 }
2420 }
2421 if (grammar != NULL)
2422 def = grammar->start;
2423 else
2424 def = NULL;
2425 } else if (IS_RELAXNG(node, "parentRef")) {
2426 if (ctxt->parentgrammar == NULL) {
2427 if (ctxt->error != NULL)
2428 ctxt->error(ctxt->userData,
2429 "Use of parentRef without a parent grammar\n");
2430 ctxt->nbErrors++;
2431 return(NULL);
2432 }
2433 def = xmlRelaxNGNewDefine(ctxt, node);
2434 if (def == NULL)
2435 return(NULL);
2436 def->type = XML_RELAXNG_PARENTREF;
2437 def->name = xmlGetProp(node, BAD_CAST "name");
2438 if (def->name == NULL) {
2439 if (ctxt->error != NULL)
2440 ctxt->error(ctxt->userData,
2441 "parentRef has no name\n");
2442 ctxt->nbErrors++;
2443 }
2444 if (node->children != NULL) {
2445 if (ctxt->error != NULL)
2446 ctxt->error(ctxt->userData,
2447 "parentRef is not empty\n");
2448 ctxt->nbErrors++;
2449 }
2450 if (ctxt->parentgrammar->refs == NULL)
2451 ctxt->parentgrammar->refs = xmlHashCreate(10);
2452 if (ctxt->parentgrammar->refs == NULL) {
2453 if (ctxt->error != NULL)
2454 ctxt->error(ctxt->userData,
2455 "Could not create references hash\n");
2456 ctxt->nbErrors++;
2457 def = NULL;
2458 } else {
2459 int tmp;
2460
2461 tmp = xmlHashAddEntry(ctxt->parentgrammar->refs, def->name, def);
2462 if (tmp < 0) {
2463 xmlRelaxNGDefinePtr prev;
2464
2465 prev = (xmlRelaxNGDefinePtr)
2466 xmlHashLookup(ctxt->parentgrammar->refs, def->name);
2467 if (prev == NULL) {
2468 if (ctxt->error != NULL)
2469 ctxt->error(ctxt->userData,
2470 "Internal error parentRef definitions '%s'\n",
2471 def->name);
2472 ctxt->nbErrors++;
2473 def = NULL;
2474 } else {
2475 def->nextHash = prev->nextHash;
2476 prev->nextHash = def;
2477 }
2478 }
2479 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002480 } else {
2481 TODO
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002482 def = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002483 }
2484 return(def);
2485}
2486
2487/**
2488 * xmlRelaxNGParseAttribute:
2489 * @ctxt: a Relax-NG parser context
2490 * @node: the element node
2491 *
2492 * parse the content of a RelaxNG attribute node.
2493 *
2494 * Returns the definition pointer or NULL in case of error.
2495 */
2496static xmlRelaxNGDefinePtr
2497xmlRelaxNGParseAttribute(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2498 xmlRelaxNGDefinePtr ret, cur, last;
2499 xmlNodePtr child;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002500 int old_flags;
2501
2502 ret = xmlRelaxNGNewDefine(ctxt, node);
2503 if (ret == NULL)
2504 return(NULL);
2505 ret->type = XML_RELAXNG_ATTRIBUTE;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002506 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002507 child = node->children;
2508 if (child == NULL) {
2509 if (ctxt->error != NULL)
2510 ctxt->error(ctxt->userData,
2511 "xmlRelaxNGParseattribute: attribute has no children\n");
2512 ctxt->nbErrors++;
2513 return(ret);
2514 }
2515 old_flags = ctxt->flags;
2516 ctxt->flags |= XML_RELAXNG_IN_ATTRIBUTE;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002517 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
2518 if (cur != NULL)
2519 child = child->next;
2520
Daniel Veillard6eadf632003-01-23 18:29:16 +00002521 last = NULL;
2522 while (child != NULL) {
2523 cur = xmlRelaxNGParsePattern(ctxt, child);
2524 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002525 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002526 switch (cur->type) {
2527 case XML_RELAXNG_EMPTY:
2528 case XML_RELAXNG_NOT_ALLOWED:
2529 case XML_RELAXNG_TEXT:
2530 case XML_RELAXNG_ELEMENT:
2531 case XML_RELAXNG_DATATYPE:
2532 case XML_RELAXNG_VALUE:
2533 case XML_RELAXNG_LIST:
2534 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00002535 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002536 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00002537 case XML_RELAXNG_DEF:
2538 case XML_RELAXNG_ONEORMORE:
2539 case XML_RELAXNG_ZEROORMORE:
2540 case XML_RELAXNG_OPTIONAL:
2541 case XML_RELAXNG_CHOICE:
2542 case XML_RELAXNG_GROUP:
2543 case XML_RELAXNG_INTERLEAVE:
2544 if (last == NULL) {
2545 ret->content = last = cur;
2546 } else {
2547 if ((last->type == XML_RELAXNG_ELEMENT) &&
2548 (ret->content == last)) {
2549 ret->content = xmlRelaxNGNewDefine(ctxt, node);
2550 if (ret->content != NULL) {
2551 ret->content->type = XML_RELAXNG_GROUP;
2552 ret->content->content = last;
2553 } else {
2554 ret->content = last;
2555 }
2556 }
2557 last->next = cur;
2558 last = cur;
2559 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002560 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002561 break;
2562 case XML_RELAXNG_ATTRIBUTE:
2563 cur->next = ret->attrs;
2564 ret->attrs = cur;
2565 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002566 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00002567 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002568 TODO
2569 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002570 }
2571 }
2572 child = child->next;
2573 }
2574 ctxt->flags = old_flags;
2575 return(ret);
2576}
2577
2578/**
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002579 * xmlRelaxNGParseExceptNameClass:
2580 * @ctxt: a Relax-NG parser context
2581 * @node: the except node
Daniel Veillard144fae12003-02-03 13:17:57 +00002582 * @attr: 1 if within an attribute, 0 if within an element
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002583 *
2584 * parse the content of a RelaxNG nameClass node.
2585 *
2586 * Returns the definition pointer or NULL in case of error.
2587 */
2588static xmlRelaxNGDefinePtr
Daniel Veillard144fae12003-02-03 13:17:57 +00002589xmlRelaxNGParseExceptNameClass(xmlRelaxNGParserCtxtPtr ctxt,
2590 xmlNodePtr node, int attr) {
2591 xmlRelaxNGDefinePtr ret, cur, last = NULL;
2592 xmlNodePtr child;
2593
2594 if (!IS_RELAXNG(node, "except"))
2595 return(NULL);
2596 if (node->children == NULL) {
2597 if (ctxt->error != NULL)
2598 ctxt->error(ctxt->userData,
2599 "except has no content\n");
2600 ctxt->nbErrors++;
2601 return(NULL);
2602 }
2603
2604 ret = xmlRelaxNGNewDefine(ctxt, node);
2605 if (ret == NULL)
2606 return(NULL);
2607 ret->type = XML_RELAXNG_EXCEPT;
2608 child = node->children;
2609 while (child != NULL) {
2610 cur = xmlRelaxNGNewDefine(ctxt, child);
2611 if (cur == NULL)
2612 break;
2613 if (attr)
2614 cur->type = XML_RELAXNG_ATTRIBUTE;
2615 else
2616 cur->type = XML_RELAXNG_ELEMENT;
2617
Daniel Veillard419a7682003-02-03 23:22:49 +00002618 if (xmlRelaxNGParseNameClass(ctxt, child, cur) != NULL) {
Daniel Veillard144fae12003-02-03 13:17:57 +00002619 if (last == NULL) {
2620 ret->content = cur;
2621 } else {
2622 last->next = cur;
2623 }
2624 last = cur;
2625 }
2626 child = child->next;
2627 }
2628
2629 return(ret);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002630}
2631
2632/**
2633 * xmlRelaxNGParseNameClass:
2634 * @ctxt: a Relax-NG parser context
2635 * @node: the nameClass node
2636 * @def: the current definition
2637 *
2638 * parse the content of a RelaxNG nameClass node.
2639 *
2640 * Returns the definition pointer or NULL in case of error.
2641 */
2642static xmlRelaxNGDefinePtr
2643xmlRelaxNGParseNameClass(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
2644 xmlRelaxNGDefinePtr def) {
2645 xmlRelaxNGDefinePtr ret = def;
2646 xmlChar *val;
2647
2648 if (IS_RELAXNG(node, "name")) {
2649 val = xmlNodeGetContent(node);
2650 ret->name = val;
2651 val = xmlGetProp(node, BAD_CAST "ns");
2652 ret->ns = val;
2653 } else if (IS_RELAXNG(node, "anyName")) {
2654 ret->name = NULL;
2655 ret->ns = NULL;
2656 if (node->children != NULL) {
2657 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00002658 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
2659 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002660 }
2661 } else if (IS_RELAXNG(node, "nsName")) {
2662 ret->name = NULL;
2663 ret->ns = xmlGetProp(node, BAD_CAST "ns");
2664 if (ret->ns == NULL) {
2665 if (ctxt->error != NULL)
2666 ctxt->error(ctxt->userData,
2667 "nsName has no ns attribute\n");
2668 ctxt->nbErrors++;
2669 }
2670 if (node->children != NULL) {
2671 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00002672 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
2673 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002674 }
2675 } else if (IS_RELAXNG(node, "choice")) {
2676 TODO
2677 } else {
2678 if (ctxt->error != NULL)
2679 ctxt->error(ctxt->userData,
2680 "expecting name, anyName, nsName or choice : got %s\n",
2681 node->name);
2682 ctxt->nbErrors++;
2683 return(NULL);
2684 }
2685 return(ret);
2686}
2687
2688/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00002689 * xmlRelaxNGParseElement:
2690 * @ctxt: a Relax-NG parser context
2691 * @node: the element node
2692 *
2693 * parse the content of a RelaxNG element node.
2694 *
2695 * Returns the definition pointer or NULL in case of error.
2696 */
2697static xmlRelaxNGDefinePtr
2698xmlRelaxNGParseElement(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2699 xmlRelaxNGDefinePtr ret, cur, last;
2700 xmlNodePtr child;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002701 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002702
2703 ret = xmlRelaxNGNewDefine(ctxt, node);
2704 if (ret == NULL)
2705 return(NULL);
2706 ret->type = XML_RELAXNG_ELEMENT;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002707 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002708 child = node->children;
2709 if (child == NULL) {
2710 if (ctxt->error != NULL)
2711 ctxt->error(ctxt->userData,
2712 "xmlRelaxNGParseElement: element has no children\n");
2713 ctxt->nbErrors++;
2714 return(ret);
2715 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002716 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
2717 if (cur != NULL)
2718 child = child->next;
2719
Daniel Veillard6eadf632003-01-23 18:29:16 +00002720 if (child == NULL) {
2721 if (ctxt->error != NULL)
2722 ctxt->error(ctxt->userData,
2723 "xmlRelaxNGParseElement: element has no content\n");
2724 ctxt->nbErrors++;
2725 return(ret);
2726 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002727 olddefine = ctxt->define;
2728 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002729 last = NULL;
2730 while (child != NULL) {
2731 cur = xmlRelaxNGParsePattern(ctxt, child);
2732 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002733 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002734 switch (cur->type) {
2735 case XML_RELAXNG_EMPTY:
2736 case XML_RELAXNG_NOT_ALLOWED:
2737 case XML_RELAXNG_TEXT:
2738 case XML_RELAXNG_ELEMENT:
2739 case XML_RELAXNG_DATATYPE:
2740 case XML_RELAXNG_VALUE:
2741 case XML_RELAXNG_LIST:
2742 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00002743 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002744 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00002745 case XML_RELAXNG_DEF:
2746 case XML_RELAXNG_ZEROORMORE:
2747 case XML_RELAXNG_ONEORMORE:
2748 case XML_RELAXNG_OPTIONAL:
2749 case XML_RELAXNG_CHOICE:
2750 case XML_RELAXNG_GROUP:
2751 case XML_RELAXNG_INTERLEAVE:
2752 if (last == NULL) {
2753 ret->content = last = cur;
2754 } else {
2755 if ((last->type == XML_RELAXNG_ELEMENT) &&
2756 (ret->content == last)) {
2757 ret->content = xmlRelaxNGNewDefine(ctxt, node);
2758 if (ret->content != NULL) {
2759 ret->content->type = XML_RELAXNG_GROUP;
2760 ret->content->content = last;
2761 } else {
2762 ret->content = last;
2763 }
2764 }
2765 last->next = cur;
2766 last = cur;
2767 }
2768 break;
2769 case XML_RELAXNG_ATTRIBUTE:
2770 cur->next = ret->attrs;
2771 ret->attrs = cur;
2772 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002773 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00002774 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002775 TODO
2776 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002777 }
2778 }
2779 child = child->next;
2780 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002781 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002782 return(ret);
2783}
2784
2785/**
2786 * xmlRelaxNGParsePatterns:
2787 * @ctxt: a Relax-NG parser context
2788 * @nodes: list of nodes
Daniel Veillard154877e2003-01-30 12:17:05 +00002789 * @group: use an implicit <group> for elements
Daniel Veillard6eadf632003-01-23 18:29:16 +00002790 *
2791 * parse the content of a RelaxNG start node.
2792 *
2793 * Returns the definition pointer or NULL in case of error.
2794 */
2795static xmlRelaxNGDefinePtr
Daniel Veillard154877e2003-01-30 12:17:05 +00002796xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes,
2797 int group) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002798 xmlRelaxNGDefinePtr def = NULL, last = NULL, cur, parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002799
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002800 parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002801 while (nodes != NULL) {
2802 if (IS_RELAXNG(nodes, "element")) {
2803 cur = xmlRelaxNGParseElement(ctxt, nodes);
2804 if (def == NULL) {
2805 def = last = cur;
2806 } else {
Daniel Veillard154877e2003-01-30 12:17:05 +00002807 if ((group == 1) && (def->type == XML_RELAXNG_ELEMENT) &&
2808 (def == last)) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00002809 def = xmlRelaxNGNewDefine(ctxt, nodes);
2810 def->type = XML_RELAXNG_GROUP;
2811 def->content = last;
2812 }
2813 last->next = cur;
2814 last = cur;
2815 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002816 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002817 } else {
2818 cur = xmlRelaxNGParsePattern(ctxt, nodes);
Daniel Veillard419a7682003-02-03 23:22:49 +00002819 if (cur != NULL) {
2820 if (def == NULL) {
2821 def = last = cur;
2822 } else {
2823 last->next = cur;
2824 last = cur;
2825 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002826 }
2827 }
2828 nodes = nodes->next;
2829 }
2830 return(def);
2831}
2832
2833/**
2834 * xmlRelaxNGParseStart:
2835 * @ctxt: a Relax-NG parser context
2836 * @nodes: start children nodes
2837 *
2838 * parse the content of a RelaxNG start node.
2839 *
2840 * Returns 0 in case of success, -1 in case of error
2841 */
2842static int
2843xmlRelaxNGParseStart(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
2844 int ret = 0;
2845 xmlRelaxNGDefinePtr def = NULL;
2846
2847 while (nodes != NULL) {
2848 if (IS_RELAXNG(nodes, "empty")) {
2849 TODO
2850 xmlElemDump(stdout, nodes->doc, nodes);
2851 } else if (IS_RELAXNG(nodes, "notAllowed")) {
2852 TODO
2853 xmlElemDump(stdout, nodes->doc, nodes);
2854 } else {
Daniel Veillard154877e2003-01-30 12:17:05 +00002855 def = xmlRelaxNGParsePatterns(ctxt, nodes, 1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002856 ctxt->grammar->start = def;
2857 }
2858 nodes = nodes->next;
2859 }
2860 return(ret);
2861}
2862
2863/**
2864 * xmlRelaxNGParseGrammarContent:
2865 * @ctxt: a Relax-NG parser context
2866 * @nodes: grammar children nodes
2867 *
2868 * parse the content of a RelaxNG grammar node.
2869 *
2870 * Returns 0 in case of success, -1 in case of error
2871 */
2872static int
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002873xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes)
Daniel Veillard6eadf632003-01-23 18:29:16 +00002874{
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002875 int ret = 0, tmp;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002876
2877 if (nodes == NULL) {
2878 if (ctxt->error != NULL)
2879 ctxt->error(ctxt->userData,
2880 "grammar has no children\n");
2881 ctxt->nbErrors++;
2882 return(-1);
2883 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002884 while (nodes != NULL) {
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002885 if (IS_RELAXNG(nodes, "start")) {
2886 if (nodes->children == NULL) {
2887 if (ctxt->error != NULL)
2888 ctxt->error(ctxt->userData,
2889 "grammar has no children\n");
2890 ctxt->nbErrors++;
2891 } else {
2892 tmp = xmlRelaxNGParseStart(ctxt, nodes->children);
2893 if (tmp != 0)
2894 ret = -1;
2895 }
2896 } else if (IS_RELAXNG(nodes, "define")) {
2897 tmp = xmlRelaxNGParseDefine(ctxt, nodes);
2898 if (tmp != 0)
2899 ret = -1;
2900 } else if (IS_RELAXNG(nodes, "include")) {
2901 tmp = xmlRelaxNGParseInclude(ctxt, nodes);
2902 if (tmp != 0)
2903 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002904 } else {
2905 if (ctxt->error != NULL)
2906 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002907 "grammar has unexpected child %s\n", nodes->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002908 ctxt->nbErrors++;
2909 ret = -1;
2910 }
2911 nodes = nodes->next;
2912 }
2913 return (ret);
2914}
2915
2916/**
2917 * xmlRelaxNGCheckReference:
2918 * @ref: the ref
2919 * @ctxt: a Relax-NG parser context
2920 * @name: the name associated to the defines
2921 *
2922 * Applies the 4.17. combine attribute rule for all the define
2923 * element of a given grammar using the same name.
2924 */
2925static void
2926xmlRelaxNGCheckReference(xmlRelaxNGDefinePtr ref,
2927 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
2928 xmlRelaxNGGrammarPtr grammar;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002929 xmlRelaxNGDefinePtr def, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002930
2931 grammar = ctxt->grammar;
2932 if (grammar == NULL) {
2933 if (ctxt->error != NULL)
2934 ctxt->error(ctxt->userData,
2935 "Internal error: no grammar in CheckReference %s\n",
2936 name);
2937 ctxt->nbErrors++;
2938 return;
2939 }
2940 if (ref->content != NULL) {
2941 if (ctxt->error != NULL)
2942 ctxt->error(ctxt->userData,
2943 "Internal error: reference has content in CheckReference %s\n",
2944 name);
2945 ctxt->nbErrors++;
2946 return;
2947 }
2948 if (grammar->defs != NULL) {
2949 def = xmlHashLookup(grammar->defs, name);
2950 if (def != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00002951 cur = ref;
2952 while (cur != NULL) {
2953 cur->content = def;
2954 cur = cur->nextHash;
2955 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002956 } else {
2957 TODO
2958 }
2959 }
2960 /*
2961 * TODO: make a closure and verify there is no loop !
2962 */
2963}
2964
2965/**
2966 * xmlRelaxNGCheckCombine:
2967 * @define: the define(s) list
2968 * @ctxt: a Relax-NG parser context
2969 * @name: the name associated to the defines
2970 *
2971 * Applies the 4.17. combine attribute rule for all the define
2972 * element of a given grammar using the same name.
2973 */
2974static void
2975xmlRelaxNGCheckCombine(xmlRelaxNGDefinePtr define,
2976 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
2977 xmlChar *combine;
2978 int choiceOrInterleave = -1;
2979 int missing = 0;
2980 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
2981
2982 if (define->nextHash == NULL)
2983 return;
2984 cur = define;
2985 while (cur != NULL) {
2986 combine = xmlGetProp(cur->node, BAD_CAST "combine");
2987 if (combine != NULL) {
2988 if (xmlStrEqual(combine, BAD_CAST "choice")) {
2989 if (choiceOrInterleave == -1)
2990 choiceOrInterleave = 1;
2991 else if (choiceOrInterleave == 0) {
2992 if (ctxt->error != NULL)
2993 ctxt->error(ctxt->userData,
2994 "Defines for %s use both 'choice' and 'interleave'\n",
2995 name);
2996 ctxt->nbErrors++;
2997 }
Daniel Veillard154877e2003-01-30 12:17:05 +00002998 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00002999 if (choiceOrInterleave == -1)
3000 choiceOrInterleave = 0;
3001 else if (choiceOrInterleave == 1) {
3002 if (ctxt->error != NULL)
3003 ctxt->error(ctxt->userData,
3004 "Defines for %s use both 'choice' and 'interleave'\n",
3005 name);
3006 ctxt->nbErrors++;
3007 }
3008 } else {
3009 if (ctxt->error != NULL)
3010 ctxt->error(ctxt->userData,
3011 "Defines for %s use unknown combine value '%s''\n",
3012 name, combine);
3013 ctxt->nbErrors++;
3014 }
3015 xmlFree(combine);
3016 } else {
3017 if (missing == 0)
3018 missing = 1;
3019 else {
3020 if (ctxt->error != NULL)
3021 ctxt->error(ctxt->userData,
3022 "Some defines for %s lacks the combine attribute\n",
3023 name);
3024 ctxt->nbErrors++;
3025 }
3026 }
3027
3028 cur = cur->nextHash;
3029 }
3030#ifdef DEBUG
3031 xmlGenericError(xmlGenericErrorContext,
3032 "xmlRelaxNGCheckCombine(): merging %s defines: %d\n",
3033 name, choiceOrInterleave);
3034#endif
3035 if (choiceOrInterleave == -1)
3036 choiceOrInterleave = 0;
3037 cur = xmlRelaxNGNewDefine(ctxt, define->node);
3038 if (cur == NULL)
3039 return;
3040 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003041 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard154877e2003-01-30 12:17:05 +00003042 else
3043 cur->type = XML_RELAXNG_CHOICE;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003044 tmp = define;
3045 last = NULL;
3046 while (tmp != NULL) {
3047 if (tmp->content != NULL) {
3048 if (tmp->content->next != NULL) {
3049 /*
3050 * we need first to create a wrapper.
3051 */
3052 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
3053 if (tmp2 == NULL)
3054 break;
3055 tmp2->type = XML_RELAXNG_GROUP;
3056 tmp2->content = tmp->content;
3057 } else {
3058 tmp2 = tmp->content;
3059 }
3060 if (last == NULL) {
3061 cur->content = tmp2;
3062 } else {
3063 last->next = tmp2;
3064 }
3065 last = tmp2;
3066 tmp->content = NULL;
3067 }
3068 tmp = tmp->nextHash;
3069 }
3070 define->content = cur;
Daniel Veillard154877e2003-01-30 12:17:05 +00003071 if (choiceOrInterleave == 0) {
3072 if (ctxt->interleaves == NULL)
3073 ctxt->interleaves = xmlHashCreate(10);
3074 if (ctxt->interleaves == NULL) {
3075 if (ctxt->error != NULL)
3076 ctxt->error(ctxt->userData,
3077 "Failed to create interleaves hash table\n");
3078 ctxt->nbErrors++;
3079 } else {
3080 char tmpname[32];
3081
3082 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
3083 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
3084 if (ctxt->error != NULL)
3085 ctxt->error(ctxt->userData,
3086 "Failed to add %s to hash table\n", tmpname);
3087 ctxt->nbErrors++;
3088 }
3089 }
3090 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003091}
3092
3093/**
3094 * xmlRelaxNGCombineStart:
3095 * @ctxt: a Relax-NG parser context
3096 * @grammar: the grammar
3097 *
3098 * Applies the 4.17. combine rule for all the start
3099 * element of a given grammar.
3100 */
3101static void
3102xmlRelaxNGCombineStart(xmlRelaxNGParserCtxtPtr ctxt,
3103 xmlRelaxNGGrammarPtr grammar) {
3104 xmlRelaxNGDefinePtr starts;
3105 xmlChar *combine;
3106 int choiceOrInterleave = -1;
3107 int missing = 0;
3108 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
3109
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003110 starts = grammar->startList;
3111 if ((starts == NULL) || (starts->nextHash == NULL))
Daniel Veillard6eadf632003-01-23 18:29:16 +00003112 return;
3113 cur = starts;
3114 while (cur != NULL) {
3115 combine = xmlGetProp(cur->node, BAD_CAST "combine");
3116 if (combine != NULL) {
3117 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3118 if (choiceOrInterleave == -1)
3119 choiceOrInterleave = 1;
3120 else if (choiceOrInterleave == 0) {
3121 if (ctxt->error != NULL)
3122 ctxt->error(ctxt->userData,
3123 "<start> use both 'choice' and 'interleave'\n");
3124 ctxt->nbErrors++;
3125 }
3126 } else if (xmlStrEqual(combine, BAD_CAST "choice")) {
3127 if (choiceOrInterleave == -1)
3128 choiceOrInterleave = 0;
3129 else if (choiceOrInterleave == 1) {
3130 if (ctxt->error != NULL)
3131 ctxt->error(ctxt->userData,
3132 "<start> use both 'choice' and 'interleave'\n");
3133 ctxt->nbErrors++;
3134 }
3135 } else {
3136 if (ctxt->error != NULL)
3137 ctxt->error(ctxt->userData,
3138 "<start> uses unknown combine value '%s''\n", combine);
3139 ctxt->nbErrors++;
3140 }
3141 xmlFree(combine);
3142 } else {
3143 if (missing == 0)
3144 missing = 1;
3145 else {
3146 if (ctxt->error != NULL)
3147 ctxt->error(ctxt->userData,
3148 "Some <start> elements lacks the combine attribute\n");
3149 ctxt->nbErrors++;
3150 }
3151 }
3152
3153 cur = cur->nextHash;
3154 }
3155#ifdef DEBUG
3156 xmlGenericError(xmlGenericErrorContext,
3157 "xmlRelaxNGCombineStart(): merging <start>: %d\n",
3158 choiceOrInterleave);
3159#endif
3160 if (choiceOrInterleave == -1)
3161 choiceOrInterleave = 0;
3162 cur = xmlRelaxNGNewDefine(ctxt, starts->node);
3163 if (cur == NULL)
3164 return;
3165 if (choiceOrInterleave == 0)
3166 cur->type = XML_RELAXNG_CHOICE;
3167 else
3168 cur->type = XML_RELAXNG_INTERLEAVE;
3169 tmp = starts;
3170 last = NULL;
3171 while (tmp != NULL) {
3172 if (tmp->content != NULL) {
3173 if (tmp->content->next != NULL) {
3174 /*
3175 * we need first to create a wrapper.
3176 */
3177 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
3178 if (tmp2 == NULL)
3179 break;
3180 tmp2->type = XML_RELAXNG_GROUP;
3181 tmp2->content = tmp->content;
3182 } else {
3183 tmp2 = tmp->content;
3184 }
3185 if (last == NULL) {
3186 cur->content = tmp2;
3187 } else {
3188 last->next = tmp2;
3189 }
3190 last = tmp2;
3191 tmp->content = NULL;
3192 }
3193 tmp = tmp->nextHash;
3194 }
3195 starts->content = cur;
3196}
3197
3198/**
3199 * xmlRelaxNGParseGrammar:
3200 * @ctxt: a Relax-NG parser context
3201 * @nodes: grammar children nodes
3202 *
3203 * parse a Relax-NG <grammar> node
3204 *
3205 * Returns the internal xmlRelaxNGGrammarPtr built or
3206 * NULL in case of error
3207 */
3208static xmlRelaxNGGrammarPtr
3209xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
3210 xmlRelaxNGGrammarPtr ret, tmp, old;
3211
Daniel Veillard6eadf632003-01-23 18:29:16 +00003212 ret = xmlRelaxNGNewGrammar(ctxt);
3213 if (ret == NULL)
3214 return(NULL);
3215
3216 /*
3217 * Link the new grammar in the tree
3218 */
3219 ret->parent = ctxt->grammar;
3220 if (ctxt->grammar != NULL) {
3221 tmp = ctxt->grammar->children;
3222 if (tmp == NULL) {
3223 ctxt->grammar->children = ret;
3224 } else {
3225 while (tmp->next != NULL)
3226 tmp = tmp->next;
3227 tmp->next = ret;
3228 }
3229 }
3230
3231 old = ctxt->grammar;
3232 ctxt->grammar = ret;
3233 xmlRelaxNGParseGrammarContent(ctxt, nodes);
3234 ctxt->grammar = ret;
3235
3236 /*
3237 * Apply 4.17 mergingd rules to defines and starts
3238 */
3239 xmlRelaxNGCombineStart(ctxt, ret);
3240 if (ret->defs != NULL) {
3241 xmlHashScan(ret->defs, (xmlHashScanner) xmlRelaxNGCheckCombine,
3242 ctxt);
3243 }
3244
3245 /*
3246 * link together defines and refs in this grammar
3247 */
3248 if (ret->refs != NULL) {
3249 xmlHashScan(ret->refs, (xmlHashScanner) xmlRelaxNGCheckReference,
3250 ctxt);
3251 }
3252 ctxt->grammar = old;
3253 return(ret);
3254}
3255
3256/**
3257 * xmlRelaxNGParseDocument:
3258 * @ctxt: a Relax-NG parser context
3259 * @node: the root node of the RelaxNG schema
3260 *
3261 * parse a Relax-NG definition resource and build an internal
3262 * xmlRelaxNG struture which can be used to validate instances.
3263 *
3264 * Returns the internal XML RelaxNG structure built or
3265 * NULL in case of error
3266 */
3267static xmlRelaxNGPtr
3268xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3269 xmlRelaxNGPtr schema = NULL;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003270 const xmlChar *olddefine;
Daniel Veillarde431a272003-01-29 23:02:33 +00003271 xmlRelaxNGGrammarPtr old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003272
3273 if ((ctxt == NULL) || (node == NULL))
3274 return (NULL);
3275
3276 schema = xmlRelaxNGNewRelaxNG(ctxt);
3277 if (schema == NULL)
3278 return(NULL);
3279
Daniel Veillard276be4a2003-01-24 01:03:34 +00003280 olddefine = ctxt->define;
3281 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003282 if (IS_RELAXNG(node, "grammar")) {
3283 schema->topgrammar = xmlRelaxNGParseGrammar(ctxt, node->children);
3284 } else {
3285 schema->topgrammar = xmlRelaxNGNewGrammar(ctxt);
3286 if (schema->topgrammar == NULL) {
3287 return(schema);
3288 }
3289 schema->topgrammar->parent = NULL;
Daniel Veillarde431a272003-01-29 23:02:33 +00003290 old = ctxt->grammar;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003291 ctxt->grammar = schema->topgrammar;
3292 xmlRelaxNGParseStart(ctxt, node);
Daniel Veillarde431a272003-01-29 23:02:33 +00003293 if (old != NULL)
3294 ctxt->grammar = old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003295 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003296 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003297
3298#ifdef DEBUG
3299 if (schema == NULL)
3300 xmlGenericError(xmlGenericErrorContext,
3301 "xmlRelaxNGParseDocument() failed\n");
3302#endif
3303
3304 return (schema);
3305}
3306
3307/************************************************************************
3308 * *
3309 * Reading RelaxNGs *
3310 * *
3311 ************************************************************************/
3312
3313/**
3314 * xmlRelaxNGNewParserCtxt:
3315 * @URL: the location of the schema
3316 *
3317 * Create an XML RelaxNGs parse context for that file/resource expected
3318 * to contain an XML RelaxNGs file.
3319 *
3320 * Returns the parser context or NULL in case of error
3321 */
3322xmlRelaxNGParserCtxtPtr
3323xmlRelaxNGNewParserCtxt(const char *URL) {
3324 xmlRelaxNGParserCtxtPtr ret;
3325
3326 if (URL == NULL)
3327 return(NULL);
3328
3329 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
3330 if (ret == NULL) {
3331 xmlGenericError(xmlGenericErrorContext,
3332 "Failed to allocate new schama parser context for %s\n", URL);
3333 return (NULL);
3334 }
3335 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
3336 ret->URL = xmlStrdup((const xmlChar *)URL);
3337 return (ret);
3338}
3339
3340/**
3341 * xmlRelaxNGNewMemParserCtxt:
3342 * @buffer: a pointer to a char array containing the schemas
3343 * @size: the size of the array
3344 *
3345 * Create an XML RelaxNGs parse context for that memory buffer expected
3346 * to contain an XML RelaxNGs file.
3347 *
3348 * Returns the parser context or NULL in case of error
3349 */
3350xmlRelaxNGParserCtxtPtr
3351xmlRelaxNGNewMemParserCtxt(const char *buffer, int size) {
3352 xmlRelaxNGParserCtxtPtr ret;
3353
3354 if ((buffer == NULL) || (size <= 0))
3355 return(NULL);
3356
3357 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
3358 if (ret == NULL) {
3359 xmlGenericError(xmlGenericErrorContext,
3360 "Failed to allocate new schama parser context\n");
3361 return (NULL);
3362 }
3363 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
3364 ret->buffer = buffer;
3365 ret->size = size;
3366 return (ret);
3367}
3368
3369/**
3370 * xmlRelaxNGFreeParserCtxt:
3371 * @ctxt: the schema parser context
3372 *
3373 * Free the resources associated to the schema parser context
3374 */
3375void
3376xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxtPtr ctxt) {
3377 if (ctxt == NULL)
3378 return;
3379 if (ctxt->URL != NULL)
3380 xmlFree(ctxt->URL);
3381 if (ctxt->doc != NULL)
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003382 xmlFreeDoc(ctxt->document);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003383 if (ctxt->interleaves != NULL)
3384 xmlHashFree(ctxt->interleaves, NULL);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003385 if (ctxt->documents != NULL)
3386 xmlHashFree(ctxt->documents, (xmlHashDeallocator)
3387 xmlRelaxNGFreeDocument);
3388 if (ctxt->docTab != NULL)
3389 xmlFree(ctxt->docTab);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003390 if (ctxt->incTab != NULL)
3391 xmlFree(ctxt->incTab);
Daniel Veillard419a7682003-02-03 23:22:49 +00003392 if (ctxt->defTab != NULL) {
3393 int i;
3394
3395 for (i = 0;i < ctxt->defNr;i++)
3396 xmlRelaxNGFreeDefine(ctxt->defTab[i]);
3397 xmlFree(ctxt->defTab);
3398 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003399 xmlFree(ctxt);
3400}
3401
Daniel Veillard6eadf632003-01-23 18:29:16 +00003402/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003403 * xmlRelaxNGCleanupDoc:
3404 * @ctxt: a Relax-NG parser context
3405 * @doc: an xmldocPtr document pointer
Daniel Veillard6eadf632003-01-23 18:29:16 +00003406 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003407 * Cleanup the document from unwanted nodes for parsing, resolve
3408 * Include and externalRef lookups.
Daniel Veillard6eadf632003-01-23 18:29:16 +00003409 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003410 * Returns the cleaned up document or NULL in case of error
Daniel Veillard6eadf632003-01-23 18:29:16 +00003411 */
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003412static xmlDocPtr
3413xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt, xmlDocPtr doc) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003414 xmlNodePtr root, cur, delete;
3415
Daniel Veillard6eadf632003-01-23 18:29:16 +00003416 /*
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003417 * Extract the root
Daniel Veillard6eadf632003-01-23 18:29:16 +00003418 */
3419 root = xmlDocGetRootElement(doc);
3420 if (root == NULL) {
3421 if (ctxt->error != NULL)
3422 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
3423 ctxt->URL);
3424 ctxt->nbErrors++;
3425 return (NULL);
3426 }
3427
3428 /*
3429 * Remove all the blank text nodes
3430 */
3431 delete = NULL;
3432 cur = root;
3433 while (cur != NULL) {
3434 if (delete != NULL) {
3435 xmlUnlinkNode(delete);
3436 xmlFreeNode(delete);
3437 delete = NULL;
3438 }
3439 if (cur->type == XML_ELEMENT_NODE) {
3440 /*
3441 * Simplification 4.1. Annotations
3442 */
3443 if ((cur->ns == NULL) ||
3444 (!xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
3445 delete = cur;
3446 goto skip_children;
3447 } else {
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003448 if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003449 xmlChar *href, *ns, *base, *URL;
3450 xmlRelaxNGDocumentPtr docu;
3451
3452 ns = xmlGetProp(cur, BAD_CAST "ns");
3453 href = xmlGetProp(cur, BAD_CAST "href");
3454 if (href == NULL) {
3455 if (ctxt->error != NULL)
3456 ctxt->error(ctxt->userData,
3457 "xmlRelaxNGParse: externalRef has no href attribute\n");
3458 ctxt->nbErrors++;
3459 delete = cur;
3460 goto skip_children;
3461 }
3462 base = xmlNodeGetBase(cur->doc, cur);
3463 URL = xmlBuildURI(href, base);
3464 if (URL == NULL) {
3465 if (ctxt->error != NULL)
3466 ctxt->error(ctxt->userData,
3467 "Failed to compute URL for externalRef %s\n", href);
3468 ctxt->nbErrors++;
3469 if (href != NULL)
3470 xmlFree(href);
3471 if (base != NULL)
3472 xmlFree(base);
3473 delete = cur;
3474 goto skip_children;
3475 }
3476 if (href != NULL)
3477 xmlFree(href);
3478 if (base != NULL)
3479 xmlFree(base);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003480 docu = xmlRelaxNGLoadExternalRef(ctxt, URL, ns);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003481 if (docu == NULL) {
3482 if (ctxt->error != NULL)
3483 ctxt->error(ctxt->userData,
3484 "Failed to load externalRef %s\n", URL);
3485 ctxt->nbErrors++;
3486 xmlFree(URL);
3487 delete = cur;
3488 goto skip_children;
3489 }
3490 xmlFree(URL);
3491 cur->_private = docu;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003492 } else if (xmlStrEqual(cur->name, BAD_CAST "include")) {
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003493 xmlChar *href, *base, *URL;
3494 xmlRelaxNGIncludePtr incl;
3495
3496 href = xmlGetProp(cur, BAD_CAST "href");
3497 if (href == NULL) {
3498 if (ctxt->error != NULL)
3499 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003500 "xmlRelaxNGParse: include has no href attribute\n");
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003501 ctxt->nbErrors++;
3502 delete = cur;
3503 goto skip_children;
3504 }
3505 base = xmlNodeGetBase(cur->doc, cur);
3506 URL = xmlBuildURI(href, base);
3507 if (URL == NULL) {
3508 if (ctxt->error != NULL)
3509 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003510 "Failed to compute URL for include %s\n", href);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003511 ctxt->nbErrors++;
3512 if (href != NULL)
3513 xmlFree(href);
3514 if (base != NULL)
3515 xmlFree(base);
3516 delete = cur;
3517 goto skip_children;
3518 }
3519 if (href != NULL)
3520 xmlFree(href);
3521 if (base != NULL)
3522 xmlFree(base);
3523 incl = xmlRelaxNGLoadInclude(ctxt, URL, cur);
3524 if (incl == NULL) {
3525 if (ctxt->error != NULL)
3526 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003527 "Failed to load include %s\n", URL);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003528 ctxt->nbErrors++;
3529 xmlFree(URL);
3530 delete = cur;
3531 goto skip_children;
3532 }
3533 xmlFree(URL);
3534 cur->_private = incl;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003535 } else if ((xmlStrEqual(cur->name, BAD_CAST "element")) ||
3536 (xmlStrEqual(cur->name, BAD_CAST "attribute"))) {
3537 xmlChar *name;
3538 xmlNodePtr text = NULL;
3539
3540 /*
3541 * Simplification 4.8. name attribute of element
3542 * and attribute elements
3543 */
3544 name = xmlGetProp(cur, BAD_CAST "name");
3545 if (name != NULL) {
3546 if (cur->children == NULL) {
3547 text = xmlNewChild(cur, cur->ns, BAD_CAST "name",
3548 name);
3549 } else {
3550 xmlNodePtr node;
3551 node = xmlNewNode(cur->ns, BAD_CAST "name");
3552 if (node != NULL) {
3553 xmlAddPrevSibling(cur->children, node);
3554 text = xmlNewText(name);
3555 xmlAddChild(node, text);
3556 text = node;
3557 }
3558 }
3559 xmlUnsetProp(cur, BAD_CAST "name");
3560 xmlFree(name);
3561 }
3562 if (xmlStrEqual(cur->name, BAD_CAST "attribute")) {
3563 if (text == NULL) {
3564 text = cur->children;
3565 while (text != NULL) {
3566 if ((text->type == XML_ELEMENT_NODE) &&
3567 (xmlStrEqual(text->name, BAD_CAST "name")))
3568 break;
3569 text = text->next;
3570 }
3571 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003572 if (text != NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003573 xmlSetProp(text, BAD_CAST "ns", BAD_CAST "");
3574 }
3575 }
3576 } else if ((xmlStrEqual(cur->name, BAD_CAST "name")) ||
3577 (xmlStrEqual(cur->name, BAD_CAST "nsName")) ||
3578 (xmlStrEqual(cur->name, BAD_CAST "value"))) {
3579 /*
3580 * Simplification 4.8. name attribute of element
3581 * and attribute elements
3582 */
3583 if (xmlHasProp(cur, BAD_CAST "ns") == NULL) {
3584 xmlNodePtr node;
3585 xmlChar *ns = NULL;
3586
3587 node = cur->parent;
3588 while ((node != NULL) &&
3589 (node->type == XML_ELEMENT_NODE)) {
3590 ns = xmlGetProp(node, BAD_CAST "ns");
3591 if (ns != NULL) {
3592 break;
3593 }
3594 node = node->parent;
3595 }
3596 if (ns == NULL) {
3597 xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
3598 } else {
3599 xmlSetProp(cur, BAD_CAST "ns", ns);
3600 xmlFree(ns);
3601 }
3602 }
3603 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
3604 xmlChar *name, *local, *prefix;
3605
3606 /*
3607 * Simplification: 4.10. QNames
3608 */
3609 name = xmlNodeGetContent(cur);
3610 if (name != NULL) {
3611 local = xmlSplitQName2(name, &prefix);
3612 if (local != NULL) {
3613 xmlNsPtr ns;
3614
3615 ns = xmlSearchNs(cur->doc, cur, prefix);
3616 if (ns == NULL) {
3617 if (ctxt->error != NULL)
3618 ctxt->error(ctxt->userData,
3619 "xmlRelaxNGParse: no namespace for prefix %s\n", prefix);
3620 ctxt->nbErrors++;
3621 } else {
3622 xmlSetProp(cur, BAD_CAST "ns", ns->href);
3623 xmlNodeSetContent(cur, local);
3624 }
3625 xmlFree(local);
3626 xmlFree(prefix);
3627 }
3628 xmlFree(name);
3629 }
3630 }
3631 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003632 /*
3633 * Thisd is not an else since "include" is transformed
3634 * into a div
3635 */
3636 if (xmlStrEqual(cur->name, BAD_CAST "div")) {
3637 /*
3638 * implements rule 4.11
3639 */
3640 xmlNodePtr child, ins, tmp;
3641
3642 child = cur->children;
3643 ins = child;
3644 while (child != NULL) {
3645 tmp = child->next;
3646 xmlUnlinkNode(child);
3647 ins = xmlAddNextSibling(ins, child);
3648 child = tmp;
3649 }
3650 delete = cur;
3651 goto skip_children;
3652 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003653 }
3654 }
3655 /*
3656 * Simplification 4.2 whitespaces
3657 */
3658 else if (cur->type == XML_TEXT_NODE) {
3659 if (IS_BLANK_NODE(cur)) {
3660 if (cur->parent->type == XML_ELEMENT_NODE) {
3661 if ((!xmlStrEqual(cur->parent->name, BAD_CAST "value")) &&
3662 (!xmlStrEqual(cur->parent->name, BAD_CAST "param")))
3663 delete = cur;
3664 } else {
3665 delete = cur;
3666 goto skip_children;
3667 }
3668 }
3669 } else if (cur->type != XML_CDATA_SECTION_NODE) {
3670 delete = cur;
3671 goto skip_children;
3672 }
3673
3674 /*
3675 * Skip to next node
3676 */
3677 if (cur->children != NULL) {
3678 if ((cur->children->type != XML_ENTITY_DECL) &&
3679 (cur->children->type != XML_ENTITY_REF_NODE) &&
3680 (cur->children->type != XML_ENTITY_NODE)) {
3681 cur = cur->children;
3682 continue;
3683 }
3684 }
3685skip_children:
3686 if (cur->next != NULL) {
3687 cur = cur->next;
3688 continue;
3689 }
3690
3691 do {
3692 cur = cur->parent;
3693 if (cur == NULL)
3694 break;
3695 if (cur == root) {
3696 cur = NULL;
3697 break;
3698 }
3699 if (cur->next != NULL) {
3700 cur = cur->next;
3701 break;
3702 }
3703 } while (cur != NULL);
3704 }
3705 if (delete != NULL) {
3706 xmlUnlinkNode(delete);
3707 xmlFreeNode(delete);
3708 delete = NULL;
3709 }
3710
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003711 return(doc);
3712}
3713
3714/**
3715 * xmlRelaxNGParse:
3716 * @ctxt: a Relax-NG parser context
3717 *
3718 * parse a schema definition resource and build an internal
3719 * XML Shema struture which can be used to validate instances.
3720 * *WARNING* this interface is highly subject to change
3721 *
3722 * Returns the internal XML RelaxNG structure built from the resource or
3723 * NULL in case of error
3724 */
3725xmlRelaxNGPtr
3726xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt)
3727{
3728 xmlRelaxNGPtr ret = NULL;
3729 xmlDocPtr doc;
3730 xmlNodePtr root;
3731
3732 xmlRelaxNGInitTypes();
3733
3734 if (ctxt == NULL)
3735 return (NULL);
3736
3737 /*
3738 * First step is to parse the input document into an DOM/Infoset
3739 */
3740 if (ctxt->URL != NULL) {
3741 doc = xmlParseFile((const char *) ctxt->URL);
3742 if (doc == NULL) {
3743 if (ctxt->error != NULL)
3744 ctxt->error(ctxt->userData,
3745 "xmlRelaxNGParse: could not load %s\n", ctxt->URL);
3746 ctxt->nbErrors++;
3747 return (NULL);
3748 }
3749 } else if (ctxt->buffer != NULL) {
3750 doc = xmlParseMemory(ctxt->buffer, ctxt->size);
3751 if (doc == NULL) {
3752 if (ctxt->error != NULL)
3753 ctxt->error(ctxt->userData,
3754 "xmlRelaxNGParse: could not parse schemas\n");
3755 ctxt->nbErrors++;
3756 return (NULL);
3757 }
3758 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
3759 ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
3760 } else {
3761 if (ctxt->error != NULL)
3762 ctxt->error(ctxt->userData,
3763 "xmlRelaxNGParse: nothing to parse\n");
3764 ctxt->nbErrors++;
3765 return (NULL);
3766 }
3767 ctxt->document = doc;
3768
3769 /*
3770 * Some preprocessing of the document content
3771 */
3772 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
3773 if (doc == NULL) {
3774 xmlFreeDoc(ctxt->document);
3775 ctxt->document = NULL;
3776 return(NULL);
3777 }
3778
Daniel Veillard6eadf632003-01-23 18:29:16 +00003779 /*
3780 * Then do the parsing for good
3781 */
3782 root = xmlDocGetRootElement(doc);
3783 if (root == NULL) {
3784 if (ctxt->error != NULL)
3785 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
3786 ctxt->URL);
3787 ctxt->nbErrors++;
3788 return (NULL);
3789 }
3790 ret = xmlRelaxNGParseDocument(ctxt, root);
3791 if (ret == NULL)
3792 return(NULL);
3793
3794 /*
3795 * Check the ref/defines links
3796 */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003797 /*
3798 * try to preprocess interleaves
3799 */
3800 if (ctxt->interleaves != NULL) {
3801 xmlHashScan(ctxt->interleaves,
3802 (xmlHashScanner)xmlRelaxNGComputeInterleaves, ctxt);
3803 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003804
3805 /*
3806 * if there was a parsing error return NULL
3807 */
3808 if (ctxt->nbErrors > 0) {
3809 xmlRelaxNGFree(ret);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003810 ctxt->document = NULL;
3811 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003812 return(NULL);
3813 }
3814
3815 /*
3816 * Transfer the pointer for cleanup at the schema level.
3817 */
3818 ret->doc = doc;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003819 ctxt->document = NULL;
3820 ret->documents = ctxt->documents;
3821 ctxt->documents = NULL;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003822 ret->includes = ctxt->includes;
3823 ctxt->includes = NULL;
Daniel Veillard419a7682003-02-03 23:22:49 +00003824 ret->defNr = ctxt->defNr;
3825 ret->defTab = ctxt->defTab;
3826 ctxt->defTab = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003827
3828 return (ret);
3829}
3830
3831/**
3832 * xmlRelaxNGSetParserErrors:
3833 * @ctxt: a Relax-NG validation context
3834 * @err: the error callback
3835 * @warn: the warning callback
3836 * @ctx: contextual data for the callbacks
3837 *
3838 * Set the callback functions used to handle errors for a validation context
3839 */
3840void
3841xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
3842 xmlRelaxNGValidityErrorFunc err,
3843 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
3844 if (ctxt == NULL)
3845 return;
3846 ctxt->error = err;
3847 ctxt->warning = warn;
3848 ctxt->userData = ctx;
3849}
3850/************************************************************************
3851 * *
3852 * Dump back a compiled form *
3853 * *
3854 ************************************************************************/
3855static void xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define);
3856
3857/**
3858 * xmlRelaxNGDumpDefines:
3859 * @output: the file output
3860 * @defines: a list of define structures
3861 *
3862 * Dump a RelaxNG structure back
3863 */
3864static void
3865xmlRelaxNGDumpDefines(FILE * output, xmlRelaxNGDefinePtr defines) {
3866 while (defines != NULL) {
3867 xmlRelaxNGDumpDefine(output, defines);
3868 defines = defines->next;
3869 }
3870}
3871
3872/**
3873 * xmlRelaxNGDumpDefine:
3874 * @output: the file output
3875 * @define: a define structure
3876 *
3877 * Dump a RelaxNG structure back
3878 */
3879static void
3880xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define) {
3881 if (define == NULL)
3882 return;
3883 switch(define->type) {
3884 case XML_RELAXNG_EMPTY:
3885 fprintf(output, "<empty/>\n");
3886 break;
3887 case XML_RELAXNG_NOT_ALLOWED:
3888 fprintf(output, "<notAllowed/>\n");
3889 break;
3890 case XML_RELAXNG_TEXT:
3891 fprintf(output, "<text/>\n");
3892 break;
3893 case XML_RELAXNG_ELEMENT:
3894 fprintf(output, "<element>\n");
3895 if (define->name != NULL) {
3896 fprintf(output, "<name");
3897 if (define->ns != NULL)
3898 fprintf(output, " ns=\"%s\"", define->ns);
3899 fprintf(output, ">%s</name>\n", define->name);
3900 }
3901 xmlRelaxNGDumpDefines(output, define->attrs);
3902 xmlRelaxNGDumpDefines(output, define->content);
3903 fprintf(output, "</element>\n");
3904 break;
3905 case XML_RELAXNG_LIST:
3906 fprintf(output, "<list>\n");
3907 xmlRelaxNGDumpDefines(output, define->content);
3908 fprintf(output, "</list>\n");
3909 break;
3910 case XML_RELAXNG_ONEORMORE:
3911 fprintf(output, "<oneOrMore>\n");
3912 xmlRelaxNGDumpDefines(output, define->content);
3913 fprintf(output, "</oneOrMore>\n");
3914 break;
3915 case XML_RELAXNG_ZEROORMORE:
3916 fprintf(output, "<zeroOrMore>\n");
3917 xmlRelaxNGDumpDefines(output, define->content);
3918 fprintf(output, "</zeroOrMore>\n");
3919 break;
3920 case XML_RELAXNG_CHOICE:
3921 fprintf(output, "<choice>\n");
3922 xmlRelaxNGDumpDefines(output, define->content);
3923 fprintf(output, "</choice>\n");
3924 break;
3925 case XML_RELAXNG_GROUP:
3926 fprintf(output, "<group>\n");
3927 xmlRelaxNGDumpDefines(output, define->content);
3928 fprintf(output, "</group>\n");
3929 break;
3930 case XML_RELAXNG_INTERLEAVE:
3931 fprintf(output, "<interleave>\n");
3932 xmlRelaxNGDumpDefines(output, define->content);
3933 fprintf(output, "</interleave>\n");
3934 break;
3935 case XML_RELAXNG_OPTIONAL:
3936 fprintf(output, "<optional>\n");
3937 xmlRelaxNGDumpDefines(output, define->content);
3938 fprintf(output, "</optional>\n");
3939 break;
3940 case XML_RELAXNG_ATTRIBUTE:
3941 fprintf(output, "<attribute>\n");
3942 xmlRelaxNGDumpDefines(output, define->content);
3943 fprintf(output, "</attribute>\n");
3944 break;
3945 case XML_RELAXNG_DEF:
3946 fprintf(output, "<define");
3947 if (define->name != NULL)
3948 fprintf(output, " name=\"%s\"", define->name);
3949 fprintf(output, ">\n");
3950 xmlRelaxNGDumpDefines(output, define->content);
3951 fprintf(output, "</define>\n");
3952 break;
3953 case XML_RELAXNG_REF:
3954 fprintf(output, "<ref");
3955 if (define->name != NULL)
3956 fprintf(output, " name=\"%s\"", define->name);
3957 fprintf(output, ">\n");
3958 xmlRelaxNGDumpDefines(output, define->content);
3959 fprintf(output, "</ref>\n");
3960 break;
Daniel Veillard419a7682003-02-03 23:22:49 +00003961 case XML_RELAXNG_PARENTREF:
3962 fprintf(output, "<parentRef");
3963 if (define->name != NULL)
3964 fprintf(output, " name=\"%s\"", define->name);
3965 fprintf(output, ">\n");
3966 xmlRelaxNGDumpDefines(output, define->content);
3967 fprintf(output, "</parentRef>\n");
3968 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003969 case XML_RELAXNG_EXTERNALREF:
Daniel Veillarde431a272003-01-29 23:02:33 +00003970 fprintf(output, "<externalRef");
3971 xmlRelaxNGDumpDefines(output, define->content);
3972 fprintf(output, "</externalRef>\n");
3973 break;
3974 case XML_RELAXNG_DATATYPE:
Daniel Veillard6eadf632003-01-23 18:29:16 +00003975 case XML_RELAXNG_VALUE:
3976 TODO
3977 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003978 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00003979 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003980 TODO
3981 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003982 }
3983}
3984
3985/**
3986 * xmlRelaxNGDumpGrammar:
3987 * @output: the file output
3988 * @grammar: a grammar structure
3989 * @top: is this a top grammar
3990 *
3991 * Dump a RelaxNG structure back
3992 */
3993static void
3994xmlRelaxNGDumpGrammar(FILE * output, xmlRelaxNGGrammarPtr grammar, int top)
3995{
3996 if (grammar == NULL)
3997 return;
3998
3999 fprintf(output, "<grammar");
4000 if (top)
4001 fprintf(output,
4002 " xmlns=\"http://relaxng.org/ns/structure/1.0\"");
4003 switch(grammar->combine) {
4004 case XML_RELAXNG_COMBINE_UNDEFINED:
4005 break;
4006 case XML_RELAXNG_COMBINE_CHOICE:
4007 fprintf(output, " combine=\"choice\"");
4008 break;
4009 case XML_RELAXNG_COMBINE_INTERLEAVE:
4010 fprintf(output, " combine=\"interleave\"");
4011 break;
4012 default:
4013 fprintf(output, " <!-- invalid combine value -->");
4014 }
4015 fprintf(output, ">\n");
4016 if (grammar->start == NULL) {
4017 fprintf(output, " <!-- grammar had no start -->");
4018 } else {
4019 fprintf(output, "<start>\n");
4020 xmlRelaxNGDumpDefine(output, grammar->start);
4021 fprintf(output, "</start>\n");
4022 }
4023 /* TODO ? Dump the defines ? */
4024 fprintf(output, "</grammar>\n");
4025}
4026
4027/**
4028 * xmlRelaxNGDump:
4029 * @output: the file output
4030 * @schema: a schema structure
4031 *
4032 * Dump a RelaxNG structure back
4033 */
4034void
4035xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema)
4036{
4037 if (schema == NULL) {
4038 fprintf(output, "RelaxNG empty or failed to compile\n");
4039 return;
4040 }
4041 fprintf(output, "RelaxNG: ");
4042 if (schema->doc == NULL) {
4043 fprintf(output, "no document\n");
4044 } else if (schema->doc->URL != NULL) {
4045 fprintf(output, "%s\n", schema->doc->URL);
4046 } else {
4047 fprintf(output, "\n");
4048 }
4049 if (schema->topgrammar == NULL) {
4050 fprintf(output, "RelaxNG has no top grammar\n");
4051 return;
4052 }
4053 xmlRelaxNGDumpGrammar(output, schema->topgrammar, 1);
4054}
4055
4056/************************************************************************
4057 * *
4058 * Validation implementation *
4059 * *
4060 ************************************************************************/
4061static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
4062 xmlRelaxNGDefinePtr define);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004063static int xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
4064 xmlRelaxNGDefinePtr define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004065
4066/**
4067 * xmlRelaxNGSkipIgnored:
4068 * @ctxt: a schema validation context
4069 * @node: the top node.
4070 *
4071 * Skip ignorable nodes in that context
4072 *
4073 * Returns the new sibling or NULL in case of error.
4074 */
4075static xmlNodePtr
4076xmlRelaxNGSkipIgnored(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
4077 xmlNodePtr node) {
4078 /*
4079 * TODO complete and handle entities
4080 */
4081 while ((node != NULL) &&
4082 ((node->type == XML_COMMENT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004083 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00004084 ((node->type == XML_TEXT_NODE) &&
4085 (IS_BLANK_NODE(node))))) {
4086 node = node->next;
4087 }
4088 return(node);
4089}
4090
4091/**
Daniel Veillardedc91922003-01-26 00:52:04 +00004092 * xmlRelaxNGNormalize:
4093 * @ctxt: a schema validation context
4094 * @str: the string to normalize
4095 *
4096 * Implements the normalizeWhiteSpace( s ) function from
4097 * section 6.2.9 of the spec
4098 *
4099 * Returns the new string or NULL in case of error.
4100 */
4101static xmlChar *
4102xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *str) {
4103 xmlChar *ret, *p;
4104 const xmlChar *tmp;
4105 int len;
4106
4107 if (str == NULL)
4108 return(NULL);
4109 tmp = str;
4110 while (*tmp != 0) tmp++;
4111 len = tmp - str;
4112
4113 ret = (xmlChar *) xmlMalloc((len + 1) * sizeof(xmlChar));
4114 if (ret == NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00004115 if (ctxt != NULL) {
4116 VALID_CTXT();
4117 VALID_ERROR("xmlRelaxNGNormalize: out of memory\n");
4118 } else {
4119 xmlGenericError(xmlGenericErrorContext,
4120 "xmlRelaxNGNormalize: out of memory\n");
4121 }
Daniel Veillardedc91922003-01-26 00:52:04 +00004122 return(NULL);
4123 }
4124 p = ret;
4125 while (IS_BLANK(*str)) str++;
4126 while (*str != 0) {
4127 if (IS_BLANK(*str)) {
4128 while (IS_BLANK(*str)) str++;
4129 if (*str == 0)
4130 break;
4131 *p++ = ' ';
4132 } else
4133 *p++ = *str++;
4134 }
4135 *p = 0;
4136 return(ret);
4137}
4138
4139/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004140 * xmlRelaxNGValidateDatatype:
4141 * @ctxt: a Relax-NG validation context
4142 * @value: the string value
4143 * @type: the datatype definition
4144 *
4145 * Validate the given value against the dataype
4146 *
4147 * Returns 0 if the validation succeeded or an error code.
4148 */
4149static int
4150xmlRelaxNGValidateDatatype(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *value,
4151 xmlRelaxNGDefinePtr define) {
4152 int ret;
4153 xmlRelaxNGTypeLibraryPtr lib;
4154
4155 if ((define == NULL) || (define->data == NULL)) {
4156 return(-1);
4157 }
4158 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
4159 if (lib->check != NULL)
4160 ret = lib->check(lib->data, define->name, value);
4161 else
4162 ret = -1;
4163 if (ret < 0) {
4164 VALID_CTXT();
4165 VALID_ERROR("Internal: failed to validate type %s\n", define->name);
4166 return(-1);
4167 } else if (ret == 1) {
4168 ret = 0;
4169 } else {
4170 VALID_CTXT();
4171 VALID_ERROR("Type %s doesn't allow value %s\n", define->name, value);
4172 return(-1);
4173 ret = -1;
4174 }
4175 return(ret);
4176}
4177
4178/**
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004179 * xmlRelaxNGNextValue:
4180 * @ctxt: a Relax-NG validation context
4181 *
4182 * Skip to the next value when validating within a list
4183 *
4184 * Returns 0 if the operation succeeded or an error code.
4185 */
4186static int
4187xmlRelaxNGNextValue(xmlRelaxNGValidCtxtPtr ctxt) {
4188 xmlChar *cur;
4189
4190 cur = ctxt->state->value;
4191 if ((cur == NULL) || (ctxt->state->endvalue == NULL)) {
4192 ctxt->state->value = NULL;
Daniel Veillarde5b110b2003-02-04 14:43:39 +00004193 ctxt->state->endvalue = NULL;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004194 return(0);
4195 }
4196 while (*cur != 0) cur++;
4197 while ((cur != ctxt->state->endvalue) && (*cur == 0)) cur++;
4198 if (cur == ctxt->state->endvalue)
4199 ctxt->state->value = NULL;
4200 else
4201 ctxt->state->value = cur;
4202 return(0);
4203}
4204
4205/**
4206 * xmlRelaxNGValidateValueList:
4207 * @ctxt: a Relax-NG validation context
4208 * @defines: the list of definitions to verify
4209 *
4210 * Validate the given set of definitions for the current value
4211 *
4212 * Returns 0 if the validation succeeded or an error code.
4213 */
4214static int
4215xmlRelaxNGValidateValueList(xmlRelaxNGValidCtxtPtr ctxt,
4216 xmlRelaxNGDefinePtr defines) {
4217 int ret = 0;
4218
4219 while (defines != NULL) {
4220 ret = xmlRelaxNGValidateValue(ctxt, defines);
4221 if (ret != 0)
4222 break;
4223 defines = defines->next;
4224 }
4225 return(ret);
4226}
4227
4228/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004229 * xmlRelaxNGValidateValue:
4230 * @ctxt: a Relax-NG validation context
4231 * @define: the definition to verify
4232 *
4233 * Validate the given definition for the current value
4234 *
4235 * Returns 0 if the validation succeeded or an error code.
4236 */
4237static int
4238xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
4239 xmlRelaxNGDefinePtr define) {
Daniel Veillardedc91922003-01-26 00:52:04 +00004240 int ret = 0, oldflags;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004241 xmlChar *value;
4242
4243 value = ctxt->state->value;
4244 switch (define->type) {
4245 case XML_RELAXNG_EMPTY:
4246 if ((value != NULL) && (value[0] != '0'))
4247 ret = -1;
4248 break;
4249 case XML_RELAXNG_TEXT:
4250 break;
Daniel Veillardedc91922003-01-26 00:52:04 +00004251 case XML_RELAXNG_VALUE: {
4252 if (!xmlStrEqual(value, define->value)) {
4253 if (define->name != NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00004254 xmlRelaxNGTypeLibraryPtr lib;
4255
4256 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
4257 if ((lib != NULL) && (lib->comp != NULL))
4258 ret = lib->comp(lib->data, define->name, value,
4259 define->value);
4260 else
4261 ret = -1;
4262 if (ret < 0) {
4263 VALID_CTXT();
4264 VALID_ERROR("Internal: failed to compare type %s\n",
4265 define->name);
4266 return(-1);
4267 } else if (ret == 1) {
4268 ret = 0;
4269 } else {
4270 ret = -1;
4271 }
Daniel Veillardedc91922003-01-26 00:52:04 +00004272 } else {
4273 xmlChar *nval, *nvalue;
4274
4275 /*
4276 * TODO: trivial optimizations are possible by
4277 * computing at compile-time
4278 */
4279 nval = xmlRelaxNGNormalize(ctxt, define->value);
4280 nvalue = xmlRelaxNGNormalize(ctxt, value);
4281
Daniel Veillardea3f3982003-01-26 19:45:18 +00004282 if ((nval == NULL) || (nvalue == NULL) ||
4283 (!xmlStrEqual(nval, nvalue)))
Daniel Veillardedc91922003-01-26 00:52:04 +00004284 ret = -1;
4285 if (nval != NULL)
4286 xmlFree(nval);
4287 if (nvalue != NULL)
4288 xmlFree(nvalue);
4289 }
4290 }
4291 break;
4292 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004293 case XML_RELAXNG_DATATYPE: {
4294 ret = xmlRelaxNGValidateDatatype(ctxt, value, define);
4295 if (ret == 0)
4296 xmlRelaxNGNextValue(ctxt);
4297
4298 break;
4299 }
4300 case XML_RELAXNG_CHOICE: {
4301 xmlRelaxNGDefinePtr list = define->content;
4302 xmlChar *oldvalue;
4303
4304 oldflags = ctxt->flags;
4305 ctxt->flags |= FLAGS_IGNORABLE;
4306
4307 oldvalue = ctxt->state->value;
4308 while (list != NULL) {
4309 ret = xmlRelaxNGValidateValue(ctxt, list);
4310 if (ret == 0) {
4311 break;
4312 }
4313 ctxt->state->value = oldvalue;
4314 list = list->next;
4315 }
4316 ctxt->flags = oldflags;
4317 break;
4318 }
4319 case XML_RELAXNG_LIST: {
4320 xmlRelaxNGDefinePtr list = define->content;
4321 xmlChar *oldvalue, *oldend, *val, *cur;
4322
4323 oldvalue = ctxt->state->value;
4324 oldend = ctxt->state->endvalue;
4325
4326 val = xmlStrdup(oldvalue);
4327 if (val == NULL) {
4328 VALID_CTXT();
4329 VALID_ERROR("Internal: no state\n");
4330 return(-1);
4331 }
4332 cur = val;
4333 while (*cur != 0) {
4334 if (IS_BLANK(*cur))
4335 *cur = 0;
4336 cur++;
4337 }
4338 ctxt->state->endvalue = cur;
4339 cur = val;
4340 while ((*cur == 0) && (cur != ctxt->state->endvalue)) cur++;
4341
4342 ctxt->state->value = cur;
4343
4344 while (list != NULL) {
4345 ret = xmlRelaxNGValidateValue(ctxt, list);
4346 if (ret != 0) {
4347 break;
4348 }
4349 list = list->next;
4350 }
4351 if ((ret == 0) && (ctxt->state->value != NULL) &&
4352 (ctxt->state->value != ctxt->state->endvalue)) {
4353 VALID_CTXT();
4354 VALID_ERROR("Extra data in list: %s\n", ctxt->state->value);
4355 ret = -1;
4356 }
4357 xmlFree(val);
4358 ctxt->state->value = oldvalue;
4359 ctxt->state->endvalue = oldend;
4360 break;
4361 }
4362 case XML_RELAXNG_ONEORMORE:
4363 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
4364 if (ret != 0) {
4365 break;
4366 }
4367 /* no break on purpose */
4368 case XML_RELAXNG_ZEROORMORE: {
4369 xmlChar *cur, *temp;
4370
4371 oldflags = ctxt->flags;
4372 ctxt->flags |= FLAGS_IGNORABLE;
4373 cur = ctxt->state->value;
4374 temp = NULL;
4375 while ((cur != NULL) && (cur != ctxt->state->endvalue) &&
4376 (temp != cur)) {
4377 temp = cur;
4378 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
4379 if (ret != 0) {
4380 ctxt->state->value = temp;
4381 ret = 0;
4382 break;
4383 }
4384 cur = ctxt->state->value;
4385 }
4386 ctxt->flags = oldflags;
4387 break;
4388 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004389 default:
4390 TODO
4391 ret = -1;
4392 }
4393 return(ret);
4394}
4395
4396/**
4397 * xmlRelaxNGValidateValueContent:
4398 * @ctxt: a Relax-NG validation context
4399 * @defines: the list of definitions to verify
4400 *
4401 * Validate the given definitions for the current value
4402 *
4403 * Returns 0 if the validation succeeded or an error code.
4404 */
4405static int
4406xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt,
4407 xmlRelaxNGDefinePtr defines) {
4408 int ret = 0;
4409
4410 while (defines != NULL) {
4411 ret = xmlRelaxNGValidateValue(ctxt, defines);
4412 if (ret != 0)
4413 break;
4414 defines = defines->next;
4415 }
4416 return(ret);
4417}
4418
4419/**
Daniel Veillard144fae12003-02-03 13:17:57 +00004420 * xmlRelaxNGAttributeMatch:
4421 * @ctxt: a Relax-NG validation context
4422 * @define: the definition to check
4423 * @prop: the attribute
4424 *
4425 * Check if the attribute matches the definition nameClass
4426 *
4427 * Returns 1 if the attribute matches, 0 if no, or -1 in case of error
4428 */
4429static int
4430xmlRelaxNGAttributeMatch(xmlRelaxNGValidCtxtPtr ctxt,
4431 xmlRelaxNGDefinePtr define,
4432 xmlAttrPtr prop) {
4433 int ret;
4434
4435 if (define->name != NULL) {
4436 if (!xmlStrEqual(define->name, prop->name))
4437 return(0);
4438 }
4439 if (define->ns != NULL) {
4440 if (define->ns[0] == 0) {
4441 if (prop->ns != NULL)
4442 return(0);
4443 } else {
4444 if ((prop->ns == NULL) ||
4445 (!xmlStrEqual(define->ns, prop->ns->href)))
4446 return(0);
4447 }
4448 }
4449 if (define->nameClass == NULL)
4450 return(1);
4451 define = define->nameClass;
4452 if (define->type == XML_RELAXNG_EXCEPT) {
4453 xmlRelaxNGDefinePtr list;
4454
4455 list = define->content;
4456 while (list != NULL) {
4457 ret = xmlRelaxNGAttributeMatch(ctxt, list, prop);
4458 if (ret == 1)
4459 return(0);
4460 if (ret < 0)
4461 return(ret);
4462 list = list->next;
4463 }
4464 } else {
4465 TODO
4466 }
4467 return(1);
4468}
4469
4470/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004471 * xmlRelaxNGValidateAttribute:
4472 * @ctxt: a Relax-NG validation context
4473 * @define: the definition to verify
4474 *
4475 * Validate the given attribute definition for that node
4476 *
4477 * Returns 0 if the validation succeeded or an error code.
4478 */
4479static int
4480xmlRelaxNGValidateAttribute(xmlRelaxNGValidCtxtPtr ctxt,
4481 xmlRelaxNGDefinePtr define) {
4482 int ret = 0, i;
4483 xmlChar *value, *oldvalue;
4484 xmlAttrPtr prop = NULL, tmp;
4485
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004486 if (ctxt->state->nbAttrLeft <= 0)
4487 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004488 if (define->name != NULL) {
4489 for (i = 0;i < ctxt->state->nbAttrs;i++) {
4490 tmp = ctxt->state->attrs[i];
4491 if ((tmp != NULL) && (xmlStrEqual(define->name, tmp->name))) {
4492 if ((((define->ns == NULL) || (define->ns[0] == 0)) &&
4493 (tmp->ns == NULL)) ||
4494 ((tmp->ns != NULL) &&
4495 (xmlStrEqual(define->ns, tmp->ns->href)))) {
4496 prop = tmp;
4497 break;
4498 }
4499 }
4500 }
4501 if (prop != NULL) {
4502 value = xmlNodeListGetString(prop->doc, prop->children, 1);
4503 oldvalue = ctxt->state->value;
4504 ctxt->state->value = value;
4505 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
4506 value = ctxt->state->value;
4507 ctxt->state->value = oldvalue;
4508 if (value != NULL)
4509 xmlFree(value);
4510 if (ret == 0) {
4511 /*
4512 * flag the attribute as processed
4513 */
4514 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004515 ctxt->state->nbAttrLeft--;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004516 }
4517 } else {
4518 ret = -1;
4519 }
4520#ifdef DEBUG
4521 xmlGenericError(xmlGenericErrorContext,
4522 "xmlRelaxNGValidateAttribute(%s): %d\n", define->name, ret);
4523#endif
4524 } else {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004525 for (i = 0;i < ctxt->state->nbAttrs;i++) {
4526 tmp = ctxt->state->attrs[i];
Daniel Veillard144fae12003-02-03 13:17:57 +00004527 if ((tmp != NULL) &&
4528 (xmlRelaxNGAttributeMatch(ctxt, define, tmp) == 1)) {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004529 prop = tmp;
4530 break;
4531 }
4532 }
4533 if (prop != NULL) {
4534 value = xmlNodeListGetString(prop->doc, prop->children, 1);
4535 oldvalue = ctxt->state->value;
4536 ctxt->state->value = value;
4537 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
4538 value = ctxt->state->value;
4539 ctxt->state->value = oldvalue;
4540 if (value != NULL)
4541 xmlFree(value);
4542 if (ret == 0) {
4543 /*
4544 * flag the attribute as processed
4545 */
4546 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004547 ctxt->state->nbAttrLeft--;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004548 }
4549 } else {
4550 ret = -1;
4551 }
4552#ifdef DEBUG
Daniel Veillard144fae12003-02-03 13:17:57 +00004553 if (define->ns != NULL) {
4554 xmlGenericError(xmlGenericErrorContext,
4555 "xmlRelaxNGValidateAttribute(nsName ns = %s): %d\n",
4556 define->ns, ret);
4557 } else {
4558 xmlGenericError(xmlGenericErrorContext,
4559 "xmlRelaxNGValidateAttribute(anyName): %d\n",
4560 ret);
4561 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004562#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00004563 }
4564
4565 return(ret);
4566}
4567
4568/**
4569 * xmlRelaxNGValidateAttributeList:
4570 * @ctxt: a Relax-NG validation context
4571 * @define: the list of definition to verify
4572 *
4573 * Validate the given node against the list of attribute definitions
4574 *
4575 * Returns 0 if the validation succeeded or an error code.
4576 */
4577static int
4578xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt,
4579 xmlRelaxNGDefinePtr defines) {
4580 int ret = 0;
4581 while (defines != NULL) {
4582 if (xmlRelaxNGValidateAttribute(ctxt, defines) != 0)
4583 ret = -1;
4584 defines = defines->next;
4585 }
4586 return(ret);
4587}
4588
4589/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004590 * xmlRelaxNGValidateTryPermutation:
4591 * @ctxt: a Relax-NG validation context
4592 * @groups: the array of groups
4593 * @nbgroups: the number of groups in the array
4594 * @array: the permutation to try
4595 * @len: the size of the set
4596 *
4597 * Try to validate a permutation for the group of definitions.
4598 *
4599 * Returns 0 if the validation succeeded or an error code.
4600 */
4601static int
4602xmlRelaxNGValidateTryPermutation(xmlRelaxNGValidCtxtPtr ctxt,
4603 xmlRelaxNGDefinePtr rule,
4604 xmlNodePtr *array, int len) {
4605 int i, ret;
4606
4607 if (len > 0) {
4608 /*
4609 * One only need the next pointer set-up to do the validation
4610 */
4611 for (i = 0;i < (len - 1);i++)
4612 array[i]->next = array[i + 1];
4613 array[i]->next = NULL;
4614
4615 /*
4616 * Now try to validate the sequence
4617 */
4618 ctxt->state->seq = array[0];
4619 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
4620 } else {
4621 ctxt->state->seq = NULL;
4622 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
4623 }
4624
4625 /*
4626 * the sequence must be fully consumed
4627 */
4628 if (ctxt->state->seq != NULL)
4629 return(-1);
4630
4631 return(ret);
4632}
4633
4634/**
4635 * xmlRelaxNGValidateWalkPermutations:
4636 * @ctxt: a Relax-NG validation context
4637 * @groups: the array of groups
4638 * @nbgroups: the number of groups in the array
4639 * @nodes: the set of nodes
4640 * @array: the current state of the parmutation
4641 * @len: the size of the set
4642 * @level: a pointer to the level variable
4643 * @k: the index in the array to fill
4644 *
4645 * Validate a set of nodes for a groups of definitions, will try the
4646 * full set of permutations
4647 *
4648 * Returns 0 if the validation succeeded or an error code.
4649 */
4650static int
4651xmlRelaxNGValidateWalkPermutations(xmlRelaxNGValidCtxtPtr ctxt,
4652 xmlRelaxNGDefinePtr rule, xmlNodePtr *nodes,
4653 xmlNodePtr *array, int len,
4654 int *level, int k) {
4655 int i, ret;
4656
4657 if ((k >= 0) && (k < len))
4658 array[k] = nodes[*level];
4659 *level = *level + 1;
4660 if (*level == len) {
4661 ret = xmlRelaxNGValidateTryPermutation(ctxt, rule, array, len);
4662 if (ret == 0)
4663 return(0);
4664 } else {
4665 for (i = 0;i < len;i++) {
4666 if (array[i] == NULL) {
4667 ret = xmlRelaxNGValidateWalkPermutations(ctxt, rule,
4668 nodes, array, len, level, i);
4669 if (ret == 0)
4670 return(0);
4671 }
4672 }
4673 }
4674 *level = *level - 1;
4675 array[k] = NULL;
4676 return(-1);
4677}
4678
4679/**
4680 * xmlRelaxNGNodeMatchesList:
4681 * @node: the node
4682 * @list: a NULL terminated array of definitions
4683 *
4684 * Check if a node can be matched by one of the definitions
4685 *
4686 * Returns 1 if matches 0 otherwise
4687 */
4688static int
4689xmlRelaxNGNodeMatchesList(xmlNodePtr node, xmlRelaxNGDefinePtr *list) {
4690 xmlRelaxNGDefinePtr cur;
4691 int i = 0;
4692
4693 if ((node == NULL) || (list == NULL))
4694 return(0);
4695
4696 cur = list[i++];
4697 while (cur != NULL) {
4698 if ((node->type == XML_ELEMENT_NODE) &&
4699 (cur->type == XML_RELAXNG_ELEMENT)) {
4700 if (cur->name == NULL) {
4701 if ((node->ns != NULL) &&
4702 (xmlStrEqual(node->ns->href, cur->ns)))
4703 return(1);
4704 } else if (xmlStrEqual(cur->name, node->name)) {
4705 if ((cur->ns == NULL) || (cur->ns[0] == 0)) {
4706 if (node->ns == NULL)
4707 return(1);
4708 } else {
4709 if ((node->ns != NULL) &&
4710 (xmlStrEqual(node->ns->href, cur->ns)))
4711 return(1);
4712 }
4713 }
4714 } else if ((node->type == XML_TEXT_NODE) &&
4715 (cur->type == XML_RELAXNG_TEXT)) {
4716 return(1);
4717 }
4718 cur = list[i++];
4719 }
4720 return(0);
4721}
4722
4723/**
4724 * xmlRelaxNGValidatePartGroup:
4725 * @ctxt: a Relax-NG validation context
4726 * @groups: the array of groups
4727 * @nbgroups: the number of groups in the array
4728 * @nodes: the set of nodes
4729 * @len: the size of the set of nodes
4730 *
4731 * Validate a set of nodes for a groups of definitions
4732 *
4733 * Returns 0 if the validation succeeded or an error code.
4734 */
4735static int
4736xmlRelaxNGValidatePartGroup(xmlRelaxNGValidCtxtPtr ctxt,
4737 xmlRelaxNGInterleaveGroupPtr *groups,
4738 int nbgroups, xmlNodePtr *nodes, int len) {
Daniel Veillardb08c9812003-01-28 23:09:49 +00004739 int level, ret = -1, i, j, k;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004740 xmlNodePtr *array = NULL, *list, oldseq;
4741 xmlRelaxNGInterleaveGroupPtr group;
4742
4743 list = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
4744 if (list == NULL) {
4745 return(-1);
4746 }
4747 array = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
4748 if (array == NULL) {
4749 xmlFree(list);
4750 return(-1);
4751 }
4752 memset(array, 0, len * sizeof(xmlNodePtr));
4753
4754 /*
4755 * Partition the elements and validate the subsets.
4756 */
4757 oldseq = ctxt->state->seq;
4758 for (i = 0;i < nbgroups;i++) {
4759 group = groups[i];
4760 if (group == NULL)
4761 continue;
4762 k = 0;
4763 for (j = 0;j < len;j++) {
4764 if (nodes[j] == NULL)
4765 continue;
4766 if (xmlRelaxNGNodeMatchesList(nodes[j], group->defs)) {
4767 list[k++] = nodes[j];
4768 nodes[j] = NULL;
4769 }
4770 }
4771 ctxt->state->seq = oldseq;
4772 if (k > 1) {
4773 memset(array, 0, k * sizeof(xmlNodePtr));
Daniel Veillardb08c9812003-01-28 23:09:49 +00004774 level = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004775 ret = xmlRelaxNGValidateWalkPermutations(ctxt, group->rule,
4776 list, array, k, &level, -1);
4777 } else {
4778 ret = xmlRelaxNGValidateTryPermutation(ctxt, group->rule, list, k);
4779 }
4780 if (ret != 0) {
4781 ctxt->state->seq = oldseq;
4782 break;
4783 }
4784 }
4785
4786 xmlFree(list);
4787 xmlFree(array);
4788 return(ret);
4789}
4790
4791/**
4792 * xmlRelaxNGValidateInterleave:
4793 * @ctxt: a Relax-NG validation context
4794 * @define: the definition to verify
4795 *
4796 * Validate an interleave definition for a node.
4797 *
4798 * Returns 0 if the validation succeeded or an error code.
4799 */
4800static int
4801xmlRelaxNGValidateInterleave(xmlRelaxNGValidCtxtPtr ctxt,
4802 xmlRelaxNGDefinePtr define) {
4803 int ret = 0, nbchildren, nbtot, i, j;
4804 xmlRelaxNGPartitionPtr partitions;
4805 xmlNodePtr *children = NULL;
4806 xmlNodePtr *order = NULL;
4807 xmlNodePtr cur;
4808
4809 if (define->data != NULL) {
4810 partitions = (xmlRelaxNGPartitionPtr) define->data;
4811 } else {
4812 VALID_CTXT();
4813 VALID_ERROR("Internal: interleave block has no data\n");
4814 return(-1);
4815 }
4816
4817 /*
4818 * Build the sequence of child and an array preserving the children
4819 * initial order.
4820 */
4821 cur = ctxt->state->seq;
4822 nbchildren = 0;
4823 nbtot = 0;
4824 while (cur != NULL) {
4825 if ((cur->type == XML_COMMENT_NODE) ||
4826 (cur->type == XML_PI_NODE) ||
4827 ((cur->type == XML_TEXT_NODE) &&
4828 (IS_BLANK_NODE(cur)))) {
4829 nbtot++;
4830 } else {
4831 nbchildren++;
4832 nbtot++;
4833 }
4834 cur = cur->next;
4835 }
4836 children = (xmlNodePtr *) xmlMalloc(nbchildren * sizeof(xmlNodePtr));
4837 if (children == NULL)
4838 goto error;
4839 order = (xmlNodePtr *) xmlMalloc(nbtot * sizeof(xmlNodePtr));
4840 if (order == NULL)
4841 goto error;
4842 cur = ctxt->state->seq;
4843 i = 0;
4844 j = 0;
4845 while (cur != NULL) {
4846 if ((cur->type == XML_COMMENT_NODE) ||
4847 (cur->type == XML_PI_NODE) ||
4848 ((cur->type == XML_TEXT_NODE) &&
4849 (IS_BLANK_NODE(cur)))) {
4850 order[j++] = cur;
4851 } else {
4852 order[j++] = cur;
4853 children[i++] = cur;
4854 }
4855 cur = cur->next;
4856 }
4857
4858 /* TODO: retry with a maller set of child if there is a next... */
4859 ret = xmlRelaxNGValidatePartGroup(ctxt, partitions->groups,
4860 partitions->nbgroups, children, nbchildren);
4861 if (ret == 0) {
4862 ctxt->state->seq = NULL;
4863 }
4864
4865 /*
4866 * Cleanup: rebuid the child sequence and free the structure
4867 */
4868 if (order != NULL) {
4869 for (i = 0;i < nbtot;i++) {
4870 if (i == 0)
4871 order[i]->prev = NULL;
4872 else
4873 order[i]->prev = order[i - 1];
4874 if (i == nbtot - 1)
4875 order[i]->next = NULL;
4876 else
4877 order[i]->next = order[i + 1];
4878 }
4879 xmlFree(order);
4880 }
4881 if (children != NULL)
4882 xmlFree(children);
4883
4884 return(ret);
4885
4886error:
4887 if (order != NULL) {
4888 for (i = 0;i < nbtot;i++) {
4889 if (i == 0)
4890 order[i]->prev = NULL;
4891 else
4892 order[i]->prev = order[i - 1];
4893 if (i == nbtot - 1)
4894 order[i]->next = NULL;
4895 else
4896 order[i]->next = order[i + 1];
4897 }
4898 xmlFree(order);
4899 }
4900 if (children != NULL)
4901 xmlFree(children);
4902 return(-1);
4903}
4904
4905/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004906 * xmlRelaxNGValidateElementContent:
4907 * @ctxt: a Relax-NG validation context
4908 * @define: the list of definition to verify
4909 *
4910 * Validate the given node content against the (list) of definitions
4911 *
4912 * Returns 0 if the validation succeeded or an error code.
4913 */
4914static int
4915xmlRelaxNGValidateElementContent(xmlRelaxNGValidCtxtPtr ctxt,
4916 xmlRelaxNGDefinePtr defines) {
4917 int ret = 0, res;
4918
4919 if (ctxt->state == NULL) {
4920 VALID_CTXT();
4921 VALID_ERROR("Internal: no state\n");
4922 return(-1);
4923 }
4924 while (defines != NULL) {
4925 res = xmlRelaxNGValidateDefinition(ctxt, defines);
4926 if (res < 0)
4927 ret = -1;
4928 defines = defines->next;
4929 }
4930
4931 return(ret);
4932}
4933
4934/**
4935 * xmlRelaxNGValidateDefinition:
4936 * @ctxt: a Relax-NG validation context
4937 * @define: the definition to verify
4938 *
4939 * Validate the current node against the definition
4940 *
4941 * Returns 0 if the validation succeeded or an error code.
4942 */
4943static int
4944xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
4945 xmlRelaxNGDefinePtr define) {
4946 xmlNodePtr node;
4947 int ret = 0, i, tmp, oldflags;
4948 xmlRelaxNGValidStatePtr oldstate, state;
4949
4950 if (define == NULL) {
4951 VALID_CTXT();
4952 VALID_ERROR("internal error: define == NULL\n");
4953 return(-1);
4954 }
4955 if (ctxt->state != NULL) {
4956 node = ctxt->state->seq;
4957 } else {
4958 node = NULL;
4959 }
4960 switch (define->type) {
4961 case XML_RELAXNG_EMPTY:
4962 if (node != NULL) {
4963 VALID_CTXT();
4964 VALID_ERROR("Expecting an empty element\n");
4965 return(-1);
4966 }
4967#ifdef DEBUG
4968 xmlGenericError(xmlGenericErrorContext,
4969 "xmlRelaxNGValidateDefinition(): validated empty\n");
4970#endif
4971 return(0);
4972 case XML_RELAXNG_NOT_ALLOWED:
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004973 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004974 case XML_RELAXNG_TEXT:
4975 if (node == NULL)
4976 return(0);
4977 while ((node != NULL) &&
4978 ((node->type == XML_TEXT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004979 (node->type == XML_COMMENT_NODE) ||
4980 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00004981 (node->type == XML_CDATA_SECTION_NODE)))
4982 node = node->next;
Daniel Veillard276be4a2003-01-24 01:03:34 +00004983 if (node == ctxt->state->seq) {
4984 VALID_CTXT();
4985 VALID_ERROR("Expecting text content\n");
4986 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004987 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00004988 ctxt->state->seq = node;
4989 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004990 case XML_RELAXNG_ELEMENT:
4991 node = xmlRelaxNGSkipIgnored(ctxt, node);
4992 if ((node == NULL) || (node->type != XML_ELEMENT_NODE)) {
4993 VALID_CTXT();
4994 VALID_ERROR("Expecting an element\n");
4995 return(-1);
4996 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00004997 /*
4998 * This node was already validated successfully against
4999 * this definition.
5000 */
5001 if (node->_private == define)
5002 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005003 if (define->name != NULL) {
5004 if (!xmlStrEqual(node->name, define->name)) {
5005 VALID_CTXT();
5006 VALID_ERROR("Expecting element %s, got %s\n",
5007 define->name, node->name);
5008 return(-1);
5009 }
5010 }
5011 if ((define->ns != NULL) && (define->ns[0] != 0)) {
5012 if (node->ns == NULL) {
5013 VALID_CTXT();
5014 VALID_ERROR("Expecting a namespace for element %s\n",
5015 node->name);
5016 return(-1);
5017 } else if (!xmlStrEqual(node->ns->href, define->ns)) {
5018 VALID_CTXT();
5019 VALID_ERROR("Expecting element %s has wrong namespace: expecting %s\n",
5020 node->name, define->ns);
5021 return(-1);
5022 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005023 } else if (define->name != NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005024 if (node->ns != NULL) {
5025 VALID_CTXT();
5026 VALID_ERROR("Expecting no namespace for element %s\n",
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005027 define->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005028 return(-1);
5029 }
5030 }
5031
5032 state = xmlRelaxNGNewValidState(ctxt, node);
5033 if (state == NULL) {
5034 return(-1);
5035 }
5036
5037 oldstate = ctxt->state;
5038 ctxt->state = state;
5039 if (define->attrs != NULL) {
5040 tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
5041 if (tmp != 0)
5042 ret = -1;
5043 }
5044 if (define->content != NULL) {
5045 tmp = xmlRelaxNGValidateElementContent(ctxt, define->content);
5046 if (tmp != 0)
5047 ret = -1;
5048 }
5049 state = ctxt->state;
5050 if (state->seq != NULL) {
5051 state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq);
5052 if (state->seq != NULL) {
5053 VALID_CTXT();
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005054 VALID_ERROR("Extra content for element %s: %s\n",
5055 node->name, state->seq->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005056 ret = -1;
5057 }
5058 }
5059 for (i = 0;i < state->nbAttrs;i++) {
5060 if (state->attrs[i] != NULL) {
5061 VALID_CTXT();
Daniel Veillardea3f3982003-01-26 19:45:18 +00005062 VALID_ERROR("Invalid attribute %s for element %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00005063 state->attrs[i]->name, node->name);
5064 ret = -1;
5065 }
5066 }
5067 ctxt->state = oldstate;
5068 xmlRelaxNGFreeValidState(state);
5069 if (oldstate != NULL)
Daniel Veillarde5b110b2003-02-04 14:43:39 +00005070 oldstate->seq = xmlRelaxNGSkipIgnored(ctxt, node->next);
5071 if (ret == 0)
5072 node->_private = define;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005073
5074
5075#ifdef DEBUG
5076 xmlGenericError(xmlGenericErrorContext,
5077 "xmlRelaxNGValidateDefinition(): validated %s : %d\n",
5078 node->name, ret);
5079#endif
5080 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005081 case XML_RELAXNG_OPTIONAL:
5082 oldflags = ctxt->flags;
5083 ctxt->flags |= FLAGS_IGNORABLE;
5084 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5085 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5086 if (ret != 0) {
5087 xmlRelaxNGFreeValidState(ctxt->state);
5088 ctxt->state = oldstate;
5089 ret = 0;
5090 break;
5091 }
5092 xmlRelaxNGFreeValidState(oldstate);
5093 ctxt->flags = oldflags;
5094 ret = 0;
5095 break;
5096 case XML_RELAXNG_ONEORMORE:
5097 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5098 if (ret != 0) {
5099 break;
5100 }
5101 /* no break on purpose */
Daniel Veillard276be4a2003-01-24 01:03:34 +00005102 case XML_RELAXNG_ZEROORMORE: {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005103 oldflags = ctxt->flags;
5104 ctxt->flags |= FLAGS_IGNORABLE;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005105 while (ctxt->state->nbAttrLeft != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005106 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5107 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5108 if (ret != 0) {
5109 xmlRelaxNGFreeValidState(ctxt->state);
5110 ctxt->state = oldstate;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005111 break;
5112 }
5113 xmlRelaxNGFreeValidState(oldstate);
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005114 }
5115 if (ret == 0) {
5116 /*
5117 * There is no attribute left to be consumed,
5118 * we can check the closure by looking at ctxt->state->seq
5119 */
5120 xmlNodePtr cur, temp;
5121
Daniel Veillard276be4a2003-01-24 01:03:34 +00005122 cur = ctxt->state->seq;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005123 temp = NULL;
5124 while ((cur != NULL) && (temp != cur)) {
5125 temp = cur;
5126 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5127 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5128 if (ret != 0) {
5129 xmlRelaxNGFreeValidState(ctxt->state);
5130 ctxt->state = oldstate;
5131 break;
5132 }
5133 xmlRelaxNGFreeValidState(oldstate);
5134 cur = ctxt->state->seq;
5135 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005136 }
5137 ctxt->flags = oldflags;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005138 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005139 break;
Daniel Veillard276be4a2003-01-24 01:03:34 +00005140 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005141 case XML_RELAXNG_CHOICE: {
5142 xmlRelaxNGDefinePtr list = define->content;
5143
5144 oldflags = ctxt->flags;
5145 ctxt->flags |= FLAGS_IGNORABLE;
5146
5147 while (list != NULL) {
5148 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5149 ret = xmlRelaxNGValidateDefinition(ctxt, list);
5150 if (ret == 0) {
5151 xmlRelaxNGFreeValidState(oldstate);
5152 break;
5153 }
5154 xmlRelaxNGFreeValidState(ctxt->state);
5155 ctxt->state = oldstate;
5156 list = list->next;
5157 }
5158 ctxt->flags = oldflags;
5159 break;
5160 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005161 case XML_RELAXNG_DEF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00005162 case XML_RELAXNG_GROUP: {
5163 xmlRelaxNGDefinePtr list = define->content;
5164
5165 while (list != NULL) {
5166 ret = xmlRelaxNGValidateDefinition(ctxt, list);
5167 if (ret != 0)
5168 break;
5169 list = list->next;
5170 }
5171 break;
5172 }
5173 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005174 ret = xmlRelaxNGValidateInterleave(ctxt, define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005175 break;
5176 case XML_RELAXNG_ATTRIBUTE:
5177 ret = xmlRelaxNGValidateAttribute(ctxt, define);
5178 break;
5179 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00005180 case XML_RELAXNG_PARENTREF:
5181 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00005182 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5183 break;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005184 case XML_RELAXNG_DATATYPE: {
5185 xmlChar *content;
5186
5187 content = xmlNodeGetContent(node);
5188 ret = xmlRelaxNGValidateDatatype(ctxt, content, define);
5189 if (ret == -1) {
5190 VALID_CTXT();
5191 VALID_ERROR("internal error validating %s\n", define->name);
5192 } else if (ret == 0) {
5193 ctxt->state->seq = node->next;
5194 }
5195 /*
5196 * TODO cover the problems with
5197 * <p>12<!-- comment -->34</p>
5198 * TODO detect full element coverage at compilation time.
5199 */
5200 if ((node != NULL) && (node->next != NULL)) {
5201 VALID_CTXT();
5202 VALID_ERROR("The data does not cover the full element %s\n",
5203 node->parent->name);
5204 ret = -1;
5205 }
5206 if (content != NULL)
5207 xmlFree(content);
5208 break;
5209 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00005210 case XML_RELAXNG_VALUE: {
5211 xmlChar *content;
5212 xmlChar *oldvalue;
5213
5214 content = xmlNodeGetContent(node);
5215 oldvalue = ctxt->state->value;
5216 ctxt->state->value = content;
5217 ret = xmlRelaxNGValidateValue(ctxt, define);
5218 ctxt->state->value = oldvalue;
5219 if (ret == -1) {
5220 VALID_CTXT();
5221 VALID_ERROR("internal error validating %s\n", define->name);
5222 } else if (ret == 0) {
5223 ctxt->state->seq = node->next;
5224 }
5225 /*
5226 * TODO cover the problems with
5227 * <p>12<!-- comment -->34</p>
5228 * TODO detect full element coverage at compilation time.
5229 */
5230 if ((node != NULL) && (node->next != NULL)) {
5231 VALID_CTXT();
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005232 VALID_ERROR("The value does not cover the full element %s\n",
Daniel Veillardea3f3982003-01-26 19:45:18 +00005233 node->parent->name);
5234 ret = -1;
5235 }
5236 if (content != NULL)
5237 xmlFree(content);
5238 break;
5239 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005240 case XML_RELAXNG_LIST: {
5241 xmlChar *content;
5242 xmlChar *oldvalue, *oldendvalue;
5243 int len;
Daniel Veillardea3f3982003-01-26 19:45:18 +00005244
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005245 content = xmlNodeGetContent(node);
5246 len = xmlStrlen(content);
5247 oldvalue = ctxt->state->value;
5248 oldendvalue = ctxt->state->endvalue;
5249 ctxt->state->value = content;
5250 ctxt->state->endvalue = content + len;
5251 ret = xmlRelaxNGValidateValue(ctxt, define);
5252 ctxt->state->value = oldvalue;
5253 ctxt->state->endvalue = oldendvalue;
5254 if (ret == -1) {
5255 VALID_CTXT();
5256 VALID_ERROR("internal error validating list\n");
5257 } else if (ret == 0) {
5258 ctxt->state->seq = node->next;
5259 }
5260 /*
5261 * TODO cover the problems with
5262 * <p>12<!-- comment -->34</p>
5263 * TODO detect full element coverage at compilation time.
5264 */
5265 if ((node != NULL) && (node->next != NULL)) {
5266 VALID_CTXT();
5267 VALID_ERROR("The list does not cover the full element %s\n",
5268 node->parent->name);
5269 ret = -1;
5270 }
5271 if (content != NULL)
5272 xmlFree(content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005273 break;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005274 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005275 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00005276 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005277 TODO
5278 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005279 }
5280 return(ret);
5281}
5282
5283/**
5284 * xmlRelaxNGValidateDocument:
5285 * @ctxt: a Relax-NG validation context
5286 * @doc: the document
5287 *
5288 * Validate the given document
5289 *
5290 * Returns 0 if the validation succeeded or an error code.
5291 */
5292static int
5293xmlRelaxNGValidateDocument(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
5294 int ret;
5295 xmlRelaxNGPtr schema;
5296 xmlRelaxNGGrammarPtr grammar;
5297 xmlRelaxNGValidStatePtr state;
5298
5299 if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL))
5300 return(-1);
5301
5302 schema = ctxt->schema;
5303 grammar = schema->topgrammar;
5304 if (grammar == NULL) {
5305 VALID_CTXT();
5306 VALID_ERROR("No top grammar defined\n");
5307 return(-1);
5308 }
5309 state = xmlRelaxNGNewValidState(ctxt, NULL);
5310 ctxt->state = state;
5311 ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start);
5312 state = ctxt->state;
5313 if ((state != NULL) && (state->seq != NULL)) {
5314 xmlNodePtr node;
5315
5316 node = state->seq;
5317 node = xmlRelaxNGSkipIgnored(ctxt, node);
5318 if (node != NULL) {
5319 VALID_CTXT();
5320 VALID_ERROR("extra data on the document\n");
5321 ret = -1;
5322 }
5323 }
5324 xmlRelaxNGFreeValidState(state);
5325
5326 return(ret);
5327}
5328
5329/************************************************************************
5330 * *
5331 * Validation interfaces *
5332 * *
5333 ************************************************************************/
5334/**
5335 * xmlRelaxNGNewValidCtxt:
5336 * @schema: a precompiled XML RelaxNGs
5337 *
5338 * Create an XML RelaxNGs validation context based on the given schema
5339 *
5340 * Returns the validation context or NULL in case of error
5341 */
5342xmlRelaxNGValidCtxtPtr
5343xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema) {
5344 xmlRelaxNGValidCtxtPtr ret;
5345
5346 ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt));
5347 if (ret == NULL) {
5348 xmlGenericError(xmlGenericErrorContext,
5349 "Failed to allocate new schama validation context\n");
5350 return (NULL);
5351 }
5352 memset(ret, 0, sizeof(xmlRelaxNGValidCtxt));
5353 ret->schema = schema;
5354 return (ret);
5355}
5356
5357/**
5358 * xmlRelaxNGFreeValidCtxt:
5359 * @ctxt: the schema validation context
5360 *
5361 * Free the resources associated to the schema validation context
5362 */
5363void
5364xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt) {
5365 if (ctxt == NULL)
5366 return;
5367 xmlFree(ctxt);
5368}
5369
5370/**
5371 * xmlRelaxNGSetValidErrors:
5372 * @ctxt: a Relax-NG validation context
5373 * @err: the error function
5374 * @warn: the warning function
5375 * @ctx: the functions context
5376 *
5377 * Set the error and warning callback informations
5378 */
5379void
5380xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
5381 xmlRelaxNGValidityErrorFunc err,
5382 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
5383 if (ctxt == NULL)
5384 return;
5385 ctxt->error = err;
5386 ctxt->warning = warn;
5387 ctxt->userData = ctx;
5388}
5389
5390/**
5391 * xmlRelaxNGValidateDoc:
5392 * @ctxt: a Relax-NG validation context
5393 * @doc: a parsed document tree
5394 *
5395 * Validate a document tree in memory.
5396 *
5397 * Returns 0 if the document is valid, a positive error code
5398 * number otherwise and -1 in case of internal or API error.
5399 */
5400int
5401xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
5402 int ret;
5403
5404 if ((ctxt == NULL) || (doc == NULL))
5405 return(-1);
5406
5407 ctxt->doc = doc;
5408
5409 ret = xmlRelaxNGValidateDocument(ctxt, doc);
Daniel Veillard71531f32003-02-05 13:19:53 +00005410 /*
5411 * TODO: build error codes
5412 */
5413 if (ret == -1)
5414 return(1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005415 return(ret);
5416}
5417
5418#endif /* LIBXML_SCHEMAS_ENABLED */
5419