blob: 3676ec6e4551b212060fab421a50e1ce59efcbb8 [file] [log] [blame]
Daniel Veillard6eadf632003-01-23 18:29:16 +00001/*
2 * relaxng.c : implementation of the Relax-NG handling and validity checking
3 *
4 * See Copyright for the status of this software.
5 *
6 * Daniel Veillard <veillard@redhat.com>
7 */
8
Daniel Veillardd41f4f42003-01-29 21:07:52 +00009/**
10 * TODO:
Daniel Veillardd41f4f42003-01-29 21:07:52 +000011 * - error reporting
Daniel Veillarde2a5a082003-02-02 14:35:17 +000012 * - simplification of the resulting compiled trees:
13 * - NOT_ALLOWED
14 * - EMPTY
Daniel Veillard1ed7f362003-02-03 10:57:45 +000015 * - handle namespace declarations as attributes.
Daniel Veillardd41f4f42003-01-29 21:07:52 +000016 */
17
Daniel Veillard6eadf632003-01-23 18:29:16 +000018#define IN_LIBXML
19#include "libxml.h"
20
21#ifdef LIBXML_SCHEMAS_ENABLED
22
23#include <string.h>
24#include <stdio.h>
25#include <libxml/xmlmemory.h>
26#include <libxml/parser.h>
27#include <libxml/parserInternals.h>
28#include <libxml/hash.h>
29#include <libxml/uri.h>
30
31#include <libxml/relaxng.h>
32
33#include <libxml/xmlschemastypes.h>
34#include <libxml/xmlautomata.h>
35#include <libxml/xmlregexp.h>
Daniel Veillardc6e997c2003-01-27 12:35:42 +000036#include <libxml/xmlschemastypes.h>
Daniel Veillard6eadf632003-01-23 18:29:16 +000037
38/*
39 * The Relax-NG namespace
40 */
41static const xmlChar *xmlRelaxNGNs = (const xmlChar *)
42 "http://relaxng.org/ns/structure/1.0";
43
44#define IS_RELAXNG(node, type) \
45 ((node != NULL) && (node->ns != NULL) && \
46 (xmlStrEqual(node->name, (const xmlChar *) type)) && \
47 (xmlStrEqual(node->ns->href, xmlRelaxNGNs)))
48
49
Daniel Veillard71531f32003-02-05 13:19:53 +000050/* #define DEBUG 1 */ /* very verbose output */
51/* #define DEBUG_CONTENT 1 */
52/* #define DEBUG_TYPE 1 */
53/* #define DEBUG_VALID 1 */
Daniel Veillarde5b110b2003-02-04 14:43:39 +000054/* #define DEBUG_INTERLEAVE 1 */
Daniel Veillard6eadf632003-01-23 18:29:16 +000055
56#define UNBOUNDED (1 << 30)
57#define TODO \
58 xmlGenericError(xmlGenericErrorContext, \
59 "Unimplemented block at %s:%d\n", \
60 __FILE__, __LINE__);
61
62typedef struct _xmlRelaxNGSchema xmlRelaxNGSchema;
63typedef xmlRelaxNGSchema *xmlRelaxNGSchemaPtr;
64
65typedef struct _xmlRelaxNGDefine xmlRelaxNGDefine;
66typedef xmlRelaxNGDefine *xmlRelaxNGDefinePtr;
67
Daniel Veillardd41f4f42003-01-29 21:07:52 +000068typedef struct _xmlRelaxNGDocument xmlRelaxNGDocument;
69typedef xmlRelaxNGDocument *xmlRelaxNGDocumentPtr;
70
Daniel Veillarda9d912d2003-02-01 17:43:10 +000071typedef struct _xmlRelaxNGInclude xmlRelaxNGInclude;
72typedef xmlRelaxNGInclude *xmlRelaxNGIncludePtr;
73
Daniel Veillard6eadf632003-01-23 18:29:16 +000074typedef enum {
75 XML_RELAXNG_COMBINE_UNDEFINED = 0, /* undefined */
76 XML_RELAXNG_COMBINE_CHOICE, /* choice */
77 XML_RELAXNG_COMBINE_INTERLEAVE /* interleave */
78} xmlRelaxNGCombine;
79
80typedef struct _xmlRelaxNGGrammar xmlRelaxNGGrammar;
81typedef xmlRelaxNGGrammar *xmlRelaxNGGrammarPtr;
82
83struct _xmlRelaxNGGrammar {
84 xmlRelaxNGGrammarPtr parent;/* the parent grammar if any */
85 xmlRelaxNGGrammarPtr children;/* the children grammar if any */
86 xmlRelaxNGGrammarPtr next; /* the next grammar if any */
87 xmlRelaxNGDefinePtr start; /* <start> content */
88 xmlRelaxNGCombine combine; /* the default combine value */
Daniel Veillardd41f4f42003-01-29 21:07:52 +000089 xmlRelaxNGDefinePtr startList;/* list of <start> definitions */
Daniel Veillard6eadf632003-01-23 18:29:16 +000090 xmlHashTablePtr defs; /* define* */
91 xmlHashTablePtr refs; /* references */
92};
93
94
Daniel Veillard6eadf632003-01-23 18:29:16 +000095typedef enum {
96 XML_RELAXNG_EMPTY = 0, /* an empty pattern */
97 XML_RELAXNG_NOT_ALLOWED, /* not allowed top */
Daniel Veillard144fae12003-02-03 13:17:57 +000098 XML_RELAXNG_EXCEPT, /* except present in nameclass defs */
Daniel Veillard6eadf632003-01-23 18:29:16 +000099 XML_RELAXNG_TEXT, /* textual content */
100 XML_RELAXNG_ELEMENT, /* an element */
101 XML_RELAXNG_DATATYPE, /* extenal data type definition */
102 XML_RELAXNG_VALUE, /* value from an extenal data type definition */
103 XML_RELAXNG_LIST, /* a list of patterns */
104 XML_RELAXNG_ATTRIBUTE, /* an attrbute following a pattern */
105 XML_RELAXNG_DEF, /* a definition */
106 XML_RELAXNG_REF, /* reference to a definition */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000107 XML_RELAXNG_EXTERNALREF, /* reference to an external def */
Daniel Veillard419a7682003-02-03 23:22:49 +0000108 XML_RELAXNG_PARENTREF, /* reference to a def in the parent grammar */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000109 XML_RELAXNG_OPTIONAL, /* optional patterns */
110 XML_RELAXNG_ZEROORMORE, /* zero or more non empty patterns */
111 XML_RELAXNG_ONEORMORE, /* one or more non empty patterns */
112 XML_RELAXNG_CHOICE, /* a choice between non empty patterns */
113 XML_RELAXNG_GROUP, /* a pair/group of non empty patterns */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000114 XML_RELAXNG_INTERLEAVE, /* interleaving choice of non-empty patterns */
115 XML_RELAXNG_START /* Used to keep track of starts on grammars */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000116} xmlRelaxNGType;
117
118struct _xmlRelaxNGDefine {
119 xmlRelaxNGType type; /* the type of definition */
120 xmlNodePtr node; /* the node in the source */
121 xmlChar *name; /* the element local name if present */
122 xmlChar *ns; /* the namespace local name if present */
Daniel Veillardedc91922003-01-26 00:52:04 +0000123 xmlChar *value; /* value when available */
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000124 void *data; /* data lib or specific pointer */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000125 xmlRelaxNGDefinePtr content;/* the expected content */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000126 xmlRelaxNGDefinePtr parent; /* the parent definition, if any */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000127 xmlRelaxNGDefinePtr next; /* list within grouping sequences */
128 xmlRelaxNGDefinePtr attrs; /* list of attributes for elements */
Daniel Veillard3b2e4e12003-02-03 08:52:58 +0000129 xmlRelaxNGDefinePtr nameClass;/* the nameClass definition if any */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000130 xmlRelaxNGDefinePtr nextHash;/* next define in defs/refs hash tables */
131};
132
133/**
134 * _xmlRelaxNG:
135 *
136 * A RelaxNGs definition
137 */
138struct _xmlRelaxNG {
139 xmlRelaxNGGrammarPtr topgrammar;
140 xmlDocPtr doc;
141
142 xmlHashTablePtr defs; /* define */
143 xmlHashTablePtr refs; /* references */
Daniel Veillard419a7682003-02-03 23:22:49 +0000144 xmlHashTablePtr documents; /* all the documents loaded */
145 xmlHashTablePtr includes; /* all the includes loaded */
146 int defNr; /* number of defines used */
147 xmlRelaxNGDefinePtr *defTab;/* pointer to the allocated definitions */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000148 void *_private; /* unused by the library for users or bindings */
149};
150
151typedef enum {
152 XML_RELAXNG_ERR_OK = 0,
153 XML_RELAXNG_ERR_NOROOT = 1,
154 XML_RELAXNG_ERR_
155} xmlRelaxNGValidError;
156
157#define XML_RELAXNG_IN_ATTRIBUTE 1
158
159struct _xmlRelaxNGParserCtxt {
160 void *userData; /* user specific data block */
161 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
162 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
163 xmlRelaxNGValidError err;
164
165 xmlRelaxNGPtr schema; /* The schema in use */
166 xmlRelaxNGGrammarPtr grammar; /* the current grammar */
Daniel Veillard419a7682003-02-03 23:22:49 +0000167 xmlRelaxNGGrammarPtr parentgrammar;/* the parent grammar */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000168 int flags; /* parser flags */
169 int nbErrors; /* number of errors at parse time */
170 int nbWarnings; /* number of warnings at parse time */
Daniel Veillard276be4a2003-01-24 01:03:34 +0000171 const xmlChar *define; /* the current define scope */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000172 xmlRelaxNGDefinePtr def; /* the current define */
173
174 int nbInterleaves;
175 xmlHashTablePtr interleaves; /* keep track of all the interleaves */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000176
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000177 xmlHashTablePtr documents; /* all the documents loaded */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000178 xmlHashTablePtr includes; /* all the includes loaded */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000179 xmlChar *URL;
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000180 xmlDocPtr document;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000181
Daniel Veillard419a7682003-02-03 23:22:49 +0000182 int defNr; /* number of defines used */
183 int defMax; /* number of defines aloocated */
184 xmlRelaxNGDefinePtr *defTab; /* pointer to the allocated definitions */
185
Daniel Veillard6eadf632003-01-23 18:29:16 +0000186 const char *buffer;
187 int size;
188
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000189 /* the document stack */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000190 xmlRelaxNGDocumentPtr doc; /* Current parsed external ref */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000191 int docNr; /* Depth of the parsing stack */
192 int docMax; /* Max depth of the parsing stack */
193 xmlRelaxNGDocumentPtr *docTab; /* array of docs */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000194
195 /* the include stack */
196 xmlRelaxNGIncludePtr inc; /* Current parsed include */
197 int incNr; /* Depth of the include parsing stack */
198 int incMax; /* Max depth of the parsing stack */
199 xmlRelaxNGIncludePtr *incTab; /* array of incs */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000200};
201
202#define FLAGS_IGNORABLE 1
203#define FLAGS_NEGATIVE 2
204
205/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000206 * xmlRelaxNGInterleaveGroup:
207 *
208 * A RelaxNGs partition set associated to lists of definitions
209 */
210typedef struct _xmlRelaxNGInterleaveGroup xmlRelaxNGInterleaveGroup;
211typedef xmlRelaxNGInterleaveGroup *xmlRelaxNGInterleaveGroupPtr;
212struct _xmlRelaxNGInterleaveGroup {
213 xmlRelaxNGDefinePtr rule; /* the rule to satisfy */
214 xmlRelaxNGDefinePtr *defs; /* the array of element definitions */
215};
216
217/**
218 * xmlRelaxNGPartitions:
219 *
220 * A RelaxNGs partition associated to an interleave group
221 */
222typedef struct _xmlRelaxNGPartition xmlRelaxNGPartition;
223typedef xmlRelaxNGPartition *xmlRelaxNGPartitionPtr;
224struct _xmlRelaxNGPartition {
225 int nbgroups; /* number of groups in the partitions */
226 xmlRelaxNGInterleaveGroupPtr *groups;
227};
228
229/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000230 * xmlRelaxNGValidState:
231 *
232 * A RelaxNGs validation state
233 */
234#define MAX_ATTR 20
235typedef struct _xmlRelaxNGValidState xmlRelaxNGValidState;
236typedef xmlRelaxNGValidState *xmlRelaxNGValidStatePtr;
237struct _xmlRelaxNGValidState {
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000238 xmlNodePtr node; /* the current node */
239 xmlNodePtr seq; /* the sequence of children left to validate */
240 int nbAttrs; /* the number of attributes */
Daniel Veillard1ed7f362003-02-03 10:57:45 +0000241 int nbAttrLeft; /* the number of attributes left to validate */
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000242 xmlChar *value; /* the value when operating on string */
243 xmlChar *endvalue; /* the end value when operating on string */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000244 xmlAttrPtr attrs[1]; /* the array of attributes */
245};
246
247/**
248 * xmlRelaxNGValidCtxt:
249 *
250 * A RelaxNGs validation context
251 */
252
253struct _xmlRelaxNGValidCtxt {
254 void *userData; /* user specific data block */
255 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
256 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
257
258 xmlRelaxNGPtr schema; /* The schema in use */
259 xmlDocPtr doc; /* the document being validated */
260 xmlRelaxNGValidStatePtr state; /* the current validation state */
261 int flags; /* validation flags */
Daniel Veillard231d7912003-02-09 14:22:17 +0000262 int depth; /* validation depth */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000263};
264
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000265/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000266 * xmlRelaxNGInclude:
267 *
268 * Structure associated to a RelaxNGs document element
269 */
270struct _xmlRelaxNGInclude {
271 xmlChar *href; /* the normalized href value */
272 xmlDocPtr doc; /* the associated XML document */
273 xmlRelaxNGDefinePtr content;/* the definitions */
274 xmlRelaxNGPtr schema; /* the schema */
275};
276
277/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000278 * xmlRelaxNGDocument:
279 *
280 * Structure associated to a RelaxNGs document element
281 */
282struct _xmlRelaxNGDocument {
283 xmlChar *href; /* the normalized href value */
284 xmlDocPtr doc; /* the associated XML document */
285 xmlRelaxNGDefinePtr content;/* the definitions */
286 xmlRelaxNGPtr schema; /* the schema */
287};
288
Daniel Veillard6eadf632003-01-23 18:29:16 +0000289/************************************************************************
290 * *
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000291 * Preliminary type checking interfaces *
292 * *
293 ************************************************************************/
294/**
295 * xmlRelaxNGTypeHave:
296 * @data: data needed for the library
297 * @type: the type name
298 * @value: the value to check
299 *
300 * Function provided by a type library to check if a type is exported
301 *
302 * Returns 1 if yes, 0 if no and -1 in case of error.
303 */
304typedef int (*xmlRelaxNGTypeHave) (void *data, const xmlChar *type);
305
306/**
307 * xmlRelaxNGTypeCheck:
308 * @data: data needed for the library
309 * @type: the type name
310 * @value: the value to check
311 *
312 * Function provided by a type library to check if a value match a type
313 *
314 * Returns 1 if yes, 0 if no and -1 in case of error.
315 */
316typedef int (*xmlRelaxNGTypeCheck) (void *data, const xmlChar *type,
317 const xmlChar *value);
318
319/**
320 * xmlRelaxNGTypeCompare:
321 * @data: data needed for the library
322 * @type: the type name
323 * @value1: the first value
324 * @value2: the second value
325 *
326 * Function provided by a type library to compare two values accordingly
327 * to a type.
328 *
329 * Returns 1 if yes, 0 if no and -1 in case of error.
330 */
331typedef int (*xmlRelaxNGTypeCompare) (void *data, const xmlChar *type,
332 const xmlChar *value1,
333 const xmlChar *value2);
334typedef struct _xmlRelaxNGTypeLibrary xmlRelaxNGTypeLibrary;
335typedef xmlRelaxNGTypeLibrary *xmlRelaxNGTypeLibraryPtr;
336struct _xmlRelaxNGTypeLibrary {
337 const xmlChar *namespace; /* the datatypeLibrary value */
338 void *data; /* data needed for the library */
339 xmlRelaxNGTypeHave have; /* the export function */
340 xmlRelaxNGTypeCheck check; /* the checking function */
341 xmlRelaxNGTypeCompare comp; /* the compare function */
342};
343
344/************************************************************************
345 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +0000346 * Allocation functions *
347 * *
348 ************************************************************************/
Daniel Veillard6eadf632003-01-23 18:29:16 +0000349static void xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar);
350static void xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define);
351
352/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000353 * xmlRelaxNGFreeDocument:
354 * @docu: a document structure
355 *
356 * Deallocate a RelaxNG document structure.
357 */
358static void
359xmlRelaxNGFreeDocument(xmlRelaxNGDocumentPtr docu)
360{
361 if (docu == NULL)
362 return;
363
364 if (docu->href != NULL)
365 xmlFree(docu->href);
366 if (docu->doc != NULL)
367 xmlFreeDoc(docu->doc);
368 if (docu->schema != NULL)
369 xmlRelaxNGFree(docu->schema);
370 xmlFree(docu);
371}
372
373/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000374 * xmlRelaxNGFreeInclude:
375 * @incl: a include structure
376 *
377 * Deallocate a RelaxNG include structure.
378 */
379static void
380xmlRelaxNGFreeInclude(xmlRelaxNGIncludePtr incl)
381{
382 if (incl == NULL)
383 return;
384
385 if (incl->href != NULL)
386 xmlFree(incl->href);
387 if (incl->doc != NULL)
388 xmlFreeDoc(incl->doc);
389 if (incl->schema != NULL)
390 xmlRelaxNGFree(incl->schema);
391 xmlFree(incl);
392}
393
394/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000395 * xmlRelaxNGNewRelaxNG:
396 * @ctxt: a Relax-NG validation context (optional)
397 *
398 * Allocate a new RelaxNG structure.
399 *
400 * Returns the newly allocated structure or NULL in case or error
401 */
402static xmlRelaxNGPtr
403xmlRelaxNGNewRelaxNG(xmlRelaxNGParserCtxtPtr ctxt)
404{
405 xmlRelaxNGPtr ret;
406
407 ret = (xmlRelaxNGPtr) xmlMalloc(sizeof(xmlRelaxNG));
408 if (ret == NULL) {
409 if ((ctxt != NULL) && (ctxt->error != NULL))
410 ctxt->error(ctxt->userData, "Out of memory\n");
411 ctxt->nbErrors++;
412 return (NULL);
413 }
414 memset(ret, 0, sizeof(xmlRelaxNG));
415
416 return (ret);
417}
418
419/**
420 * xmlRelaxNGFree:
421 * @schema: a schema structure
422 *
423 * Deallocate a RelaxNG structure.
424 */
425void
426xmlRelaxNGFree(xmlRelaxNGPtr schema)
427{
428 if (schema == NULL)
429 return;
430
Daniel Veillard6eadf632003-01-23 18:29:16 +0000431 if (schema->topgrammar != NULL)
432 xmlRelaxNGFreeGrammar(schema->topgrammar);
433 if (schema->doc != NULL)
434 xmlFreeDoc(schema->doc);
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000435 if (schema->documents != NULL)
436 xmlHashFree(schema->documents, (xmlHashDeallocator)
437 xmlRelaxNGFreeDocument);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000438 if (schema->includes != NULL)
439 xmlHashFree(schema->includes, (xmlHashDeallocator)
440 xmlRelaxNGFreeInclude);
Daniel Veillard419a7682003-02-03 23:22:49 +0000441 if (schema->defTab != NULL) {
442 int i;
443
444 for (i = 0;i < schema->defNr;i++)
445 xmlRelaxNGFreeDefine(schema->defTab[i]);
446 xmlFree(schema->defTab);
447 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000448
449 xmlFree(schema);
450}
451
452/**
453 * xmlRelaxNGNewGrammar:
454 * @ctxt: a Relax-NG validation context (optional)
455 *
456 * Allocate a new RelaxNG grammar.
457 *
458 * Returns the newly allocated structure or NULL in case or error
459 */
460static xmlRelaxNGGrammarPtr
461xmlRelaxNGNewGrammar(xmlRelaxNGParserCtxtPtr ctxt)
462{
463 xmlRelaxNGGrammarPtr ret;
464
465 ret = (xmlRelaxNGGrammarPtr) xmlMalloc(sizeof(xmlRelaxNGGrammar));
466 if (ret == NULL) {
467 if ((ctxt != NULL) && (ctxt->error != NULL))
468 ctxt->error(ctxt->userData, "Out of memory\n");
469 ctxt->nbErrors++;
470 return (NULL);
471 }
472 memset(ret, 0, sizeof(xmlRelaxNGGrammar));
473
474 return (ret);
475}
476
477/**
478 * xmlRelaxNGFreeGrammar:
479 * @grammar: a grammar structure
480 *
481 * Deallocate a RelaxNG grammar structure.
482 */
483static void
484xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar)
485{
486 if (grammar == NULL)
487 return;
488
Daniel Veillard419a7682003-02-03 23:22:49 +0000489 if (grammar->next != NULL) {
490 xmlRelaxNGFreeGrammar(grammar->next);
491 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000492 if (grammar->refs != NULL) {
493 xmlHashFree(grammar->refs, NULL);
494 }
495 if (grammar->defs != NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000496 xmlHashFree(grammar->defs, NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000497 }
498
499 xmlFree(grammar);
500}
501
502/**
503 * xmlRelaxNGNewDefine:
504 * @ctxt: a Relax-NG validation context
505 * @node: the node in the input document.
506 *
507 * Allocate a new RelaxNG define.
508 *
509 * Returns the newly allocated structure or NULL in case or error
510 */
511static xmlRelaxNGDefinePtr
512xmlRelaxNGNewDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
513{
514 xmlRelaxNGDefinePtr ret;
515
Daniel Veillard419a7682003-02-03 23:22:49 +0000516 if (ctxt->defMax == 0) {
517 ctxt->defMax = 16;
518 ctxt->defNr = 0;
519 ctxt->defTab = (xmlRelaxNGDefinePtr *)
520 xmlMalloc(ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
521 if (ctxt->defTab == NULL) {
522 if ((ctxt != NULL) && (ctxt->error != NULL))
523 ctxt->error(ctxt->userData, "Out of memory\n");
524 ctxt->nbErrors++;
525 return (NULL);
526 }
527 } else if (ctxt->defMax <= ctxt->defNr) {
528 xmlRelaxNGDefinePtr *tmp;
529 ctxt->defMax *= 2;
530 tmp = (xmlRelaxNGDefinePtr *) xmlRealloc(ctxt->defTab,
531 ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
532 if (tmp == NULL) {
533 if ((ctxt != NULL) && (ctxt->error != NULL))
534 ctxt->error(ctxt->userData, "Out of memory\n");
535 ctxt->nbErrors++;
536 return (NULL);
537 }
538 ctxt->defTab = tmp;
539 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000540 ret = (xmlRelaxNGDefinePtr) xmlMalloc(sizeof(xmlRelaxNGDefine));
541 if (ret == NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000542 if ((ctxt != NULL) && (ctxt->error != NULL))
543 ctxt->error(ctxt->userData, "Out of memory\n");
Daniel Veillard6eadf632003-01-23 18:29:16 +0000544 ctxt->nbErrors++;
Daniel Veillard419a7682003-02-03 23:22:49 +0000545 return(NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000546 }
547 memset(ret, 0, sizeof(xmlRelaxNGDefine));
Daniel Veillard419a7682003-02-03 23:22:49 +0000548 ctxt->defTab[ctxt->defNr++] = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000549 ret->node = node;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000550 return (ret);
551}
552
553/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000554 * xmlRelaxNGFreePartition:
555 * @partitions: a partition set structure
556 *
557 * Deallocate RelaxNG partition set structures.
558 */
559static void
560xmlRelaxNGFreePartition(xmlRelaxNGPartitionPtr partitions) {
561 xmlRelaxNGInterleaveGroupPtr group;
562 int j;
563
564 if (partitions != NULL) {
565 if (partitions->groups != NULL) {
566 for (j = 0;j < partitions->nbgroups;j++) {
567 group = partitions->groups[j];
568 if (group != NULL) {
569 if (group->defs != NULL)
570 xmlFree(group->defs);
571 xmlFree(group);
572 }
573 }
574 xmlFree(partitions->groups);
575 }
576 xmlFree(partitions);
577 }
578}
579/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000580 * xmlRelaxNGFreeDefine:
581 * @define: a define structure
582 *
583 * Deallocate a RelaxNG define structure.
584 */
585static void
586xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define)
587{
588 if (define == NULL)
589 return;
590
Daniel Veillard419a7682003-02-03 23:22:49 +0000591 if ((define->data != NULL) &&
592 (define->type == XML_RELAXNG_INTERLEAVE))
593 xmlRelaxNGFreePartition((xmlRelaxNGPartitionPtr) define->data);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000594 if (define->name != NULL)
595 xmlFree(define->name);
596 if (define->ns != NULL)
597 xmlFree(define->ns);
Daniel Veillardedc91922003-01-26 00:52:04 +0000598 if (define->value != NULL)
599 xmlFree(define->value);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000600 xmlFree(define);
601}
602
603/**
604 * xmlRelaxNGNewValidState:
605 * @ctxt: a Relax-NG validation context
606 * @node: the current node or NULL for the document
607 *
608 * Allocate a new RelaxNG validation state
609 *
610 * Returns the newly allocated structure or NULL in case or error
611 */
612static xmlRelaxNGValidStatePtr
613xmlRelaxNGNewValidState(xmlRelaxNGValidCtxtPtr ctxt, xmlNodePtr node)
614{
615 xmlRelaxNGValidStatePtr ret;
616 xmlAttrPtr attr;
617 xmlAttrPtr attrs[MAX_ATTR];
618 int nbAttrs = 0;
619 xmlNodePtr root = NULL;
620
621 if (node == NULL) {
622 root = xmlDocGetRootElement(ctxt->doc);
623 if (root == NULL)
624 return(NULL);
625 } else {
626 attr = node->properties;
627 while (attr != NULL) {
628 if (nbAttrs < MAX_ATTR)
629 attrs[nbAttrs++] = attr;
630 else
631 nbAttrs++;
632 attr = attr->next;
633 }
634 }
635
636 if (nbAttrs < MAX_ATTR)
637 attrs[nbAttrs] = NULL;
638 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(sizeof(xmlRelaxNGValidState) +
639 nbAttrs * sizeof(xmlAttrPtr));
640 if (ret == NULL) {
641 if ((ctxt != NULL) && (ctxt->error != NULL))
642 ctxt->error(ctxt->userData, "Out of memory\n");
643 return (NULL);
644 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +0000645 ret->value = NULL;
646 ret->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000647 if (node == NULL) {
648 ret->node = (xmlNodePtr) ctxt->doc;
649 ret->seq = root;
650 ret->nbAttrs = 0;
651 } else {
652 ret->node = node;
653 ret->seq = node->children;
654 ret->nbAttrs = nbAttrs;
655 if (nbAttrs > 0) {
656 if (nbAttrs < MAX_ATTR) {
657 memcpy(&(ret->attrs[0]), attrs,
658 sizeof(xmlAttrPtr) * (nbAttrs + 1));
659 } else {
660 attr = node->properties;
661 nbAttrs = 0;
662 while (attr != NULL) {
663 ret->attrs[nbAttrs++] = attr;
664 attr = attr->next;
665 }
666 ret->attrs[nbAttrs] = NULL;
667 }
668 }
669 }
Daniel Veillard1ed7f362003-02-03 10:57:45 +0000670 ret->nbAttrLeft = ret->nbAttrs;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000671 return (ret);
672}
673
674/**
675 * xmlRelaxNGCopyValidState:
676 * @ctxt: a Relax-NG validation context
677 * @state: a validation state
678 *
679 * Copy the validation state
680 *
681 * Returns the newly allocated structure or NULL in case or error
682 */
683static xmlRelaxNGValidStatePtr
684xmlRelaxNGCopyValidState(xmlRelaxNGValidCtxtPtr ctxt,
685 xmlRelaxNGValidStatePtr state)
686{
687 xmlRelaxNGValidStatePtr ret;
688 unsigned int size;
689
690 if (state == NULL)
691 return(NULL);
692
693 size = sizeof(xmlRelaxNGValidState) +
694 state->nbAttrs * sizeof(xmlAttrPtr);
695 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(size);
696 if (ret == NULL) {
697 if ((ctxt != NULL) && (ctxt->error != NULL))
698 ctxt->error(ctxt->userData, "Out of memory\n");
699 return (NULL);
700 }
701 memcpy(ret, state, size);
702 return(ret);
703}
704
705/**
706 * xmlRelaxNGFreeValidState:
707 * @state: a validation state structure
708 *
709 * Deallocate a RelaxNG validation state structure.
710 */
711static void
712xmlRelaxNGFreeValidState(xmlRelaxNGValidStatePtr state)
713{
714 if (state == NULL)
715 return;
716
717 xmlFree(state);
718}
719
720/************************************************************************
721 * *
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000722 * Document functions *
723 * *
724 ************************************************************************/
725static xmlDocPtr xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt,
726 xmlDocPtr doc);
727
728/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000729 * xmlRelaxNGIncludePush:
730 * @ctxt: the parser context
731 * @value: the element doc
732 *
733 * Pushes a new include on top of the include stack
734 *
735 * Returns 0 in case of error, the index in the stack otherwise
736 */
737static int
738xmlRelaxNGIncludePush(xmlRelaxNGParserCtxtPtr ctxt,
739 xmlRelaxNGIncludePtr value)
740{
741 if (ctxt->incTab == NULL) {
742 ctxt->incMax = 4;
743 ctxt->incNr = 0;
744 ctxt->incTab = (xmlRelaxNGIncludePtr *) xmlMalloc(
745 ctxt->incMax * sizeof(ctxt->incTab[0]));
746 if (ctxt->incTab == NULL) {
747 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
748 return (0);
749 }
750 }
751 if (ctxt->incNr >= ctxt->incMax) {
752 ctxt->incMax *= 2;
753 ctxt->incTab =
754 (xmlRelaxNGIncludePtr *) xmlRealloc(ctxt->incTab,
755 ctxt->incMax *
756 sizeof(ctxt->incTab[0]));
757 if (ctxt->incTab == NULL) {
758 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
759 return (0);
760 }
761 }
762 ctxt->incTab[ctxt->incNr] = value;
763 ctxt->inc = value;
764 return (ctxt->incNr++);
765}
766
767/**
768 * xmlRelaxNGIncludePop:
769 * @ctxt: the parser context
770 *
771 * Pops the top include from the include stack
772 *
773 * Returns the include just removed
774 */
775static xmlRelaxNGIncludePtr
776xmlRelaxNGIncludePop(xmlRelaxNGParserCtxtPtr ctxt)
777{
778 xmlRelaxNGIncludePtr ret;
779
780 if (ctxt->incNr <= 0)
781 return (0);
782 ctxt->incNr--;
783 if (ctxt->incNr > 0)
784 ctxt->inc = ctxt->incTab[ctxt->incNr - 1];
785 else
786 ctxt->inc = NULL;
787 ret = ctxt->incTab[ctxt->incNr];
788 ctxt->incTab[ctxt->incNr] = 0;
789 return (ret);
790}
791
792/**
793 * xmlRelaxNGLoadInclude:
794 * @ctxt: the parser context
795 * @URL: the normalized URL
796 * @node: the include node.
797 *
798 * First lookup if the document is already loaded into the parser context,
799 * check against recursion. If not found the resource is loaded and
800 * the content is preprocessed before being returned back to the caller.
801 *
802 * Returns the xmlRelaxNGIncludePtr or NULL in case of error
803 */
804static xmlRelaxNGIncludePtr
805xmlRelaxNGLoadInclude(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
806 xmlNodePtr node) {
807 xmlRelaxNGIncludePtr ret = NULL;
808 xmlDocPtr doc;
809 int i;
810 xmlNodePtr root, tmp, tmp2, cur;
811
812 /*
813 * check against recursion in the stack
814 */
815 for (i = 0;i < ctxt->incNr;i++) {
816 if (xmlStrEqual(ctxt->incTab[i]->href, URL)) {
817 if (ctxt->error != NULL)
818 ctxt->error(ctxt->userData,
819 "Detected an externalRef recursion for %s\n",
820 URL);
821 ctxt->nbErrors++;
822 return(NULL);
823 }
824 }
825
826 /*
827 * Lookup in the hash table
828 */
829 if (ctxt->includes == NULL) {
830 ctxt->includes = xmlHashCreate(10);
831 if (ctxt->includes == NULL) {
832 if (ctxt->error != NULL)
833 ctxt->error(ctxt->userData,
834 "Failed to allocate hash table for document\n");
835 ctxt->nbErrors++;
836 return(NULL);
837 }
838 } else {
839 ret = xmlHashLookup(ctxt->includes, URL);
840 if (ret != NULL)
841 return(ret);
842 }
843
844
845 /*
846 * load the document
847 */
848 doc = xmlParseFile((const char *) URL);
849 if (doc == NULL) {
850 if (ctxt->error != NULL)
851 ctxt->error(ctxt->userData,
852 "xmlRelaxNG: could not load %s\n", URL);
853 ctxt->nbErrors++;
854 return (NULL);
855 }
856
857 /*
858 * Allocate the document structures and register it first.
859 */
860 ret = (xmlRelaxNGIncludePtr) xmlMalloc(sizeof(xmlRelaxNGInclude));
861 if (ret == NULL) {
862 if (ctxt->error != NULL)
863 ctxt->error(ctxt->userData,
864 "xmlRelaxNG: allocate memory for doc %s\n", URL);
865 ctxt->nbErrors++;
866 xmlFreeDoc(doc);
867 return (NULL);
868 }
869 memset(ret, 0, sizeof(xmlRelaxNGInclude));
870 ret->doc = doc;
871 ret->href = xmlStrdup(URL);
872
873 /*
874 * push it on the stack and register it in the hash table
875 */
876 xmlHashAddEntry(ctxt->includes, URL, ret);
877 xmlRelaxNGIncludePush(ctxt, ret);
878
879 /*
880 * Some preprocessing of the document content, this include recursing
881 * in the include stack.
882 */
883 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
884 if (doc == NULL) {
885 /* xmlFreeDoc(ctxt->include); */
886 ctxt->inc = NULL;
887 return(NULL);
888 }
889
890 /*
891 * Pop up the include from the stack
892 */
893 xmlRelaxNGIncludePop(ctxt);
894
895 /*
896 * Check that the top element is a grammar
897 */
898 root = xmlDocGetRootElement(doc);
899 if (root == NULL) {
900 if (ctxt->error != NULL)
901 ctxt->error(ctxt->userData,
902 "xmlRelaxNG: included document is empty %s\n", URL);
903 ctxt->nbErrors++;
904 xmlFreeDoc(doc);
905 return (NULL);
906 }
907 if (!IS_RELAXNG(root, "grammar")) {
908 if (ctxt->error != NULL)
909 ctxt->error(ctxt->userData,
910 "xmlRelaxNG: included document %s root is not a grammar\n",
911 URL);
912 ctxt->nbErrors++;
913 xmlFreeDoc(doc);
914 return (NULL);
915 }
916
917 /*
918 * Elimination of redefined rules in the include.
919 */
920 cur = node->children;
921 while (cur != NULL) {
922 if (IS_RELAXNG(cur, "start")) {
923 int found = 0;
924
925 tmp = root->children;
926 while (tmp != NULL) {
927 tmp2 = tmp->next;
928 if (IS_RELAXNG(tmp, "start")) {
929 found = 1;
930 xmlUnlinkNode(tmp);
931 xmlFreeNode(tmp);
932 }
933 tmp = tmp2;
934 }
935 if (!found) {
936 if (ctxt->error != NULL)
937 ctxt->error(ctxt->userData,
938 "xmlRelaxNG: include %s has a start but not the included grammar\n",
939 URL);
940 ctxt->nbErrors++;
941 }
942 } else if (IS_RELAXNG(cur, "define")) {
943 xmlChar *name, *name2;
944
945 name = xmlGetProp(cur, BAD_CAST "name");
946 if (name == NULL) {
947 if (ctxt->error != NULL)
948 ctxt->error(ctxt->userData,
949 "xmlRelaxNG: include %s has define without name\n",
950 URL);
951 ctxt->nbErrors++;
952 } else {
953 int found = 0;
954
955 tmp = root->children;
956 while (tmp != NULL) {
957 tmp2 = tmp->next;
958 if (IS_RELAXNG(tmp, "define")) {
959 name2 = xmlGetProp(tmp, BAD_CAST "name");
960 if (name2 != NULL) {
961 if (xmlStrEqual(name, name2)) {
962 found = 1;
963 xmlUnlinkNode(tmp);
964 xmlFreeNode(tmp);
965 }
966 xmlFree(name2);
967 }
968 }
969 tmp = tmp2;
970 }
971 if (!found) {
972 if (ctxt->error != NULL)
973 ctxt->error(ctxt->userData,
974 "xmlRelaxNG: include %s has a define %s but not the included grammar\n",
975 URL, name);
976 ctxt->nbErrors++;
977 }
978 xmlFree(name);
979 }
980 }
981 cur = cur->next;
982 }
983
984
985 return(ret);
986}
987
988/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000989 * xmlRelaxNGDocumentPush:
990 * @ctxt: the parser context
991 * @value: the element doc
992 *
993 * Pushes a new doc on top of the doc stack
994 *
995 * Returns 0 in case of error, the index in the stack otherwise
996 */
997static int
998xmlRelaxNGDocumentPush(xmlRelaxNGParserCtxtPtr ctxt,
999 xmlRelaxNGDocumentPtr value)
1000{
1001 if (ctxt->docTab == NULL) {
1002 ctxt->docMax = 4;
1003 ctxt->docNr = 0;
1004 ctxt->docTab = (xmlRelaxNGDocumentPtr *) xmlMalloc(
1005 ctxt->docMax * sizeof(ctxt->docTab[0]));
1006 if (ctxt->docTab == NULL) {
1007 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
1008 return (0);
1009 }
1010 }
1011 if (ctxt->docNr >= ctxt->docMax) {
1012 ctxt->docMax *= 2;
1013 ctxt->docTab =
1014 (xmlRelaxNGDocumentPtr *) xmlRealloc(ctxt->docTab,
1015 ctxt->docMax *
1016 sizeof(ctxt->docTab[0]));
1017 if (ctxt->docTab == NULL) {
1018 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
1019 return (0);
1020 }
1021 }
1022 ctxt->docTab[ctxt->docNr] = value;
1023 ctxt->doc = value;
1024 return (ctxt->docNr++);
1025}
1026
1027/**
1028 * xmlRelaxNGDocumentPop:
1029 * @ctxt: the parser context
1030 *
1031 * Pops the top doc from the doc stack
1032 *
1033 * Returns the doc just removed
1034 */
1035static xmlRelaxNGDocumentPtr
1036xmlRelaxNGDocumentPop(xmlRelaxNGParserCtxtPtr ctxt)
1037{
1038 xmlRelaxNGDocumentPtr ret;
1039
1040 if (ctxt->docNr <= 0)
1041 return (0);
1042 ctxt->docNr--;
1043 if (ctxt->docNr > 0)
1044 ctxt->doc = ctxt->docTab[ctxt->docNr - 1];
1045 else
1046 ctxt->doc = NULL;
1047 ret = ctxt->docTab[ctxt->docNr];
1048 ctxt->docTab[ctxt->docNr] = 0;
1049 return (ret);
1050}
1051
1052/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001053 * xmlRelaxNGLoadExternalRef:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001054 * @ctxt: the parser context
1055 * @URL: the normalized URL
1056 * @ns: the inherited ns if any
1057 *
1058 * First lookup if the document is already loaded into the parser context,
1059 * check against recursion. If not found the resource is loaded and
1060 * the content is preprocessed before being returned back to the caller.
1061 *
1062 * Returns the xmlRelaxNGDocumentPtr or NULL in case of error
1063 */
1064static xmlRelaxNGDocumentPtr
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001065xmlRelaxNGLoadExternalRef(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001066 const xmlChar *ns) {
1067 xmlRelaxNGDocumentPtr ret = NULL;
1068 xmlDocPtr doc;
1069 xmlNodePtr root;
1070 int i;
1071
1072 /*
1073 * check against recursion in the stack
1074 */
1075 for (i = 0;i < ctxt->docNr;i++) {
1076 if (xmlStrEqual(ctxt->docTab[i]->href, URL)) {
1077 if (ctxt->error != NULL)
1078 ctxt->error(ctxt->userData,
1079 "Detected an externalRef recursion for %s\n",
1080 URL);
1081 ctxt->nbErrors++;
1082 return(NULL);
1083 }
1084 }
1085
1086 /*
1087 * Lookup in the hash table
1088 */
1089 if (ctxt->documents == NULL) {
1090 ctxt->documents = xmlHashCreate(10);
1091 if (ctxt->documents == NULL) {
1092 if (ctxt->error != NULL)
1093 ctxt->error(ctxt->userData,
1094 "Failed to allocate hash table for document\n");
1095 ctxt->nbErrors++;
1096 return(NULL);
1097 }
1098 } else {
1099 if (ns == NULL)
1100 ret = xmlHashLookup2(ctxt->documents, BAD_CAST "", URL);
1101 else
1102 ret = xmlHashLookup2(ctxt->documents, ns, URL);
1103 if (ret != NULL)
1104 return(ret);
1105 }
1106
1107
1108 /*
1109 * load the document
1110 */
1111 doc = xmlParseFile((const char *) URL);
1112 if (doc == NULL) {
1113 if (ctxt->error != NULL)
1114 ctxt->error(ctxt->userData,
1115 "xmlRelaxNG: could not load %s\n", URL);
1116 ctxt->nbErrors++;
1117 return (NULL);
1118 }
1119
1120 /*
1121 * Allocate the document structures and register it first.
1122 */
1123 ret = (xmlRelaxNGDocumentPtr) xmlMalloc(sizeof(xmlRelaxNGDocument));
1124 if (ret == NULL) {
1125 if (ctxt->error != NULL)
1126 ctxt->error(ctxt->userData,
1127 "xmlRelaxNG: allocate memory for doc %s\n", URL);
1128 ctxt->nbErrors++;
1129 xmlFreeDoc(doc);
1130 return (NULL);
1131 }
1132 memset(ret, 0, sizeof(xmlRelaxNGDocument));
1133 ret->doc = doc;
1134 ret->href = xmlStrdup(URL);
1135
1136 /*
1137 * transmit the ns if needed
1138 */
1139 if (ns != NULL) {
1140 root = xmlDocGetRootElement(doc);
1141 if (root != NULL) {
1142 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
1143 xmlSetProp(root, BAD_CAST"ns", ns);
1144 }
1145 }
1146 }
1147
1148 /*
1149 * push it on the stack and register it in the hash table
1150 */
1151 if (ns == NULL)
1152 xmlHashAddEntry2(ctxt->documents, BAD_CAST "", URL, ret);
1153 else
1154 xmlHashAddEntry2(ctxt->documents, ns, URL, ret);
1155 xmlRelaxNGDocumentPush(ctxt, ret);
1156
1157 /*
1158 * Some preprocessing of the document content
1159 */
1160 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
1161 if (doc == NULL) {
1162 xmlFreeDoc(ctxt->document);
1163 ctxt->doc = NULL;
1164 return(NULL);
1165 }
1166
1167 xmlRelaxNGDocumentPop(ctxt);
1168
1169 return(ret);
1170}
1171
1172/************************************************************************
1173 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +00001174 * Error functions *
1175 * *
1176 ************************************************************************/
1177
1178#define VALID_CTXT() \
Daniel Veillard231d7912003-02-09 14:22:17 +00001179 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1180 xmlGenericError(xmlGenericErrorContext, \
Daniel Veillard6eadf632003-01-23 18:29:16 +00001181 "error detected at %s:%d\n", \
1182 __FILE__, __LINE__);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001183
1184#define VALID_ERROR(a) \
Daniel Veillard231d7912003-02-09 14:22:17 +00001185 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001186 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a)
1187#define VALID_ERROR2(a, b) \
1188 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1189 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a, b)
1190#define VALID_ERROR3(a, b, c) \
1191 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1192 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a, b, c)
Daniel Veillard6eadf632003-01-23 18:29:16 +00001193
Daniel Veillard231d7912003-02-09 14:22:17 +00001194static const char *
1195xmlRelaxNGDefName(xmlRelaxNGDefinePtr def) {
1196 if (def == NULL)
1197 return("none");
1198 switch(def->type) {
1199 case XML_RELAXNG_EMPTY: return("empty");
1200 case XML_RELAXNG_NOT_ALLOWED: return("notAllowed");
1201 case XML_RELAXNG_EXCEPT: return("except");
1202 case XML_RELAXNG_TEXT: return("text");
1203 case XML_RELAXNG_ELEMENT: return("element");
1204 case XML_RELAXNG_DATATYPE: return("datatype");
1205 case XML_RELAXNG_VALUE: return("value");
1206 case XML_RELAXNG_LIST: return("list");
1207 case XML_RELAXNG_ATTRIBUTE: return("attribute");
1208 case XML_RELAXNG_DEF: return("def");
1209 case XML_RELAXNG_REF: return("ref");
1210 case XML_RELAXNG_EXTERNALREF: return("externalRef");
1211 case XML_RELAXNG_PARENTREF: return("parentRef");
1212 case XML_RELAXNG_OPTIONAL: return("optional");
1213 case XML_RELAXNG_ZEROORMORE: return("zeroOrMore");
1214 case XML_RELAXNG_ONEORMORE: return("oneOrMore");
1215 case XML_RELAXNG_CHOICE: return("choice");
1216 case XML_RELAXNG_GROUP: return("group");
1217 case XML_RELAXNG_INTERLEAVE: return("interleave");
1218 case XML_RELAXNG_START: return("start");
1219 }
1220 return("unknown");
1221}
Daniel Veillard6eadf632003-01-23 18:29:16 +00001222#if 0
1223/**
1224 * xmlRelaxNGErrorContext:
1225 * @ctxt: the parsing context
1226 * @schema: the schema being built
1227 * @node: the node being processed
1228 * @child: the child being processed
1229 *
1230 * Dump a RelaxNGType structure
1231 */
1232static void
1233xmlRelaxNGErrorContext(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGPtr schema,
1234 xmlNodePtr node, xmlNodePtr child)
1235{
1236 int line = 0;
1237 const xmlChar *file = NULL;
1238 const xmlChar *name = NULL;
1239 const char *type = "error";
1240
1241 if ((ctxt == NULL) || (ctxt->error == NULL))
1242 return;
1243
1244 if (child != NULL)
1245 node = child;
1246
1247 if (node != NULL) {
1248 if ((node->type == XML_DOCUMENT_NODE) ||
1249 (node->type == XML_HTML_DOCUMENT_NODE)) {
1250 xmlDocPtr doc = (xmlDocPtr) node;
1251
1252 file = doc->URL;
1253 } else {
1254 /*
1255 * Try to find contextual informations to report
1256 */
1257 if (node->type == XML_ELEMENT_NODE) {
1258 line = (int) node->content;
1259 } else if ((node->prev != NULL) &&
1260 (node->prev->type == XML_ELEMENT_NODE)) {
1261 line = (int) node->prev->content;
1262 } else if ((node->parent != NULL) &&
1263 (node->parent->type == XML_ELEMENT_NODE)) {
1264 line = (int) node->parent->content;
1265 }
1266 if ((node->doc != NULL) && (node->doc->URL != NULL))
1267 file = node->doc->URL;
1268 if (node->name != NULL)
1269 name = node->name;
1270 }
1271 }
1272
1273 if (ctxt != NULL)
1274 type = "compilation error";
1275 else if (schema != NULL)
1276 type = "runtime error";
1277
1278 if ((file != NULL) && (line != 0) && (name != NULL))
1279 ctxt->error(ctxt->userData, "%s: file %s line %d element %s\n",
1280 type, file, line, name);
1281 else if ((file != NULL) && (name != NULL))
1282 ctxt->error(ctxt->userData, "%s: file %s element %s\n",
1283 type, file, name);
1284 else if ((file != NULL) && (line != 0))
1285 ctxt->error(ctxt->userData, "%s: file %s line %d\n", type, file, line);
1286 else if (file != NULL)
1287 ctxt->error(ctxt->userData, "%s: file %s\n", type, file);
1288 else if (name != NULL)
1289 ctxt->error(ctxt->userData, "%s: element %s\n", type, name);
1290 else
1291 ctxt->error(ctxt->userData, "%s\n", type);
1292}
1293#endif
1294
1295/************************************************************************
1296 * *
1297 * Type library hooks *
1298 * *
1299 ************************************************************************/
Daniel Veillardea3f3982003-01-26 19:45:18 +00001300static xmlChar *xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt,
1301 const xmlChar *str);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001302
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001303/**
1304 * xmlRelaxNGSchemaTypeHave:
1305 * @data: data needed for the library
1306 * @type: the type name
1307 *
1308 * Check if the given type is provided by
1309 * the W3C XMLSchema Datatype library.
1310 *
1311 * Returns 1 if yes, 0 if no and -1 in case of error.
1312 */
1313static int
1314xmlRelaxNGSchemaTypeHave(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001315 const xmlChar *type) {
1316 xmlSchemaTypePtr typ;
1317
1318 if (type == NULL)
1319 return(-1);
1320 typ = xmlSchemaGetPredefinedType(type,
1321 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1322 if (typ == NULL)
1323 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001324 return(1);
1325}
1326
1327/**
1328 * xmlRelaxNGSchemaTypeCheck:
1329 * @data: data needed for the library
1330 * @type: the type name
1331 * @value: the value to check
1332 *
1333 * Check if the given type and value are validated by
1334 * the W3C XMLSchema Datatype library.
1335 *
1336 * Returns 1 if yes, 0 if no and -1 in case of error.
1337 */
1338static int
1339xmlRelaxNGSchemaTypeCheck(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001340 const xmlChar *type,
1341 const xmlChar *value) {
1342 xmlSchemaTypePtr typ;
1343 int ret;
1344
1345 /*
1346 * TODO: the type should be cached ab provided back, interface subject
1347 * to changes.
1348 * TODO: handle facets, may require an additional interface and keep
1349 * the value returned from the validation.
1350 */
1351 if ((type == NULL) || (value == NULL))
1352 return(-1);
1353 typ = xmlSchemaGetPredefinedType(type,
1354 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1355 if (typ == NULL)
1356 return(-1);
1357 ret = xmlSchemaValidatePredefinedType(typ, value, NULL);
1358 if (ret == 0)
1359 return(1);
1360 if (ret > 0)
1361 return(0);
1362 return(-1);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001363}
1364
1365/**
1366 * xmlRelaxNGSchemaTypeCompare:
1367 * @data: data needed for the library
1368 * @type: the type name
1369 * @value1: the first value
1370 * @value2: the second value
1371 *
1372 * Compare two values accordingly a type from the W3C XMLSchema
1373 * Datatype library.
1374 *
1375 * Returns 1 if yes, 0 if no and -1 in case of error.
1376 */
1377static int
1378xmlRelaxNGSchemaTypeCompare(void *data ATTRIBUTE_UNUSED,
1379 const xmlChar *type ATTRIBUTE_UNUSED,
1380 const xmlChar *value1 ATTRIBUTE_UNUSED,
1381 const xmlChar *value2 ATTRIBUTE_UNUSED) {
1382 TODO
1383 return(1);
1384}
1385
1386/**
1387 * xmlRelaxNGDefaultTypeHave:
1388 * @data: data needed for the library
1389 * @type: the type name
1390 *
1391 * Check if the given type is provided by
1392 * the default datatype library.
1393 *
1394 * Returns 1 if yes, 0 if no and -1 in case of error.
1395 */
1396static int
1397xmlRelaxNGDefaultTypeHave(void *data ATTRIBUTE_UNUSED, const xmlChar *type) {
1398 if (type == NULL)
1399 return(-1);
1400 if (xmlStrEqual(type, BAD_CAST "string"))
1401 return(1);
1402 if (xmlStrEqual(type, BAD_CAST "token"))
1403 return(1);
1404 return(0);
1405}
1406
1407/**
1408 * xmlRelaxNGDefaultTypeCheck:
1409 * @data: data needed for the library
1410 * @type: the type name
1411 * @value: the value to check
1412 *
1413 * Check if the given type and value are validated by
1414 * the default datatype library.
1415 *
1416 * Returns 1 if yes, 0 if no and -1 in case of error.
1417 */
1418static int
1419xmlRelaxNGDefaultTypeCheck(void *data ATTRIBUTE_UNUSED,
1420 const xmlChar *type ATTRIBUTE_UNUSED,
1421 const xmlChar *value ATTRIBUTE_UNUSED) {
1422 return(1);
1423}
1424
1425/**
1426 * xmlRelaxNGDefaultTypeCompare:
1427 * @data: data needed for the library
1428 * @type: the type name
1429 * @value1: the first value
1430 * @value2: the second value
1431 *
1432 * Compare two values accordingly a type from the default
1433 * datatype library.
1434 *
1435 * Returns 1 if yes, 0 if no and -1 in case of error.
1436 */
1437static int
1438xmlRelaxNGDefaultTypeCompare(void *data ATTRIBUTE_UNUSED,
1439 const xmlChar *type ATTRIBUTE_UNUSED,
1440 const xmlChar *value1 ATTRIBUTE_UNUSED,
1441 const xmlChar *value2 ATTRIBUTE_UNUSED) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00001442 int ret = -1;
1443
1444 if (xmlStrEqual(type, BAD_CAST "string")) {
1445 ret = xmlStrEqual(value1, value2);
1446 } else if (xmlStrEqual(type, BAD_CAST "token")) {
1447 if (!xmlStrEqual(value1, value2)) {
1448 xmlChar *nval, *nvalue;
1449
1450 /*
1451 * TODO: trivial optimizations are possible by
1452 * computing at compile-time
1453 */
1454 nval = xmlRelaxNGNormalize(NULL, value1);
1455 nvalue = xmlRelaxNGNormalize(NULL, value2);
1456
1457 if ((nval == NULL) || (nvalue == NULL) ||
1458 (!xmlStrEqual(nval, nvalue)))
1459 ret = -1;
1460 if (nval != NULL)
1461 xmlFree(nval);
1462 if (nvalue != NULL)
1463 xmlFree(nvalue);
1464 }
1465 }
1466 return(ret);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001467}
1468
1469static int xmlRelaxNGTypeInitialized = 0;
1470static xmlHashTablePtr xmlRelaxNGRegisteredTypes = NULL;
1471
1472/**
1473 * xmlRelaxNGFreeTypeLibrary:
1474 * @lib: the type library structure
1475 * @namespace: the URI bound to the library
1476 *
1477 * Free the structure associated to the type library
1478 */
Daniel Veillard6eadf632003-01-23 18:29:16 +00001479static void
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001480xmlRelaxNGFreeTypeLibrary(xmlRelaxNGTypeLibraryPtr lib,
1481 const xmlChar *namespace ATTRIBUTE_UNUSED) {
1482 if (lib == NULL)
1483 return;
1484 if (lib->namespace != NULL)
1485 xmlFree((xmlChar *)lib->namespace);
1486 xmlFree(lib);
1487}
1488
1489/**
1490 * xmlRelaxNGRegisterTypeLibrary:
1491 * @namespace: the URI bound to the library
1492 * @data: data associated to the library
1493 * @have: the provide function
1494 * @check: the checking function
1495 * @comp: the comparison function
1496 *
1497 * Register a new type library
1498 *
1499 * Returns 0 in case of success and -1 in case of error.
1500 */
1501static int
1502xmlRelaxNGRegisterTypeLibrary(const xmlChar *namespace, void *data,
1503 xmlRelaxNGTypeHave have, xmlRelaxNGTypeCheck check,
1504 xmlRelaxNGTypeCompare comp) {
1505 xmlRelaxNGTypeLibraryPtr lib;
1506 int ret;
1507
1508 if ((xmlRelaxNGRegisteredTypes == NULL) || (namespace == NULL) ||
1509 (check == NULL) || (comp == NULL))
1510 return(-1);
1511 if (xmlHashLookup(xmlRelaxNGRegisteredTypes, namespace) != NULL) {
1512 xmlGenericError(xmlGenericErrorContext,
1513 "Relax-NG types library '%s' already registered\n",
1514 namespace);
1515 return(-1);
1516 }
1517 lib = (xmlRelaxNGTypeLibraryPtr) xmlMalloc(sizeof(xmlRelaxNGTypeLibrary));
1518 if (lib == NULL) {
1519 xmlGenericError(xmlGenericErrorContext,
1520 "Relax-NG types library '%s' malloc() failed\n",
1521 namespace);
1522 return (-1);
1523 }
1524 memset(lib, 0, sizeof(xmlRelaxNGTypeLibrary));
1525 lib->namespace = xmlStrdup(namespace);
1526 lib->data = data;
1527 lib->have = have;
1528 lib->comp = comp;
1529 lib->check = check;
1530 ret = xmlHashAddEntry(xmlRelaxNGRegisteredTypes, namespace, lib);
1531 if (ret < 0) {
1532 xmlGenericError(xmlGenericErrorContext,
1533 "Relax-NG types library failed to register '%s'\n",
1534 namespace);
1535 xmlRelaxNGFreeTypeLibrary(lib, namespace);
1536 return(-1);
1537 }
1538 return(0);
1539}
1540
1541/**
1542 * xmlRelaxNGInitTypes:
1543 *
1544 * Initilize the default type libraries.
1545 *
1546 * Returns 0 in case of success and -1 in case of error.
1547 */
1548static int
Daniel Veillard6eadf632003-01-23 18:29:16 +00001549xmlRelaxNGInitTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001550 if (xmlRelaxNGTypeInitialized != 0)
1551 return(0);
1552 xmlRelaxNGRegisteredTypes = xmlHashCreate(10);
1553 if (xmlRelaxNGRegisteredTypes == NULL) {
1554 xmlGenericError(xmlGenericErrorContext,
1555 "Failed to allocate sh table for Relax-NG types\n");
1556 return(-1);
1557 }
1558 xmlRelaxNGRegisterTypeLibrary(
1559 BAD_CAST "http://www.w3.org/2001/XMLSchema-datatypes",
1560 NULL,
1561 xmlRelaxNGSchemaTypeHave,
1562 xmlRelaxNGSchemaTypeCheck,
1563 xmlRelaxNGSchemaTypeCompare);
1564 xmlRelaxNGRegisterTypeLibrary(
1565 xmlRelaxNGNs,
1566 NULL,
1567 xmlRelaxNGDefaultTypeHave,
1568 xmlRelaxNGDefaultTypeCheck,
1569 xmlRelaxNGDefaultTypeCompare);
1570 xmlRelaxNGTypeInitialized = 1;
1571 return(0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001572}
1573
1574/**
1575 * xmlRelaxNGCleanupTypes:
1576 *
1577 * Cleanup the default Schemas type library associated to RelaxNG
1578 */
1579void
1580xmlRelaxNGCleanupTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001581 if (xmlRelaxNGTypeInitialized == 0)
1582 return;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001583 xmlSchemaCleanupTypes();
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001584 xmlHashFree(xmlRelaxNGRegisteredTypes, (xmlHashDeallocator)
1585 xmlRelaxNGFreeTypeLibrary);
1586 xmlRelaxNGTypeInitialized = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001587}
1588
1589/************************************************************************
1590 * *
1591 * Parsing functions *
1592 * *
1593 ************************************************************************/
1594
1595static xmlRelaxNGDefinePtr xmlRelaxNGParseAttribute(
1596 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1597static xmlRelaxNGDefinePtr xmlRelaxNGParseElement(
1598 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1599static xmlRelaxNGDefinePtr xmlRelaxNGParsePatterns(
Daniel Veillard154877e2003-01-30 12:17:05 +00001600 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes, int group);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001601static xmlRelaxNGDefinePtr xmlRelaxNGParsePattern(
1602 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001603static xmlRelaxNGPtr xmlRelaxNGParseDocument(
1604 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00001605static int xmlRelaxNGParseGrammarContent(
1606 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00001607static xmlRelaxNGDefinePtr xmlRelaxNGParseNameClass(
1608 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
1609 xmlRelaxNGDefinePtr def);
Daniel Veillard419a7682003-02-03 23:22:49 +00001610static xmlRelaxNGGrammarPtr xmlRelaxNGParseGrammar(
1611 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001612
1613
1614#define IS_BLANK_NODE(n) \
1615 (((n)->type == XML_TEXT_NODE) && (xmlRelaxNGIsBlank((n)->content)))
1616
1617/**
1618 * xmlRelaxNGIsBlank:
1619 * @str: a string
1620 *
1621 * Check if a string is ignorable c.f. 4.2. Whitespace
1622 *
1623 * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
1624 */
1625static int
1626xmlRelaxNGIsBlank(xmlChar *str) {
1627 if (str == NULL)
1628 return(1);
1629 while (*str != 0) {
1630 if (!(IS_BLANK(*str))) return(0);
1631 str++;
1632 }
1633 return(1);
1634}
1635
Daniel Veillard6eadf632003-01-23 18:29:16 +00001636/**
1637 * xmlRelaxNGGetDataTypeLibrary:
1638 * @ctxt: a Relax-NG parser context
1639 * @node: the current data or value element
1640 *
1641 * Applies algorithm from 4.3. datatypeLibrary attribute
1642 *
1643 * Returns the datatypeLibary value or NULL if not found
1644 */
1645static xmlChar *
1646xmlRelaxNGGetDataTypeLibrary(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1647 xmlNodePtr node) {
1648 xmlChar *ret, *escape;
1649
Daniel Veillard6eadf632003-01-23 18:29:16 +00001650 if ((IS_RELAXNG(node, "data")) || (IS_RELAXNG(node, "value"))) {
1651 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1652 if (ret != NULL) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001653 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +00001654 if (escape == NULL) {
1655 return(ret);
1656 }
1657 xmlFree(ret);
1658 return(escape);
1659 }
1660 }
1661 node = node->parent;
1662 while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) {
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001663 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1664 if (ret != NULL) {
1665 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
1666 if (escape == NULL) {
1667 return(ret);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001668 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001669 xmlFree(ret);
1670 return(escape);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001671 }
1672 node = node->parent;
1673 }
1674 return(NULL);
1675}
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001676
1677/**
Daniel Veillardedc91922003-01-26 00:52:04 +00001678 * xmlRelaxNGParseValue:
1679 * @ctxt: a Relax-NG parser context
1680 * @node: the data node.
1681 *
1682 * parse the content of a RelaxNG value node.
1683 *
1684 * Returns the definition pointer or NULL in case of error
1685 */
1686static xmlRelaxNGDefinePtr
1687xmlRelaxNGParseValue(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1688 xmlRelaxNGDefinePtr def = NULL;
1689 xmlRelaxNGTypeLibraryPtr lib;
1690 xmlChar *type;
1691 xmlChar *library;
1692 int tmp;
1693
1694 def = xmlRelaxNGNewDefine(ctxt, node);
1695 if (def == NULL)
1696 return(NULL);
1697 def->type = XML_RELAXNG_VALUE;
1698
1699 type = xmlGetProp(node, BAD_CAST "type");
1700 if (type != NULL) {
1701 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1702 if (library == NULL)
1703 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1704
1705 def->name = type;
1706 def->ns = library;
1707
1708 lib = (xmlRelaxNGTypeLibraryPtr)
1709 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1710 if (lib == NULL) {
1711 if (ctxt->error != NULL)
1712 ctxt->error(ctxt->userData,
1713 "Use of unregistered type library '%s'\n",
1714 library);
1715 ctxt->nbErrors++;
1716 def->data = NULL;
1717 } else {
1718 def->data = lib;
1719 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001720 if (ctxt->error != NULL)
1721 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00001722 "Internal error with type library '%s': no 'have'\n",
1723 library);
1724 ctxt->nbErrors++;
1725 } else {
1726 tmp = lib->have(lib->data, def->name);
1727 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001728 if (ctxt->error != NULL)
1729 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00001730 "Error type '%s' is not exported by type library '%s'\n",
1731 def->name, library);
1732 ctxt->nbErrors++;
1733 }
1734 }
1735 }
1736 }
1737 if (node->children == NULL) {
1738 if (ctxt->error != NULL)
1739 ctxt->error(ctxt->userData,
1740 "Element <value> has no content\n");
1741 ctxt->nbErrors++;
1742 } else if ((node->children->type != XML_TEXT_NODE) ||
1743 (node->children->next != NULL)) {
1744 if (ctxt->error != NULL)
1745 ctxt->error(ctxt->userData,
1746 "Expecting a single text value for <value>content\n");
1747 ctxt->nbErrors++;
1748 } else {
1749 def->value = xmlNodeGetContent(node);
1750 if (def->value == NULL) {
1751 if (ctxt->error != NULL)
1752 ctxt->error(ctxt->userData,
1753 "Element <value> has no content\n");
1754 ctxt->nbErrors++;
1755 }
1756 }
1757 /* TODO check ahead of time that the value is okay per the type */
1758 return(def);
1759}
1760
1761/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001762 * xmlRelaxNGParseData:
1763 * @ctxt: a Relax-NG parser context
1764 * @node: the data node.
1765 *
1766 * parse the content of a RelaxNG data node.
1767 *
1768 * Returns the definition pointer or NULL in case of error
1769 */
1770static xmlRelaxNGDefinePtr
1771xmlRelaxNGParseData(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1772 xmlRelaxNGDefinePtr def = NULL;
1773 xmlRelaxNGTypeLibraryPtr lib;
1774 xmlChar *type;
1775 xmlChar *library;
1776 xmlNodePtr content;
1777 int tmp;
1778
1779 type = xmlGetProp(node, BAD_CAST "type");
1780 if (type == NULL) {
1781 if (ctxt->error != NULL)
1782 ctxt->error(ctxt->userData,
1783 "data has no type\n");
1784 ctxt->nbErrors++;
1785 return(NULL);
1786 }
1787 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1788 if (library == NULL)
1789 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1790
1791 def = xmlRelaxNGNewDefine(ctxt, node);
1792 if (def == NULL) {
1793 xmlFree(type);
1794 return(NULL);
1795 }
1796 def->type = XML_RELAXNG_DATATYPE;
1797 def->name = type;
1798 def->ns = library;
1799
1800 lib = (xmlRelaxNGTypeLibraryPtr)
1801 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1802 if (lib == NULL) {
1803 if (ctxt->error != NULL)
1804 ctxt->error(ctxt->userData,
1805 "Use of unregistered type library '%s'\n",
1806 library);
1807 ctxt->nbErrors++;
1808 def->data = NULL;
1809 } else {
1810 def->data = lib;
1811 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001812 if (ctxt->error != NULL)
1813 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001814 "Internal error with type library '%s': no 'have'\n",
1815 library);
1816 ctxt->nbErrors++;
1817 } else {
1818 tmp = lib->have(lib->data, def->name);
1819 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001820 if (ctxt->error != NULL)
1821 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001822 "Error type '%s' is not exported by type library '%s'\n",
1823 def->name, library);
1824 ctxt->nbErrors++;
1825 }
1826 }
1827 }
1828 content = node->children;
1829 while (content != NULL) {
1830 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001831 ctxt->nbErrors++;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001832 content = content->next;
1833 }
1834
1835 return(def);
1836}
1837
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001838/**
1839 * xmlRelaxNGCompareElemDefLists:
1840 * @ctxt: a Relax-NG parser context
1841 * @defs1: the first list of element defs
1842 * @defs2: the second list of element defs
1843 *
1844 * Compare the 2 lists of element definitions. The comparison is
1845 * that if both lists do not accept the same QNames, it returns 1
1846 * If the 2 lists can accept the same QName the comparison returns 0
1847 *
1848 * Returns 1 disttinct, 0 if equal
1849 */
1850static int
1851xmlRelaxNGCompareElemDefLists(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1852 xmlRelaxNGDefinePtr *def1,
1853 xmlRelaxNGDefinePtr *def2) {
1854 xmlRelaxNGDefinePtr *basedef2 = def2;
1855
Daniel Veillard154877e2003-01-30 12:17:05 +00001856 if ((def1 == NULL) || (def2 == NULL))
1857 return(1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001858 if ((*def1 == NULL) || (*def2 == NULL))
1859 return(1);
1860 while (*def1 != NULL) {
1861 while ((*def2) != NULL) {
1862 if ((*def1)->name == NULL) {
1863 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1864 return(0);
1865 } else if ((*def2)->name == NULL) {
1866 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1867 return(0);
1868 } else if (xmlStrEqual((*def1)->name, (*def2)->name)) {
1869 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1870 return(0);
1871 }
1872 def2++;
1873 }
1874 def2 = basedef2;
1875 def1++;
1876 }
1877 return(1);
1878}
1879
1880/**
1881 * xmlRelaxNGGetElements:
1882 * @ctxt: a Relax-NG parser context
1883 * @def: the interleave definition
1884 *
1885 * Compute the list of top elements a definition can generate
1886 *
1887 * Returns a list of elements or NULL if none was found.
1888 */
1889static xmlRelaxNGDefinePtr *
1890xmlRelaxNGGetElements(xmlRelaxNGParserCtxtPtr ctxt,
1891 xmlRelaxNGDefinePtr def) {
1892 xmlRelaxNGDefinePtr *ret = NULL, parent, cur, tmp;
1893 int len = 0;
1894 int max = 0;
1895
1896 parent = NULL;
1897 cur = def;
1898 while (cur != NULL) {
Daniel Veillardb08c9812003-01-28 23:09:49 +00001899 if ((cur->type == XML_RELAXNG_ELEMENT) ||
1900 (cur->type == XML_RELAXNG_TEXT)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001901 if (ret == NULL) {
1902 max = 10;
1903 ret = (xmlRelaxNGDefinePtr *)
1904 xmlMalloc((max + 1) * sizeof(xmlRelaxNGDefinePtr));
1905 if (ret == NULL) {
1906 if (ctxt->error != NULL)
1907 ctxt->error(ctxt->userData,
1908 "Out of memory in element search\n");
1909 ctxt->nbErrors++;
1910 return(NULL);
1911 }
1912 } else if (max <= len) {
1913 max *= 2;
1914 ret = xmlRealloc(ret, (max + 1) * sizeof(xmlRelaxNGDefinePtr));
1915 if (ret == NULL) {
1916 if (ctxt->error != NULL)
1917 ctxt->error(ctxt->userData,
1918 "Out of memory in element search\n");
1919 ctxt->nbErrors++;
1920 return(NULL);
1921 }
1922 }
Daniel Veillardb08c9812003-01-28 23:09:49 +00001923 ret[len++] = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001924 ret[len] = NULL;
1925 } else if ((cur->type == XML_RELAXNG_CHOICE) ||
1926 (cur->type == XML_RELAXNG_INTERLEAVE) ||
1927 (cur->type == XML_RELAXNG_GROUP) ||
1928 (cur->type == XML_RELAXNG_ONEORMORE) ||
Daniel Veillardb08c9812003-01-28 23:09:49 +00001929 (cur->type == XML_RELAXNG_ZEROORMORE) ||
1930 (cur->type == XML_RELAXNG_OPTIONAL) ||
1931 (cur->type == XML_RELAXNG_REF) ||
1932 (cur->type == XML_RELAXNG_DEF)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001933 /*
1934 * Don't go within elements or attributes or string values.
1935 * Just gather the element top list
1936 */
1937 if (cur->content != NULL) {
1938 parent = cur;
1939 cur = cur->content;
1940 tmp = cur;
1941 while (tmp != NULL) {
1942 tmp->parent = parent;
1943 tmp = tmp->next;
1944 }
1945 continue;
1946 }
1947 }
Daniel Veillard154877e2003-01-30 12:17:05 +00001948 if (cur == def)
1949 return(ret);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001950 if (cur->next != NULL) {
1951 cur = cur->next;
1952 continue;
1953 }
1954 do {
1955 cur = cur->parent;
1956 if (cur == NULL) break;
1957 if (cur == def) return(ret);
1958 if (cur->next != NULL) {
1959 cur = cur->next;
1960 break;
1961 }
1962 } while (cur != NULL);
1963 }
1964 return(ret);
1965}
1966
1967/**
1968 * xmlRelaxNGComputeInterleaves:
1969 * @def: the interleave definition
1970 * @ctxt: a Relax-NG parser context
1971 * @node: the data node.
1972 *
1973 * A lot of work for preprocessing interleave definitions
1974 * is potentially needed to get a decent execution speed at runtime
1975 * - trying to get a total order on the element nodes generated
1976 * by the interleaves, order the list of interleave definitions
1977 * following that order.
1978 * - if <text/> is used to handle mixed content, it is better to
1979 * flag this in the define and simplify the runtime checking
1980 * algorithm
1981 */
1982static void
1983xmlRelaxNGComputeInterleaves(xmlRelaxNGDefinePtr def,
1984 xmlRelaxNGParserCtxtPtr ctxt,
1985 xmlChar *name ATTRIBUTE_UNUSED) {
1986 xmlRelaxNGDefinePtr cur;
1987
1988 xmlRelaxNGDefinePtr *list = NULL;
1989 xmlRelaxNGPartitionPtr partitions = NULL;
1990 xmlRelaxNGInterleaveGroupPtr *groups = NULL;
1991 xmlRelaxNGInterleaveGroupPtr group;
1992 int i,j,ret;
1993 int nbgroups = 0;
1994 int nbchild = 0;
1995
1996#ifdef DEBUG_INTERLEAVE
1997 xmlGenericError(xmlGenericErrorContext,
1998 "xmlRelaxNGComputeInterleaves(%s)\n",
1999 name);
2000#endif
2001 cur = def->content;
2002 while (cur != NULL) {
2003 nbchild++;
2004 cur = cur->next;
2005 }
2006
2007#ifdef DEBUG_INTERLEAVE
2008 xmlGenericError(xmlGenericErrorContext, " %d child\n", nbchild);
2009#endif
2010 groups = (xmlRelaxNGInterleaveGroupPtr *)
2011 xmlMalloc(nbchild * sizeof(xmlRelaxNGInterleaveGroupPtr));
2012 if (groups == NULL)
2013 goto error;
2014 cur = def->content;
2015 while (cur != NULL) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002016 groups[nbgroups] = (xmlRelaxNGInterleaveGroupPtr)
2017 xmlMalloc(sizeof(xmlRelaxNGInterleaveGroup));
2018 if (groups[nbgroups] == NULL)
2019 goto error;
2020 groups[nbgroups]->rule = cur;
2021 groups[nbgroups]->defs = xmlRelaxNGGetElements(ctxt, cur);
2022 nbgroups++;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002023 cur = cur->next;
2024 }
2025 list = NULL;
2026#ifdef DEBUG_INTERLEAVE
2027 xmlGenericError(xmlGenericErrorContext, " %d groups\n", nbgroups);
2028#endif
2029
2030 /*
2031 * Let's check that all rules makes a partitions according to 7.4
2032 */
2033 partitions = (xmlRelaxNGPartitionPtr)
2034 xmlMalloc(sizeof(xmlRelaxNGPartition));
2035 if (partitions == NULL)
2036 goto error;
2037 partitions->nbgroups = nbgroups;
2038 for (i = 0;i < nbgroups;i++) {
2039 group = groups[i];
2040 for (j = i+1;j < nbgroups;j++) {
2041 if (groups[j] == NULL)
2042 continue;
2043 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->defs,
2044 groups[j]->defs);
2045 if (ret == 0) {
2046 if (ctxt->error != NULL)
2047 ctxt->error(ctxt->userData,
2048 "Element or text conflicts in interleave\n");
2049 ctxt->nbErrors++;
2050 }
2051 }
2052 }
2053 partitions->groups = groups;
2054
2055 /*
2056 * Free Up the child list, and save the partition list back in the def
2057 */
2058 def->data = partitions;
2059 return;
2060
2061error:
2062 if (ctxt->error != NULL)
2063 ctxt->error(ctxt->userData,
2064 "Out of memory in interleave computation\n");
2065 ctxt->nbErrors++;
2066 if (list == NULL)
2067 xmlFree(list);
2068 if (groups != NULL) {
2069 for (i = 0;i < nbgroups;i++)
2070 if (groups[i] != NULL) {
2071 if (groups[i]->defs != NULL)
2072 xmlFree(groups[i]->defs);
2073 xmlFree(groups[i]);
2074 }
2075 xmlFree(groups);
2076 }
2077 xmlRelaxNGFreePartition(partitions);
2078}
2079
2080/**
2081 * xmlRelaxNGParseInterleave:
2082 * @ctxt: a Relax-NG parser context
2083 * @node: the data node.
2084 *
2085 * parse the content of a RelaxNG interleave node.
2086 *
2087 * Returns the definition pointer or NULL in case of error
2088 */
2089static xmlRelaxNGDefinePtr
2090xmlRelaxNGParseInterleave(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2091 xmlRelaxNGDefinePtr def = NULL;
2092 xmlRelaxNGDefinePtr last = NULL, cur;
2093 xmlNodePtr child;
2094
2095 def = xmlRelaxNGNewDefine(ctxt, node);
2096 if (def == NULL) {
2097 return(NULL);
2098 }
2099 def->type = XML_RELAXNG_INTERLEAVE;
2100
2101 if (ctxt->interleaves == NULL)
2102 ctxt->interleaves = xmlHashCreate(10);
2103 if (ctxt->interleaves == NULL) {
2104 if (ctxt->error != NULL)
2105 ctxt->error(ctxt->userData,
2106 "Failed to create interleaves hash table\n");
2107 ctxt->nbErrors++;
2108 } else {
2109 char name[32];
2110
2111 snprintf(name, 32, "interleave%d", ctxt->nbInterleaves++);
2112 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST name, def) < 0) {
2113 if (ctxt->error != NULL)
2114 ctxt->error(ctxt->userData,
2115 "Failed to add %s to hash table\n", name);
2116 ctxt->nbErrors++;
2117 }
2118 }
2119 child = node->children;
2120 while (child != NULL) {
2121 if (IS_RELAXNG(child, "element")) {
2122 cur = xmlRelaxNGParseElement(ctxt, child);
2123 } else {
2124 cur = xmlRelaxNGParsePattern(ctxt, child);
2125 }
2126 if (cur != NULL) {
2127 cur->parent = def;
2128 if (last == NULL) {
2129 def->content = last = cur;
2130 } else {
2131 last->next = cur;
2132 last = cur;
2133 }
2134 }
2135 child = child->next;
2136 }
2137
2138 return(def);
2139}
Daniel Veillard6eadf632003-01-23 18:29:16 +00002140
2141/**
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002142 * xmlRelaxNGParseInclude:
2143 * @ctxt: a Relax-NG parser context
2144 * @node: the include node
2145 *
2146 * Integrate the content of an include node in the current grammar
2147 *
2148 * Returns 0 in case of success or -1 in case of error
2149 */
2150static int
2151xmlRelaxNGParseInclude(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2152 xmlRelaxNGIncludePtr incl;
2153 xmlNodePtr root;
2154 int ret = 0, tmp;
2155
2156 incl = node->_private;
2157 if (incl == NULL) {
2158 if (ctxt->error != NULL)
2159 ctxt->error(ctxt->userData,
2160 "Include node has no data\n");
2161 ctxt->nbErrors++;
2162 return(-1);
2163 }
2164 root = xmlDocGetRootElement(incl->doc);
2165 if (root == NULL) {
2166 if (ctxt->error != NULL)
2167 ctxt->error(ctxt->userData,
2168 "Include document is empty\n");
2169 ctxt->nbErrors++;
2170 return(-1);
2171 }
2172 if (!xmlStrEqual(root->name, BAD_CAST "grammar")) {
2173 if (ctxt->error != NULL)
2174 ctxt->error(ctxt->userData,
2175 "Include document root is not a grammar\n");
2176 ctxt->nbErrors++;
2177 return(-1);
2178 }
2179
2180 /*
2181 * Merge the definition from both the include and the internal list
2182 */
2183 if (root->children != NULL) {
2184 tmp = xmlRelaxNGParseGrammarContent(ctxt, root->children);
2185 if (tmp != 0)
2186 ret = -1;
2187 }
2188 if (node->children != NULL) {
2189 tmp = xmlRelaxNGParseGrammarContent(ctxt, node->children);
2190 if (tmp != 0)
2191 ret = -1;
2192 }
2193 return(ret);
2194}
2195
2196/**
Daniel Veillard276be4a2003-01-24 01:03:34 +00002197 * xmlRelaxNGParseDefine:
2198 * @ctxt: a Relax-NG parser context
2199 * @node: the define node
2200 *
2201 * parse the content of a RelaxNG define element node.
2202 *
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002203 * Returns 0 in case of success or -1 in case of error
Daniel Veillard276be4a2003-01-24 01:03:34 +00002204 */
2205static int
2206xmlRelaxNGParseDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2207 xmlChar *name;
2208 int ret = 0, tmp;
2209 xmlRelaxNGDefinePtr def;
2210 const xmlChar *olddefine;
2211
2212 name = xmlGetProp(node, BAD_CAST "name");
2213 if (name == NULL) {
2214 if (ctxt->error != NULL)
2215 ctxt->error(ctxt->userData,
2216 "define has no name\n");
2217 ctxt->nbErrors++;
2218 } else {
2219 def = xmlRelaxNGNewDefine(ctxt, node);
2220 if (def == NULL) {
2221 xmlFree(name);
2222 return(-1);
2223 }
2224 def->type = XML_RELAXNG_DEF;
2225 def->name = name;
2226 if (node->children == NULL) {
2227 if (ctxt->error != NULL)
2228 ctxt->error(ctxt->userData,
2229 "define has no children\n");
2230 ctxt->nbErrors++;
2231 } else {
2232 olddefine = ctxt->define;
2233 ctxt->define = name;
Daniel Veillard154877e2003-01-30 12:17:05 +00002234 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard276be4a2003-01-24 01:03:34 +00002235 ctxt->define = olddefine;
2236 }
2237 if (ctxt->grammar->defs == NULL)
2238 ctxt->grammar->defs = xmlHashCreate(10);
2239 if (ctxt->grammar->defs == NULL) {
2240 if (ctxt->error != NULL)
2241 ctxt->error(ctxt->userData,
2242 "Could not create definition hash\n");
2243 ctxt->nbErrors++;
2244 ret = -1;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002245 } else {
2246 tmp = xmlHashAddEntry(ctxt->grammar->defs, name, def);
2247 if (tmp < 0) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002248 xmlRelaxNGDefinePtr prev;
2249
2250 prev = xmlHashLookup(ctxt->grammar->defs, name);
2251 if (prev == NULL) {
2252 if (ctxt->error != NULL)
2253 ctxt->error(ctxt->userData,
2254 "Internal error on define aggregation of %s\n",
2255 name);
2256 ctxt->nbErrors++;
2257 ret = -1;
Daniel Veillard154877e2003-01-30 12:17:05 +00002258 } else {
2259 while (prev->nextHash != NULL)
2260 prev = prev->nextHash;
2261 prev->nextHash = def;
2262 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002263 }
2264 }
2265 }
2266 return(ret);
2267}
2268
2269/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00002270 * xmlRelaxNGParsePattern:
2271 * @ctxt: a Relax-NG parser context
2272 * @node: the pattern node.
2273 *
2274 * parse the content of a RelaxNG pattern node.
2275 *
Daniel Veillard276be4a2003-01-24 01:03:34 +00002276 * Returns the definition pointer or NULL in case of error or if no
2277 * pattern is generated.
Daniel Veillard6eadf632003-01-23 18:29:16 +00002278 */
2279static xmlRelaxNGDefinePtr
2280xmlRelaxNGParsePattern(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2281 xmlRelaxNGDefinePtr def = NULL;
2282
2283 if (IS_RELAXNG(node, "element")) {
2284 def = xmlRelaxNGParseElement(ctxt, node);
2285 } else if (IS_RELAXNG(node, "attribute")) {
2286 def = xmlRelaxNGParseAttribute(ctxt, node);
2287 } else if (IS_RELAXNG(node, "empty")) {
2288 def = xmlRelaxNGNewDefine(ctxt, node);
2289 if (def == NULL)
2290 return(NULL);
2291 def->type = XML_RELAXNG_EMPTY;
2292 } else if (IS_RELAXNG(node, "text")) {
2293 def = xmlRelaxNGNewDefine(ctxt, node);
2294 if (def == NULL)
2295 return(NULL);
2296 def->type = XML_RELAXNG_TEXT;
2297 if (node->children != NULL) {
2298 if (ctxt->error != NULL)
2299 ctxt->error(ctxt->userData, "text: had a child node\n");
2300 ctxt->nbErrors++;
2301 }
2302 } else if (IS_RELAXNG(node, "zeroOrMore")) {
2303 def = xmlRelaxNGNewDefine(ctxt, node);
2304 if (def == NULL)
2305 return(NULL);
2306 def->type = XML_RELAXNG_ZEROORMORE;
Daniel Veillard154877e2003-01-30 12:17:05 +00002307 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002308 } else if (IS_RELAXNG(node, "oneOrMore")) {
2309 def = xmlRelaxNGNewDefine(ctxt, node);
2310 if (def == NULL)
2311 return(NULL);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002312 def->type = XML_RELAXNG_ONEORMORE;
Daniel Veillard154877e2003-01-30 12:17:05 +00002313 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002314 } else if (IS_RELAXNG(node, "optional")) {
2315 def = xmlRelaxNGNewDefine(ctxt, node);
2316 if (def == NULL)
2317 return(NULL);
2318 def->type = XML_RELAXNG_OPTIONAL;
Daniel Veillard154877e2003-01-30 12:17:05 +00002319 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002320 } else if (IS_RELAXNG(node, "choice")) {
2321 def = xmlRelaxNGNewDefine(ctxt, node);
2322 if (def == NULL)
2323 return(NULL);
2324 def->type = XML_RELAXNG_CHOICE;
Daniel Veillard154877e2003-01-30 12:17:05 +00002325 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002326 } else if (IS_RELAXNG(node, "group")) {
2327 def = xmlRelaxNGNewDefine(ctxt, node);
2328 if (def == NULL)
2329 return(NULL);
2330 def->type = XML_RELAXNG_GROUP;
Daniel Veillard154877e2003-01-30 12:17:05 +00002331 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002332 } else if (IS_RELAXNG(node, "ref")) {
2333 def = xmlRelaxNGNewDefine(ctxt, node);
2334 if (def == NULL)
2335 return(NULL);
2336 def->type = XML_RELAXNG_REF;
2337 def->name = xmlGetProp(node, BAD_CAST "name");
2338 if (def->name == NULL) {
2339 if (ctxt->error != NULL)
2340 ctxt->error(ctxt->userData,
2341 "ref has no name\n");
2342 ctxt->nbErrors++;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002343 } else {
2344 if ((ctxt->define != NULL) &&
2345 (xmlStrEqual(ctxt->define, def->name))) {
2346 if (ctxt->error != NULL)
2347 ctxt->error(ctxt->userData,
2348 "Recursive reference to %s not in an element\n",
2349 def->name);
2350 ctxt->nbErrors++;
2351 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002352 }
2353 if (node->children != NULL) {
2354 if (ctxt->error != NULL)
2355 ctxt->error(ctxt->userData,
2356 "ref is not empty\n");
2357 ctxt->nbErrors++;
2358 }
2359 if (ctxt->grammar->refs == NULL)
2360 ctxt->grammar->refs = xmlHashCreate(10);
2361 if (ctxt->grammar->refs == NULL) {
2362 if (ctxt->error != NULL)
2363 ctxt->error(ctxt->userData,
2364 "Could not create references hash\n");
2365 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002366 def = NULL;
2367 } else {
2368 int tmp;
2369
2370 tmp = xmlHashAddEntry(ctxt->grammar->refs, def->name, def);
2371 if (tmp < 0) {
2372 xmlRelaxNGDefinePtr prev;
2373
2374 prev = (xmlRelaxNGDefinePtr)
2375 xmlHashLookup(ctxt->grammar->refs, def->name);
2376 if (prev == NULL) {
2377 if (ctxt->error != NULL)
2378 ctxt->error(ctxt->userData,
2379 "Internal error refs definitions '%s'\n",
2380 def->name);
2381 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002382 def = NULL;
2383 } else {
2384 def->nextHash = prev->nextHash;
2385 prev->nextHash = def;
2386 }
2387 }
2388 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002389 } else if (IS_RELAXNG(node, "data")) {
2390 def = xmlRelaxNGParseData(ctxt, node);
Daniel Veillard276be4a2003-01-24 01:03:34 +00002391 } else if (IS_RELAXNG(node, "define")) {
2392 xmlRelaxNGParseDefine(ctxt, node);
2393 def = NULL;
Daniel Veillardedc91922003-01-26 00:52:04 +00002394 } else if (IS_RELAXNG(node, "value")) {
2395 def = xmlRelaxNGParseValue(ctxt, node);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002396 } else if (IS_RELAXNG(node, "list")) {
2397 def = xmlRelaxNGNewDefine(ctxt, node);
2398 if (def == NULL)
2399 return(NULL);
2400 def->type = XML_RELAXNG_LIST;
Daniel Veillard154877e2003-01-30 12:17:05 +00002401 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002402 } else if (IS_RELAXNG(node, "interleave")) {
2403 def = xmlRelaxNGParseInterleave(ctxt, node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002404 } else if (IS_RELAXNG(node, "externalRef")) {
2405 xmlRelaxNGDocumentPtr docu;
2406 xmlNodePtr root;
2407
2408 docu = node->_private;
2409 if (docu != NULL) {
2410 def = xmlRelaxNGNewDefine(ctxt, node);
2411 if (def == NULL)
2412 return(NULL);
2413 def->type = XML_RELAXNG_EXTERNALREF;
2414
2415 if (docu->content == NULL) {
2416 /*
2417 * Then do the parsing for good
2418 */
2419 root = xmlDocGetRootElement(docu->doc);
2420 if (root == NULL) {
2421 if (ctxt->error != NULL)
2422 ctxt->error(ctxt->userData,
2423 "xmlRelaxNGParse: %s is empty\n",
2424 ctxt->URL);
2425 ctxt->nbErrors++;
2426 return (NULL);
2427 }
2428 docu->schema = xmlRelaxNGParseDocument(ctxt, root);
2429 if ((docu->schema != NULL) &&
2430 (docu->schema->topgrammar != NULL)) {
2431 docu->content = docu->schema->topgrammar->start;
2432 }
2433 }
2434 def->content = docu->content;
2435 } else {
2436 def = NULL;
2437 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002438 } else if (IS_RELAXNG(node, "notAllowed")) {
2439 def = xmlRelaxNGNewDefine(ctxt, node);
2440 if (def == NULL)
2441 return(NULL);
2442 def->type = XML_RELAXNG_NOT_ALLOWED;
2443 if (node->children != NULL) {
2444 if (ctxt->error != NULL)
2445 ctxt->error(ctxt->userData,
2446 "xmlRelaxNGParse: notAllowed element is not empty\n");
2447 ctxt->nbErrors++;
2448 }
Daniel Veillard419a7682003-02-03 23:22:49 +00002449 } else if (IS_RELAXNG(node, "grammar")) {
2450 xmlRelaxNGGrammarPtr grammar, old;
2451 xmlRelaxNGGrammarPtr oldparent;
2452
2453 oldparent = ctxt->parentgrammar;
2454 old = ctxt->grammar;
2455 ctxt->parentgrammar = old;
2456 grammar = xmlRelaxNGParseGrammar(ctxt, node->children);
2457 if (old != NULL) {
2458 ctxt->grammar = old;
2459 ctxt->parentgrammar = oldparent;
2460 if (grammar != NULL) {
2461 grammar->next = old->next;
2462 old->next = grammar;
2463 }
2464 }
2465 if (grammar != NULL)
2466 def = grammar->start;
2467 else
2468 def = NULL;
2469 } else if (IS_RELAXNG(node, "parentRef")) {
2470 if (ctxt->parentgrammar == NULL) {
2471 if (ctxt->error != NULL)
2472 ctxt->error(ctxt->userData,
2473 "Use of parentRef without a parent grammar\n");
2474 ctxt->nbErrors++;
2475 return(NULL);
2476 }
2477 def = xmlRelaxNGNewDefine(ctxt, node);
2478 if (def == NULL)
2479 return(NULL);
2480 def->type = XML_RELAXNG_PARENTREF;
2481 def->name = xmlGetProp(node, BAD_CAST "name");
2482 if (def->name == NULL) {
2483 if (ctxt->error != NULL)
2484 ctxt->error(ctxt->userData,
2485 "parentRef has no name\n");
2486 ctxt->nbErrors++;
2487 }
2488 if (node->children != NULL) {
2489 if (ctxt->error != NULL)
2490 ctxt->error(ctxt->userData,
2491 "parentRef is not empty\n");
2492 ctxt->nbErrors++;
2493 }
2494 if (ctxt->parentgrammar->refs == NULL)
2495 ctxt->parentgrammar->refs = xmlHashCreate(10);
2496 if (ctxt->parentgrammar->refs == NULL) {
2497 if (ctxt->error != NULL)
2498 ctxt->error(ctxt->userData,
2499 "Could not create references hash\n");
2500 ctxt->nbErrors++;
2501 def = NULL;
2502 } else {
2503 int tmp;
2504
2505 tmp = xmlHashAddEntry(ctxt->parentgrammar->refs, def->name, def);
2506 if (tmp < 0) {
2507 xmlRelaxNGDefinePtr prev;
2508
2509 prev = (xmlRelaxNGDefinePtr)
2510 xmlHashLookup(ctxt->parentgrammar->refs, def->name);
2511 if (prev == NULL) {
2512 if (ctxt->error != NULL)
2513 ctxt->error(ctxt->userData,
2514 "Internal error parentRef definitions '%s'\n",
2515 def->name);
2516 ctxt->nbErrors++;
2517 def = NULL;
2518 } else {
2519 def->nextHash = prev->nextHash;
2520 prev->nextHash = def;
2521 }
2522 }
2523 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002524 } else {
2525 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002526 ctxt->nbErrors++;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002527 def = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002528 }
2529 return(def);
2530}
2531
2532/**
2533 * xmlRelaxNGParseAttribute:
2534 * @ctxt: a Relax-NG parser context
2535 * @node: the element node
2536 *
2537 * parse the content of a RelaxNG attribute node.
2538 *
2539 * Returns the definition pointer or NULL in case of error.
2540 */
2541static xmlRelaxNGDefinePtr
2542xmlRelaxNGParseAttribute(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2543 xmlRelaxNGDefinePtr ret, cur, last;
2544 xmlNodePtr child;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002545 int old_flags;
2546
2547 ret = xmlRelaxNGNewDefine(ctxt, node);
2548 if (ret == NULL)
2549 return(NULL);
2550 ret->type = XML_RELAXNG_ATTRIBUTE;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002551 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002552 child = node->children;
2553 if (child == NULL) {
2554 if (ctxt->error != NULL)
2555 ctxt->error(ctxt->userData,
2556 "xmlRelaxNGParseattribute: attribute has no children\n");
2557 ctxt->nbErrors++;
2558 return(ret);
2559 }
2560 old_flags = ctxt->flags;
2561 ctxt->flags |= XML_RELAXNG_IN_ATTRIBUTE;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002562 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
2563 if (cur != NULL)
2564 child = child->next;
2565
Daniel Veillard6eadf632003-01-23 18:29:16 +00002566 last = NULL;
2567 while (child != NULL) {
2568 cur = xmlRelaxNGParsePattern(ctxt, child);
2569 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002570 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002571 switch (cur->type) {
2572 case XML_RELAXNG_EMPTY:
2573 case XML_RELAXNG_NOT_ALLOWED:
2574 case XML_RELAXNG_TEXT:
2575 case XML_RELAXNG_ELEMENT:
2576 case XML_RELAXNG_DATATYPE:
2577 case XML_RELAXNG_VALUE:
2578 case XML_RELAXNG_LIST:
2579 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00002580 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002581 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00002582 case XML_RELAXNG_DEF:
2583 case XML_RELAXNG_ONEORMORE:
2584 case XML_RELAXNG_ZEROORMORE:
2585 case XML_RELAXNG_OPTIONAL:
2586 case XML_RELAXNG_CHOICE:
2587 case XML_RELAXNG_GROUP:
2588 case XML_RELAXNG_INTERLEAVE:
2589 if (last == NULL) {
2590 ret->content = last = cur;
2591 } else {
2592 if ((last->type == XML_RELAXNG_ELEMENT) &&
2593 (ret->content == last)) {
2594 ret->content = xmlRelaxNGNewDefine(ctxt, node);
2595 if (ret->content != NULL) {
2596 ret->content->type = XML_RELAXNG_GROUP;
2597 ret->content->content = last;
2598 } else {
2599 ret->content = last;
2600 }
2601 }
2602 last->next = cur;
2603 last = cur;
2604 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002605 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002606 break;
2607 case XML_RELAXNG_ATTRIBUTE:
2608 cur->next = ret->attrs;
2609 ret->attrs = cur;
2610 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002611 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00002612 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002613 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002614 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002615 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002616 }
2617 }
2618 child = child->next;
2619 }
2620 ctxt->flags = old_flags;
2621 return(ret);
2622}
2623
2624/**
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002625 * xmlRelaxNGParseExceptNameClass:
2626 * @ctxt: a Relax-NG parser context
2627 * @node: the except node
Daniel Veillard144fae12003-02-03 13:17:57 +00002628 * @attr: 1 if within an attribute, 0 if within an element
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002629 *
2630 * parse the content of a RelaxNG nameClass node.
2631 *
2632 * Returns the definition pointer or NULL in case of error.
2633 */
2634static xmlRelaxNGDefinePtr
Daniel Veillard144fae12003-02-03 13:17:57 +00002635xmlRelaxNGParseExceptNameClass(xmlRelaxNGParserCtxtPtr ctxt,
2636 xmlNodePtr node, int attr) {
2637 xmlRelaxNGDefinePtr ret, cur, last = NULL;
2638 xmlNodePtr child;
2639
2640 if (!IS_RELAXNG(node, "except"))
2641 return(NULL);
2642 if (node->children == NULL) {
2643 if (ctxt->error != NULL)
2644 ctxt->error(ctxt->userData,
2645 "except has no content\n");
2646 ctxt->nbErrors++;
2647 return(NULL);
2648 }
2649
2650 ret = xmlRelaxNGNewDefine(ctxt, node);
2651 if (ret == NULL)
2652 return(NULL);
2653 ret->type = XML_RELAXNG_EXCEPT;
2654 child = node->children;
2655 while (child != NULL) {
2656 cur = xmlRelaxNGNewDefine(ctxt, child);
2657 if (cur == NULL)
2658 break;
2659 if (attr)
2660 cur->type = XML_RELAXNG_ATTRIBUTE;
2661 else
2662 cur->type = XML_RELAXNG_ELEMENT;
2663
Daniel Veillard419a7682003-02-03 23:22:49 +00002664 if (xmlRelaxNGParseNameClass(ctxt, child, cur) != NULL) {
Daniel Veillard144fae12003-02-03 13:17:57 +00002665 if (last == NULL) {
2666 ret->content = cur;
2667 } else {
2668 last->next = cur;
2669 }
2670 last = cur;
2671 }
2672 child = child->next;
2673 }
2674
2675 return(ret);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002676}
2677
2678/**
2679 * xmlRelaxNGParseNameClass:
2680 * @ctxt: a Relax-NG parser context
2681 * @node: the nameClass node
2682 * @def: the current definition
2683 *
2684 * parse the content of a RelaxNG nameClass node.
2685 *
2686 * Returns the definition pointer or NULL in case of error.
2687 */
2688static xmlRelaxNGDefinePtr
2689xmlRelaxNGParseNameClass(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
2690 xmlRelaxNGDefinePtr def) {
2691 xmlRelaxNGDefinePtr ret = def;
2692 xmlChar *val;
2693
2694 if (IS_RELAXNG(node, "name")) {
2695 val = xmlNodeGetContent(node);
2696 ret->name = val;
2697 val = xmlGetProp(node, BAD_CAST "ns");
2698 ret->ns = val;
2699 } else if (IS_RELAXNG(node, "anyName")) {
2700 ret->name = NULL;
2701 ret->ns = NULL;
2702 if (node->children != NULL) {
2703 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00002704 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
2705 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002706 }
2707 } else if (IS_RELAXNG(node, "nsName")) {
2708 ret->name = NULL;
2709 ret->ns = xmlGetProp(node, BAD_CAST "ns");
2710 if (ret->ns == NULL) {
2711 if (ctxt->error != NULL)
2712 ctxt->error(ctxt->userData,
2713 "nsName has no ns attribute\n");
2714 ctxt->nbErrors++;
2715 }
2716 if (node->children != NULL) {
2717 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00002718 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
2719 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002720 }
2721 } else if (IS_RELAXNG(node, "choice")) {
2722 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002723 ctxt->nbErrors++;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002724 } else {
2725 if (ctxt->error != NULL)
2726 ctxt->error(ctxt->userData,
2727 "expecting name, anyName, nsName or choice : got %s\n",
2728 node->name);
2729 ctxt->nbErrors++;
2730 return(NULL);
2731 }
2732 return(ret);
2733}
2734
2735/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00002736 * xmlRelaxNGParseElement:
2737 * @ctxt: a Relax-NG parser context
2738 * @node: the element node
2739 *
2740 * parse the content of a RelaxNG element node.
2741 *
2742 * Returns the definition pointer or NULL in case of error.
2743 */
2744static xmlRelaxNGDefinePtr
2745xmlRelaxNGParseElement(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2746 xmlRelaxNGDefinePtr ret, cur, last;
2747 xmlNodePtr child;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002748 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002749
2750 ret = xmlRelaxNGNewDefine(ctxt, node);
2751 if (ret == NULL)
2752 return(NULL);
2753 ret->type = XML_RELAXNG_ELEMENT;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002754 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002755 child = node->children;
2756 if (child == NULL) {
2757 if (ctxt->error != NULL)
2758 ctxt->error(ctxt->userData,
2759 "xmlRelaxNGParseElement: element has no children\n");
2760 ctxt->nbErrors++;
2761 return(ret);
2762 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002763 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
2764 if (cur != NULL)
2765 child = child->next;
2766
Daniel Veillard6eadf632003-01-23 18:29:16 +00002767 if (child == NULL) {
2768 if (ctxt->error != NULL)
2769 ctxt->error(ctxt->userData,
2770 "xmlRelaxNGParseElement: element has no content\n");
2771 ctxt->nbErrors++;
2772 return(ret);
2773 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002774 olddefine = ctxt->define;
2775 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002776 last = NULL;
2777 while (child != NULL) {
2778 cur = xmlRelaxNGParsePattern(ctxt, child);
2779 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002780 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002781 switch (cur->type) {
2782 case XML_RELAXNG_EMPTY:
2783 case XML_RELAXNG_NOT_ALLOWED:
2784 case XML_RELAXNG_TEXT:
2785 case XML_RELAXNG_ELEMENT:
2786 case XML_RELAXNG_DATATYPE:
2787 case XML_RELAXNG_VALUE:
2788 case XML_RELAXNG_LIST:
2789 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00002790 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002791 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00002792 case XML_RELAXNG_DEF:
2793 case XML_RELAXNG_ZEROORMORE:
2794 case XML_RELAXNG_ONEORMORE:
2795 case XML_RELAXNG_OPTIONAL:
2796 case XML_RELAXNG_CHOICE:
2797 case XML_RELAXNG_GROUP:
2798 case XML_RELAXNG_INTERLEAVE:
2799 if (last == NULL) {
2800 ret->content = last = cur;
2801 } else {
2802 if ((last->type == XML_RELAXNG_ELEMENT) &&
2803 (ret->content == last)) {
2804 ret->content = xmlRelaxNGNewDefine(ctxt, node);
2805 if (ret->content != NULL) {
2806 ret->content->type = XML_RELAXNG_GROUP;
2807 ret->content->content = last;
2808 } else {
2809 ret->content = last;
2810 }
2811 }
2812 last->next = cur;
2813 last = cur;
2814 }
2815 break;
2816 case XML_RELAXNG_ATTRIBUTE:
2817 cur->next = ret->attrs;
2818 ret->attrs = cur;
2819 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002820 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00002821 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002822 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002823 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002824 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002825 }
2826 }
2827 child = child->next;
2828 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002829 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002830 return(ret);
2831}
2832
2833/**
2834 * xmlRelaxNGParsePatterns:
2835 * @ctxt: a Relax-NG parser context
2836 * @nodes: list of nodes
Daniel Veillard154877e2003-01-30 12:17:05 +00002837 * @group: use an implicit <group> for elements
Daniel Veillard6eadf632003-01-23 18:29:16 +00002838 *
2839 * parse the content of a RelaxNG start node.
2840 *
2841 * Returns the definition pointer or NULL in case of error.
2842 */
2843static xmlRelaxNGDefinePtr
Daniel Veillard154877e2003-01-30 12:17:05 +00002844xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes,
2845 int group) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002846 xmlRelaxNGDefinePtr def = NULL, last = NULL, cur, parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002847
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002848 parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002849 while (nodes != NULL) {
2850 if (IS_RELAXNG(nodes, "element")) {
2851 cur = xmlRelaxNGParseElement(ctxt, nodes);
2852 if (def == NULL) {
2853 def = last = cur;
2854 } else {
Daniel Veillard154877e2003-01-30 12:17:05 +00002855 if ((group == 1) && (def->type == XML_RELAXNG_ELEMENT) &&
2856 (def == last)) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00002857 def = xmlRelaxNGNewDefine(ctxt, nodes);
2858 def->type = XML_RELAXNG_GROUP;
2859 def->content = last;
2860 }
2861 last->next = cur;
2862 last = cur;
2863 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002864 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002865 } else {
2866 cur = xmlRelaxNGParsePattern(ctxt, nodes);
Daniel Veillard419a7682003-02-03 23:22:49 +00002867 if (cur != NULL) {
2868 if (def == NULL) {
2869 def = last = cur;
2870 } else {
2871 last->next = cur;
2872 last = cur;
2873 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002874 }
2875 }
2876 nodes = nodes->next;
2877 }
2878 return(def);
2879}
2880
2881/**
2882 * xmlRelaxNGParseStart:
2883 * @ctxt: a Relax-NG parser context
2884 * @nodes: start children nodes
2885 *
2886 * parse the content of a RelaxNG start node.
2887 *
2888 * Returns 0 in case of success, -1 in case of error
2889 */
2890static int
2891xmlRelaxNGParseStart(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
2892 int ret = 0;
2893 xmlRelaxNGDefinePtr def = NULL;
2894
2895 while (nodes != NULL) {
2896 if (IS_RELAXNG(nodes, "empty")) {
2897 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002898 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002899 } else if (IS_RELAXNG(nodes, "notAllowed")) {
2900 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002901 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002902 } else {
Daniel Veillard154877e2003-01-30 12:17:05 +00002903 def = xmlRelaxNGParsePatterns(ctxt, nodes, 1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002904 ctxt->grammar->start = def;
2905 }
2906 nodes = nodes->next;
2907 }
2908 return(ret);
2909}
2910
2911/**
2912 * xmlRelaxNGParseGrammarContent:
2913 * @ctxt: a Relax-NG parser context
2914 * @nodes: grammar children nodes
2915 *
2916 * parse the content of a RelaxNG grammar node.
2917 *
2918 * Returns 0 in case of success, -1 in case of error
2919 */
2920static int
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002921xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes)
Daniel Veillard6eadf632003-01-23 18:29:16 +00002922{
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002923 int ret = 0, tmp;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002924
2925 if (nodes == NULL) {
2926 if (ctxt->error != NULL)
2927 ctxt->error(ctxt->userData,
2928 "grammar has no children\n");
2929 ctxt->nbErrors++;
2930 return(-1);
2931 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002932 while (nodes != NULL) {
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002933 if (IS_RELAXNG(nodes, "start")) {
2934 if (nodes->children == NULL) {
2935 if (ctxt->error != NULL)
2936 ctxt->error(ctxt->userData,
2937 "grammar has no children\n");
2938 ctxt->nbErrors++;
2939 } else {
2940 tmp = xmlRelaxNGParseStart(ctxt, nodes->children);
2941 if (tmp != 0)
2942 ret = -1;
2943 }
2944 } else if (IS_RELAXNG(nodes, "define")) {
2945 tmp = xmlRelaxNGParseDefine(ctxt, nodes);
2946 if (tmp != 0)
2947 ret = -1;
2948 } else if (IS_RELAXNG(nodes, "include")) {
2949 tmp = xmlRelaxNGParseInclude(ctxt, nodes);
2950 if (tmp != 0)
2951 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002952 } else {
2953 if (ctxt->error != NULL)
2954 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002955 "grammar has unexpected child %s\n", nodes->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002956 ctxt->nbErrors++;
2957 ret = -1;
2958 }
2959 nodes = nodes->next;
2960 }
2961 return (ret);
2962}
2963
2964/**
2965 * xmlRelaxNGCheckReference:
2966 * @ref: the ref
2967 * @ctxt: a Relax-NG parser context
2968 * @name: the name associated to the defines
2969 *
2970 * Applies the 4.17. combine attribute rule for all the define
2971 * element of a given grammar using the same name.
2972 */
2973static void
2974xmlRelaxNGCheckReference(xmlRelaxNGDefinePtr ref,
2975 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
2976 xmlRelaxNGGrammarPtr grammar;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002977 xmlRelaxNGDefinePtr def, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002978
2979 grammar = ctxt->grammar;
2980 if (grammar == NULL) {
2981 if (ctxt->error != NULL)
2982 ctxt->error(ctxt->userData,
2983 "Internal error: no grammar in CheckReference %s\n",
2984 name);
2985 ctxt->nbErrors++;
2986 return;
2987 }
2988 if (ref->content != NULL) {
2989 if (ctxt->error != NULL)
2990 ctxt->error(ctxt->userData,
2991 "Internal error: reference has content in CheckReference %s\n",
2992 name);
2993 ctxt->nbErrors++;
2994 return;
2995 }
2996 if (grammar->defs != NULL) {
2997 def = xmlHashLookup(grammar->defs, name);
2998 if (def != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00002999 cur = ref;
3000 while (cur != NULL) {
3001 cur->content = def;
3002 cur = cur->nextHash;
3003 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003004 } else {
3005 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003006 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003007 }
3008 }
3009 /*
3010 * TODO: make a closure and verify there is no loop !
3011 */
3012}
3013
3014/**
3015 * xmlRelaxNGCheckCombine:
3016 * @define: the define(s) list
3017 * @ctxt: a Relax-NG parser context
3018 * @name: the name associated to the defines
3019 *
3020 * Applies the 4.17. combine attribute rule for all the define
3021 * element of a given grammar using the same name.
3022 */
3023static void
3024xmlRelaxNGCheckCombine(xmlRelaxNGDefinePtr define,
3025 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
3026 xmlChar *combine;
3027 int choiceOrInterleave = -1;
3028 int missing = 0;
3029 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
3030
3031 if (define->nextHash == NULL)
3032 return;
3033 cur = define;
3034 while (cur != NULL) {
3035 combine = xmlGetProp(cur->node, BAD_CAST "combine");
3036 if (combine != NULL) {
3037 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3038 if (choiceOrInterleave == -1)
3039 choiceOrInterleave = 1;
3040 else if (choiceOrInterleave == 0) {
3041 if (ctxt->error != NULL)
3042 ctxt->error(ctxt->userData,
3043 "Defines for %s use both 'choice' and 'interleave'\n",
3044 name);
3045 ctxt->nbErrors++;
3046 }
Daniel Veillard154877e2003-01-30 12:17:05 +00003047 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003048 if (choiceOrInterleave == -1)
3049 choiceOrInterleave = 0;
3050 else if (choiceOrInterleave == 1) {
3051 if (ctxt->error != NULL)
3052 ctxt->error(ctxt->userData,
3053 "Defines for %s use both 'choice' and 'interleave'\n",
3054 name);
3055 ctxt->nbErrors++;
3056 }
3057 } else {
3058 if (ctxt->error != NULL)
3059 ctxt->error(ctxt->userData,
3060 "Defines for %s use unknown combine value '%s''\n",
3061 name, combine);
3062 ctxt->nbErrors++;
3063 }
3064 xmlFree(combine);
3065 } else {
3066 if (missing == 0)
3067 missing = 1;
3068 else {
3069 if (ctxt->error != NULL)
3070 ctxt->error(ctxt->userData,
3071 "Some defines for %s lacks the combine attribute\n",
3072 name);
3073 ctxt->nbErrors++;
3074 }
3075 }
3076
3077 cur = cur->nextHash;
3078 }
3079#ifdef DEBUG
3080 xmlGenericError(xmlGenericErrorContext,
3081 "xmlRelaxNGCheckCombine(): merging %s defines: %d\n",
3082 name, choiceOrInterleave);
3083#endif
3084 if (choiceOrInterleave == -1)
3085 choiceOrInterleave = 0;
3086 cur = xmlRelaxNGNewDefine(ctxt, define->node);
3087 if (cur == NULL)
3088 return;
3089 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003090 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard154877e2003-01-30 12:17:05 +00003091 else
3092 cur->type = XML_RELAXNG_CHOICE;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003093 tmp = define;
3094 last = NULL;
3095 while (tmp != NULL) {
3096 if (tmp->content != NULL) {
3097 if (tmp->content->next != NULL) {
3098 /*
3099 * we need first to create a wrapper.
3100 */
3101 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
3102 if (tmp2 == NULL)
3103 break;
3104 tmp2->type = XML_RELAXNG_GROUP;
3105 tmp2->content = tmp->content;
3106 } else {
3107 tmp2 = tmp->content;
3108 }
3109 if (last == NULL) {
3110 cur->content = tmp2;
3111 } else {
3112 last->next = tmp2;
3113 }
3114 last = tmp2;
3115 tmp->content = NULL;
3116 }
3117 tmp = tmp->nextHash;
3118 }
3119 define->content = cur;
Daniel Veillard154877e2003-01-30 12:17:05 +00003120 if (choiceOrInterleave == 0) {
3121 if (ctxt->interleaves == NULL)
3122 ctxt->interleaves = xmlHashCreate(10);
3123 if (ctxt->interleaves == NULL) {
3124 if (ctxt->error != NULL)
3125 ctxt->error(ctxt->userData,
3126 "Failed to create interleaves hash table\n");
3127 ctxt->nbErrors++;
3128 } else {
3129 char tmpname[32];
3130
3131 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
3132 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
3133 if (ctxt->error != NULL)
3134 ctxt->error(ctxt->userData,
3135 "Failed to add %s to hash table\n", tmpname);
3136 ctxt->nbErrors++;
3137 }
3138 }
3139 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003140}
3141
3142/**
3143 * xmlRelaxNGCombineStart:
3144 * @ctxt: a Relax-NG parser context
3145 * @grammar: the grammar
3146 *
3147 * Applies the 4.17. combine rule for all the start
3148 * element of a given grammar.
3149 */
3150static void
3151xmlRelaxNGCombineStart(xmlRelaxNGParserCtxtPtr ctxt,
3152 xmlRelaxNGGrammarPtr grammar) {
3153 xmlRelaxNGDefinePtr starts;
3154 xmlChar *combine;
3155 int choiceOrInterleave = -1;
3156 int missing = 0;
3157 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
3158
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003159 starts = grammar->startList;
3160 if ((starts == NULL) || (starts->nextHash == NULL))
Daniel Veillard6eadf632003-01-23 18:29:16 +00003161 return;
3162 cur = starts;
3163 while (cur != NULL) {
3164 combine = xmlGetProp(cur->node, BAD_CAST "combine");
3165 if (combine != NULL) {
3166 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3167 if (choiceOrInterleave == -1)
3168 choiceOrInterleave = 1;
3169 else if (choiceOrInterleave == 0) {
3170 if (ctxt->error != NULL)
3171 ctxt->error(ctxt->userData,
3172 "<start> use both 'choice' and 'interleave'\n");
3173 ctxt->nbErrors++;
3174 }
3175 } else if (xmlStrEqual(combine, BAD_CAST "choice")) {
3176 if (choiceOrInterleave == -1)
3177 choiceOrInterleave = 0;
3178 else if (choiceOrInterleave == 1) {
3179 if (ctxt->error != NULL)
3180 ctxt->error(ctxt->userData,
3181 "<start> use both 'choice' and 'interleave'\n");
3182 ctxt->nbErrors++;
3183 }
3184 } else {
3185 if (ctxt->error != NULL)
3186 ctxt->error(ctxt->userData,
3187 "<start> uses unknown combine value '%s''\n", combine);
3188 ctxt->nbErrors++;
3189 }
3190 xmlFree(combine);
3191 } else {
3192 if (missing == 0)
3193 missing = 1;
3194 else {
3195 if (ctxt->error != NULL)
3196 ctxt->error(ctxt->userData,
3197 "Some <start> elements lacks the combine attribute\n");
3198 ctxt->nbErrors++;
3199 }
3200 }
3201
3202 cur = cur->nextHash;
3203 }
3204#ifdef DEBUG
3205 xmlGenericError(xmlGenericErrorContext,
3206 "xmlRelaxNGCombineStart(): merging <start>: %d\n",
3207 choiceOrInterleave);
3208#endif
3209 if (choiceOrInterleave == -1)
3210 choiceOrInterleave = 0;
3211 cur = xmlRelaxNGNewDefine(ctxt, starts->node);
3212 if (cur == NULL)
3213 return;
3214 if (choiceOrInterleave == 0)
3215 cur->type = XML_RELAXNG_CHOICE;
3216 else
3217 cur->type = XML_RELAXNG_INTERLEAVE;
3218 tmp = starts;
3219 last = NULL;
3220 while (tmp != NULL) {
3221 if (tmp->content != NULL) {
3222 if (tmp->content->next != NULL) {
3223 /*
3224 * we need first to create a wrapper.
3225 */
3226 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
3227 if (tmp2 == NULL)
3228 break;
3229 tmp2->type = XML_RELAXNG_GROUP;
3230 tmp2->content = tmp->content;
3231 } else {
3232 tmp2 = tmp->content;
3233 }
3234 if (last == NULL) {
3235 cur->content = tmp2;
3236 } else {
3237 last->next = tmp2;
3238 }
3239 last = tmp2;
3240 tmp->content = NULL;
3241 }
3242 tmp = tmp->nextHash;
3243 }
3244 starts->content = cur;
3245}
3246
3247/**
3248 * xmlRelaxNGParseGrammar:
3249 * @ctxt: a Relax-NG parser context
3250 * @nodes: grammar children nodes
3251 *
3252 * parse a Relax-NG <grammar> node
3253 *
3254 * Returns the internal xmlRelaxNGGrammarPtr built or
3255 * NULL in case of error
3256 */
3257static xmlRelaxNGGrammarPtr
3258xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
3259 xmlRelaxNGGrammarPtr ret, tmp, old;
3260
Daniel Veillard6eadf632003-01-23 18:29:16 +00003261 ret = xmlRelaxNGNewGrammar(ctxt);
3262 if (ret == NULL)
3263 return(NULL);
3264
3265 /*
3266 * Link the new grammar in the tree
3267 */
3268 ret->parent = ctxt->grammar;
3269 if (ctxt->grammar != NULL) {
3270 tmp = ctxt->grammar->children;
3271 if (tmp == NULL) {
3272 ctxt->grammar->children = ret;
3273 } else {
3274 while (tmp->next != NULL)
3275 tmp = tmp->next;
3276 tmp->next = ret;
3277 }
3278 }
3279
3280 old = ctxt->grammar;
3281 ctxt->grammar = ret;
3282 xmlRelaxNGParseGrammarContent(ctxt, nodes);
3283 ctxt->grammar = ret;
3284
3285 /*
3286 * Apply 4.17 mergingd rules to defines and starts
3287 */
3288 xmlRelaxNGCombineStart(ctxt, ret);
3289 if (ret->defs != NULL) {
3290 xmlHashScan(ret->defs, (xmlHashScanner) xmlRelaxNGCheckCombine,
3291 ctxt);
3292 }
3293
3294 /*
3295 * link together defines and refs in this grammar
3296 */
3297 if (ret->refs != NULL) {
3298 xmlHashScan(ret->refs, (xmlHashScanner) xmlRelaxNGCheckReference,
3299 ctxt);
3300 }
3301 ctxt->grammar = old;
3302 return(ret);
3303}
3304
3305/**
3306 * xmlRelaxNGParseDocument:
3307 * @ctxt: a Relax-NG parser context
3308 * @node: the root node of the RelaxNG schema
3309 *
3310 * parse a Relax-NG definition resource and build an internal
3311 * xmlRelaxNG struture which can be used to validate instances.
3312 *
3313 * Returns the internal XML RelaxNG structure built or
3314 * NULL in case of error
3315 */
3316static xmlRelaxNGPtr
3317xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3318 xmlRelaxNGPtr schema = NULL;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003319 const xmlChar *olddefine;
Daniel Veillarde431a272003-01-29 23:02:33 +00003320 xmlRelaxNGGrammarPtr old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003321
3322 if ((ctxt == NULL) || (node == NULL))
3323 return (NULL);
3324
3325 schema = xmlRelaxNGNewRelaxNG(ctxt);
3326 if (schema == NULL)
3327 return(NULL);
3328
Daniel Veillard276be4a2003-01-24 01:03:34 +00003329 olddefine = ctxt->define;
3330 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003331 if (IS_RELAXNG(node, "grammar")) {
3332 schema->topgrammar = xmlRelaxNGParseGrammar(ctxt, node->children);
3333 } else {
3334 schema->topgrammar = xmlRelaxNGNewGrammar(ctxt);
3335 if (schema->topgrammar == NULL) {
3336 return(schema);
3337 }
3338 schema->topgrammar->parent = NULL;
Daniel Veillarde431a272003-01-29 23:02:33 +00003339 old = ctxt->grammar;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003340 ctxt->grammar = schema->topgrammar;
3341 xmlRelaxNGParseStart(ctxt, node);
Daniel Veillarde431a272003-01-29 23:02:33 +00003342 if (old != NULL)
3343 ctxt->grammar = old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003344 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003345 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003346
3347#ifdef DEBUG
3348 if (schema == NULL)
3349 xmlGenericError(xmlGenericErrorContext,
3350 "xmlRelaxNGParseDocument() failed\n");
3351#endif
3352
3353 return (schema);
3354}
3355
3356/************************************************************************
3357 * *
3358 * Reading RelaxNGs *
3359 * *
3360 ************************************************************************/
3361
3362/**
3363 * xmlRelaxNGNewParserCtxt:
3364 * @URL: the location of the schema
3365 *
3366 * Create an XML RelaxNGs parse context for that file/resource expected
3367 * to contain an XML RelaxNGs file.
3368 *
3369 * Returns the parser context or NULL in case of error
3370 */
3371xmlRelaxNGParserCtxtPtr
3372xmlRelaxNGNewParserCtxt(const char *URL) {
3373 xmlRelaxNGParserCtxtPtr ret;
3374
3375 if (URL == NULL)
3376 return(NULL);
3377
3378 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
3379 if (ret == NULL) {
3380 xmlGenericError(xmlGenericErrorContext,
3381 "Failed to allocate new schama parser context for %s\n", URL);
3382 return (NULL);
3383 }
3384 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
3385 ret->URL = xmlStrdup((const xmlChar *)URL);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003386 ret->error = xmlGenericError;
3387 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003388 return (ret);
3389}
3390
3391/**
3392 * xmlRelaxNGNewMemParserCtxt:
3393 * @buffer: a pointer to a char array containing the schemas
3394 * @size: the size of the array
3395 *
3396 * Create an XML RelaxNGs parse context for that memory buffer expected
3397 * to contain an XML RelaxNGs file.
3398 *
3399 * Returns the parser context or NULL in case of error
3400 */
3401xmlRelaxNGParserCtxtPtr
3402xmlRelaxNGNewMemParserCtxt(const char *buffer, int size) {
3403 xmlRelaxNGParserCtxtPtr ret;
3404
3405 if ((buffer == NULL) || (size <= 0))
3406 return(NULL);
3407
3408 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
3409 if (ret == NULL) {
3410 xmlGenericError(xmlGenericErrorContext,
3411 "Failed to allocate new schama parser context\n");
3412 return (NULL);
3413 }
3414 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
3415 ret->buffer = buffer;
3416 ret->size = size;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003417 ret->error = xmlGenericError;
3418 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003419 return (ret);
3420}
3421
3422/**
3423 * xmlRelaxNGFreeParserCtxt:
3424 * @ctxt: the schema parser context
3425 *
3426 * Free the resources associated to the schema parser context
3427 */
3428void
3429xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxtPtr ctxt) {
3430 if (ctxt == NULL)
3431 return;
3432 if (ctxt->URL != NULL)
3433 xmlFree(ctxt->URL);
3434 if (ctxt->doc != NULL)
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003435 xmlFreeDoc(ctxt->document);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003436 if (ctxt->interleaves != NULL)
3437 xmlHashFree(ctxt->interleaves, NULL);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003438 if (ctxt->documents != NULL)
3439 xmlHashFree(ctxt->documents, (xmlHashDeallocator)
3440 xmlRelaxNGFreeDocument);
3441 if (ctxt->docTab != NULL)
3442 xmlFree(ctxt->docTab);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003443 if (ctxt->incTab != NULL)
3444 xmlFree(ctxt->incTab);
Daniel Veillard419a7682003-02-03 23:22:49 +00003445 if (ctxt->defTab != NULL) {
3446 int i;
3447
3448 for (i = 0;i < ctxt->defNr;i++)
3449 xmlRelaxNGFreeDefine(ctxt->defTab[i]);
3450 xmlFree(ctxt->defTab);
3451 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003452 xmlFree(ctxt);
3453}
3454
Daniel Veillard6eadf632003-01-23 18:29:16 +00003455/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003456 * xmlRelaxNGCleanupDoc:
3457 * @ctxt: a Relax-NG parser context
3458 * @doc: an xmldocPtr document pointer
Daniel Veillard6eadf632003-01-23 18:29:16 +00003459 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003460 * Cleanup the document from unwanted nodes for parsing, resolve
3461 * Include and externalRef lookups.
Daniel Veillard6eadf632003-01-23 18:29:16 +00003462 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003463 * Returns the cleaned up document or NULL in case of error
Daniel Veillard6eadf632003-01-23 18:29:16 +00003464 */
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003465static xmlDocPtr
3466xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt, xmlDocPtr doc) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003467 xmlNodePtr root, cur, delete;
3468
Daniel Veillard6eadf632003-01-23 18:29:16 +00003469 /*
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003470 * Extract the root
Daniel Veillard6eadf632003-01-23 18:29:16 +00003471 */
3472 root = xmlDocGetRootElement(doc);
3473 if (root == NULL) {
3474 if (ctxt->error != NULL)
3475 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
3476 ctxt->URL);
3477 ctxt->nbErrors++;
3478 return (NULL);
3479 }
3480
3481 /*
3482 * Remove all the blank text nodes
3483 */
3484 delete = NULL;
3485 cur = root;
3486 while (cur != NULL) {
3487 if (delete != NULL) {
3488 xmlUnlinkNode(delete);
3489 xmlFreeNode(delete);
3490 delete = NULL;
3491 }
3492 if (cur->type == XML_ELEMENT_NODE) {
3493 /*
3494 * Simplification 4.1. Annotations
3495 */
3496 if ((cur->ns == NULL) ||
3497 (!xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
3498 delete = cur;
3499 goto skip_children;
3500 } else {
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003501 if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003502 xmlChar *href, *ns, *base, *URL;
3503 xmlRelaxNGDocumentPtr docu;
3504
3505 ns = xmlGetProp(cur, BAD_CAST "ns");
3506 href = xmlGetProp(cur, BAD_CAST "href");
3507 if (href == NULL) {
3508 if (ctxt->error != NULL)
3509 ctxt->error(ctxt->userData,
3510 "xmlRelaxNGParse: externalRef has no href attribute\n");
3511 ctxt->nbErrors++;
3512 delete = cur;
3513 goto skip_children;
3514 }
3515 base = xmlNodeGetBase(cur->doc, cur);
3516 URL = xmlBuildURI(href, base);
3517 if (URL == NULL) {
3518 if (ctxt->error != NULL)
3519 ctxt->error(ctxt->userData,
3520 "Failed to compute URL for externalRef %s\n", href);
3521 ctxt->nbErrors++;
3522 if (href != NULL)
3523 xmlFree(href);
3524 if (base != NULL)
3525 xmlFree(base);
3526 delete = cur;
3527 goto skip_children;
3528 }
3529 if (href != NULL)
3530 xmlFree(href);
3531 if (base != NULL)
3532 xmlFree(base);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003533 docu = xmlRelaxNGLoadExternalRef(ctxt, URL, ns);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003534 if (docu == NULL) {
3535 if (ctxt->error != NULL)
3536 ctxt->error(ctxt->userData,
3537 "Failed to load externalRef %s\n", URL);
3538 ctxt->nbErrors++;
3539 xmlFree(URL);
3540 delete = cur;
3541 goto skip_children;
3542 }
3543 xmlFree(URL);
3544 cur->_private = docu;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003545 } else if (xmlStrEqual(cur->name, BAD_CAST "include")) {
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003546 xmlChar *href, *base, *URL;
3547 xmlRelaxNGIncludePtr incl;
3548
3549 href = xmlGetProp(cur, BAD_CAST "href");
3550 if (href == NULL) {
3551 if (ctxt->error != NULL)
3552 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003553 "xmlRelaxNGParse: include has no href attribute\n");
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003554 ctxt->nbErrors++;
3555 delete = cur;
3556 goto skip_children;
3557 }
3558 base = xmlNodeGetBase(cur->doc, cur);
3559 URL = xmlBuildURI(href, base);
3560 if (URL == NULL) {
3561 if (ctxt->error != NULL)
3562 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003563 "Failed to compute URL for include %s\n", href);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003564 ctxt->nbErrors++;
3565 if (href != NULL)
3566 xmlFree(href);
3567 if (base != NULL)
3568 xmlFree(base);
3569 delete = cur;
3570 goto skip_children;
3571 }
3572 if (href != NULL)
3573 xmlFree(href);
3574 if (base != NULL)
3575 xmlFree(base);
3576 incl = xmlRelaxNGLoadInclude(ctxt, URL, cur);
3577 if (incl == NULL) {
3578 if (ctxt->error != NULL)
3579 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003580 "Failed to load include %s\n", URL);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003581 ctxt->nbErrors++;
3582 xmlFree(URL);
3583 delete = cur;
3584 goto skip_children;
3585 }
3586 xmlFree(URL);
3587 cur->_private = incl;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003588 } else if ((xmlStrEqual(cur->name, BAD_CAST "element")) ||
3589 (xmlStrEqual(cur->name, BAD_CAST "attribute"))) {
3590 xmlChar *name;
3591 xmlNodePtr text = NULL;
3592
3593 /*
3594 * Simplification 4.8. name attribute of element
3595 * and attribute elements
3596 */
3597 name = xmlGetProp(cur, BAD_CAST "name");
3598 if (name != NULL) {
3599 if (cur->children == NULL) {
3600 text = xmlNewChild(cur, cur->ns, BAD_CAST "name",
3601 name);
3602 } else {
3603 xmlNodePtr node;
3604 node = xmlNewNode(cur->ns, BAD_CAST "name");
3605 if (node != NULL) {
3606 xmlAddPrevSibling(cur->children, node);
3607 text = xmlNewText(name);
3608 xmlAddChild(node, text);
3609 text = node;
3610 }
3611 }
3612 xmlUnsetProp(cur, BAD_CAST "name");
3613 xmlFree(name);
3614 }
3615 if (xmlStrEqual(cur->name, BAD_CAST "attribute")) {
3616 if (text == NULL) {
3617 text = cur->children;
3618 while (text != NULL) {
3619 if ((text->type == XML_ELEMENT_NODE) &&
3620 (xmlStrEqual(text->name, BAD_CAST "name")))
3621 break;
3622 text = text->next;
3623 }
3624 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003625 if (text != NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003626 xmlSetProp(text, BAD_CAST "ns", BAD_CAST "");
3627 }
3628 }
3629 } else if ((xmlStrEqual(cur->name, BAD_CAST "name")) ||
3630 (xmlStrEqual(cur->name, BAD_CAST "nsName")) ||
3631 (xmlStrEqual(cur->name, BAD_CAST "value"))) {
3632 /*
3633 * Simplification 4.8. name attribute of element
3634 * and attribute elements
3635 */
3636 if (xmlHasProp(cur, BAD_CAST "ns") == NULL) {
3637 xmlNodePtr node;
3638 xmlChar *ns = NULL;
3639
3640 node = cur->parent;
3641 while ((node != NULL) &&
3642 (node->type == XML_ELEMENT_NODE)) {
3643 ns = xmlGetProp(node, BAD_CAST "ns");
3644 if (ns != NULL) {
3645 break;
3646 }
3647 node = node->parent;
3648 }
3649 if (ns == NULL) {
3650 xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
3651 } else {
3652 xmlSetProp(cur, BAD_CAST "ns", ns);
3653 xmlFree(ns);
3654 }
3655 }
3656 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
3657 xmlChar *name, *local, *prefix;
3658
3659 /*
3660 * Simplification: 4.10. QNames
3661 */
3662 name = xmlNodeGetContent(cur);
3663 if (name != NULL) {
3664 local = xmlSplitQName2(name, &prefix);
3665 if (local != NULL) {
3666 xmlNsPtr ns;
3667
3668 ns = xmlSearchNs(cur->doc, cur, prefix);
3669 if (ns == NULL) {
3670 if (ctxt->error != NULL)
3671 ctxt->error(ctxt->userData,
3672 "xmlRelaxNGParse: no namespace for prefix %s\n", prefix);
3673 ctxt->nbErrors++;
3674 } else {
3675 xmlSetProp(cur, BAD_CAST "ns", ns->href);
3676 xmlNodeSetContent(cur, local);
3677 }
3678 xmlFree(local);
3679 xmlFree(prefix);
3680 }
3681 xmlFree(name);
3682 }
3683 }
3684 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003685 /*
3686 * Thisd is not an else since "include" is transformed
3687 * into a div
3688 */
3689 if (xmlStrEqual(cur->name, BAD_CAST "div")) {
3690 /*
3691 * implements rule 4.11
3692 */
3693 xmlNodePtr child, ins, tmp;
3694
3695 child = cur->children;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003696 ins = cur;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003697 while (child != NULL) {
3698 tmp = child->next;
3699 xmlUnlinkNode(child);
3700 ins = xmlAddNextSibling(ins, child);
3701 child = tmp;
3702 }
3703 delete = cur;
3704 goto skip_children;
3705 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003706 }
3707 }
3708 /*
3709 * Simplification 4.2 whitespaces
3710 */
3711 else if (cur->type == XML_TEXT_NODE) {
3712 if (IS_BLANK_NODE(cur)) {
3713 if (cur->parent->type == XML_ELEMENT_NODE) {
3714 if ((!xmlStrEqual(cur->parent->name, BAD_CAST "value")) &&
3715 (!xmlStrEqual(cur->parent->name, BAD_CAST "param")))
3716 delete = cur;
3717 } else {
3718 delete = cur;
3719 goto skip_children;
3720 }
3721 }
3722 } else if (cur->type != XML_CDATA_SECTION_NODE) {
3723 delete = cur;
3724 goto skip_children;
3725 }
3726
3727 /*
3728 * Skip to next node
3729 */
3730 if (cur->children != NULL) {
3731 if ((cur->children->type != XML_ENTITY_DECL) &&
3732 (cur->children->type != XML_ENTITY_REF_NODE) &&
3733 (cur->children->type != XML_ENTITY_NODE)) {
3734 cur = cur->children;
3735 continue;
3736 }
3737 }
3738skip_children:
3739 if (cur->next != NULL) {
3740 cur = cur->next;
3741 continue;
3742 }
3743
3744 do {
3745 cur = cur->parent;
3746 if (cur == NULL)
3747 break;
3748 if (cur == root) {
3749 cur = NULL;
3750 break;
3751 }
3752 if (cur->next != NULL) {
3753 cur = cur->next;
3754 break;
3755 }
3756 } while (cur != NULL);
3757 }
3758 if (delete != NULL) {
3759 xmlUnlinkNode(delete);
3760 xmlFreeNode(delete);
3761 delete = NULL;
3762 }
3763
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003764 return(doc);
3765}
3766
3767/**
3768 * xmlRelaxNGParse:
3769 * @ctxt: a Relax-NG parser context
3770 *
3771 * parse a schema definition resource and build an internal
3772 * XML Shema struture which can be used to validate instances.
3773 * *WARNING* this interface is highly subject to change
3774 *
3775 * Returns the internal XML RelaxNG structure built from the resource or
3776 * NULL in case of error
3777 */
3778xmlRelaxNGPtr
3779xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt)
3780{
3781 xmlRelaxNGPtr ret = NULL;
3782 xmlDocPtr doc;
3783 xmlNodePtr root;
3784
3785 xmlRelaxNGInitTypes();
3786
3787 if (ctxt == NULL)
3788 return (NULL);
3789
3790 /*
3791 * First step is to parse the input document into an DOM/Infoset
3792 */
3793 if (ctxt->URL != NULL) {
3794 doc = xmlParseFile((const char *) ctxt->URL);
3795 if (doc == NULL) {
3796 if (ctxt->error != NULL)
3797 ctxt->error(ctxt->userData,
3798 "xmlRelaxNGParse: could not load %s\n", ctxt->URL);
3799 ctxt->nbErrors++;
3800 return (NULL);
3801 }
3802 } else if (ctxt->buffer != NULL) {
3803 doc = xmlParseMemory(ctxt->buffer, ctxt->size);
3804 if (doc == NULL) {
3805 if (ctxt->error != NULL)
3806 ctxt->error(ctxt->userData,
3807 "xmlRelaxNGParse: could not parse schemas\n");
3808 ctxt->nbErrors++;
3809 return (NULL);
3810 }
3811 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
3812 ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
3813 } else {
3814 if (ctxt->error != NULL)
3815 ctxt->error(ctxt->userData,
3816 "xmlRelaxNGParse: nothing to parse\n");
3817 ctxt->nbErrors++;
3818 return (NULL);
3819 }
3820 ctxt->document = doc;
3821
3822 /*
3823 * Some preprocessing of the document content
3824 */
3825 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
3826 if (doc == NULL) {
3827 xmlFreeDoc(ctxt->document);
3828 ctxt->document = NULL;
3829 return(NULL);
3830 }
3831
Daniel Veillard6eadf632003-01-23 18:29:16 +00003832 /*
3833 * Then do the parsing for good
3834 */
3835 root = xmlDocGetRootElement(doc);
3836 if (root == NULL) {
3837 if (ctxt->error != NULL)
3838 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
3839 ctxt->URL);
3840 ctxt->nbErrors++;
3841 return (NULL);
3842 }
3843 ret = xmlRelaxNGParseDocument(ctxt, root);
3844 if (ret == NULL)
3845 return(NULL);
3846
3847 /*
3848 * Check the ref/defines links
3849 */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003850 /*
3851 * try to preprocess interleaves
3852 */
3853 if (ctxt->interleaves != NULL) {
3854 xmlHashScan(ctxt->interleaves,
3855 (xmlHashScanner)xmlRelaxNGComputeInterleaves, ctxt);
3856 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003857
3858 /*
3859 * if there was a parsing error return NULL
3860 */
3861 if (ctxt->nbErrors > 0) {
3862 xmlRelaxNGFree(ret);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003863 ctxt->document = NULL;
3864 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003865 return(NULL);
3866 }
3867
3868 /*
3869 * Transfer the pointer for cleanup at the schema level.
3870 */
3871 ret->doc = doc;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003872 ctxt->document = NULL;
3873 ret->documents = ctxt->documents;
3874 ctxt->documents = NULL;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003875 ret->includes = ctxt->includes;
3876 ctxt->includes = NULL;
Daniel Veillard419a7682003-02-03 23:22:49 +00003877 ret->defNr = ctxt->defNr;
3878 ret->defTab = ctxt->defTab;
3879 ctxt->defTab = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003880
3881 return (ret);
3882}
3883
3884/**
3885 * xmlRelaxNGSetParserErrors:
3886 * @ctxt: a Relax-NG validation context
3887 * @err: the error callback
3888 * @warn: the warning callback
3889 * @ctx: contextual data for the callbacks
3890 *
3891 * Set the callback functions used to handle errors for a validation context
3892 */
3893void
3894xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
3895 xmlRelaxNGValidityErrorFunc err,
3896 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
3897 if (ctxt == NULL)
3898 return;
3899 ctxt->error = err;
3900 ctxt->warning = warn;
3901 ctxt->userData = ctx;
3902}
3903/************************************************************************
3904 * *
3905 * Dump back a compiled form *
3906 * *
3907 ************************************************************************/
3908static void xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define);
3909
3910/**
3911 * xmlRelaxNGDumpDefines:
3912 * @output: the file output
3913 * @defines: a list of define structures
3914 *
3915 * Dump a RelaxNG structure back
3916 */
3917static void
3918xmlRelaxNGDumpDefines(FILE * output, xmlRelaxNGDefinePtr defines) {
3919 while (defines != NULL) {
3920 xmlRelaxNGDumpDefine(output, defines);
3921 defines = defines->next;
3922 }
3923}
3924
3925/**
3926 * xmlRelaxNGDumpDefine:
3927 * @output: the file output
3928 * @define: a define structure
3929 *
3930 * Dump a RelaxNG structure back
3931 */
3932static void
3933xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define) {
3934 if (define == NULL)
3935 return;
3936 switch(define->type) {
3937 case XML_RELAXNG_EMPTY:
3938 fprintf(output, "<empty/>\n");
3939 break;
3940 case XML_RELAXNG_NOT_ALLOWED:
3941 fprintf(output, "<notAllowed/>\n");
3942 break;
3943 case XML_RELAXNG_TEXT:
3944 fprintf(output, "<text/>\n");
3945 break;
3946 case XML_RELAXNG_ELEMENT:
3947 fprintf(output, "<element>\n");
3948 if (define->name != NULL) {
3949 fprintf(output, "<name");
3950 if (define->ns != NULL)
3951 fprintf(output, " ns=\"%s\"", define->ns);
3952 fprintf(output, ">%s</name>\n", define->name);
3953 }
3954 xmlRelaxNGDumpDefines(output, define->attrs);
3955 xmlRelaxNGDumpDefines(output, define->content);
3956 fprintf(output, "</element>\n");
3957 break;
3958 case XML_RELAXNG_LIST:
3959 fprintf(output, "<list>\n");
3960 xmlRelaxNGDumpDefines(output, define->content);
3961 fprintf(output, "</list>\n");
3962 break;
3963 case XML_RELAXNG_ONEORMORE:
3964 fprintf(output, "<oneOrMore>\n");
3965 xmlRelaxNGDumpDefines(output, define->content);
3966 fprintf(output, "</oneOrMore>\n");
3967 break;
3968 case XML_RELAXNG_ZEROORMORE:
3969 fprintf(output, "<zeroOrMore>\n");
3970 xmlRelaxNGDumpDefines(output, define->content);
3971 fprintf(output, "</zeroOrMore>\n");
3972 break;
3973 case XML_RELAXNG_CHOICE:
3974 fprintf(output, "<choice>\n");
3975 xmlRelaxNGDumpDefines(output, define->content);
3976 fprintf(output, "</choice>\n");
3977 break;
3978 case XML_RELAXNG_GROUP:
3979 fprintf(output, "<group>\n");
3980 xmlRelaxNGDumpDefines(output, define->content);
3981 fprintf(output, "</group>\n");
3982 break;
3983 case XML_RELAXNG_INTERLEAVE:
3984 fprintf(output, "<interleave>\n");
3985 xmlRelaxNGDumpDefines(output, define->content);
3986 fprintf(output, "</interleave>\n");
3987 break;
3988 case XML_RELAXNG_OPTIONAL:
3989 fprintf(output, "<optional>\n");
3990 xmlRelaxNGDumpDefines(output, define->content);
3991 fprintf(output, "</optional>\n");
3992 break;
3993 case XML_RELAXNG_ATTRIBUTE:
3994 fprintf(output, "<attribute>\n");
3995 xmlRelaxNGDumpDefines(output, define->content);
3996 fprintf(output, "</attribute>\n");
3997 break;
3998 case XML_RELAXNG_DEF:
3999 fprintf(output, "<define");
4000 if (define->name != NULL)
4001 fprintf(output, " name=\"%s\"", define->name);
4002 fprintf(output, ">\n");
4003 xmlRelaxNGDumpDefines(output, define->content);
4004 fprintf(output, "</define>\n");
4005 break;
4006 case XML_RELAXNG_REF:
4007 fprintf(output, "<ref");
4008 if (define->name != NULL)
4009 fprintf(output, " name=\"%s\"", define->name);
4010 fprintf(output, ">\n");
4011 xmlRelaxNGDumpDefines(output, define->content);
4012 fprintf(output, "</ref>\n");
4013 break;
Daniel Veillard419a7682003-02-03 23:22:49 +00004014 case XML_RELAXNG_PARENTREF:
4015 fprintf(output, "<parentRef");
4016 if (define->name != NULL)
4017 fprintf(output, " name=\"%s\"", define->name);
4018 fprintf(output, ">\n");
4019 xmlRelaxNGDumpDefines(output, define->content);
4020 fprintf(output, "</parentRef>\n");
4021 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004022 case XML_RELAXNG_EXTERNALREF:
Daniel Veillarde431a272003-01-29 23:02:33 +00004023 fprintf(output, "<externalRef");
4024 xmlRelaxNGDumpDefines(output, define->content);
4025 fprintf(output, "</externalRef>\n");
4026 break;
4027 case XML_RELAXNG_DATATYPE:
Daniel Veillard6eadf632003-01-23 18:29:16 +00004028 case XML_RELAXNG_VALUE:
4029 TODO
4030 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004031 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00004032 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004033 TODO
4034 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004035 }
4036}
4037
4038/**
4039 * xmlRelaxNGDumpGrammar:
4040 * @output: the file output
4041 * @grammar: a grammar structure
4042 * @top: is this a top grammar
4043 *
4044 * Dump a RelaxNG structure back
4045 */
4046static void
4047xmlRelaxNGDumpGrammar(FILE * output, xmlRelaxNGGrammarPtr grammar, int top)
4048{
4049 if (grammar == NULL)
4050 return;
4051
4052 fprintf(output, "<grammar");
4053 if (top)
4054 fprintf(output,
4055 " xmlns=\"http://relaxng.org/ns/structure/1.0\"");
4056 switch(grammar->combine) {
4057 case XML_RELAXNG_COMBINE_UNDEFINED:
4058 break;
4059 case XML_RELAXNG_COMBINE_CHOICE:
4060 fprintf(output, " combine=\"choice\"");
4061 break;
4062 case XML_RELAXNG_COMBINE_INTERLEAVE:
4063 fprintf(output, " combine=\"interleave\"");
4064 break;
4065 default:
4066 fprintf(output, " <!-- invalid combine value -->");
4067 }
4068 fprintf(output, ">\n");
4069 if (grammar->start == NULL) {
4070 fprintf(output, " <!-- grammar had no start -->");
4071 } else {
4072 fprintf(output, "<start>\n");
4073 xmlRelaxNGDumpDefine(output, grammar->start);
4074 fprintf(output, "</start>\n");
4075 }
4076 /* TODO ? Dump the defines ? */
4077 fprintf(output, "</grammar>\n");
4078}
4079
4080/**
4081 * xmlRelaxNGDump:
4082 * @output: the file output
4083 * @schema: a schema structure
4084 *
4085 * Dump a RelaxNG structure back
4086 */
4087void
4088xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema)
4089{
4090 if (schema == NULL) {
4091 fprintf(output, "RelaxNG empty or failed to compile\n");
4092 return;
4093 }
4094 fprintf(output, "RelaxNG: ");
4095 if (schema->doc == NULL) {
4096 fprintf(output, "no document\n");
4097 } else if (schema->doc->URL != NULL) {
4098 fprintf(output, "%s\n", schema->doc->URL);
4099 } else {
4100 fprintf(output, "\n");
4101 }
4102 if (schema->topgrammar == NULL) {
4103 fprintf(output, "RelaxNG has no top grammar\n");
4104 return;
4105 }
4106 xmlRelaxNGDumpGrammar(output, schema->topgrammar, 1);
4107}
4108
4109/************************************************************************
4110 * *
4111 * Validation implementation *
4112 * *
4113 ************************************************************************/
4114static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
4115 xmlRelaxNGDefinePtr define);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004116static int xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
4117 xmlRelaxNGDefinePtr define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004118
4119/**
4120 * xmlRelaxNGSkipIgnored:
4121 * @ctxt: a schema validation context
4122 * @node: the top node.
4123 *
4124 * Skip ignorable nodes in that context
4125 *
4126 * Returns the new sibling or NULL in case of error.
4127 */
4128static xmlNodePtr
4129xmlRelaxNGSkipIgnored(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
4130 xmlNodePtr node) {
4131 /*
4132 * TODO complete and handle entities
4133 */
4134 while ((node != NULL) &&
4135 ((node->type == XML_COMMENT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004136 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00004137 ((node->type == XML_TEXT_NODE) &&
4138 (IS_BLANK_NODE(node))))) {
4139 node = node->next;
4140 }
4141 return(node);
4142}
4143
4144/**
Daniel Veillardedc91922003-01-26 00:52:04 +00004145 * xmlRelaxNGNormalize:
4146 * @ctxt: a schema validation context
4147 * @str: the string to normalize
4148 *
4149 * Implements the normalizeWhiteSpace( s ) function from
4150 * section 6.2.9 of the spec
4151 *
4152 * Returns the new string or NULL in case of error.
4153 */
4154static xmlChar *
4155xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *str) {
4156 xmlChar *ret, *p;
4157 const xmlChar *tmp;
4158 int len;
4159
4160 if (str == NULL)
4161 return(NULL);
4162 tmp = str;
4163 while (*tmp != 0) tmp++;
4164 len = tmp - str;
4165
4166 ret = (xmlChar *) xmlMalloc((len + 1) * sizeof(xmlChar));
4167 if (ret == NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00004168 if (ctxt != NULL) {
4169 VALID_CTXT();
4170 VALID_ERROR("xmlRelaxNGNormalize: out of memory\n");
4171 } else {
4172 xmlGenericError(xmlGenericErrorContext,
4173 "xmlRelaxNGNormalize: out of memory\n");
4174 }
Daniel Veillardedc91922003-01-26 00:52:04 +00004175 return(NULL);
4176 }
4177 p = ret;
4178 while (IS_BLANK(*str)) str++;
4179 while (*str != 0) {
4180 if (IS_BLANK(*str)) {
4181 while (IS_BLANK(*str)) str++;
4182 if (*str == 0)
4183 break;
4184 *p++ = ' ';
4185 } else
4186 *p++ = *str++;
4187 }
4188 *p = 0;
4189 return(ret);
4190}
4191
4192/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004193 * xmlRelaxNGValidateDatatype:
4194 * @ctxt: a Relax-NG validation context
4195 * @value: the string value
4196 * @type: the datatype definition
4197 *
4198 * Validate the given value against the dataype
4199 *
4200 * Returns 0 if the validation succeeded or an error code.
4201 */
4202static int
4203xmlRelaxNGValidateDatatype(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *value,
4204 xmlRelaxNGDefinePtr define) {
4205 int ret;
4206 xmlRelaxNGTypeLibraryPtr lib;
4207
4208 if ((define == NULL) || (define->data == NULL)) {
4209 return(-1);
4210 }
4211 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
4212 if (lib->check != NULL)
4213 ret = lib->check(lib->data, define->name, value);
4214 else
4215 ret = -1;
4216 if (ret < 0) {
4217 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004218 VALID_ERROR2("Internal: failed to validate type %s\n", define->name);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004219 return(-1);
4220 } else if (ret == 1) {
4221 ret = 0;
4222 } else {
4223 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004224 VALID_ERROR3("Type %s doesn't allow value %s\n", define->name, value);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004225 return(-1);
4226 ret = -1;
4227 }
4228 return(ret);
4229}
4230
4231/**
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004232 * xmlRelaxNGNextValue:
4233 * @ctxt: a Relax-NG validation context
4234 *
4235 * Skip to the next value when validating within a list
4236 *
4237 * Returns 0 if the operation succeeded or an error code.
4238 */
4239static int
4240xmlRelaxNGNextValue(xmlRelaxNGValidCtxtPtr ctxt) {
4241 xmlChar *cur;
4242
4243 cur = ctxt->state->value;
4244 if ((cur == NULL) || (ctxt->state->endvalue == NULL)) {
4245 ctxt->state->value = NULL;
Daniel Veillarde5b110b2003-02-04 14:43:39 +00004246 ctxt->state->endvalue = NULL;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004247 return(0);
4248 }
4249 while (*cur != 0) cur++;
4250 while ((cur != ctxt->state->endvalue) && (*cur == 0)) cur++;
4251 if (cur == ctxt->state->endvalue)
4252 ctxt->state->value = NULL;
4253 else
4254 ctxt->state->value = cur;
4255 return(0);
4256}
4257
4258/**
4259 * xmlRelaxNGValidateValueList:
4260 * @ctxt: a Relax-NG validation context
4261 * @defines: the list of definitions to verify
4262 *
4263 * Validate the given set of definitions for the current value
4264 *
4265 * Returns 0 if the validation succeeded or an error code.
4266 */
4267static int
4268xmlRelaxNGValidateValueList(xmlRelaxNGValidCtxtPtr ctxt,
4269 xmlRelaxNGDefinePtr defines) {
4270 int ret = 0;
4271
4272 while (defines != NULL) {
4273 ret = xmlRelaxNGValidateValue(ctxt, defines);
4274 if (ret != 0)
4275 break;
4276 defines = defines->next;
4277 }
4278 return(ret);
4279}
4280
4281/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004282 * xmlRelaxNGValidateValue:
4283 * @ctxt: a Relax-NG validation context
4284 * @define: the definition to verify
4285 *
4286 * Validate the given definition for the current value
4287 *
4288 * Returns 0 if the validation succeeded or an error code.
4289 */
4290static int
4291xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
4292 xmlRelaxNGDefinePtr define) {
Daniel Veillardedc91922003-01-26 00:52:04 +00004293 int ret = 0, oldflags;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004294 xmlChar *value;
4295
4296 value = ctxt->state->value;
4297 switch (define->type) {
4298 case XML_RELAXNG_EMPTY:
4299 if ((value != NULL) && (value[0] != '0'))
4300 ret = -1;
4301 break;
4302 case XML_RELAXNG_TEXT:
4303 break;
Daniel Veillardedc91922003-01-26 00:52:04 +00004304 case XML_RELAXNG_VALUE: {
4305 if (!xmlStrEqual(value, define->value)) {
4306 if (define->name != NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00004307 xmlRelaxNGTypeLibraryPtr lib;
4308
4309 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
4310 if ((lib != NULL) && (lib->comp != NULL))
4311 ret = lib->comp(lib->data, define->name, value,
4312 define->value);
4313 else
4314 ret = -1;
4315 if (ret < 0) {
4316 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004317 VALID_ERROR2("Internal: failed to compare type %s\n",
Daniel Veillardea3f3982003-01-26 19:45:18 +00004318 define->name);
4319 return(-1);
4320 } else if (ret == 1) {
4321 ret = 0;
4322 } else {
4323 ret = -1;
4324 }
Daniel Veillardedc91922003-01-26 00:52:04 +00004325 } else {
4326 xmlChar *nval, *nvalue;
4327
4328 /*
4329 * TODO: trivial optimizations are possible by
4330 * computing at compile-time
4331 */
4332 nval = xmlRelaxNGNormalize(ctxt, define->value);
4333 nvalue = xmlRelaxNGNormalize(ctxt, value);
4334
Daniel Veillardea3f3982003-01-26 19:45:18 +00004335 if ((nval == NULL) || (nvalue == NULL) ||
4336 (!xmlStrEqual(nval, nvalue)))
Daniel Veillardedc91922003-01-26 00:52:04 +00004337 ret = -1;
4338 if (nval != NULL)
4339 xmlFree(nval);
4340 if (nvalue != NULL)
4341 xmlFree(nvalue);
4342 }
4343 }
4344 break;
4345 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004346 case XML_RELAXNG_DATATYPE: {
4347 ret = xmlRelaxNGValidateDatatype(ctxt, value, define);
4348 if (ret == 0)
4349 xmlRelaxNGNextValue(ctxt);
4350
4351 break;
4352 }
4353 case XML_RELAXNG_CHOICE: {
4354 xmlRelaxNGDefinePtr list = define->content;
4355 xmlChar *oldvalue;
4356
4357 oldflags = ctxt->flags;
4358 ctxt->flags |= FLAGS_IGNORABLE;
4359
4360 oldvalue = ctxt->state->value;
4361 while (list != NULL) {
4362 ret = xmlRelaxNGValidateValue(ctxt, list);
4363 if (ret == 0) {
4364 break;
4365 }
4366 ctxt->state->value = oldvalue;
4367 list = list->next;
4368 }
4369 ctxt->flags = oldflags;
4370 break;
4371 }
4372 case XML_RELAXNG_LIST: {
4373 xmlRelaxNGDefinePtr list = define->content;
4374 xmlChar *oldvalue, *oldend, *val, *cur;
4375
4376 oldvalue = ctxt->state->value;
4377 oldend = ctxt->state->endvalue;
4378
4379 val = xmlStrdup(oldvalue);
4380 if (val == NULL) {
4381 VALID_CTXT();
4382 VALID_ERROR("Internal: no state\n");
4383 return(-1);
4384 }
4385 cur = val;
4386 while (*cur != 0) {
4387 if (IS_BLANK(*cur))
4388 *cur = 0;
4389 cur++;
4390 }
4391 ctxt->state->endvalue = cur;
4392 cur = val;
4393 while ((*cur == 0) && (cur != ctxt->state->endvalue)) cur++;
4394
4395 ctxt->state->value = cur;
4396
4397 while (list != NULL) {
4398 ret = xmlRelaxNGValidateValue(ctxt, list);
4399 if (ret != 0) {
4400 break;
4401 }
4402 list = list->next;
4403 }
4404 if ((ret == 0) && (ctxt->state->value != NULL) &&
4405 (ctxt->state->value != ctxt->state->endvalue)) {
4406 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004407 VALID_ERROR2("Extra data in list: %s\n", ctxt->state->value);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004408 ret = -1;
4409 }
4410 xmlFree(val);
4411 ctxt->state->value = oldvalue;
4412 ctxt->state->endvalue = oldend;
4413 break;
4414 }
4415 case XML_RELAXNG_ONEORMORE:
4416 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
4417 if (ret != 0) {
4418 break;
4419 }
4420 /* no break on purpose */
4421 case XML_RELAXNG_ZEROORMORE: {
4422 xmlChar *cur, *temp;
4423
4424 oldflags = ctxt->flags;
4425 ctxt->flags |= FLAGS_IGNORABLE;
4426 cur = ctxt->state->value;
4427 temp = NULL;
4428 while ((cur != NULL) && (cur != ctxt->state->endvalue) &&
4429 (temp != cur)) {
4430 temp = cur;
4431 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
4432 if (ret != 0) {
4433 ctxt->state->value = temp;
4434 ret = 0;
4435 break;
4436 }
4437 cur = ctxt->state->value;
4438 }
4439 ctxt->flags = oldflags;
4440 break;
4441 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004442 default:
4443 TODO
4444 ret = -1;
4445 }
4446 return(ret);
4447}
4448
4449/**
4450 * xmlRelaxNGValidateValueContent:
4451 * @ctxt: a Relax-NG validation context
4452 * @defines: the list of definitions to verify
4453 *
4454 * Validate the given definitions for the current value
4455 *
4456 * Returns 0 if the validation succeeded or an error code.
4457 */
4458static int
4459xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt,
4460 xmlRelaxNGDefinePtr defines) {
4461 int ret = 0;
4462
4463 while (defines != NULL) {
4464 ret = xmlRelaxNGValidateValue(ctxt, defines);
4465 if (ret != 0)
4466 break;
4467 defines = defines->next;
4468 }
4469 return(ret);
4470}
4471
4472/**
Daniel Veillard144fae12003-02-03 13:17:57 +00004473 * xmlRelaxNGAttributeMatch:
4474 * @ctxt: a Relax-NG validation context
4475 * @define: the definition to check
4476 * @prop: the attribute
4477 *
4478 * Check if the attribute matches the definition nameClass
4479 *
4480 * Returns 1 if the attribute matches, 0 if no, or -1 in case of error
4481 */
4482static int
4483xmlRelaxNGAttributeMatch(xmlRelaxNGValidCtxtPtr ctxt,
4484 xmlRelaxNGDefinePtr define,
4485 xmlAttrPtr prop) {
4486 int ret;
4487
4488 if (define->name != NULL) {
4489 if (!xmlStrEqual(define->name, prop->name))
4490 return(0);
4491 }
4492 if (define->ns != NULL) {
4493 if (define->ns[0] == 0) {
4494 if (prop->ns != NULL)
4495 return(0);
4496 } else {
4497 if ((prop->ns == NULL) ||
4498 (!xmlStrEqual(define->ns, prop->ns->href)))
4499 return(0);
4500 }
4501 }
4502 if (define->nameClass == NULL)
4503 return(1);
4504 define = define->nameClass;
4505 if (define->type == XML_RELAXNG_EXCEPT) {
4506 xmlRelaxNGDefinePtr list;
4507
4508 list = define->content;
4509 while (list != NULL) {
4510 ret = xmlRelaxNGAttributeMatch(ctxt, list, prop);
4511 if (ret == 1)
4512 return(0);
4513 if (ret < 0)
4514 return(ret);
4515 list = list->next;
4516 }
4517 } else {
4518 TODO
4519 }
4520 return(1);
4521}
4522
4523/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004524 * xmlRelaxNGValidateAttribute:
4525 * @ctxt: a Relax-NG validation context
4526 * @define: the definition to verify
4527 *
4528 * Validate the given attribute definition for that node
4529 *
4530 * Returns 0 if the validation succeeded or an error code.
4531 */
4532static int
4533xmlRelaxNGValidateAttribute(xmlRelaxNGValidCtxtPtr ctxt,
4534 xmlRelaxNGDefinePtr define) {
4535 int ret = 0, i;
4536 xmlChar *value, *oldvalue;
4537 xmlAttrPtr prop = NULL, tmp;
4538
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004539 if (ctxt->state->nbAttrLeft <= 0)
4540 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004541 if (define->name != NULL) {
4542 for (i = 0;i < ctxt->state->nbAttrs;i++) {
4543 tmp = ctxt->state->attrs[i];
4544 if ((tmp != NULL) && (xmlStrEqual(define->name, tmp->name))) {
4545 if ((((define->ns == NULL) || (define->ns[0] == 0)) &&
4546 (tmp->ns == NULL)) ||
4547 ((tmp->ns != NULL) &&
4548 (xmlStrEqual(define->ns, tmp->ns->href)))) {
4549 prop = tmp;
4550 break;
4551 }
4552 }
4553 }
4554 if (prop != NULL) {
4555 value = xmlNodeListGetString(prop->doc, prop->children, 1);
4556 oldvalue = ctxt->state->value;
4557 ctxt->state->value = value;
Daniel Veillard231d7912003-02-09 14:22:17 +00004558 ctxt->state->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004559 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00004560 if (ctxt->state->value != NULL)
4561 value = ctxt->state->value;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004562 if (value != NULL)
4563 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00004564 ctxt->state->value = oldvalue;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004565 if (ret == 0) {
4566 /*
4567 * flag the attribute as processed
4568 */
4569 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004570 ctxt->state->nbAttrLeft--;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004571 }
4572 } else {
4573 ret = -1;
4574 }
4575#ifdef DEBUG
4576 xmlGenericError(xmlGenericErrorContext,
4577 "xmlRelaxNGValidateAttribute(%s): %d\n", define->name, ret);
4578#endif
4579 } else {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004580 for (i = 0;i < ctxt->state->nbAttrs;i++) {
4581 tmp = ctxt->state->attrs[i];
Daniel Veillard144fae12003-02-03 13:17:57 +00004582 if ((tmp != NULL) &&
4583 (xmlRelaxNGAttributeMatch(ctxt, define, tmp) == 1)) {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004584 prop = tmp;
4585 break;
4586 }
4587 }
4588 if (prop != NULL) {
4589 value = xmlNodeListGetString(prop->doc, prop->children, 1);
4590 oldvalue = ctxt->state->value;
4591 ctxt->state->value = value;
4592 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00004593 if (ctxt->state->value != NULL)
4594 value = ctxt->state->value;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004595 if (value != NULL)
4596 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00004597 ctxt->state->value = oldvalue;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004598 if (ret == 0) {
4599 /*
4600 * flag the attribute as processed
4601 */
4602 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004603 ctxt->state->nbAttrLeft--;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004604 }
4605 } else {
4606 ret = -1;
4607 }
4608#ifdef DEBUG
Daniel Veillard144fae12003-02-03 13:17:57 +00004609 if (define->ns != NULL) {
4610 xmlGenericError(xmlGenericErrorContext,
4611 "xmlRelaxNGValidateAttribute(nsName ns = %s): %d\n",
4612 define->ns, ret);
4613 } else {
4614 xmlGenericError(xmlGenericErrorContext,
4615 "xmlRelaxNGValidateAttribute(anyName): %d\n",
4616 ret);
4617 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004618#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00004619 }
4620
4621 return(ret);
4622}
4623
4624/**
4625 * xmlRelaxNGValidateAttributeList:
4626 * @ctxt: a Relax-NG validation context
4627 * @define: the list of definition to verify
4628 *
4629 * Validate the given node against the list of attribute definitions
4630 *
4631 * Returns 0 if the validation succeeded or an error code.
4632 */
4633static int
4634xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt,
4635 xmlRelaxNGDefinePtr defines) {
4636 int ret = 0;
4637 while (defines != NULL) {
4638 if (xmlRelaxNGValidateAttribute(ctxt, defines) != 0)
4639 ret = -1;
4640 defines = defines->next;
4641 }
4642 return(ret);
4643}
4644
4645/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004646 * xmlRelaxNGValidateTryPermutation:
4647 * @ctxt: a Relax-NG validation context
4648 * @groups: the array of groups
4649 * @nbgroups: the number of groups in the array
4650 * @array: the permutation to try
4651 * @len: the size of the set
4652 *
4653 * Try to validate a permutation for the group of definitions.
4654 *
4655 * Returns 0 if the validation succeeded or an error code.
4656 */
4657static int
4658xmlRelaxNGValidateTryPermutation(xmlRelaxNGValidCtxtPtr ctxt,
4659 xmlRelaxNGDefinePtr rule,
4660 xmlNodePtr *array, int len) {
4661 int i, ret;
4662
4663 if (len > 0) {
4664 /*
4665 * One only need the next pointer set-up to do the validation
4666 */
4667 for (i = 0;i < (len - 1);i++)
4668 array[i]->next = array[i + 1];
4669 array[i]->next = NULL;
4670
4671 /*
4672 * Now try to validate the sequence
4673 */
4674 ctxt->state->seq = array[0];
4675 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
4676 } else {
4677 ctxt->state->seq = NULL;
4678 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
4679 }
4680
4681 /*
4682 * the sequence must be fully consumed
4683 */
4684 if (ctxt->state->seq != NULL)
4685 return(-1);
4686
4687 return(ret);
4688}
4689
4690/**
4691 * xmlRelaxNGValidateWalkPermutations:
4692 * @ctxt: a Relax-NG validation context
4693 * @groups: the array of groups
4694 * @nbgroups: the number of groups in the array
4695 * @nodes: the set of nodes
4696 * @array: the current state of the parmutation
4697 * @len: the size of the set
4698 * @level: a pointer to the level variable
4699 * @k: the index in the array to fill
4700 *
4701 * Validate a set of nodes for a groups of definitions, will try the
4702 * full set of permutations
4703 *
4704 * Returns 0 if the validation succeeded or an error code.
4705 */
4706static int
4707xmlRelaxNGValidateWalkPermutations(xmlRelaxNGValidCtxtPtr ctxt,
4708 xmlRelaxNGDefinePtr rule, xmlNodePtr *nodes,
4709 xmlNodePtr *array, int len,
4710 int *level, int k) {
4711 int i, ret;
4712
4713 if ((k >= 0) && (k < len))
4714 array[k] = nodes[*level];
4715 *level = *level + 1;
4716 if (*level == len) {
4717 ret = xmlRelaxNGValidateTryPermutation(ctxt, rule, array, len);
4718 if (ret == 0)
4719 return(0);
4720 } else {
4721 for (i = 0;i < len;i++) {
4722 if (array[i] == NULL) {
4723 ret = xmlRelaxNGValidateWalkPermutations(ctxt, rule,
4724 nodes, array, len, level, i);
4725 if (ret == 0)
4726 return(0);
4727 }
4728 }
4729 }
4730 *level = *level - 1;
4731 array[k] = NULL;
4732 return(-1);
4733}
4734
4735/**
4736 * xmlRelaxNGNodeMatchesList:
4737 * @node: the node
4738 * @list: a NULL terminated array of definitions
4739 *
4740 * Check if a node can be matched by one of the definitions
4741 *
4742 * Returns 1 if matches 0 otherwise
4743 */
4744static int
4745xmlRelaxNGNodeMatchesList(xmlNodePtr node, xmlRelaxNGDefinePtr *list) {
4746 xmlRelaxNGDefinePtr cur;
4747 int i = 0;
4748
4749 if ((node == NULL) || (list == NULL))
4750 return(0);
4751
4752 cur = list[i++];
4753 while (cur != NULL) {
4754 if ((node->type == XML_ELEMENT_NODE) &&
4755 (cur->type == XML_RELAXNG_ELEMENT)) {
4756 if (cur->name == NULL) {
4757 if ((node->ns != NULL) &&
4758 (xmlStrEqual(node->ns->href, cur->ns)))
4759 return(1);
4760 } else if (xmlStrEqual(cur->name, node->name)) {
4761 if ((cur->ns == NULL) || (cur->ns[0] == 0)) {
4762 if (node->ns == NULL)
4763 return(1);
4764 } else {
4765 if ((node->ns != NULL) &&
4766 (xmlStrEqual(node->ns->href, cur->ns)))
4767 return(1);
4768 }
4769 }
4770 } else if ((node->type == XML_TEXT_NODE) &&
4771 (cur->type == XML_RELAXNG_TEXT)) {
4772 return(1);
4773 }
4774 cur = list[i++];
4775 }
4776 return(0);
4777}
4778
4779/**
4780 * xmlRelaxNGValidatePartGroup:
4781 * @ctxt: a Relax-NG validation context
4782 * @groups: the array of groups
4783 * @nbgroups: the number of groups in the array
4784 * @nodes: the set of nodes
4785 * @len: the size of the set of nodes
4786 *
4787 * Validate a set of nodes for a groups of definitions
4788 *
4789 * Returns 0 if the validation succeeded or an error code.
4790 */
4791static int
4792xmlRelaxNGValidatePartGroup(xmlRelaxNGValidCtxtPtr ctxt,
4793 xmlRelaxNGInterleaveGroupPtr *groups,
4794 int nbgroups, xmlNodePtr *nodes, int len) {
Daniel Veillard231d7912003-02-09 14:22:17 +00004795 int level, ret = -1, i, j, k, top_j, max_j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004796 xmlNodePtr *array = NULL, *list, oldseq;
4797 xmlRelaxNGInterleaveGroupPtr group;
4798
4799 list = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
4800 if (list == NULL) {
4801 return(-1);
4802 }
4803 array = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
4804 if (array == NULL) {
4805 xmlFree(list);
4806 return(-1);
4807 }
4808 memset(array, 0, len * sizeof(xmlNodePtr));
4809
4810 /*
4811 * Partition the elements and validate the subsets.
4812 */
4813 oldseq = ctxt->state->seq;
Daniel Veillard231d7912003-02-09 14:22:17 +00004814 max_j = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004815 for (i = 0;i < nbgroups;i++) {
4816 group = groups[i];
4817 if (group == NULL)
4818 continue;
4819 k = 0;
Daniel Veillard231d7912003-02-09 14:22:17 +00004820 top_j = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004821 for (j = 0;j < len;j++) {
4822 if (nodes[j] == NULL)
4823 continue;
4824 if (xmlRelaxNGNodeMatchesList(nodes[j], group->defs)) {
4825 list[k++] = nodes[j];
4826 nodes[j] = NULL;
Daniel Veillard231d7912003-02-09 14:22:17 +00004827 top_j = j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004828 }
4829 }
Daniel Veillard231d7912003-02-09 14:22:17 +00004830 if (top_j > max_j)
4831 max_j = top_j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004832 ctxt->state->seq = oldseq;
4833 if (k > 1) {
4834 memset(array, 0, k * sizeof(xmlNodePtr));
Daniel Veillardb08c9812003-01-28 23:09:49 +00004835 level = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004836 ret = xmlRelaxNGValidateWalkPermutations(ctxt, group->rule,
4837 list, array, k, &level, -1);
4838 } else {
4839 ret = xmlRelaxNGValidateTryPermutation(ctxt, group->rule, list, k);
4840 }
4841 if (ret != 0) {
4842 ctxt->state->seq = oldseq;
4843 break;
4844 }
4845 }
4846
Daniel Veillard231d7912003-02-09 14:22:17 +00004847 for (j = 0;j < max_j;j++) {
4848 if (nodes[j] != NULL) {
4849 TODO /* problem, one of the nodes didn't got a match */
4850 }
4851 }
4852 if (ret == 0) {
4853 if (max_j + 1 < len)
4854 ctxt->state->seq = nodes[max_j + 1];
4855 else
4856 ctxt->state->seq = NULL;
4857 }
4858
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004859 xmlFree(list);
4860 xmlFree(array);
4861 return(ret);
4862}
4863
4864/**
4865 * xmlRelaxNGValidateInterleave:
4866 * @ctxt: a Relax-NG validation context
4867 * @define: the definition to verify
4868 *
4869 * Validate an interleave definition for a node.
4870 *
4871 * Returns 0 if the validation succeeded or an error code.
4872 */
4873static int
4874xmlRelaxNGValidateInterleave(xmlRelaxNGValidCtxtPtr ctxt,
4875 xmlRelaxNGDefinePtr define) {
4876 int ret = 0, nbchildren, nbtot, i, j;
4877 xmlRelaxNGPartitionPtr partitions;
4878 xmlNodePtr *children = NULL;
4879 xmlNodePtr *order = NULL;
Daniel Veillard231d7912003-02-09 14:22:17 +00004880 xmlNodePtr cur, oldseq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004881
4882 if (define->data != NULL) {
4883 partitions = (xmlRelaxNGPartitionPtr) define->data;
4884 } else {
4885 VALID_CTXT();
4886 VALID_ERROR("Internal: interleave block has no data\n");
4887 return(-1);
4888 }
4889
4890 /*
4891 * Build the sequence of child and an array preserving the children
4892 * initial order.
4893 */
4894 cur = ctxt->state->seq;
Daniel Veillard231d7912003-02-09 14:22:17 +00004895 oldseq = ctxt->state->seq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004896 nbchildren = 0;
4897 nbtot = 0;
4898 while (cur != NULL) {
4899 if ((cur->type == XML_COMMENT_NODE) ||
4900 (cur->type == XML_PI_NODE) ||
4901 ((cur->type == XML_TEXT_NODE) &&
4902 (IS_BLANK_NODE(cur)))) {
4903 nbtot++;
4904 } else {
4905 nbchildren++;
4906 nbtot++;
4907 }
4908 cur = cur->next;
4909 }
4910 children = (xmlNodePtr *) xmlMalloc(nbchildren * sizeof(xmlNodePtr));
4911 if (children == NULL)
4912 goto error;
4913 order = (xmlNodePtr *) xmlMalloc(nbtot * sizeof(xmlNodePtr));
4914 if (order == NULL)
4915 goto error;
4916 cur = ctxt->state->seq;
4917 i = 0;
4918 j = 0;
4919 while (cur != NULL) {
4920 if ((cur->type == XML_COMMENT_NODE) ||
4921 (cur->type == XML_PI_NODE) ||
4922 ((cur->type == XML_TEXT_NODE) &&
4923 (IS_BLANK_NODE(cur)))) {
4924 order[j++] = cur;
4925 } else {
4926 order[j++] = cur;
4927 children[i++] = cur;
4928 }
4929 cur = cur->next;
4930 }
4931
4932 /* TODO: retry with a maller set of child if there is a next... */
4933 ret = xmlRelaxNGValidatePartGroup(ctxt, partitions->groups,
4934 partitions->nbgroups, children, nbchildren);
Daniel Veillard231d7912003-02-09 14:22:17 +00004935 if (ret != 0)
4936 ctxt->state->seq = oldseq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004937
4938 /*
4939 * Cleanup: rebuid the child sequence and free the structure
4940 */
4941 if (order != NULL) {
4942 for (i = 0;i < nbtot;i++) {
4943 if (i == 0)
4944 order[i]->prev = NULL;
4945 else
4946 order[i]->prev = order[i - 1];
4947 if (i == nbtot - 1)
4948 order[i]->next = NULL;
4949 else
4950 order[i]->next = order[i + 1];
4951 }
4952 xmlFree(order);
4953 }
4954 if (children != NULL)
4955 xmlFree(children);
4956
4957 return(ret);
4958
4959error:
4960 if (order != NULL) {
4961 for (i = 0;i < nbtot;i++) {
4962 if (i == 0)
4963 order[i]->prev = NULL;
4964 else
4965 order[i]->prev = order[i - 1];
4966 if (i == nbtot - 1)
4967 order[i]->next = NULL;
4968 else
4969 order[i]->next = order[i + 1];
4970 }
4971 xmlFree(order);
4972 }
4973 if (children != NULL)
4974 xmlFree(children);
4975 return(-1);
4976}
4977
4978/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004979 * xmlRelaxNGValidateElementContent:
4980 * @ctxt: a Relax-NG validation context
4981 * @define: the list of definition to verify
4982 *
4983 * Validate the given node content against the (list) of definitions
4984 *
4985 * Returns 0 if the validation succeeded or an error code.
4986 */
4987static int
4988xmlRelaxNGValidateElementContent(xmlRelaxNGValidCtxtPtr ctxt,
4989 xmlRelaxNGDefinePtr defines) {
4990 int ret = 0, res;
4991
4992 if (ctxt->state == NULL) {
4993 VALID_CTXT();
4994 VALID_ERROR("Internal: no state\n");
4995 return(-1);
4996 }
4997 while (defines != NULL) {
4998 res = xmlRelaxNGValidateDefinition(ctxt, defines);
4999 if (res < 0)
5000 ret = -1;
5001 defines = defines->next;
5002 }
5003
5004 return(ret);
5005}
5006
5007/**
5008 * xmlRelaxNGValidateDefinition:
5009 * @ctxt: a Relax-NG validation context
5010 * @define: the definition to verify
5011 *
5012 * Validate the current node against the definition
5013 *
5014 * Returns 0 if the validation succeeded or an error code.
5015 */
5016static int
5017xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
5018 xmlRelaxNGDefinePtr define) {
5019 xmlNodePtr node;
5020 int ret = 0, i, tmp, oldflags;
5021 xmlRelaxNGValidStatePtr oldstate, state;
5022
5023 if (define == NULL) {
5024 VALID_CTXT();
5025 VALID_ERROR("internal error: define == NULL\n");
5026 return(-1);
5027 }
5028 if (ctxt->state != NULL) {
5029 node = ctxt->state->seq;
5030 } else {
5031 node = NULL;
5032 }
Daniel Veillard231d7912003-02-09 14:22:17 +00005033#ifdef DEBUG
5034 for (i = 0;i < ctxt->depth;i++)
5035 xmlGenericError(xmlGenericErrorContext, " ");
5036 xmlGenericError(xmlGenericErrorContext,
5037 "Start validating %s ", xmlRelaxNGDefName(define));
5038 if (define->name != NULL)
5039 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
5040 if ((node != NULL) && (node->name != NULL))
5041 xmlGenericError(xmlGenericErrorContext, "on %s\n", node->name);
5042 else
5043 xmlGenericError(xmlGenericErrorContext, "\n");
5044#endif
5045 ctxt->depth++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005046 switch (define->type) {
5047 case XML_RELAXNG_EMPTY:
5048 if (node != NULL) {
5049 VALID_CTXT();
5050 VALID_ERROR("Expecting an empty element\n");
Daniel Veillard231d7912003-02-09 14:22:17 +00005051 ret = -1;
5052 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005053 }
Daniel Veillard231d7912003-02-09 14:22:17 +00005054 ret = 0;
5055 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005056 case XML_RELAXNG_NOT_ALLOWED:
Daniel Veillard231d7912003-02-09 14:22:17 +00005057 ret = -1;
5058 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005059 case XML_RELAXNG_TEXT:
Daniel Veillard231d7912003-02-09 14:22:17 +00005060 if (node == NULL) {
5061 ret = 0;
5062 break;
5063 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005064 while ((node != NULL) &&
5065 ((node->type == XML_TEXT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005066 (node->type == XML_COMMENT_NODE) ||
5067 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00005068 (node->type == XML_CDATA_SECTION_NODE)))
5069 node = node->next;
Daniel Veillard276be4a2003-01-24 01:03:34 +00005070 if (node == ctxt->state->seq) {
5071 VALID_CTXT();
5072 VALID_ERROR("Expecting text content\n");
5073 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005074 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00005075 ctxt->state->seq = node;
5076 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005077 case XML_RELAXNG_ELEMENT:
5078 node = xmlRelaxNGSkipIgnored(ctxt, node);
Daniel Veillard231d7912003-02-09 14:22:17 +00005079 if (node == NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005080 VALID_CTXT();
Daniel Veillard231d7912003-02-09 14:22:17 +00005081 VALID_ERROR("Expecting an element, got empty\n");
5082 ret = -1;
5083 break;
5084 }
5085 if (node->type != XML_ELEMENT_NODE) {
5086 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005087 VALID_ERROR2("Expecting an element got %d type\n", node->type);
Daniel Veillard231d7912003-02-09 14:22:17 +00005088 ret = -1;
5089 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005090 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00005091 /*
5092 * This node was already validated successfully against
5093 * this definition.
5094 */
5095 if (node->_private == define)
5096 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005097 if (define->name != NULL) {
5098 if (!xmlStrEqual(node->name, define->name)) {
5099 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005100 VALID_ERROR3("Expecting element %s, got %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00005101 define->name, node->name);
Daniel Veillard231d7912003-02-09 14:22:17 +00005102 ret = -1;
5103 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005104 }
5105 }
5106 if ((define->ns != NULL) && (define->ns[0] != 0)) {
5107 if (node->ns == NULL) {
5108 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005109 VALID_ERROR2("Expecting a namespace for element %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00005110 node->name);
Daniel Veillard231d7912003-02-09 14:22:17 +00005111 ret = -1;
5112 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005113 } else if (!xmlStrEqual(node->ns->href, define->ns)) {
5114 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005115 VALID_ERROR3("Expecting element %s has wrong namespace: expecting %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00005116 node->name, define->ns);
Daniel Veillard231d7912003-02-09 14:22:17 +00005117 ret = -1;
5118 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005119 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005120 } else if (define->name != NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005121 if (node->ns != NULL) {
5122 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005123 VALID_ERROR2("Expecting no namespace for element %s\n",
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005124 define->name);
Daniel Veillard231d7912003-02-09 14:22:17 +00005125 ret = -1;
5126 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005127 }
5128 }
5129
5130 state = xmlRelaxNGNewValidState(ctxt, node);
5131 if (state == NULL) {
Daniel Veillard231d7912003-02-09 14:22:17 +00005132 ret = -1;
5133 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005134 }
5135
5136 oldstate = ctxt->state;
5137 ctxt->state = state;
5138 if (define->attrs != NULL) {
5139 tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
Daniel Veillard231d7912003-02-09 14:22:17 +00005140 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005141 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00005142#ifdef DEBUG
5143 xmlGenericError(xmlGenericErrorContext,
5144 "E: Element %s failed to validate attributes\n",
5145 node->name);
5146#endif
5147 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005148 }
5149 if (define->content != NULL) {
5150 tmp = xmlRelaxNGValidateElementContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00005151 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005152 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00005153#ifdef DEBUG
5154 xmlGenericError(xmlGenericErrorContext,
5155 "E: Element %s failed to validate element content\n",
5156 node->name);
5157#endif
5158 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005159 }
5160 state = ctxt->state;
5161 if (state->seq != NULL) {
5162 state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq);
5163 if (state->seq != NULL) {
5164 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005165 VALID_ERROR3("Extra content for element %s: %s\n",
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005166 node->name, state->seq->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005167 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00005168#ifdef DEBUG
5169 xmlGenericError(xmlGenericErrorContext,
5170 "E: Element %s has extra content: %s\n",
5171 node->name, state->seq->name);
5172#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00005173 }
5174 }
5175 for (i = 0;i < state->nbAttrs;i++) {
5176 if (state->attrs[i] != NULL) {
5177 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005178 VALID_ERROR3("Invalid attribute %s for element %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00005179 state->attrs[i]->name, node->name);
5180 ret = -1;
5181 }
5182 }
5183 ctxt->state = oldstate;
5184 xmlRelaxNGFreeValidState(state);
5185 if (oldstate != NULL)
Daniel Veillarde5b110b2003-02-04 14:43:39 +00005186 oldstate->seq = xmlRelaxNGSkipIgnored(ctxt, node->next);
5187 if (ret == 0)
5188 node->_private = define;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005189
5190
5191#ifdef DEBUG
5192 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard231d7912003-02-09 14:22:17 +00005193 "xmlRelaxNGValidateDefinition(): validated %s : %d",
Daniel Veillard6eadf632003-01-23 18:29:16 +00005194 node->name, ret);
Daniel Veillard231d7912003-02-09 14:22:17 +00005195 if (oldstate == NULL)
5196 xmlGenericError(xmlGenericErrorContext, ": no state\n");
5197 else if (oldstate->seq == NULL)
5198 xmlGenericError(xmlGenericErrorContext, ": done\n");
5199 else if (oldstate->seq->type == XML_ELEMENT_NODE)
5200 xmlGenericError(xmlGenericErrorContext, ": next elem %s\n",
5201 oldstate->seq->name);
5202 else
5203 xmlGenericError(xmlGenericErrorContext, ": next %s %d\n",
5204 oldstate->seq->name, oldstate->seq->type);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005205#endif
5206 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005207 case XML_RELAXNG_OPTIONAL:
5208 oldflags = ctxt->flags;
5209 ctxt->flags |= FLAGS_IGNORABLE;
5210 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5211 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5212 if (ret != 0) {
5213 xmlRelaxNGFreeValidState(ctxt->state);
5214 ctxt->state = oldstate;
5215 ret = 0;
5216 break;
5217 }
5218 xmlRelaxNGFreeValidState(oldstate);
5219 ctxt->flags = oldflags;
5220 ret = 0;
5221 break;
5222 case XML_RELAXNG_ONEORMORE:
5223 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5224 if (ret != 0) {
5225 break;
5226 }
5227 /* no break on purpose */
Daniel Veillard276be4a2003-01-24 01:03:34 +00005228 case XML_RELAXNG_ZEROORMORE: {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005229 oldflags = ctxt->flags;
5230 ctxt->flags |= FLAGS_IGNORABLE;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005231 while (ctxt->state->nbAttrLeft != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005232 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5233 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5234 if (ret != 0) {
5235 xmlRelaxNGFreeValidState(ctxt->state);
5236 ctxt->state = oldstate;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005237 break;
5238 }
5239 xmlRelaxNGFreeValidState(oldstate);
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005240 }
5241 if (ret == 0) {
5242 /*
5243 * There is no attribute left to be consumed,
5244 * we can check the closure by looking at ctxt->state->seq
5245 */
5246 xmlNodePtr cur, temp;
5247
Daniel Veillard276be4a2003-01-24 01:03:34 +00005248 cur = ctxt->state->seq;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005249 temp = NULL;
5250 while ((cur != NULL) && (temp != cur)) {
5251 temp = cur;
5252 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5253 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5254 if (ret != 0) {
5255 xmlRelaxNGFreeValidState(ctxt->state);
5256 ctxt->state = oldstate;
5257 break;
5258 }
5259 xmlRelaxNGFreeValidState(oldstate);
5260 cur = ctxt->state->seq;
5261 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005262 }
5263 ctxt->flags = oldflags;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005264 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005265 break;
Daniel Veillard276be4a2003-01-24 01:03:34 +00005266 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005267 case XML_RELAXNG_CHOICE: {
5268 xmlRelaxNGDefinePtr list = define->content;
5269
5270 oldflags = ctxt->flags;
5271 ctxt->flags |= FLAGS_IGNORABLE;
5272
5273 while (list != NULL) {
5274 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5275 ret = xmlRelaxNGValidateDefinition(ctxt, list);
5276 if (ret == 0) {
5277 xmlRelaxNGFreeValidState(oldstate);
5278 break;
5279 }
5280 xmlRelaxNGFreeValidState(ctxt->state);
5281 ctxt->state = oldstate;
5282 list = list->next;
5283 }
5284 ctxt->flags = oldflags;
5285 break;
5286 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005287 case XML_RELAXNG_DEF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00005288 case XML_RELAXNG_GROUP: {
5289 xmlRelaxNGDefinePtr list = define->content;
5290
5291 while (list != NULL) {
5292 ret = xmlRelaxNGValidateDefinition(ctxt, list);
5293 if (ret != 0)
5294 break;
5295 list = list->next;
5296 }
5297 break;
5298 }
5299 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005300 ret = xmlRelaxNGValidateInterleave(ctxt, define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005301 break;
5302 case XML_RELAXNG_ATTRIBUTE:
5303 ret = xmlRelaxNGValidateAttribute(ctxt, define);
5304 break;
5305 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00005306 case XML_RELAXNG_PARENTREF:
5307 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00005308 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5309 break;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005310 case XML_RELAXNG_DATATYPE: {
5311 xmlChar *content;
5312
5313 content = xmlNodeGetContent(node);
5314 ret = xmlRelaxNGValidateDatatype(ctxt, content, define);
5315 if (ret == -1) {
5316 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005317 VALID_ERROR2("internal error validating %s\n", define->name);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005318 } else if (ret == 0) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005319 if (node != NULL)
5320 ctxt->state->seq = node->next;
5321 else
5322 ctxt->state->seq = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005323 }
5324 /*
5325 * TODO cover the problems with
5326 * <p>12<!-- comment -->34</p>
5327 * TODO detect full element coverage at compilation time.
5328 */
5329 if ((node != NULL) && (node->next != NULL)) {
5330 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005331 VALID_ERROR2("The data does not cover the full element %s\n",
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005332 node->parent->name);
5333 ret = -1;
5334 }
5335 if (content != NULL)
5336 xmlFree(content);
5337 break;
5338 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00005339 case XML_RELAXNG_VALUE: {
5340 xmlChar *content;
5341 xmlChar *oldvalue;
5342
5343 content = xmlNodeGetContent(node);
5344 oldvalue = ctxt->state->value;
5345 ctxt->state->value = content;
5346 ret = xmlRelaxNGValidateValue(ctxt, define);
5347 ctxt->state->value = oldvalue;
5348 if (ret == -1) {
5349 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005350 VALID_ERROR2("internal error validating %s\n", define->name);
Daniel Veillardea3f3982003-01-26 19:45:18 +00005351 } else if (ret == 0) {
5352 ctxt->state->seq = node->next;
5353 }
5354 /*
5355 * TODO cover the problems with
5356 * <p>12<!-- comment -->34</p>
5357 * TODO detect full element coverage at compilation time.
5358 */
5359 if ((node != NULL) && (node->next != NULL)) {
5360 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005361 VALID_ERROR2("The value does not cover the full element %s\n",
Daniel Veillardea3f3982003-01-26 19:45:18 +00005362 node->parent->name);
5363 ret = -1;
5364 }
5365 if (content != NULL)
5366 xmlFree(content);
5367 break;
5368 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005369 case XML_RELAXNG_LIST: {
5370 xmlChar *content;
5371 xmlChar *oldvalue, *oldendvalue;
5372 int len;
Daniel Veillardea3f3982003-01-26 19:45:18 +00005373
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005374 content = xmlNodeGetContent(node);
5375 len = xmlStrlen(content);
5376 oldvalue = ctxt->state->value;
5377 oldendvalue = ctxt->state->endvalue;
5378 ctxt->state->value = content;
5379 ctxt->state->endvalue = content + len;
5380 ret = xmlRelaxNGValidateValue(ctxt, define);
5381 ctxt->state->value = oldvalue;
5382 ctxt->state->endvalue = oldendvalue;
5383 if (ret == -1) {
5384 VALID_CTXT();
5385 VALID_ERROR("internal error validating list\n");
5386 } else if (ret == 0) {
5387 ctxt->state->seq = node->next;
5388 }
5389 /*
5390 * TODO cover the problems with
5391 * <p>12<!-- comment -->34</p>
5392 * TODO detect full element coverage at compilation time.
5393 */
5394 if ((node != NULL) && (node->next != NULL)) {
5395 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005396 VALID_ERROR2("The list does not cover the full element %s\n",
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005397 node->parent->name);
5398 ret = -1;
5399 }
5400 if (content != NULL)
5401 xmlFree(content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005402 break;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005403 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005404 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00005405 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005406 TODO
5407 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005408 }
Daniel Veillard231d7912003-02-09 14:22:17 +00005409 ctxt->depth--;
5410#ifdef DEBUG
5411 for (i = 0;i < ctxt->depth;i++)
5412 xmlGenericError(xmlGenericErrorContext, " ");
5413 xmlGenericError(xmlGenericErrorContext,
5414 "Validating %s ", xmlRelaxNGDefName(define));
5415 if (define->name != NULL)
5416 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
5417 if (ret == 0)
5418 xmlGenericError(xmlGenericErrorContext, "suceeded\n");
5419 else
5420 xmlGenericError(xmlGenericErrorContext, "failed\n");
5421#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00005422 return(ret);
5423}
5424
5425/**
5426 * xmlRelaxNGValidateDocument:
5427 * @ctxt: a Relax-NG validation context
5428 * @doc: the document
5429 *
5430 * Validate the given document
5431 *
5432 * Returns 0 if the validation succeeded or an error code.
5433 */
5434static int
5435xmlRelaxNGValidateDocument(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
5436 int ret;
5437 xmlRelaxNGPtr schema;
5438 xmlRelaxNGGrammarPtr grammar;
5439 xmlRelaxNGValidStatePtr state;
5440
5441 if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL))
5442 return(-1);
5443
5444 schema = ctxt->schema;
5445 grammar = schema->topgrammar;
5446 if (grammar == NULL) {
5447 VALID_CTXT();
5448 VALID_ERROR("No top grammar defined\n");
5449 return(-1);
5450 }
5451 state = xmlRelaxNGNewValidState(ctxt, NULL);
5452 ctxt->state = state;
5453 ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start);
5454 state = ctxt->state;
5455 if ((state != NULL) && (state->seq != NULL)) {
5456 xmlNodePtr node;
5457
5458 node = state->seq;
5459 node = xmlRelaxNGSkipIgnored(ctxt, node);
5460 if (node != NULL) {
5461 VALID_CTXT();
5462 VALID_ERROR("extra data on the document\n");
5463 ret = -1;
5464 }
5465 }
5466 xmlRelaxNGFreeValidState(state);
5467
5468 return(ret);
5469}
5470
5471/************************************************************************
5472 * *
5473 * Validation interfaces *
5474 * *
5475 ************************************************************************/
5476/**
5477 * xmlRelaxNGNewValidCtxt:
5478 * @schema: a precompiled XML RelaxNGs
5479 *
5480 * Create an XML RelaxNGs validation context based on the given schema
5481 *
5482 * Returns the validation context or NULL in case of error
5483 */
5484xmlRelaxNGValidCtxtPtr
5485xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema) {
5486 xmlRelaxNGValidCtxtPtr ret;
5487
5488 ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt));
5489 if (ret == NULL) {
5490 xmlGenericError(xmlGenericErrorContext,
5491 "Failed to allocate new schama validation context\n");
5492 return (NULL);
5493 }
5494 memset(ret, 0, sizeof(xmlRelaxNGValidCtxt));
5495 ret->schema = schema;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005496 ret->error = xmlGenericError;
5497 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005498 return (ret);
5499}
5500
5501/**
5502 * xmlRelaxNGFreeValidCtxt:
5503 * @ctxt: the schema validation context
5504 *
5505 * Free the resources associated to the schema validation context
5506 */
5507void
5508xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt) {
5509 if (ctxt == NULL)
5510 return;
5511 xmlFree(ctxt);
5512}
5513
5514/**
5515 * xmlRelaxNGSetValidErrors:
5516 * @ctxt: a Relax-NG validation context
5517 * @err: the error function
5518 * @warn: the warning function
5519 * @ctx: the functions context
5520 *
5521 * Set the error and warning callback informations
5522 */
5523void
5524xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
5525 xmlRelaxNGValidityErrorFunc err,
5526 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
5527 if (ctxt == NULL)
5528 return;
5529 ctxt->error = err;
5530 ctxt->warning = warn;
5531 ctxt->userData = ctx;
5532}
5533
5534/**
5535 * xmlRelaxNGValidateDoc:
5536 * @ctxt: a Relax-NG validation context
5537 * @doc: a parsed document tree
5538 *
5539 * Validate a document tree in memory.
5540 *
5541 * Returns 0 if the document is valid, a positive error code
5542 * number otherwise and -1 in case of internal or API error.
5543 */
5544int
5545xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
5546 int ret;
5547
5548 if ((ctxt == NULL) || (doc == NULL))
5549 return(-1);
5550
5551 ctxt->doc = doc;
5552
5553 ret = xmlRelaxNGValidateDocument(ctxt, doc);
Daniel Veillard71531f32003-02-05 13:19:53 +00005554 /*
5555 * TODO: build error codes
5556 */
5557 if (ret == -1)
5558 return(1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005559 return(ret);
5560}
5561
5562#endif /* LIBXML_SCHEMAS_ENABLED */
5563