blob: ab25bffb790f2759aa0c0d0c1408a7f7f6ddc70d [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
50#define DEBUG 1 /* very verbose output */
51#define DEBUG_CONTENT 1
52#define DEBUG_TYPE 1
Daniel Veillard276be4a2003-01-24 01:03:34 +000053#define DEBUG_VALID 1
Daniel Veillardb08c9812003-01-28 23:09:49 +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 }
644 if (node == NULL) {
645 ret->node = (xmlNodePtr) ctxt->doc;
646 ret->seq = root;
647 ret->nbAttrs = 0;
648 } else {
649 ret->node = node;
650 ret->seq = node->children;
651 ret->nbAttrs = nbAttrs;
652 if (nbAttrs > 0) {
653 if (nbAttrs < MAX_ATTR) {
654 memcpy(&(ret->attrs[0]), attrs,
655 sizeof(xmlAttrPtr) * (nbAttrs + 1));
656 } else {
657 attr = node->properties;
658 nbAttrs = 0;
659 while (attr != NULL) {
660 ret->attrs[nbAttrs++] = attr;
661 attr = attr->next;
662 }
663 ret->attrs[nbAttrs] = NULL;
664 }
665 }
666 }
Daniel Veillard1ed7f362003-02-03 10:57:45 +0000667 ret->nbAttrLeft = ret->nbAttrs;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000668 return (ret);
669}
670
671/**
672 * xmlRelaxNGCopyValidState:
673 * @ctxt: a Relax-NG validation context
674 * @state: a validation state
675 *
676 * Copy the validation state
677 *
678 * Returns the newly allocated structure or NULL in case or error
679 */
680static xmlRelaxNGValidStatePtr
681xmlRelaxNGCopyValidState(xmlRelaxNGValidCtxtPtr ctxt,
682 xmlRelaxNGValidStatePtr state)
683{
684 xmlRelaxNGValidStatePtr ret;
685 unsigned int size;
686
687 if (state == NULL)
688 return(NULL);
689
690 size = sizeof(xmlRelaxNGValidState) +
691 state->nbAttrs * sizeof(xmlAttrPtr);
692 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(size);
693 if (ret == NULL) {
694 if ((ctxt != NULL) && (ctxt->error != NULL))
695 ctxt->error(ctxt->userData, "Out of memory\n");
696 return (NULL);
697 }
698 memcpy(ret, state, size);
699 return(ret);
700}
701
702/**
703 * xmlRelaxNGFreeValidState:
704 * @state: a validation state structure
705 *
706 * Deallocate a RelaxNG validation state structure.
707 */
708static void
709xmlRelaxNGFreeValidState(xmlRelaxNGValidStatePtr state)
710{
711 if (state == NULL)
712 return;
713
714 xmlFree(state);
715}
716
717/************************************************************************
718 * *
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000719 * Document functions *
720 * *
721 ************************************************************************/
722static xmlDocPtr xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt,
723 xmlDocPtr doc);
724
725/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000726 * xmlRelaxNGIncludePush:
727 * @ctxt: the parser context
728 * @value: the element doc
729 *
730 * Pushes a new include on top of the include stack
731 *
732 * Returns 0 in case of error, the index in the stack otherwise
733 */
734static int
735xmlRelaxNGIncludePush(xmlRelaxNGParserCtxtPtr ctxt,
736 xmlRelaxNGIncludePtr value)
737{
738 if (ctxt->incTab == NULL) {
739 ctxt->incMax = 4;
740 ctxt->incNr = 0;
741 ctxt->incTab = (xmlRelaxNGIncludePtr *) xmlMalloc(
742 ctxt->incMax * sizeof(ctxt->incTab[0]));
743 if (ctxt->incTab == NULL) {
744 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
745 return (0);
746 }
747 }
748 if (ctxt->incNr >= ctxt->incMax) {
749 ctxt->incMax *= 2;
750 ctxt->incTab =
751 (xmlRelaxNGIncludePtr *) xmlRealloc(ctxt->incTab,
752 ctxt->incMax *
753 sizeof(ctxt->incTab[0]));
754 if (ctxt->incTab == NULL) {
755 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
756 return (0);
757 }
758 }
759 ctxt->incTab[ctxt->incNr] = value;
760 ctxt->inc = value;
761 return (ctxt->incNr++);
762}
763
764/**
765 * xmlRelaxNGIncludePop:
766 * @ctxt: the parser context
767 *
768 * Pops the top include from the include stack
769 *
770 * Returns the include just removed
771 */
772static xmlRelaxNGIncludePtr
773xmlRelaxNGIncludePop(xmlRelaxNGParserCtxtPtr ctxt)
774{
775 xmlRelaxNGIncludePtr ret;
776
777 if (ctxt->incNr <= 0)
778 return (0);
779 ctxt->incNr--;
780 if (ctxt->incNr > 0)
781 ctxt->inc = ctxt->incTab[ctxt->incNr - 1];
782 else
783 ctxt->inc = NULL;
784 ret = ctxt->incTab[ctxt->incNr];
785 ctxt->incTab[ctxt->incNr] = 0;
786 return (ret);
787}
788
789/**
790 * xmlRelaxNGLoadInclude:
791 * @ctxt: the parser context
792 * @URL: the normalized URL
793 * @node: the include node.
794 *
795 * First lookup if the document is already loaded into the parser context,
796 * check against recursion. If not found the resource is loaded and
797 * the content is preprocessed before being returned back to the caller.
798 *
799 * Returns the xmlRelaxNGIncludePtr or NULL in case of error
800 */
801static xmlRelaxNGIncludePtr
802xmlRelaxNGLoadInclude(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
803 xmlNodePtr node) {
804 xmlRelaxNGIncludePtr ret = NULL;
805 xmlDocPtr doc;
806 int i;
807 xmlNodePtr root, tmp, tmp2, cur;
808
809 /*
810 * check against recursion in the stack
811 */
812 for (i = 0;i < ctxt->incNr;i++) {
813 if (xmlStrEqual(ctxt->incTab[i]->href, URL)) {
814 if (ctxt->error != NULL)
815 ctxt->error(ctxt->userData,
816 "Detected an externalRef recursion for %s\n",
817 URL);
818 ctxt->nbErrors++;
819 return(NULL);
820 }
821 }
822
823 /*
824 * Lookup in the hash table
825 */
826 if (ctxt->includes == NULL) {
827 ctxt->includes = xmlHashCreate(10);
828 if (ctxt->includes == NULL) {
829 if (ctxt->error != NULL)
830 ctxt->error(ctxt->userData,
831 "Failed to allocate hash table for document\n");
832 ctxt->nbErrors++;
833 return(NULL);
834 }
835 } else {
836 ret = xmlHashLookup(ctxt->includes, URL);
837 if (ret != NULL)
838 return(ret);
839 }
840
841
842 /*
843 * load the document
844 */
845 doc = xmlParseFile((const char *) URL);
846 if (doc == NULL) {
847 if (ctxt->error != NULL)
848 ctxt->error(ctxt->userData,
849 "xmlRelaxNG: could not load %s\n", URL);
850 ctxt->nbErrors++;
851 return (NULL);
852 }
853
854 /*
855 * Allocate the document structures and register it first.
856 */
857 ret = (xmlRelaxNGIncludePtr) xmlMalloc(sizeof(xmlRelaxNGInclude));
858 if (ret == NULL) {
859 if (ctxt->error != NULL)
860 ctxt->error(ctxt->userData,
861 "xmlRelaxNG: allocate memory for doc %s\n", URL);
862 ctxt->nbErrors++;
863 xmlFreeDoc(doc);
864 return (NULL);
865 }
866 memset(ret, 0, sizeof(xmlRelaxNGInclude));
867 ret->doc = doc;
868 ret->href = xmlStrdup(URL);
869
870 /*
871 * push it on the stack and register it in the hash table
872 */
873 xmlHashAddEntry(ctxt->includes, URL, ret);
874 xmlRelaxNGIncludePush(ctxt, ret);
875
876 /*
877 * Some preprocessing of the document content, this include recursing
878 * in the include stack.
879 */
880 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
881 if (doc == NULL) {
882 /* xmlFreeDoc(ctxt->include); */
883 ctxt->inc = NULL;
884 return(NULL);
885 }
886
887 /*
888 * Pop up the include from the stack
889 */
890 xmlRelaxNGIncludePop(ctxt);
891
892 /*
893 * Check that the top element is a grammar
894 */
895 root = xmlDocGetRootElement(doc);
896 if (root == NULL) {
897 if (ctxt->error != NULL)
898 ctxt->error(ctxt->userData,
899 "xmlRelaxNG: included document is empty %s\n", URL);
900 ctxt->nbErrors++;
901 xmlFreeDoc(doc);
902 return (NULL);
903 }
904 if (!IS_RELAXNG(root, "grammar")) {
905 if (ctxt->error != NULL)
906 ctxt->error(ctxt->userData,
907 "xmlRelaxNG: included document %s root is not a grammar\n",
908 URL);
909 ctxt->nbErrors++;
910 xmlFreeDoc(doc);
911 return (NULL);
912 }
913
914 /*
915 * Elimination of redefined rules in the include.
916 */
917 cur = node->children;
918 while (cur != NULL) {
919 if (IS_RELAXNG(cur, "start")) {
920 int found = 0;
921
922 tmp = root->children;
923 while (tmp != NULL) {
924 tmp2 = tmp->next;
925 if (IS_RELAXNG(tmp, "start")) {
926 found = 1;
927 xmlUnlinkNode(tmp);
928 xmlFreeNode(tmp);
929 }
930 tmp = tmp2;
931 }
932 if (!found) {
933 if (ctxt->error != NULL)
934 ctxt->error(ctxt->userData,
935 "xmlRelaxNG: include %s has a start but not the included grammar\n",
936 URL);
937 ctxt->nbErrors++;
938 }
939 } else if (IS_RELAXNG(cur, "define")) {
940 xmlChar *name, *name2;
941
942 name = xmlGetProp(cur, BAD_CAST "name");
943 if (name == NULL) {
944 if (ctxt->error != NULL)
945 ctxt->error(ctxt->userData,
946 "xmlRelaxNG: include %s has define without name\n",
947 URL);
948 ctxt->nbErrors++;
949 } else {
950 int found = 0;
951
952 tmp = root->children;
953 while (tmp != NULL) {
954 tmp2 = tmp->next;
955 if (IS_RELAXNG(tmp, "define")) {
956 name2 = xmlGetProp(tmp, BAD_CAST "name");
957 if (name2 != NULL) {
958 if (xmlStrEqual(name, name2)) {
959 found = 1;
960 xmlUnlinkNode(tmp);
961 xmlFreeNode(tmp);
962 }
963 xmlFree(name2);
964 }
965 }
966 tmp = tmp2;
967 }
968 if (!found) {
969 if (ctxt->error != NULL)
970 ctxt->error(ctxt->userData,
971 "xmlRelaxNG: include %s has a define %s but not the included grammar\n",
972 URL, name);
973 ctxt->nbErrors++;
974 }
975 xmlFree(name);
976 }
977 }
978 cur = cur->next;
979 }
980
981
982 return(ret);
983}
984
985/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000986 * xmlRelaxNGDocumentPush:
987 * @ctxt: the parser context
988 * @value: the element doc
989 *
990 * Pushes a new doc on top of the doc stack
991 *
992 * Returns 0 in case of error, the index in the stack otherwise
993 */
994static int
995xmlRelaxNGDocumentPush(xmlRelaxNGParserCtxtPtr ctxt,
996 xmlRelaxNGDocumentPtr value)
997{
998 if (ctxt->docTab == NULL) {
999 ctxt->docMax = 4;
1000 ctxt->docNr = 0;
1001 ctxt->docTab = (xmlRelaxNGDocumentPtr *) xmlMalloc(
1002 ctxt->docMax * sizeof(ctxt->docTab[0]));
1003 if (ctxt->docTab == NULL) {
1004 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
1005 return (0);
1006 }
1007 }
1008 if (ctxt->docNr >= ctxt->docMax) {
1009 ctxt->docMax *= 2;
1010 ctxt->docTab =
1011 (xmlRelaxNGDocumentPtr *) xmlRealloc(ctxt->docTab,
1012 ctxt->docMax *
1013 sizeof(ctxt->docTab[0]));
1014 if (ctxt->docTab == NULL) {
1015 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
1016 return (0);
1017 }
1018 }
1019 ctxt->docTab[ctxt->docNr] = value;
1020 ctxt->doc = value;
1021 return (ctxt->docNr++);
1022}
1023
1024/**
1025 * xmlRelaxNGDocumentPop:
1026 * @ctxt: the parser context
1027 *
1028 * Pops the top doc from the doc stack
1029 *
1030 * Returns the doc just removed
1031 */
1032static xmlRelaxNGDocumentPtr
1033xmlRelaxNGDocumentPop(xmlRelaxNGParserCtxtPtr ctxt)
1034{
1035 xmlRelaxNGDocumentPtr ret;
1036
1037 if (ctxt->docNr <= 0)
1038 return (0);
1039 ctxt->docNr--;
1040 if (ctxt->docNr > 0)
1041 ctxt->doc = ctxt->docTab[ctxt->docNr - 1];
1042 else
1043 ctxt->doc = NULL;
1044 ret = ctxt->docTab[ctxt->docNr];
1045 ctxt->docTab[ctxt->docNr] = 0;
1046 return (ret);
1047}
1048
1049/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001050 * xmlRelaxNGLoadExternalRef:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001051 * @ctxt: the parser context
1052 * @URL: the normalized URL
1053 * @ns: the inherited ns if any
1054 *
1055 * First lookup if the document is already loaded into the parser context,
1056 * check against recursion. If not found the resource is loaded and
1057 * the content is preprocessed before being returned back to the caller.
1058 *
1059 * Returns the xmlRelaxNGDocumentPtr or NULL in case of error
1060 */
1061static xmlRelaxNGDocumentPtr
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001062xmlRelaxNGLoadExternalRef(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001063 const xmlChar *ns) {
1064 xmlRelaxNGDocumentPtr ret = NULL;
1065 xmlDocPtr doc;
1066 xmlNodePtr root;
1067 int i;
1068
1069 /*
1070 * check against recursion in the stack
1071 */
1072 for (i = 0;i < ctxt->docNr;i++) {
1073 if (xmlStrEqual(ctxt->docTab[i]->href, URL)) {
1074 if (ctxt->error != NULL)
1075 ctxt->error(ctxt->userData,
1076 "Detected an externalRef recursion for %s\n",
1077 URL);
1078 ctxt->nbErrors++;
1079 return(NULL);
1080 }
1081 }
1082
1083 /*
1084 * Lookup in the hash table
1085 */
1086 if (ctxt->documents == NULL) {
1087 ctxt->documents = xmlHashCreate(10);
1088 if (ctxt->documents == NULL) {
1089 if (ctxt->error != NULL)
1090 ctxt->error(ctxt->userData,
1091 "Failed to allocate hash table for document\n");
1092 ctxt->nbErrors++;
1093 return(NULL);
1094 }
1095 } else {
1096 if (ns == NULL)
1097 ret = xmlHashLookup2(ctxt->documents, BAD_CAST "", URL);
1098 else
1099 ret = xmlHashLookup2(ctxt->documents, ns, URL);
1100 if (ret != NULL)
1101 return(ret);
1102 }
1103
1104
1105 /*
1106 * load the document
1107 */
1108 doc = xmlParseFile((const char *) URL);
1109 if (doc == NULL) {
1110 if (ctxt->error != NULL)
1111 ctxt->error(ctxt->userData,
1112 "xmlRelaxNG: could not load %s\n", URL);
1113 ctxt->nbErrors++;
1114 return (NULL);
1115 }
1116
1117 /*
1118 * Allocate the document structures and register it first.
1119 */
1120 ret = (xmlRelaxNGDocumentPtr) xmlMalloc(sizeof(xmlRelaxNGDocument));
1121 if (ret == NULL) {
1122 if (ctxt->error != NULL)
1123 ctxt->error(ctxt->userData,
1124 "xmlRelaxNG: allocate memory for doc %s\n", URL);
1125 ctxt->nbErrors++;
1126 xmlFreeDoc(doc);
1127 return (NULL);
1128 }
1129 memset(ret, 0, sizeof(xmlRelaxNGDocument));
1130 ret->doc = doc;
1131 ret->href = xmlStrdup(URL);
1132
1133 /*
1134 * transmit the ns if needed
1135 */
1136 if (ns != NULL) {
1137 root = xmlDocGetRootElement(doc);
1138 if (root != NULL) {
1139 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
1140 xmlSetProp(root, BAD_CAST"ns", ns);
1141 }
1142 }
1143 }
1144
1145 /*
1146 * push it on the stack and register it in the hash table
1147 */
1148 if (ns == NULL)
1149 xmlHashAddEntry2(ctxt->documents, BAD_CAST "", URL, ret);
1150 else
1151 xmlHashAddEntry2(ctxt->documents, ns, URL, ret);
1152 xmlRelaxNGDocumentPush(ctxt, ret);
1153
1154 /*
1155 * Some preprocessing of the document content
1156 */
1157 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
1158 if (doc == NULL) {
1159 xmlFreeDoc(ctxt->document);
1160 ctxt->doc = NULL;
1161 return(NULL);
1162 }
1163
1164 xmlRelaxNGDocumentPop(ctxt);
1165
1166 return(ret);
1167}
1168
1169/************************************************************************
1170 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +00001171 * Error functions *
1172 * *
1173 ************************************************************************/
1174
1175#define VALID_CTXT() \
1176 if (ctxt->flags == 0) xmlGenericError(xmlGenericErrorContext, \
1177 "error detected at %s:%d\n", \
1178 __FILE__, __LINE__);
1179#define VALID_ERROR if (ctxt->flags == 0) printf
1180
1181#if 0
1182/**
1183 * xmlRelaxNGErrorContext:
1184 * @ctxt: the parsing context
1185 * @schema: the schema being built
1186 * @node: the node being processed
1187 * @child: the child being processed
1188 *
1189 * Dump a RelaxNGType structure
1190 */
1191static void
1192xmlRelaxNGErrorContext(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGPtr schema,
1193 xmlNodePtr node, xmlNodePtr child)
1194{
1195 int line = 0;
1196 const xmlChar *file = NULL;
1197 const xmlChar *name = NULL;
1198 const char *type = "error";
1199
1200 if ((ctxt == NULL) || (ctxt->error == NULL))
1201 return;
1202
1203 if (child != NULL)
1204 node = child;
1205
1206 if (node != NULL) {
1207 if ((node->type == XML_DOCUMENT_NODE) ||
1208 (node->type == XML_HTML_DOCUMENT_NODE)) {
1209 xmlDocPtr doc = (xmlDocPtr) node;
1210
1211 file = doc->URL;
1212 } else {
1213 /*
1214 * Try to find contextual informations to report
1215 */
1216 if (node->type == XML_ELEMENT_NODE) {
1217 line = (int) node->content;
1218 } else if ((node->prev != NULL) &&
1219 (node->prev->type == XML_ELEMENT_NODE)) {
1220 line = (int) node->prev->content;
1221 } else if ((node->parent != NULL) &&
1222 (node->parent->type == XML_ELEMENT_NODE)) {
1223 line = (int) node->parent->content;
1224 }
1225 if ((node->doc != NULL) && (node->doc->URL != NULL))
1226 file = node->doc->URL;
1227 if (node->name != NULL)
1228 name = node->name;
1229 }
1230 }
1231
1232 if (ctxt != NULL)
1233 type = "compilation error";
1234 else if (schema != NULL)
1235 type = "runtime error";
1236
1237 if ((file != NULL) && (line != 0) && (name != NULL))
1238 ctxt->error(ctxt->userData, "%s: file %s line %d element %s\n",
1239 type, file, line, name);
1240 else if ((file != NULL) && (name != NULL))
1241 ctxt->error(ctxt->userData, "%s: file %s element %s\n",
1242 type, file, name);
1243 else if ((file != NULL) && (line != 0))
1244 ctxt->error(ctxt->userData, "%s: file %s line %d\n", type, file, line);
1245 else if (file != NULL)
1246 ctxt->error(ctxt->userData, "%s: file %s\n", type, file);
1247 else if (name != NULL)
1248 ctxt->error(ctxt->userData, "%s: element %s\n", type, name);
1249 else
1250 ctxt->error(ctxt->userData, "%s\n", type);
1251}
1252#endif
1253
1254/************************************************************************
1255 * *
1256 * Type library hooks *
1257 * *
1258 ************************************************************************/
Daniel Veillardea3f3982003-01-26 19:45:18 +00001259static xmlChar *xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt,
1260 const xmlChar *str);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001261
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001262/**
1263 * xmlRelaxNGSchemaTypeHave:
1264 * @data: data needed for the library
1265 * @type: the type name
1266 *
1267 * Check if the given type is provided by
1268 * the W3C XMLSchema Datatype library.
1269 *
1270 * Returns 1 if yes, 0 if no and -1 in case of error.
1271 */
1272static int
1273xmlRelaxNGSchemaTypeHave(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001274 const xmlChar *type) {
1275 xmlSchemaTypePtr typ;
1276
1277 if (type == NULL)
1278 return(-1);
1279 typ = xmlSchemaGetPredefinedType(type,
1280 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1281 if (typ == NULL)
1282 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001283 return(1);
1284}
1285
1286/**
1287 * xmlRelaxNGSchemaTypeCheck:
1288 * @data: data needed for the library
1289 * @type: the type name
1290 * @value: the value to check
1291 *
1292 * Check if the given type and value are validated by
1293 * the W3C XMLSchema Datatype library.
1294 *
1295 * Returns 1 if yes, 0 if no and -1 in case of error.
1296 */
1297static int
1298xmlRelaxNGSchemaTypeCheck(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001299 const xmlChar *type,
1300 const xmlChar *value) {
1301 xmlSchemaTypePtr typ;
1302 int ret;
1303
1304 /*
1305 * TODO: the type should be cached ab provided back, interface subject
1306 * to changes.
1307 * TODO: handle facets, may require an additional interface and keep
1308 * the value returned from the validation.
1309 */
1310 if ((type == NULL) || (value == NULL))
1311 return(-1);
1312 typ = xmlSchemaGetPredefinedType(type,
1313 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1314 if (typ == NULL)
1315 return(-1);
1316 ret = xmlSchemaValidatePredefinedType(typ, value, NULL);
1317 if (ret == 0)
1318 return(1);
1319 if (ret > 0)
1320 return(0);
1321 return(-1);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001322}
1323
1324/**
1325 * xmlRelaxNGSchemaTypeCompare:
1326 * @data: data needed for the library
1327 * @type: the type name
1328 * @value1: the first value
1329 * @value2: the second value
1330 *
1331 * Compare two values accordingly a type from the W3C XMLSchema
1332 * Datatype library.
1333 *
1334 * Returns 1 if yes, 0 if no and -1 in case of error.
1335 */
1336static int
1337xmlRelaxNGSchemaTypeCompare(void *data ATTRIBUTE_UNUSED,
1338 const xmlChar *type ATTRIBUTE_UNUSED,
1339 const xmlChar *value1 ATTRIBUTE_UNUSED,
1340 const xmlChar *value2 ATTRIBUTE_UNUSED) {
1341 TODO
1342 return(1);
1343}
1344
1345/**
1346 * xmlRelaxNGDefaultTypeHave:
1347 * @data: data needed for the library
1348 * @type: the type name
1349 *
1350 * Check if the given type is provided by
1351 * the default datatype library.
1352 *
1353 * Returns 1 if yes, 0 if no and -1 in case of error.
1354 */
1355static int
1356xmlRelaxNGDefaultTypeHave(void *data ATTRIBUTE_UNUSED, const xmlChar *type) {
1357 if (type == NULL)
1358 return(-1);
1359 if (xmlStrEqual(type, BAD_CAST "string"))
1360 return(1);
1361 if (xmlStrEqual(type, BAD_CAST "token"))
1362 return(1);
1363 return(0);
1364}
1365
1366/**
1367 * xmlRelaxNGDefaultTypeCheck:
1368 * @data: data needed for the library
1369 * @type: the type name
1370 * @value: the value to check
1371 *
1372 * Check if the given type and value are validated by
1373 * the default datatype library.
1374 *
1375 * Returns 1 if yes, 0 if no and -1 in case of error.
1376 */
1377static int
1378xmlRelaxNGDefaultTypeCheck(void *data ATTRIBUTE_UNUSED,
1379 const xmlChar *type ATTRIBUTE_UNUSED,
1380 const xmlChar *value ATTRIBUTE_UNUSED) {
1381 return(1);
1382}
1383
1384/**
1385 * xmlRelaxNGDefaultTypeCompare:
1386 * @data: data needed for the library
1387 * @type: the type name
1388 * @value1: the first value
1389 * @value2: the second value
1390 *
1391 * Compare two values accordingly a type from the default
1392 * datatype library.
1393 *
1394 * Returns 1 if yes, 0 if no and -1 in case of error.
1395 */
1396static int
1397xmlRelaxNGDefaultTypeCompare(void *data ATTRIBUTE_UNUSED,
1398 const xmlChar *type ATTRIBUTE_UNUSED,
1399 const xmlChar *value1 ATTRIBUTE_UNUSED,
1400 const xmlChar *value2 ATTRIBUTE_UNUSED) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00001401 int ret = -1;
1402
1403 if (xmlStrEqual(type, BAD_CAST "string")) {
1404 ret = xmlStrEqual(value1, value2);
1405 } else if (xmlStrEqual(type, BAD_CAST "token")) {
1406 if (!xmlStrEqual(value1, value2)) {
1407 xmlChar *nval, *nvalue;
1408
1409 /*
1410 * TODO: trivial optimizations are possible by
1411 * computing at compile-time
1412 */
1413 nval = xmlRelaxNGNormalize(NULL, value1);
1414 nvalue = xmlRelaxNGNormalize(NULL, value2);
1415
1416 if ((nval == NULL) || (nvalue == NULL) ||
1417 (!xmlStrEqual(nval, nvalue)))
1418 ret = -1;
1419 if (nval != NULL)
1420 xmlFree(nval);
1421 if (nvalue != NULL)
1422 xmlFree(nvalue);
1423 }
1424 }
1425 return(ret);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001426}
1427
1428static int xmlRelaxNGTypeInitialized = 0;
1429static xmlHashTablePtr xmlRelaxNGRegisteredTypes = NULL;
1430
1431/**
1432 * xmlRelaxNGFreeTypeLibrary:
1433 * @lib: the type library structure
1434 * @namespace: the URI bound to the library
1435 *
1436 * Free the structure associated to the type library
1437 */
Daniel Veillard6eadf632003-01-23 18:29:16 +00001438static void
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001439xmlRelaxNGFreeTypeLibrary(xmlRelaxNGTypeLibraryPtr lib,
1440 const xmlChar *namespace ATTRIBUTE_UNUSED) {
1441 if (lib == NULL)
1442 return;
1443 if (lib->namespace != NULL)
1444 xmlFree((xmlChar *)lib->namespace);
1445 xmlFree(lib);
1446}
1447
1448/**
1449 * xmlRelaxNGRegisterTypeLibrary:
1450 * @namespace: the URI bound to the library
1451 * @data: data associated to the library
1452 * @have: the provide function
1453 * @check: the checking function
1454 * @comp: the comparison function
1455 *
1456 * Register a new type library
1457 *
1458 * Returns 0 in case of success and -1 in case of error.
1459 */
1460static int
1461xmlRelaxNGRegisterTypeLibrary(const xmlChar *namespace, void *data,
1462 xmlRelaxNGTypeHave have, xmlRelaxNGTypeCheck check,
1463 xmlRelaxNGTypeCompare comp) {
1464 xmlRelaxNGTypeLibraryPtr lib;
1465 int ret;
1466
1467 if ((xmlRelaxNGRegisteredTypes == NULL) || (namespace == NULL) ||
1468 (check == NULL) || (comp == NULL))
1469 return(-1);
1470 if (xmlHashLookup(xmlRelaxNGRegisteredTypes, namespace) != NULL) {
1471 xmlGenericError(xmlGenericErrorContext,
1472 "Relax-NG types library '%s' already registered\n",
1473 namespace);
1474 return(-1);
1475 }
1476 lib = (xmlRelaxNGTypeLibraryPtr) xmlMalloc(sizeof(xmlRelaxNGTypeLibrary));
1477 if (lib == NULL) {
1478 xmlGenericError(xmlGenericErrorContext,
1479 "Relax-NG types library '%s' malloc() failed\n",
1480 namespace);
1481 return (-1);
1482 }
1483 memset(lib, 0, sizeof(xmlRelaxNGTypeLibrary));
1484 lib->namespace = xmlStrdup(namespace);
1485 lib->data = data;
1486 lib->have = have;
1487 lib->comp = comp;
1488 lib->check = check;
1489 ret = xmlHashAddEntry(xmlRelaxNGRegisteredTypes, namespace, lib);
1490 if (ret < 0) {
1491 xmlGenericError(xmlGenericErrorContext,
1492 "Relax-NG types library failed to register '%s'\n",
1493 namespace);
1494 xmlRelaxNGFreeTypeLibrary(lib, namespace);
1495 return(-1);
1496 }
1497 return(0);
1498}
1499
1500/**
1501 * xmlRelaxNGInitTypes:
1502 *
1503 * Initilize the default type libraries.
1504 *
1505 * Returns 0 in case of success and -1 in case of error.
1506 */
1507static int
Daniel Veillard6eadf632003-01-23 18:29:16 +00001508xmlRelaxNGInitTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001509 if (xmlRelaxNGTypeInitialized != 0)
1510 return(0);
1511 xmlRelaxNGRegisteredTypes = xmlHashCreate(10);
1512 if (xmlRelaxNGRegisteredTypes == NULL) {
1513 xmlGenericError(xmlGenericErrorContext,
1514 "Failed to allocate sh table for Relax-NG types\n");
1515 return(-1);
1516 }
1517 xmlRelaxNGRegisterTypeLibrary(
1518 BAD_CAST "http://www.w3.org/2001/XMLSchema-datatypes",
1519 NULL,
1520 xmlRelaxNGSchemaTypeHave,
1521 xmlRelaxNGSchemaTypeCheck,
1522 xmlRelaxNGSchemaTypeCompare);
1523 xmlRelaxNGRegisterTypeLibrary(
1524 xmlRelaxNGNs,
1525 NULL,
1526 xmlRelaxNGDefaultTypeHave,
1527 xmlRelaxNGDefaultTypeCheck,
1528 xmlRelaxNGDefaultTypeCompare);
1529 xmlRelaxNGTypeInitialized = 1;
1530 return(0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001531}
1532
1533/**
1534 * xmlRelaxNGCleanupTypes:
1535 *
1536 * Cleanup the default Schemas type library associated to RelaxNG
1537 */
1538void
1539xmlRelaxNGCleanupTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001540 if (xmlRelaxNGTypeInitialized == 0)
1541 return;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001542 xmlSchemaCleanupTypes();
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001543 xmlHashFree(xmlRelaxNGRegisteredTypes, (xmlHashDeallocator)
1544 xmlRelaxNGFreeTypeLibrary);
1545 xmlRelaxNGTypeInitialized = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001546}
1547
1548/************************************************************************
1549 * *
1550 * Parsing functions *
1551 * *
1552 ************************************************************************/
1553
1554static xmlRelaxNGDefinePtr xmlRelaxNGParseAttribute(
1555 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1556static xmlRelaxNGDefinePtr xmlRelaxNGParseElement(
1557 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1558static xmlRelaxNGDefinePtr xmlRelaxNGParsePatterns(
Daniel Veillard154877e2003-01-30 12:17:05 +00001559 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes, int group);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001560static xmlRelaxNGDefinePtr xmlRelaxNGParsePattern(
1561 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001562static xmlRelaxNGPtr xmlRelaxNGParseDocument(
1563 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00001564static int xmlRelaxNGParseGrammarContent(
1565 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00001566static xmlRelaxNGDefinePtr xmlRelaxNGParseNameClass(
1567 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
1568 xmlRelaxNGDefinePtr def);
Daniel Veillard419a7682003-02-03 23:22:49 +00001569static xmlRelaxNGGrammarPtr xmlRelaxNGParseGrammar(
1570 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001571
1572
1573#define IS_BLANK_NODE(n) \
1574 (((n)->type == XML_TEXT_NODE) && (xmlRelaxNGIsBlank((n)->content)))
1575
1576/**
1577 * xmlRelaxNGIsBlank:
1578 * @str: a string
1579 *
1580 * Check if a string is ignorable c.f. 4.2. Whitespace
1581 *
1582 * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
1583 */
1584static int
1585xmlRelaxNGIsBlank(xmlChar *str) {
1586 if (str == NULL)
1587 return(1);
1588 while (*str != 0) {
1589 if (!(IS_BLANK(*str))) return(0);
1590 str++;
1591 }
1592 return(1);
1593}
1594
Daniel Veillard6eadf632003-01-23 18:29:16 +00001595/**
1596 * xmlRelaxNGGetDataTypeLibrary:
1597 * @ctxt: a Relax-NG parser context
1598 * @node: the current data or value element
1599 *
1600 * Applies algorithm from 4.3. datatypeLibrary attribute
1601 *
1602 * Returns the datatypeLibary value or NULL if not found
1603 */
1604static xmlChar *
1605xmlRelaxNGGetDataTypeLibrary(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1606 xmlNodePtr node) {
1607 xmlChar *ret, *escape;
1608
Daniel Veillard6eadf632003-01-23 18:29:16 +00001609 if ((IS_RELAXNG(node, "data")) || (IS_RELAXNG(node, "value"))) {
1610 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1611 if (ret != NULL) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001612 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +00001613 if (escape == NULL) {
1614 return(ret);
1615 }
1616 xmlFree(ret);
1617 return(escape);
1618 }
1619 }
1620 node = node->parent;
1621 while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) {
1622 if (IS_RELAXNG(node, "element")) {
1623 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1624 if (ret != NULL) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001625 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +00001626 if (escape == NULL) {
1627 return(ret);
1628 }
1629 xmlFree(ret);
1630 return(escape);
1631 }
1632 }
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;
4193 return(0);
4194 }
4195 while (*cur != 0) cur++;
4196 while ((cur != ctxt->state->endvalue) && (*cur == 0)) cur++;
4197 if (cur == ctxt->state->endvalue)
4198 ctxt->state->value = NULL;
4199 else
4200 ctxt->state->value = cur;
4201 return(0);
4202}
4203
4204/**
4205 * xmlRelaxNGValidateValueList:
4206 * @ctxt: a Relax-NG validation context
4207 * @defines: the list of definitions to verify
4208 *
4209 * Validate the given set of definitions for the current value
4210 *
4211 * Returns 0 if the validation succeeded or an error code.
4212 */
4213static int
4214xmlRelaxNGValidateValueList(xmlRelaxNGValidCtxtPtr ctxt,
4215 xmlRelaxNGDefinePtr defines) {
4216 int ret = 0;
4217
4218 while (defines != NULL) {
4219 ret = xmlRelaxNGValidateValue(ctxt, defines);
4220 if (ret != 0)
4221 break;
4222 defines = defines->next;
4223 }
4224 return(ret);
4225}
4226
4227/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004228 * xmlRelaxNGValidateValue:
4229 * @ctxt: a Relax-NG validation context
4230 * @define: the definition to verify
4231 *
4232 * Validate the given definition for the current value
4233 *
4234 * Returns 0 if the validation succeeded or an error code.
4235 */
4236static int
4237xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
4238 xmlRelaxNGDefinePtr define) {
Daniel Veillardedc91922003-01-26 00:52:04 +00004239 int ret = 0, oldflags;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004240 xmlChar *value;
4241
4242 value = ctxt->state->value;
4243 switch (define->type) {
4244 case XML_RELAXNG_EMPTY:
4245 if ((value != NULL) && (value[0] != '0'))
4246 ret = -1;
4247 break;
4248 case XML_RELAXNG_TEXT:
4249 break;
Daniel Veillardedc91922003-01-26 00:52:04 +00004250 case XML_RELAXNG_VALUE: {
4251 if (!xmlStrEqual(value, define->value)) {
4252 if (define->name != NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00004253 xmlRelaxNGTypeLibraryPtr lib;
4254
4255 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
4256 if ((lib != NULL) && (lib->comp != NULL))
4257 ret = lib->comp(lib->data, define->name, value,
4258 define->value);
4259 else
4260 ret = -1;
4261 if (ret < 0) {
4262 VALID_CTXT();
4263 VALID_ERROR("Internal: failed to compare type %s\n",
4264 define->name);
4265 return(-1);
4266 } else if (ret == 1) {
4267 ret = 0;
4268 } else {
4269 ret = -1;
4270 }
Daniel Veillardedc91922003-01-26 00:52:04 +00004271 } else {
4272 xmlChar *nval, *nvalue;
4273
4274 /*
4275 * TODO: trivial optimizations are possible by
4276 * computing at compile-time
4277 */
4278 nval = xmlRelaxNGNormalize(ctxt, define->value);
4279 nvalue = xmlRelaxNGNormalize(ctxt, value);
4280
Daniel Veillardea3f3982003-01-26 19:45:18 +00004281 if ((nval == NULL) || (nvalue == NULL) ||
4282 (!xmlStrEqual(nval, nvalue)))
Daniel Veillardedc91922003-01-26 00:52:04 +00004283 ret = -1;
4284 if (nval != NULL)
4285 xmlFree(nval);
4286 if (nvalue != NULL)
4287 xmlFree(nvalue);
4288 }
4289 }
4290 break;
4291 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004292 case XML_RELAXNG_DATATYPE: {
4293 ret = xmlRelaxNGValidateDatatype(ctxt, value, define);
4294 if (ret == 0)
4295 xmlRelaxNGNextValue(ctxt);
4296
4297 break;
4298 }
4299 case XML_RELAXNG_CHOICE: {
4300 xmlRelaxNGDefinePtr list = define->content;
4301 xmlChar *oldvalue;
4302
4303 oldflags = ctxt->flags;
4304 ctxt->flags |= FLAGS_IGNORABLE;
4305
4306 oldvalue = ctxt->state->value;
4307 while (list != NULL) {
4308 ret = xmlRelaxNGValidateValue(ctxt, list);
4309 if (ret == 0) {
4310 break;
4311 }
4312 ctxt->state->value = oldvalue;
4313 list = list->next;
4314 }
4315 ctxt->flags = oldflags;
4316 break;
4317 }
4318 case XML_RELAXNG_LIST: {
4319 xmlRelaxNGDefinePtr list = define->content;
4320 xmlChar *oldvalue, *oldend, *val, *cur;
4321
4322 oldvalue = ctxt->state->value;
4323 oldend = ctxt->state->endvalue;
4324
4325 val = xmlStrdup(oldvalue);
4326 if (val == NULL) {
4327 VALID_CTXT();
4328 VALID_ERROR("Internal: no state\n");
4329 return(-1);
4330 }
4331 cur = val;
4332 while (*cur != 0) {
4333 if (IS_BLANK(*cur))
4334 *cur = 0;
4335 cur++;
4336 }
4337 ctxt->state->endvalue = cur;
4338 cur = val;
4339 while ((*cur == 0) && (cur != ctxt->state->endvalue)) cur++;
4340
4341 ctxt->state->value = cur;
4342
4343 while (list != NULL) {
4344 ret = xmlRelaxNGValidateValue(ctxt, list);
4345 if (ret != 0) {
4346 break;
4347 }
4348 list = list->next;
4349 }
4350 if ((ret == 0) && (ctxt->state->value != NULL) &&
4351 (ctxt->state->value != ctxt->state->endvalue)) {
4352 VALID_CTXT();
4353 VALID_ERROR("Extra data in list: %s\n", ctxt->state->value);
4354 ret = -1;
4355 }
4356 xmlFree(val);
4357 ctxt->state->value = oldvalue;
4358 ctxt->state->endvalue = oldend;
4359 break;
4360 }
4361 case XML_RELAXNG_ONEORMORE:
4362 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
4363 if (ret != 0) {
4364 break;
4365 }
4366 /* no break on purpose */
4367 case XML_RELAXNG_ZEROORMORE: {
4368 xmlChar *cur, *temp;
4369
4370 oldflags = ctxt->flags;
4371 ctxt->flags |= FLAGS_IGNORABLE;
4372 cur = ctxt->state->value;
4373 temp = NULL;
4374 while ((cur != NULL) && (cur != ctxt->state->endvalue) &&
4375 (temp != cur)) {
4376 temp = cur;
4377 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
4378 if (ret != 0) {
4379 ctxt->state->value = temp;
4380 ret = 0;
4381 break;
4382 }
4383 cur = ctxt->state->value;
4384 }
4385 ctxt->flags = oldflags;
4386 break;
4387 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004388 default:
4389 TODO
4390 ret = -1;
4391 }
4392 return(ret);
4393}
4394
4395/**
4396 * xmlRelaxNGValidateValueContent:
4397 * @ctxt: a Relax-NG validation context
4398 * @defines: the list of definitions to verify
4399 *
4400 * Validate the given definitions for the current value
4401 *
4402 * Returns 0 if the validation succeeded or an error code.
4403 */
4404static int
4405xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt,
4406 xmlRelaxNGDefinePtr defines) {
4407 int ret = 0;
4408
4409 while (defines != NULL) {
4410 ret = xmlRelaxNGValidateValue(ctxt, defines);
4411 if (ret != 0)
4412 break;
4413 defines = defines->next;
4414 }
4415 return(ret);
4416}
4417
4418/**
Daniel Veillard144fae12003-02-03 13:17:57 +00004419 * xmlRelaxNGAttributeMatch:
4420 * @ctxt: a Relax-NG validation context
4421 * @define: the definition to check
4422 * @prop: the attribute
4423 *
4424 * Check if the attribute matches the definition nameClass
4425 *
4426 * Returns 1 if the attribute matches, 0 if no, or -1 in case of error
4427 */
4428static int
4429xmlRelaxNGAttributeMatch(xmlRelaxNGValidCtxtPtr ctxt,
4430 xmlRelaxNGDefinePtr define,
4431 xmlAttrPtr prop) {
4432 int ret;
4433
4434 if (define->name != NULL) {
4435 if (!xmlStrEqual(define->name, prop->name))
4436 return(0);
4437 }
4438 if (define->ns != NULL) {
4439 if (define->ns[0] == 0) {
4440 if (prop->ns != NULL)
4441 return(0);
4442 } else {
4443 if ((prop->ns == NULL) ||
4444 (!xmlStrEqual(define->ns, prop->ns->href)))
4445 return(0);
4446 }
4447 }
4448 if (define->nameClass == NULL)
4449 return(1);
4450 define = define->nameClass;
4451 if (define->type == XML_RELAXNG_EXCEPT) {
4452 xmlRelaxNGDefinePtr list;
4453
4454 list = define->content;
4455 while (list != NULL) {
4456 ret = xmlRelaxNGAttributeMatch(ctxt, list, prop);
4457 if (ret == 1)
4458 return(0);
4459 if (ret < 0)
4460 return(ret);
4461 list = list->next;
4462 }
4463 } else {
4464 TODO
4465 }
4466 return(1);
4467}
4468
4469/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004470 * xmlRelaxNGValidateAttribute:
4471 * @ctxt: a Relax-NG validation context
4472 * @define: the definition to verify
4473 *
4474 * Validate the given attribute definition for that node
4475 *
4476 * Returns 0 if the validation succeeded or an error code.
4477 */
4478static int
4479xmlRelaxNGValidateAttribute(xmlRelaxNGValidCtxtPtr ctxt,
4480 xmlRelaxNGDefinePtr define) {
4481 int ret = 0, i;
4482 xmlChar *value, *oldvalue;
4483 xmlAttrPtr prop = NULL, tmp;
4484
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004485 if (ctxt->state->nbAttrLeft <= 0)
4486 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004487 if (define->name != NULL) {
4488 for (i = 0;i < ctxt->state->nbAttrs;i++) {
4489 tmp = ctxt->state->attrs[i];
4490 if ((tmp != NULL) && (xmlStrEqual(define->name, tmp->name))) {
4491 if ((((define->ns == NULL) || (define->ns[0] == 0)) &&
4492 (tmp->ns == NULL)) ||
4493 ((tmp->ns != NULL) &&
4494 (xmlStrEqual(define->ns, tmp->ns->href)))) {
4495 prop = tmp;
4496 break;
4497 }
4498 }
4499 }
4500 if (prop != NULL) {
4501 value = xmlNodeListGetString(prop->doc, prop->children, 1);
4502 oldvalue = ctxt->state->value;
4503 ctxt->state->value = value;
4504 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
4505 value = ctxt->state->value;
4506 ctxt->state->value = oldvalue;
4507 if (value != NULL)
4508 xmlFree(value);
4509 if (ret == 0) {
4510 /*
4511 * flag the attribute as processed
4512 */
4513 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004514 ctxt->state->nbAttrLeft--;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004515 }
4516 } else {
4517 ret = -1;
4518 }
4519#ifdef DEBUG
4520 xmlGenericError(xmlGenericErrorContext,
4521 "xmlRelaxNGValidateAttribute(%s): %d\n", define->name, ret);
4522#endif
4523 } else {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004524 for (i = 0;i < ctxt->state->nbAttrs;i++) {
4525 tmp = ctxt->state->attrs[i];
Daniel Veillard144fae12003-02-03 13:17:57 +00004526 if ((tmp != NULL) &&
4527 (xmlRelaxNGAttributeMatch(ctxt, define, tmp) == 1)) {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004528 prop = tmp;
4529 break;
4530 }
4531 }
4532 if (prop != NULL) {
4533 value = xmlNodeListGetString(prop->doc, prop->children, 1);
4534 oldvalue = ctxt->state->value;
4535 ctxt->state->value = value;
4536 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
4537 value = ctxt->state->value;
4538 ctxt->state->value = oldvalue;
4539 if (value != NULL)
4540 xmlFree(value);
4541 if (ret == 0) {
4542 /*
4543 * flag the attribute as processed
4544 */
4545 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004546 ctxt->state->nbAttrLeft--;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004547 }
4548 } else {
4549 ret = -1;
4550 }
4551#ifdef DEBUG
Daniel Veillard144fae12003-02-03 13:17:57 +00004552 if (define->ns != NULL) {
4553 xmlGenericError(xmlGenericErrorContext,
4554 "xmlRelaxNGValidateAttribute(nsName ns = %s): %d\n",
4555 define->ns, ret);
4556 } else {
4557 xmlGenericError(xmlGenericErrorContext,
4558 "xmlRelaxNGValidateAttribute(anyName): %d\n",
4559 ret);
4560 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004561#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00004562 }
4563
4564 return(ret);
4565}
4566
4567/**
4568 * xmlRelaxNGValidateAttributeList:
4569 * @ctxt: a Relax-NG validation context
4570 * @define: the list of definition to verify
4571 *
4572 * Validate the given node against the list of attribute definitions
4573 *
4574 * Returns 0 if the validation succeeded or an error code.
4575 */
4576static int
4577xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt,
4578 xmlRelaxNGDefinePtr defines) {
4579 int ret = 0;
4580 while (defines != NULL) {
4581 if (xmlRelaxNGValidateAttribute(ctxt, defines) != 0)
4582 ret = -1;
4583 defines = defines->next;
4584 }
4585 return(ret);
4586}
4587
4588/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004589 * xmlRelaxNGValidateTryPermutation:
4590 * @ctxt: a Relax-NG validation context
4591 * @groups: the array of groups
4592 * @nbgroups: the number of groups in the array
4593 * @array: the permutation to try
4594 * @len: the size of the set
4595 *
4596 * Try to validate a permutation for the group of definitions.
4597 *
4598 * Returns 0 if the validation succeeded or an error code.
4599 */
4600static int
4601xmlRelaxNGValidateTryPermutation(xmlRelaxNGValidCtxtPtr ctxt,
4602 xmlRelaxNGDefinePtr rule,
4603 xmlNodePtr *array, int len) {
4604 int i, ret;
4605
4606 if (len > 0) {
4607 /*
4608 * One only need the next pointer set-up to do the validation
4609 */
4610 for (i = 0;i < (len - 1);i++)
4611 array[i]->next = array[i + 1];
4612 array[i]->next = NULL;
4613
4614 /*
4615 * Now try to validate the sequence
4616 */
4617 ctxt->state->seq = array[0];
4618 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
4619 } else {
4620 ctxt->state->seq = NULL;
4621 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
4622 }
4623
4624 /*
4625 * the sequence must be fully consumed
4626 */
4627 if (ctxt->state->seq != NULL)
4628 return(-1);
4629
4630 return(ret);
4631}
4632
4633/**
4634 * xmlRelaxNGValidateWalkPermutations:
4635 * @ctxt: a Relax-NG validation context
4636 * @groups: the array of groups
4637 * @nbgroups: the number of groups in the array
4638 * @nodes: the set of nodes
4639 * @array: the current state of the parmutation
4640 * @len: the size of the set
4641 * @level: a pointer to the level variable
4642 * @k: the index in the array to fill
4643 *
4644 * Validate a set of nodes for a groups of definitions, will try the
4645 * full set of permutations
4646 *
4647 * Returns 0 if the validation succeeded or an error code.
4648 */
4649static int
4650xmlRelaxNGValidateWalkPermutations(xmlRelaxNGValidCtxtPtr ctxt,
4651 xmlRelaxNGDefinePtr rule, xmlNodePtr *nodes,
4652 xmlNodePtr *array, int len,
4653 int *level, int k) {
4654 int i, ret;
4655
4656 if ((k >= 0) && (k < len))
4657 array[k] = nodes[*level];
4658 *level = *level + 1;
4659 if (*level == len) {
4660 ret = xmlRelaxNGValidateTryPermutation(ctxt, rule, array, len);
4661 if (ret == 0)
4662 return(0);
4663 } else {
4664 for (i = 0;i < len;i++) {
4665 if (array[i] == NULL) {
4666 ret = xmlRelaxNGValidateWalkPermutations(ctxt, rule,
4667 nodes, array, len, level, i);
4668 if (ret == 0)
4669 return(0);
4670 }
4671 }
4672 }
4673 *level = *level - 1;
4674 array[k] = NULL;
4675 return(-1);
4676}
4677
4678/**
4679 * xmlRelaxNGNodeMatchesList:
4680 * @node: the node
4681 * @list: a NULL terminated array of definitions
4682 *
4683 * Check if a node can be matched by one of the definitions
4684 *
4685 * Returns 1 if matches 0 otherwise
4686 */
4687static int
4688xmlRelaxNGNodeMatchesList(xmlNodePtr node, xmlRelaxNGDefinePtr *list) {
4689 xmlRelaxNGDefinePtr cur;
4690 int i = 0;
4691
4692 if ((node == NULL) || (list == NULL))
4693 return(0);
4694
4695 cur = list[i++];
4696 while (cur != NULL) {
4697 if ((node->type == XML_ELEMENT_NODE) &&
4698 (cur->type == XML_RELAXNG_ELEMENT)) {
4699 if (cur->name == NULL) {
4700 if ((node->ns != NULL) &&
4701 (xmlStrEqual(node->ns->href, cur->ns)))
4702 return(1);
4703 } else if (xmlStrEqual(cur->name, node->name)) {
4704 if ((cur->ns == NULL) || (cur->ns[0] == 0)) {
4705 if (node->ns == NULL)
4706 return(1);
4707 } else {
4708 if ((node->ns != NULL) &&
4709 (xmlStrEqual(node->ns->href, cur->ns)))
4710 return(1);
4711 }
4712 }
4713 } else if ((node->type == XML_TEXT_NODE) &&
4714 (cur->type == XML_RELAXNG_TEXT)) {
4715 return(1);
4716 }
4717 cur = list[i++];
4718 }
4719 return(0);
4720}
4721
4722/**
4723 * xmlRelaxNGValidatePartGroup:
4724 * @ctxt: a Relax-NG validation context
4725 * @groups: the array of groups
4726 * @nbgroups: the number of groups in the array
4727 * @nodes: the set of nodes
4728 * @len: the size of the set of nodes
4729 *
4730 * Validate a set of nodes for a groups of definitions
4731 *
4732 * Returns 0 if the validation succeeded or an error code.
4733 */
4734static int
4735xmlRelaxNGValidatePartGroup(xmlRelaxNGValidCtxtPtr ctxt,
4736 xmlRelaxNGInterleaveGroupPtr *groups,
4737 int nbgroups, xmlNodePtr *nodes, int len) {
Daniel Veillardb08c9812003-01-28 23:09:49 +00004738 int level, ret = -1, i, j, k;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004739 xmlNodePtr *array = NULL, *list, oldseq;
4740 xmlRelaxNGInterleaveGroupPtr group;
4741
4742 list = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
4743 if (list == NULL) {
4744 return(-1);
4745 }
4746 array = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
4747 if (array == NULL) {
4748 xmlFree(list);
4749 return(-1);
4750 }
4751 memset(array, 0, len * sizeof(xmlNodePtr));
4752
4753 /*
4754 * Partition the elements and validate the subsets.
4755 */
4756 oldseq = ctxt->state->seq;
4757 for (i = 0;i < nbgroups;i++) {
4758 group = groups[i];
4759 if (group == NULL)
4760 continue;
4761 k = 0;
4762 for (j = 0;j < len;j++) {
4763 if (nodes[j] == NULL)
4764 continue;
4765 if (xmlRelaxNGNodeMatchesList(nodes[j], group->defs)) {
4766 list[k++] = nodes[j];
4767 nodes[j] = NULL;
4768 }
4769 }
4770 ctxt->state->seq = oldseq;
4771 if (k > 1) {
4772 memset(array, 0, k * sizeof(xmlNodePtr));
Daniel Veillardb08c9812003-01-28 23:09:49 +00004773 level = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004774 ret = xmlRelaxNGValidateWalkPermutations(ctxt, group->rule,
4775 list, array, k, &level, -1);
4776 } else {
4777 ret = xmlRelaxNGValidateTryPermutation(ctxt, group->rule, list, k);
4778 }
4779 if (ret != 0) {
4780 ctxt->state->seq = oldseq;
4781 break;
4782 }
4783 }
4784
4785 xmlFree(list);
4786 xmlFree(array);
4787 return(ret);
4788}
4789
4790/**
4791 * xmlRelaxNGValidateInterleave:
4792 * @ctxt: a Relax-NG validation context
4793 * @define: the definition to verify
4794 *
4795 * Validate an interleave definition for a node.
4796 *
4797 * Returns 0 if the validation succeeded or an error code.
4798 */
4799static int
4800xmlRelaxNGValidateInterleave(xmlRelaxNGValidCtxtPtr ctxt,
4801 xmlRelaxNGDefinePtr define) {
4802 int ret = 0, nbchildren, nbtot, i, j;
4803 xmlRelaxNGPartitionPtr partitions;
4804 xmlNodePtr *children = NULL;
4805 xmlNodePtr *order = NULL;
4806 xmlNodePtr cur;
4807
4808 if (define->data != NULL) {
4809 partitions = (xmlRelaxNGPartitionPtr) define->data;
4810 } else {
4811 VALID_CTXT();
4812 VALID_ERROR("Internal: interleave block has no data\n");
4813 return(-1);
4814 }
4815
4816 /*
4817 * Build the sequence of child and an array preserving the children
4818 * initial order.
4819 */
4820 cur = ctxt->state->seq;
4821 nbchildren = 0;
4822 nbtot = 0;
4823 while (cur != NULL) {
4824 if ((cur->type == XML_COMMENT_NODE) ||
4825 (cur->type == XML_PI_NODE) ||
4826 ((cur->type == XML_TEXT_NODE) &&
4827 (IS_BLANK_NODE(cur)))) {
4828 nbtot++;
4829 } else {
4830 nbchildren++;
4831 nbtot++;
4832 }
4833 cur = cur->next;
4834 }
4835 children = (xmlNodePtr *) xmlMalloc(nbchildren * sizeof(xmlNodePtr));
4836 if (children == NULL)
4837 goto error;
4838 order = (xmlNodePtr *) xmlMalloc(nbtot * sizeof(xmlNodePtr));
4839 if (order == NULL)
4840 goto error;
4841 cur = ctxt->state->seq;
4842 i = 0;
4843 j = 0;
4844 while (cur != NULL) {
4845 if ((cur->type == XML_COMMENT_NODE) ||
4846 (cur->type == XML_PI_NODE) ||
4847 ((cur->type == XML_TEXT_NODE) &&
4848 (IS_BLANK_NODE(cur)))) {
4849 order[j++] = cur;
4850 } else {
4851 order[j++] = cur;
4852 children[i++] = cur;
4853 }
4854 cur = cur->next;
4855 }
4856
4857 /* TODO: retry with a maller set of child if there is a next... */
4858 ret = xmlRelaxNGValidatePartGroup(ctxt, partitions->groups,
4859 partitions->nbgroups, children, nbchildren);
4860 if (ret == 0) {
4861 ctxt->state->seq = NULL;
4862 }
4863
4864 /*
4865 * Cleanup: rebuid the child sequence and free the structure
4866 */
4867 if (order != NULL) {
4868 for (i = 0;i < nbtot;i++) {
4869 if (i == 0)
4870 order[i]->prev = NULL;
4871 else
4872 order[i]->prev = order[i - 1];
4873 if (i == nbtot - 1)
4874 order[i]->next = NULL;
4875 else
4876 order[i]->next = order[i + 1];
4877 }
4878 xmlFree(order);
4879 }
4880 if (children != NULL)
4881 xmlFree(children);
4882
4883 return(ret);
4884
4885error:
4886 if (order != NULL) {
4887 for (i = 0;i < nbtot;i++) {
4888 if (i == 0)
4889 order[i]->prev = NULL;
4890 else
4891 order[i]->prev = order[i - 1];
4892 if (i == nbtot - 1)
4893 order[i]->next = NULL;
4894 else
4895 order[i]->next = order[i + 1];
4896 }
4897 xmlFree(order);
4898 }
4899 if (children != NULL)
4900 xmlFree(children);
4901 return(-1);
4902}
4903
4904/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004905 * xmlRelaxNGValidateElementContent:
4906 * @ctxt: a Relax-NG validation context
4907 * @define: the list of definition to verify
4908 *
4909 * Validate the given node content against the (list) of definitions
4910 *
4911 * Returns 0 if the validation succeeded or an error code.
4912 */
4913static int
4914xmlRelaxNGValidateElementContent(xmlRelaxNGValidCtxtPtr ctxt,
4915 xmlRelaxNGDefinePtr defines) {
4916 int ret = 0, res;
4917
4918 if (ctxt->state == NULL) {
4919 VALID_CTXT();
4920 VALID_ERROR("Internal: no state\n");
4921 return(-1);
4922 }
4923 while (defines != NULL) {
4924 res = xmlRelaxNGValidateDefinition(ctxt, defines);
4925 if (res < 0)
4926 ret = -1;
4927 defines = defines->next;
4928 }
4929
4930 return(ret);
4931}
4932
4933/**
4934 * xmlRelaxNGValidateDefinition:
4935 * @ctxt: a Relax-NG validation context
4936 * @define: the definition to verify
4937 *
4938 * Validate the current node against the definition
4939 *
4940 * Returns 0 if the validation succeeded or an error code.
4941 */
4942static int
4943xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
4944 xmlRelaxNGDefinePtr define) {
4945 xmlNodePtr node;
4946 int ret = 0, i, tmp, oldflags;
4947 xmlRelaxNGValidStatePtr oldstate, state;
4948
4949 if (define == NULL) {
4950 VALID_CTXT();
4951 VALID_ERROR("internal error: define == NULL\n");
4952 return(-1);
4953 }
4954 if (ctxt->state != NULL) {
4955 node = ctxt->state->seq;
4956 } else {
4957 node = NULL;
4958 }
4959 switch (define->type) {
4960 case XML_RELAXNG_EMPTY:
4961 if (node != NULL) {
4962 VALID_CTXT();
4963 VALID_ERROR("Expecting an empty element\n");
4964 return(-1);
4965 }
4966#ifdef DEBUG
4967 xmlGenericError(xmlGenericErrorContext,
4968 "xmlRelaxNGValidateDefinition(): validated empty\n");
4969#endif
4970 return(0);
4971 case XML_RELAXNG_NOT_ALLOWED:
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004972 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004973 case XML_RELAXNG_TEXT:
4974 if (node == NULL)
4975 return(0);
4976 while ((node != NULL) &&
4977 ((node->type == XML_TEXT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004978 (node->type == XML_COMMENT_NODE) ||
4979 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00004980 (node->type == XML_CDATA_SECTION_NODE)))
4981 node = node->next;
Daniel Veillard276be4a2003-01-24 01:03:34 +00004982 if (node == ctxt->state->seq) {
4983 VALID_CTXT();
4984 VALID_ERROR("Expecting text content\n");
4985 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004986 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00004987 ctxt->state->seq = node;
4988 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004989 case XML_RELAXNG_ELEMENT:
4990 node = xmlRelaxNGSkipIgnored(ctxt, node);
4991 if ((node == NULL) || (node->type != XML_ELEMENT_NODE)) {
4992 VALID_CTXT();
4993 VALID_ERROR("Expecting an element\n");
4994 return(-1);
4995 }
4996 if (define->name != NULL) {
4997 if (!xmlStrEqual(node->name, define->name)) {
4998 VALID_CTXT();
4999 VALID_ERROR("Expecting element %s, got %s\n",
5000 define->name, node->name);
5001 return(-1);
5002 }
5003 }
5004 if ((define->ns != NULL) && (define->ns[0] != 0)) {
5005 if (node->ns == NULL) {
5006 VALID_CTXT();
5007 VALID_ERROR("Expecting a namespace for element %s\n",
5008 node->name);
5009 return(-1);
5010 } else if (!xmlStrEqual(node->ns->href, define->ns)) {
5011 VALID_CTXT();
5012 VALID_ERROR("Expecting element %s has wrong namespace: expecting %s\n",
5013 node->name, define->ns);
5014 return(-1);
5015 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005016 } else if (define->name != NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005017 if (node->ns != NULL) {
5018 VALID_CTXT();
5019 VALID_ERROR("Expecting no namespace for element %s\n",
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005020 define->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005021 return(-1);
5022 }
5023 }
5024
5025 state = xmlRelaxNGNewValidState(ctxt, node);
5026 if (state == NULL) {
5027 return(-1);
5028 }
5029
5030 oldstate = ctxt->state;
5031 ctxt->state = state;
5032 if (define->attrs != NULL) {
5033 tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
5034 if (tmp != 0)
5035 ret = -1;
5036 }
5037 if (define->content != NULL) {
5038 tmp = xmlRelaxNGValidateElementContent(ctxt, define->content);
5039 if (tmp != 0)
5040 ret = -1;
5041 }
5042 state = ctxt->state;
5043 if (state->seq != NULL) {
5044 state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq);
5045 if (state->seq != NULL) {
5046 VALID_CTXT();
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005047 VALID_ERROR("Extra content for element %s: %s\n",
5048 node->name, state->seq->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005049 ret = -1;
5050 }
5051 }
5052 for (i = 0;i < state->nbAttrs;i++) {
5053 if (state->attrs[i] != NULL) {
5054 VALID_CTXT();
Daniel Veillardea3f3982003-01-26 19:45:18 +00005055 VALID_ERROR("Invalid attribute %s for element %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00005056 state->attrs[i]->name, node->name);
5057 ret = -1;
5058 }
5059 }
5060 ctxt->state = oldstate;
5061 xmlRelaxNGFreeValidState(state);
5062 if (oldstate != NULL)
5063 oldstate->seq = node->next;
5064
5065
5066#ifdef DEBUG
5067 xmlGenericError(xmlGenericErrorContext,
5068 "xmlRelaxNGValidateDefinition(): validated %s : %d\n",
5069 node->name, ret);
5070#endif
5071 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005072 case XML_RELAXNG_OPTIONAL:
5073 oldflags = ctxt->flags;
5074 ctxt->flags |= FLAGS_IGNORABLE;
5075 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5076 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5077 if (ret != 0) {
5078 xmlRelaxNGFreeValidState(ctxt->state);
5079 ctxt->state = oldstate;
5080 ret = 0;
5081 break;
5082 }
5083 xmlRelaxNGFreeValidState(oldstate);
5084 ctxt->flags = oldflags;
5085 ret = 0;
5086 break;
5087 case XML_RELAXNG_ONEORMORE:
5088 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5089 if (ret != 0) {
5090 break;
5091 }
5092 /* no break on purpose */
Daniel Veillard276be4a2003-01-24 01:03:34 +00005093 case XML_RELAXNG_ZEROORMORE: {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005094 oldflags = ctxt->flags;
5095 ctxt->flags |= FLAGS_IGNORABLE;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005096 while (ctxt->state->nbAttrLeft != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005097 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5098 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5099 if (ret != 0) {
5100 xmlRelaxNGFreeValidState(ctxt->state);
5101 ctxt->state = oldstate;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005102 break;
5103 }
5104 xmlRelaxNGFreeValidState(oldstate);
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005105 }
5106 if (ret == 0) {
5107 /*
5108 * There is no attribute left to be consumed,
5109 * we can check the closure by looking at ctxt->state->seq
5110 */
5111 xmlNodePtr cur, temp;
5112
Daniel Veillard276be4a2003-01-24 01:03:34 +00005113 cur = ctxt->state->seq;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005114 temp = NULL;
5115 while ((cur != NULL) && (temp != cur)) {
5116 temp = cur;
5117 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5118 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5119 if (ret != 0) {
5120 xmlRelaxNGFreeValidState(ctxt->state);
5121 ctxt->state = oldstate;
5122 break;
5123 }
5124 xmlRelaxNGFreeValidState(oldstate);
5125 cur = ctxt->state->seq;
5126 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005127 }
5128 ctxt->flags = oldflags;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005129 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005130 break;
Daniel Veillard276be4a2003-01-24 01:03:34 +00005131 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005132 case XML_RELAXNG_CHOICE: {
5133 xmlRelaxNGDefinePtr list = define->content;
5134
5135 oldflags = ctxt->flags;
5136 ctxt->flags |= FLAGS_IGNORABLE;
5137
5138 while (list != NULL) {
5139 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5140 ret = xmlRelaxNGValidateDefinition(ctxt, list);
5141 if (ret == 0) {
5142 xmlRelaxNGFreeValidState(oldstate);
5143 break;
5144 }
5145 xmlRelaxNGFreeValidState(ctxt->state);
5146 ctxt->state = oldstate;
5147 list = list->next;
5148 }
5149 ctxt->flags = oldflags;
5150 break;
5151 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005152 case XML_RELAXNG_DEF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00005153 case XML_RELAXNG_GROUP: {
5154 xmlRelaxNGDefinePtr list = define->content;
5155
5156 while (list != NULL) {
5157 ret = xmlRelaxNGValidateDefinition(ctxt, list);
5158 if (ret != 0)
5159 break;
5160 list = list->next;
5161 }
5162 break;
5163 }
5164 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005165 ret = xmlRelaxNGValidateInterleave(ctxt, define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005166 break;
5167 case XML_RELAXNG_ATTRIBUTE:
5168 ret = xmlRelaxNGValidateAttribute(ctxt, define);
5169 break;
5170 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00005171 case XML_RELAXNG_PARENTREF:
5172 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00005173 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5174 break;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005175 case XML_RELAXNG_DATATYPE: {
5176 xmlChar *content;
5177
5178 content = xmlNodeGetContent(node);
5179 ret = xmlRelaxNGValidateDatatype(ctxt, content, define);
5180 if (ret == -1) {
5181 VALID_CTXT();
5182 VALID_ERROR("internal error validating %s\n", define->name);
5183 } else if (ret == 0) {
5184 ctxt->state->seq = node->next;
5185 }
5186 /*
5187 * TODO cover the problems with
5188 * <p>12<!-- comment -->34</p>
5189 * TODO detect full element coverage at compilation time.
5190 */
5191 if ((node != NULL) && (node->next != NULL)) {
5192 VALID_CTXT();
5193 VALID_ERROR("The data does not cover the full element %s\n",
5194 node->parent->name);
5195 ret = -1;
5196 }
5197 if (content != NULL)
5198 xmlFree(content);
5199 break;
5200 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00005201 case XML_RELAXNG_VALUE: {
5202 xmlChar *content;
5203 xmlChar *oldvalue;
5204
5205 content = xmlNodeGetContent(node);
5206 oldvalue = ctxt->state->value;
5207 ctxt->state->value = content;
5208 ret = xmlRelaxNGValidateValue(ctxt, define);
5209 ctxt->state->value = oldvalue;
5210 if (ret == -1) {
5211 VALID_CTXT();
5212 VALID_ERROR("internal error validating %s\n", define->name);
5213 } else if (ret == 0) {
5214 ctxt->state->seq = node->next;
5215 }
5216 /*
5217 * TODO cover the problems with
5218 * <p>12<!-- comment -->34</p>
5219 * TODO detect full element coverage at compilation time.
5220 */
5221 if ((node != NULL) && (node->next != NULL)) {
5222 VALID_CTXT();
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005223 VALID_ERROR("The value does not cover the full element %s\n",
Daniel Veillardea3f3982003-01-26 19:45:18 +00005224 node->parent->name);
5225 ret = -1;
5226 }
5227 if (content != NULL)
5228 xmlFree(content);
5229 break;
5230 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005231 case XML_RELAXNG_LIST: {
5232 xmlChar *content;
5233 xmlChar *oldvalue, *oldendvalue;
5234 int len;
Daniel Veillardea3f3982003-01-26 19:45:18 +00005235
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005236 content = xmlNodeGetContent(node);
5237 len = xmlStrlen(content);
5238 oldvalue = ctxt->state->value;
5239 oldendvalue = ctxt->state->endvalue;
5240 ctxt->state->value = content;
5241 ctxt->state->endvalue = content + len;
5242 ret = xmlRelaxNGValidateValue(ctxt, define);
5243 ctxt->state->value = oldvalue;
5244 ctxt->state->endvalue = oldendvalue;
5245 if (ret == -1) {
5246 VALID_CTXT();
5247 VALID_ERROR("internal error validating list\n");
5248 } else if (ret == 0) {
5249 ctxt->state->seq = node->next;
5250 }
5251 /*
5252 * TODO cover the problems with
5253 * <p>12<!-- comment -->34</p>
5254 * TODO detect full element coverage at compilation time.
5255 */
5256 if ((node != NULL) && (node->next != NULL)) {
5257 VALID_CTXT();
5258 VALID_ERROR("The list does not cover the full element %s\n",
5259 node->parent->name);
5260 ret = -1;
5261 }
5262 if (content != NULL)
5263 xmlFree(content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005264 break;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005265 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005266 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00005267 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005268 TODO
5269 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005270 }
5271 return(ret);
5272}
5273
5274/**
5275 * xmlRelaxNGValidateDocument:
5276 * @ctxt: a Relax-NG validation context
5277 * @doc: the document
5278 *
5279 * Validate the given document
5280 *
5281 * Returns 0 if the validation succeeded or an error code.
5282 */
5283static int
5284xmlRelaxNGValidateDocument(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
5285 int ret;
5286 xmlRelaxNGPtr schema;
5287 xmlRelaxNGGrammarPtr grammar;
5288 xmlRelaxNGValidStatePtr state;
5289
5290 if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL))
5291 return(-1);
5292
5293 schema = ctxt->schema;
5294 grammar = schema->topgrammar;
5295 if (grammar == NULL) {
5296 VALID_CTXT();
5297 VALID_ERROR("No top grammar defined\n");
5298 return(-1);
5299 }
5300 state = xmlRelaxNGNewValidState(ctxt, NULL);
5301 ctxt->state = state;
5302 ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start);
5303 state = ctxt->state;
5304 if ((state != NULL) && (state->seq != NULL)) {
5305 xmlNodePtr node;
5306
5307 node = state->seq;
5308 node = xmlRelaxNGSkipIgnored(ctxt, node);
5309 if (node != NULL) {
5310 VALID_CTXT();
5311 VALID_ERROR("extra data on the document\n");
5312 ret = -1;
5313 }
5314 }
5315 xmlRelaxNGFreeValidState(state);
5316
5317 return(ret);
5318}
5319
5320/************************************************************************
5321 * *
5322 * Validation interfaces *
5323 * *
5324 ************************************************************************/
5325/**
5326 * xmlRelaxNGNewValidCtxt:
5327 * @schema: a precompiled XML RelaxNGs
5328 *
5329 * Create an XML RelaxNGs validation context based on the given schema
5330 *
5331 * Returns the validation context or NULL in case of error
5332 */
5333xmlRelaxNGValidCtxtPtr
5334xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema) {
5335 xmlRelaxNGValidCtxtPtr ret;
5336
5337 ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt));
5338 if (ret == NULL) {
5339 xmlGenericError(xmlGenericErrorContext,
5340 "Failed to allocate new schama validation context\n");
5341 return (NULL);
5342 }
5343 memset(ret, 0, sizeof(xmlRelaxNGValidCtxt));
5344 ret->schema = schema;
5345 return (ret);
5346}
5347
5348/**
5349 * xmlRelaxNGFreeValidCtxt:
5350 * @ctxt: the schema validation context
5351 *
5352 * Free the resources associated to the schema validation context
5353 */
5354void
5355xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt) {
5356 if (ctxt == NULL)
5357 return;
5358 xmlFree(ctxt);
5359}
5360
5361/**
5362 * xmlRelaxNGSetValidErrors:
5363 * @ctxt: a Relax-NG validation context
5364 * @err: the error function
5365 * @warn: the warning function
5366 * @ctx: the functions context
5367 *
5368 * Set the error and warning callback informations
5369 */
5370void
5371xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
5372 xmlRelaxNGValidityErrorFunc err,
5373 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
5374 if (ctxt == NULL)
5375 return;
5376 ctxt->error = err;
5377 ctxt->warning = warn;
5378 ctxt->userData = ctx;
5379}
5380
5381/**
5382 * xmlRelaxNGValidateDoc:
5383 * @ctxt: a Relax-NG validation context
5384 * @doc: a parsed document tree
5385 *
5386 * Validate a document tree in memory.
5387 *
5388 * Returns 0 if the document is valid, a positive error code
5389 * number otherwise and -1 in case of internal or API error.
5390 */
5391int
5392xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
5393 int ret;
5394
5395 if ((ctxt == NULL) || (doc == NULL))
5396 return(-1);
5397
5398 ctxt->doc = doc;
5399
5400 ret = xmlRelaxNGValidateDocument(ctxt, doc);
5401 return(ret);
5402}
5403
5404#endif /* LIBXML_SCHEMAS_ENABLED */
5405