blob: 4ff9a7c0ae9aedfab29770e1cdec7435bec50a24 [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 Veillard231d7912003-02-09 14:22:17 +00001183#define VALID_ERROR \
1184 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1185 printf
Daniel Veillard6eadf632003-01-23 18:29:16 +00001186
Daniel Veillard231d7912003-02-09 14:22:17 +00001187static const char *
1188xmlRelaxNGDefName(xmlRelaxNGDefinePtr def) {
1189 if (def == NULL)
1190 return("none");
1191 switch(def->type) {
1192 case XML_RELAXNG_EMPTY: return("empty");
1193 case XML_RELAXNG_NOT_ALLOWED: return("notAllowed");
1194 case XML_RELAXNG_EXCEPT: return("except");
1195 case XML_RELAXNG_TEXT: return("text");
1196 case XML_RELAXNG_ELEMENT: return("element");
1197 case XML_RELAXNG_DATATYPE: return("datatype");
1198 case XML_RELAXNG_VALUE: return("value");
1199 case XML_RELAXNG_LIST: return("list");
1200 case XML_RELAXNG_ATTRIBUTE: return("attribute");
1201 case XML_RELAXNG_DEF: return("def");
1202 case XML_RELAXNG_REF: return("ref");
1203 case XML_RELAXNG_EXTERNALREF: return("externalRef");
1204 case XML_RELAXNG_PARENTREF: return("parentRef");
1205 case XML_RELAXNG_OPTIONAL: return("optional");
1206 case XML_RELAXNG_ZEROORMORE: return("zeroOrMore");
1207 case XML_RELAXNG_ONEORMORE: return("oneOrMore");
1208 case XML_RELAXNG_CHOICE: return("choice");
1209 case XML_RELAXNG_GROUP: return("group");
1210 case XML_RELAXNG_INTERLEAVE: return("interleave");
1211 case XML_RELAXNG_START: return("start");
1212 }
1213 return("unknown");
1214}
Daniel Veillard6eadf632003-01-23 18:29:16 +00001215#if 0
1216/**
1217 * xmlRelaxNGErrorContext:
1218 * @ctxt: the parsing context
1219 * @schema: the schema being built
1220 * @node: the node being processed
1221 * @child: the child being processed
1222 *
1223 * Dump a RelaxNGType structure
1224 */
1225static void
1226xmlRelaxNGErrorContext(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGPtr schema,
1227 xmlNodePtr node, xmlNodePtr child)
1228{
1229 int line = 0;
1230 const xmlChar *file = NULL;
1231 const xmlChar *name = NULL;
1232 const char *type = "error";
1233
1234 if ((ctxt == NULL) || (ctxt->error == NULL))
1235 return;
1236
1237 if (child != NULL)
1238 node = child;
1239
1240 if (node != NULL) {
1241 if ((node->type == XML_DOCUMENT_NODE) ||
1242 (node->type == XML_HTML_DOCUMENT_NODE)) {
1243 xmlDocPtr doc = (xmlDocPtr) node;
1244
1245 file = doc->URL;
1246 } else {
1247 /*
1248 * Try to find contextual informations to report
1249 */
1250 if (node->type == XML_ELEMENT_NODE) {
1251 line = (int) node->content;
1252 } else if ((node->prev != NULL) &&
1253 (node->prev->type == XML_ELEMENT_NODE)) {
1254 line = (int) node->prev->content;
1255 } else if ((node->parent != NULL) &&
1256 (node->parent->type == XML_ELEMENT_NODE)) {
1257 line = (int) node->parent->content;
1258 }
1259 if ((node->doc != NULL) && (node->doc->URL != NULL))
1260 file = node->doc->URL;
1261 if (node->name != NULL)
1262 name = node->name;
1263 }
1264 }
1265
1266 if (ctxt != NULL)
1267 type = "compilation error";
1268 else if (schema != NULL)
1269 type = "runtime error";
1270
1271 if ((file != NULL) && (line != 0) && (name != NULL))
1272 ctxt->error(ctxt->userData, "%s: file %s line %d element %s\n",
1273 type, file, line, name);
1274 else if ((file != NULL) && (name != NULL))
1275 ctxt->error(ctxt->userData, "%s: file %s element %s\n",
1276 type, file, name);
1277 else if ((file != NULL) && (line != 0))
1278 ctxt->error(ctxt->userData, "%s: file %s line %d\n", type, file, line);
1279 else if (file != NULL)
1280 ctxt->error(ctxt->userData, "%s: file %s\n", type, file);
1281 else if (name != NULL)
1282 ctxt->error(ctxt->userData, "%s: element %s\n", type, name);
1283 else
1284 ctxt->error(ctxt->userData, "%s\n", type);
1285}
1286#endif
1287
1288/************************************************************************
1289 * *
1290 * Type library hooks *
1291 * *
1292 ************************************************************************/
Daniel Veillardea3f3982003-01-26 19:45:18 +00001293static xmlChar *xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt,
1294 const xmlChar *str);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001295
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001296/**
1297 * xmlRelaxNGSchemaTypeHave:
1298 * @data: data needed for the library
1299 * @type: the type name
1300 *
1301 * Check if the given type is provided by
1302 * the W3C XMLSchema Datatype library.
1303 *
1304 * Returns 1 if yes, 0 if no and -1 in case of error.
1305 */
1306static int
1307xmlRelaxNGSchemaTypeHave(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001308 const xmlChar *type) {
1309 xmlSchemaTypePtr typ;
1310
1311 if (type == NULL)
1312 return(-1);
1313 typ = xmlSchemaGetPredefinedType(type,
1314 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1315 if (typ == NULL)
1316 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001317 return(1);
1318}
1319
1320/**
1321 * xmlRelaxNGSchemaTypeCheck:
1322 * @data: data needed for the library
1323 * @type: the type name
1324 * @value: the value to check
1325 *
1326 * Check if the given type and value are validated by
1327 * the W3C XMLSchema Datatype library.
1328 *
1329 * Returns 1 if yes, 0 if no and -1 in case of error.
1330 */
1331static int
1332xmlRelaxNGSchemaTypeCheck(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001333 const xmlChar *type,
1334 const xmlChar *value) {
1335 xmlSchemaTypePtr typ;
1336 int ret;
1337
1338 /*
1339 * TODO: the type should be cached ab provided back, interface subject
1340 * to changes.
1341 * TODO: handle facets, may require an additional interface and keep
1342 * the value returned from the validation.
1343 */
1344 if ((type == NULL) || (value == NULL))
1345 return(-1);
1346 typ = xmlSchemaGetPredefinedType(type,
1347 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1348 if (typ == NULL)
1349 return(-1);
1350 ret = xmlSchemaValidatePredefinedType(typ, value, NULL);
1351 if (ret == 0)
1352 return(1);
1353 if (ret > 0)
1354 return(0);
1355 return(-1);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001356}
1357
1358/**
1359 * xmlRelaxNGSchemaTypeCompare:
1360 * @data: data needed for the library
1361 * @type: the type name
1362 * @value1: the first value
1363 * @value2: the second value
1364 *
1365 * Compare two values accordingly a type from the W3C XMLSchema
1366 * Datatype library.
1367 *
1368 * Returns 1 if yes, 0 if no and -1 in case of error.
1369 */
1370static int
1371xmlRelaxNGSchemaTypeCompare(void *data ATTRIBUTE_UNUSED,
1372 const xmlChar *type ATTRIBUTE_UNUSED,
1373 const xmlChar *value1 ATTRIBUTE_UNUSED,
1374 const xmlChar *value2 ATTRIBUTE_UNUSED) {
1375 TODO
1376 return(1);
1377}
1378
1379/**
1380 * xmlRelaxNGDefaultTypeHave:
1381 * @data: data needed for the library
1382 * @type: the type name
1383 *
1384 * Check if the given type is provided by
1385 * the default datatype library.
1386 *
1387 * Returns 1 if yes, 0 if no and -1 in case of error.
1388 */
1389static int
1390xmlRelaxNGDefaultTypeHave(void *data ATTRIBUTE_UNUSED, const xmlChar *type) {
1391 if (type == NULL)
1392 return(-1);
1393 if (xmlStrEqual(type, BAD_CAST "string"))
1394 return(1);
1395 if (xmlStrEqual(type, BAD_CAST "token"))
1396 return(1);
1397 return(0);
1398}
1399
1400/**
1401 * xmlRelaxNGDefaultTypeCheck:
1402 * @data: data needed for the library
1403 * @type: the type name
1404 * @value: the value to check
1405 *
1406 * Check if the given type and value are validated by
1407 * the default datatype library.
1408 *
1409 * Returns 1 if yes, 0 if no and -1 in case of error.
1410 */
1411static int
1412xmlRelaxNGDefaultTypeCheck(void *data ATTRIBUTE_UNUSED,
1413 const xmlChar *type ATTRIBUTE_UNUSED,
1414 const xmlChar *value ATTRIBUTE_UNUSED) {
1415 return(1);
1416}
1417
1418/**
1419 * xmlRelaxNGDefaultTypeCompare:
1420 * @data: data needed for the library
1421 * @type: the type name
1422 * @value1: the first value
1423 * @value2: the second value
1424 *
1425 * Compare two values accordingly a type from the default
1426 * datatype library.
1427 *
1428 * Returns 1 if yes, 0 if no and -1 in case of error.
1429 */
1430static int
1431xmlRelaxNGDefaultTypeCompare(void *data ATTRIBUTE_UNUSED,
1432 const xmlChar *type ATTRIBUTE_UNUSED,
1433 const xmlChar *value1 ATTRIBUTE_UNUSED,
1434 const xmlChar *value2 ATTRIBUTE_UNUSED) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00001435 int ret = -1;
1436
1437 if (xmlStrEqual(type, BAD_CAST "string")) {
1438 ret = xmlStrEqual(value1, value2);
1439 } else if (xmlStrEqual(type, BAD_CAST "token")) {
1440 if (!xmlStrEqual(value1, value2)) {
1441 xmlChar *nval, *nvalue;
1442
1443 /*
1444 * TODO: trivial optimizations are possible by
1445 * computing at compile-time
1446 */
1447 nval = xmlRelaxNGNormalize(NULL, value1);
1448 nvalue = xmlRelaxNGNormalize(NULL, value2);
1449
1450 if ((nval == NULL) || (nvalue == NULL) ||
1451 (!xmlStrEqual(nval, nvalue)))
1452 ret = -1;
1453 if (nval != NULL)
1454 xmlFree(nval);
1455 if (nvalue != NULL)
1456 xmlFree(nvalue);
1457 }
1458 }
1459 return(ret);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001460}
1461
1462static int xmlRelaxNGTypeInitialized = 0;
1463static xmlHashTablePtr xmlRelaxNGRegisteredTypes = NULL;
1464
1465/**
1466 * xmlRelaxNGFreeTypeLibrary:
1467 * @lib: the type library structure
1468 * @namespace: the URI bound to the library
1469 *
1470 * Free the structure associated to the type library
1471 */
Daniel Veillard6eadf632003-01-23 18:29:16 +00001472static void
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001473xmlRelaxNGFreeTypeLibrary(xmlRelaxNGTypeLibraryPtr lib,
1474 const xmlChar *namespace ATTRIBUTE_UNUSED) {
1475 if (lib == NULL)
1476 return;
1477 if (lib->namespace != NULL)
1478 xmlFree((xmlChar *)lib->namespace);
1479 xmlFree(lib);
1480}
1481
1482/**
1483 * xmlRelaxNGRegisterTypeLibrary:
1484 * @namespace: the URI bound to the library
1485 * @data: data associated to the library
1486 * @have: the provide function
1487 * @check: the checking function
1488 * @comp: the comparison function
1489 *
1490 * Register a new type library
1491 *
1492 * Returns 0 in case of success and -1 in case of error.
1493 */
1494static int
1495xmlRelaxNGRegisterTypeLibrary(const xmlChar *namespace, void *data,
1496 xmlRelaxNGTypeHave have, xmlRelaxNGTypeCheck check,
1497 xmlRelaxNGTypeCompare comp) {
1498 xmlRelaxNGTypeLibraryPtr lib;
1499 int ret;
1500
1501 if ((xmlRelaxNGRegisteredTypes == NULL) || (namespace == NULL) ||
1502 (check == NULL) || (comp == NULL))
1503 return(-1);
1504 if (xmlHashLookup(xmlRelaxNGRegisteredTypes, namespace) != NULL) {
1505 xmlGenericError(xmlGenericErrorContext,
1506 "Relax-NG types library '%s' already registered\n",
1507 namespace);
1508 return(-1);
1509 }
1510 lib = (xmlRelaxNGTypeLibraryPtr) xmlMalloc(sizeof(xmlRelaxNGTypeLibrary));
1511 if (lib == NULL) {
1512 xmlGenericError(xmlGenericErrorContext,
1513 "Relax-NG types library '%s' malloc() failed\n",
1514 namespace);
1515 return (-1);
1516 }
1517 memset(lib, 0, sizeof(xmlRelaxNGTypeLibrary));
1518 lib->namespace = xmlStrdup(namespace);
1519 lib->data = data;
1520 lib->have = have;
1521 lib->comp = comp;
1522 lib->check = check;
1523 ret = xmlHashAddEntry(xmlRelaxNGRegisteredTypes, namespace, lib);
1524 if (ret < 0) {
1525 xmlGenericError(xmlGenericErrorContext,
1526 "Relax-NG types library failed to register '%s'\n",
1527 namespace);
1528 xmlRelaxNGFreeTypeLibrary(lib, namespace);
1529 return(-1);
1530 }
1531 return(0);
1532}
1533
1534/**
1535 * xmlRelaxNGInitTypes:
1536 *
1537 * Initilize the default type libraries.
1538 *
1539 * Returns 0 in case of success and -1 in case of error.
1540 */
1541static int
Daniel Veillard6eadf632003-01-23 18:29:16 +00001542xmlRelaxNGInitTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001543 if (xmlRelaxNGTypeInitialized != 0)
1544 return(0);
1545 xmlRelaxNGRegisteredTypes = xmlHashCreate(10);
1546 if (xmlRelaxNGRegisteredTypes == NULL) {
1547 xmlGenericError(xmlGenericErrorContext,
1548 "Failed to allocate sh table for Relax-NG types\n");
1549 return(-1);
1550 }
1551 xmlRelaxNGRegisterTypeLibrary(
1552 BAD_CAST "http://www.w3.org/2001/XMLSchema-datatypes",
1553 NULL,
1554 xmlRelaxNGSchemaTypeHave,
1555 xmlRelaxNGSchemaTypeCheck,
1556 xmlRelaxNGSchemaTypeCompare);
1557 xmlRelaxNGRegisterTypeLibrary(
1558 xmlRelaxNGNs,
1559 NULL,
1560 xmlRelaxNGDefaultTypeHave,
1561 xmlRelaxNGDefaultTypeCheck,
1562 xmlRelaxNGDefaultTypeCompare);
1563 xmlRelaxNGTypeInitialized = 1;
1564 return(0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001565}
1566
1567/**
1568 * xmlRelaxNGCleanupTypes:
1569 *
1570 * Cleanup the default Schemas type library associated to RelaxNG
1571 */
1572void
1573xmlRelaxNGCleanupTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001574 if (xmlRelaxNGTypeInitialized == 0)
1575 return;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001576 xmlSchemaCleanupTypes();
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001577 xmlHashFree(xmlRelaxNGRegisteredTypes, (xmlHashDeallocator)
1578 xmlRelaxNGFreeTypeLibrary);
1579 xmlRelaxNGTypeInitialized = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001580}
1581
1582/************************************************************************
1583 * *
1584 * Parsing functions *
1585 * *
1586 ************************************************************************/
1587
1588static xmlRelaxNGDefinePtr xmlRelaxNGParseAttribute(
1589 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1590static xmlRelaxNGDefinePtr xmlRelaxNGParseElement(
1591 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1592static xmlRelaxNGDefinePtr xmlRelaxNGParsePatterns(
Daniel Veillard154877e2003-01-30 12:17:05 +00001593 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes, int group);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001594static xmlRelaxNGDefinePtr xmlRelaxNGParsePattern(
1595 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001596static xmlRelaxNGPtr xmlRelaxNGParseDocument(
1597 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00001598static int xmlRelaxNGParseGrammarContent(
1599 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00001600static xmlRelaxNGDefinePtr xmlRelaxNGParseNameClass(
1601 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
1602 xmlRelaxNGDefinePtr def);
Daniel Veillard419a7682003-02-03 23:22:49 +00001603static xmlRelaxNGGrammarPtr xmlRelaxNGParseGrammar(
1604 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001605
1606
1607#define IS_BLANK_NODE(n) \
1608 (((n)->type == XML_TEXT_NODE) && (xmlRelaxNGIsBlank((n)->content)))
1609
1610/**
1611 * xmlRelaxNGIsBlank:
1612 * @str: a string
1613 *
1614 * Check if a string is ignorable c.f. 4.2. Whitespace
1615 *
1616 * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
1617 */
1618static int
1619xmlRelaxNGIsBlank(xmlChar *str) {
1620 if (str == NULL)
1621 return(1);
1622 while (*str != 0) {
1623 if (!(IS_BLANK(*str))) return(0);
1624 str++;
1625 }
1626 return(1);
1627}
1628
Daniel Veillard6eadf632003-01-23 18:29:16 +00001629/**
1630 * xmlRelaxNGGetDataTypeLibrary:
1631 * @ctxt: a Relax-NG parser context
1632 * @node: the current data or value element
1633 *
1634 * Applies algorithm from 4.3. datatypeLibrary attribute
1635 *
1636 * Returns the datatypeLibary value or NULL if not found
1637 */
1638static xmlChar *
1639xmlRelaxNGGetDataTypeLibrary(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1640 xmlNodePtr node) {
1641 xmlChar *ret, *escape;
1642
Daniel Veillard6eadf632003-01-23 18:29:16 +00001643 if ((IS_RELAXNG(node, "data")) || (IS_RELAXNG(node, "value"))) {
1644 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1645 if (ret != NULL) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001646 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +00001647 if (escape == NULL) {
1648 return(ret);
1649 }
1650 xmlFree(ret);
1651 return(escape);
1652 }
1653 }
1654 node = node->parent;
1655 while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) {
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001656 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1657 if (ret != NULL) {
1658 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
1659 if (escape == NULL) {
1660 return(ret);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001661 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001662 xmlFree(ret);
1663 return(escape);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001664 }
1665 node = node->parent;
1666 }
1667 return(NULL);
1668}
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001669
1670/**
Daniel Veillardedc91922003-01-26 00:52:04 +00001671 * xmlRelaxNGParseValue:
1672 * @ctxt: a Relax-NG parser context
1673 * @node: the data node.
1674 *
1675 * parse the content of a RelaxNG value node.
1676 *
1677 * Returns the definition pointer or NULL in case of error
1678 */
1679static xmlRelaxNGDefinePtr
1680xmlRelaxNGParseValue(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1681 xmlRelaxNGDefinePtr def = NULL;
1682 xmlRelaxNGTypeLibraryPtr lib;
1683 xmlChar *type;
1684 xmlChar *library;
1685 int tmp;
1686
1687 def = xmlRelaxNGNewDefine(ctxt, node);
1688 if (def == NULL)
1689 return(NULL);
1690 def->type = XML_RELAXNG_VALUE;
1691
1692 type = xmlGetProp(node, BAD_CAST "type");
1693 if (type != NULL) {
1694 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1695 if (library == NULL)
1696 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1697
1698 def->name = type;
1699 def->ns = library;
1700
1701 lib = (xmlRelaxNGTypeLibraryPtr)
1702 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1703 if (lib == NULL) {
1704 if (ctxt->error != NULL)
1705 ctxt->error(ctxt->userData,
1706 "Use of unregistered type library '%s'\n",
1707 library);
1708 ctxt->nbErrors++;
1709 def->data = NULL;
1710 } else {
1711 def->data = lib;
1712 if (lib->have == NULL) {
1713 ctxt->error(ctxt->userData,
1714 "Internal error with type library '%s': no 'have'\n",
1715 library);
1716 ctxt->nbErrors++;
1717 } else {
1718 tmp = lib->have(lib->data, def->name);
1719 if (tmp != 1) {
1720 ctxt->error(ctxt->userData,
1721 "Error type '%s' is not exported by type library '%s'\n",
1722 def->name, library);
1723 ctxt->nbErrors++;
1724 }
1725 }
1726 }
1727 }
1728 if (node->children == NULL) {
1729 if (ctxt->error != NULL)
1730 ctxt->error(ctxt->userData,
1731 "Element <value> has no content\n");
1732 ctxt->nbErrors++;
1733 } else if ((node->children->type != XML_TEXT_NODE) ||
1734 (node->children->next != NULL)) {
1735 if (ctxt->error != NULL)
1736 ctxt->error(ctxt->userData,
1737 "Expecting a single text value for <value>content\n");
1738 ctxt->nbErrors++;
1739 } else {
1740 def->value = xmlNodeGetContent(node);
1741 if (def->value == NULL) {
1742 if (ctxt->error != NULL)
1743 ctxt->error(ctxt->userData,
1744 "Element <value> has no content\n");
1745 ctxt->nbErrors++;
1746 }
1747 }
1748 /* TODO check ahead of time that the value is okay per the type */
1749 return(def);
1750}
1751
1752/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001753 * xmlRelaxNGParseData:
1754 * @ctxt: a Relax-NG parser context
1755 * @node: the data node.
1756 *
1757 * parse the content of a RelaxNG data node.
1758 *
1759 * Returns the definition pointer or NULL in case of error
1760 */
1761static xmlRelaxNGDefinePtr
1762xmlRelaxNGParseData(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1763 xmlRelaxNGDefinePtr def = NULL;
1764 xmlRelaxNGTypeLibraryPtr lib;
1765 xmlChar *type;
1766 xmlChar *library;
1767 xmlNodePtr content;
1768 int tmp;
1769
1770 type = xmlGetProp(node, BAD_CAST "type");
1771 if (type == NULL) {
1772 if (ctxt->error != NULL)
1773 ctxt->error(ctxt->userData,
1774 "data has no type\n");
1775 ctxt->nbErrors++;
1776 return(NULL);
1777 }
1778 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1779 if (library == NULL)
1780 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1781
1782 def = xmlRelaxNGNewDefine(ctxt, node);
1783 if (def == NULL) {
1784 xmlFree(type);
1785 return(NULL);
1786 }
1787 def->type = XML_RELAXNG_DATATYPE;
1788 def->name = type;
1789 def->ns = library;
1790
1791 lib = (xmlRelaxNGTypeLibraryPtr)
1792 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1793 if (lib == NULL) {
1794 if (ctxt->error != NULL)
1795 ctxt->error(ctxt->userData,
1796 "Use of unregistered type library '%s'\n",
1797 library);
1798 ctxt->nbErrors++;
1799 def->data = NULL;
1800 } else {
1801 def->data = lib;
1802 if (lib->have == NULL) {
1803 ctxt->error(ctxt->userData,
1804 "Internal error with type library '%s': no 'have'\n",
1805 library);
1806 ctxt->nbErrors++;
1807 } else {
1808 tmp = lib->have(lib->data, def->name);
1809 if (tmp != 1) {
1810 ctxt->error(ctxt->userData,
1811 "Error type '%s' is not exported by type library '%s'\n",
1812 def->name, library);
1813 ctxt->nbErrors++;
1814 }
1815 }
1816 }
1817 content = node->children;
1818 while (content != NULL) {
1819 TODO
1820 content = content->next;
1821 }
1822
1823 return(def);
1824}
1825
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001826/**
1827 * xmlRelaxNGCompareElemDefLists:
1828 * @ctxt: a Relax-NG parser context
1829 * @defs1: the first list of element defs
1830 * @defs2: the second list of element defs
1831 *
1832 * Compare the 2 lists of element definitions. The comparison is
1833 * that if both lists do not accept the same QNames, it returns 1
1834 * If the 2 lists can accept the same QName the comparison returns 0
1835 *
1836 * Returns 1 disttinct, 0 if equal
1837 */
1838static int
1839xmlRelaxNGCompareElemDefLists(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1840 xmlRelaxNGDefinePtr *def1,
1841 xmlRelaxNGDefinePtr *def2) {
1842 xmlRelaxNGDefinePtr *basedef2 = def2;
1843
Daniel Veillard154877e2003-01-30 12:17:05 +00001844 if ((def1 == NULL) || (def2 == NULL))
1845 return(1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001846 if ((*def1 == NULL) || (*def2 == NULL))
1847 return(1);
1848 while (*def1 != NULL) {
1849 while ((*def2) != NULL) {
1850 if ((*def1)->name == NULL) {
1851 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1852 return(0);
1853 } else if ((*def2)->name == NULL) {
1854 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1855 return(0);
1856 } else if (xmlStrEqual((*def1)->name, (*def2)->name)) {
1857 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1858 return(0);
1859 }
1860 def2++;
1861 }
1862 def2 = basedef2;
1863 def1++;
1864 }
1865 return(1);
1866}
1867
1868/**
1869 * xmlRelaxNGGetElements:
1870 * @ctxt: a Relax-NG parser context
1871 * @def: the interleave definition
1872 *
1873 * Compute the list of top elements a definition can generate
1874 *
1875 * Returns a list of elements or NULL if none was found.
1876 */
1877static xmlRelaxNGDefinePtr *
1878xmlRelaxNGGetElements(xmlRelaxNGParserCtxtPtr ctxt,
1879 xmlRelaxNGDefinePtr def) {
1880 xmlRelaxNGDefinePtr *ret = NULL, parent, cur, tmp;
1881 int len = 0;
1882 int max = 0;
1883
1884 parent = NULL;
1885 cur = def;
1886 while (cur != NULL) {
Daniel Veillardb08c9812003-01-28 23:09:49 +00001887 if ((cur->type == XML_RELAXNG_ELEMENT) ||
1888 (cur->type == XML_RELAXNG_TEXT)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001889 if (ret == NULL) {
1890 max = 10;
1891 ret = (xmlRelaxNGDefinePtr *)
1892 xmlMalloc((max + 1) * sizeof(xmlRelaxNGDefinePtr));
1893 if (ret == NULL) {
1894 if (ctxt->error != NULL)
1895 ctxt->error(ctxt->userData,
1896 "Out of memory in element search\n");
1897 ctxt->nbErrors++;
1898 return(NULL);
1899 }
1900 } else if (max <= len) {
1901 max *= 2;
1902 ret = xmlRealloc(ret, (max + 1) * sizeof(xmlRelaxNGDefinePtr));
1903 if (ret == NULL) {
1904 if (ctxt->error != NULL)
1905 ctxt->error(ctxt->userData,
1906 "Out of memory in element search\n");
1907 ctxt->nbErrors++;
1908 return(NULL);
1909 }
1910 }
Daniel Veillardb08c9812003-01-28 23:09:49 +00001911 ret[len++] = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001912 ret[len] = NULL;
1913 } else if ((cur->type == XML_RELAXNG_CHOICE) ||
1914 (cur->type == XML_RELAXNG_INTERLEAVE) ||
1915 (cur->type == XML_RELAXNG_GROUP) ||
1916 (cur->type == XML_RELAXNG_ONEORMORE) ||
Daniel Veillardb08c9812003-01-28 23:09:49 +00001917 (cur->type == XML_RELAXNG_ZEROORMORE) ||
1918 (cur->type == XML_RELAXNG_OPTIONAL) ||
1919 (cur->type == XML_RELAXNG_REF) ||
1920 (cur->type == XML_RELAXNG_DEF)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001921 /*
1922 * Don't go within elements or attributes or string values.
1923 * Just gather the element top list
1924 */
1925 if (cur->content != NULL) {
1926 parent = cur;
1927 cur = cur->content;
1928 tmp = cur;
1929 while (tmp != NULL) {
1930 tmp->parent = parent;
1931 tmp = tmp->next;
1932 }
1933 continue;
1934 }
1935 }
Daniel Veillard154877e2003-01-30 12:17:05 +00001936 if (cur == def)
1937 return(ret);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001938 if (cur->next != NULL) {
1939 cur = cur->next;
1940 continue;
1941 }
1942 do {
1943 cur = cur->parent;
1944 if (cur == NULL) break;
1945 if (cur == def) return(ret);
1946 if (cur->next != NULL) {
1947 cur = cur->next;
1948 break;
1949 }
1950 } while (cur != NULL);
1951 }
1952 return(ret);
1953}
1954
1955/**
1956 * xmlRelaxNGComputeInterleaves:
1957 * @def: the interleave definition
1958 * @ctxt: a Relax-NG parser context
1959 * @node: the data node.
1960 *
1961 * A lot of work for preprocessing interleave definitions
1962 * is potentially needed to get a decent execution speed at runtime
1963 * - trying to get a total order on the element nodes generated
1964 * by the interleaves, order the list of interleave definitions
1965 * following that order.
1966 * - if <text/> is used to handle mixed content, it is better to
1967 * flag this in the define and simplify the runtime checking
1968 * algorithm
1969 */
1970static void
1971xmlRelaxNGComputeInterleaves(xmlRelaxNGDefinePtr def,
1972 xmlRelaxNGParserCtxtPtr ctxt,
1973 xmlChar *name ATTRIBUTE_UNUSED) {
1974 xmlRelaxNGDefinePtr cur;
1975
1976 xmlRelaxNGDefinePtr *list = NULL;
1977 xmlRelaxNGPartitionPtr partitions = NULL;
1978 xmlRelaxNGInterleaveGroupPtr *groups = NULL;
1979 xmlRelaxNGInterleaveGroupPtr group;
1980 int i,j,ret;
1981 int nbgroups = 0;
1982 int nbchild = 0;
1983
1984#ifdef DEBUG_INTERLEAVE
1985 xmlGenericError(xmlGenericErrorContext,
1986 "xmlRelaxNGComputeInterleaves(%s)\n",
1987 name);
1988#endif
1989 cur = def->content;
1990 while (cur != NULL) {
1991 nbchild++;
1992 cur = cur->next;
1993 }
1994
1995#ifdef DEBUG_INTERLEAVE
1996 xmlGenericError(xmlGenericErrorContext, " %d child\n", nbchild);
1997#endif
1998 groups = (xmlRelaxNGInterleaveGroupPtr *)
1999 xmlMalloc(nbchild * sizeof(xmlRelaxNGInterleaveGroupPtr));
2000 if (groups == NULL)
2001 goto error;
2002 cur = def->content;
2003 while (cur != NULL) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002004 groups[nbgroups] = (xmlRelaxNGInterleaveGroupPtr)
2005 xmlMalloc(sizeof(xmlRelaxNGInterleaveGroup));
2006 if (groups[nbgroups] == NULL)
2007 goto error;
2008 groups[nbgroups]->rule = cur;
2009 groups[nbgroups]->defs = xmlRelaxNGGetElements(ctxt, cur);
2010 nbgroups++;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002011 cur = cur->next;
2012 }
2013 list = NULL;
2014#ifdef DEBUG_INTERLEAVE
2015 xmlGenericError(xmlGenericErrorContext, " %d groups\n", nbgroups);
2016#endif
2017
2018 /*
2019 * Let's check that all rules makes a partitions according to 7.4
2020 */
2021 partitions = (xmlRelaxNGPartitionPtr)
2022 xmlMalloc(sizeof(xmlRelaxNGPartition));
2023 if (partitions == NULL)
2024 goto error;
2025 partitions->nbgroups = nbgroups;
2026 for (i = 0;i < nbgroups;i++) {
2027 group = groups[i];
2028 for (j = i+1;j < nbgroups;j++) {
2029 if (groups[j] == NULL)
2030 continue;
2031 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->defs,
2032 groups[j]->defs);
2033 if (ret == 0) {
2034 if (ctxt->error != NULL)
2035 ctxt->error(ctxt->userData,
2036 "Element or text conflicts in interleave\n");
2037 ctxt->nbErrors++;
2038 }
2039 }
2040 }
2041 partitions->groups = groups;
2042
2043 /*
2044 * Free Up the child list, and save the partition list back in the def
2045 */
2046 def->data = partitions;
2047 return;
2048
2049error:
2050 if (ctxt->error != NULL)
2051 ctxt->error(ctxt->userData,
2052 "Out of memory in interleave computation\n");
2053 ctxt->nbErrors++;
2054 if (list == NULL)
2055 xmlFree(list);
2056 if (groups != NULL) {
2057 for (i = 0;i < nbgroups;i++)
2058 if (groups[i] != NULL) {
2059 if (groups[i]->defs != NULL)
2060 xmlFree(groups[i]->defs);
2061 xmlFree(groups[i]);
2062 }
2063 xmlFree(groups);
2064 }
2065 xmlRelaxNGFreePartition(partitions);
2066}
2067
2068/**
2069 * xmlRelaxNGParseInterleave:
2070 * @ctxt: a Relax-NG parser context
2071 * @node: the data node.
2072 *
2073 * parse the content of a RelaxNG interleave node.
2074 *
2075 * Returns the definition pointer or NULL in case of error
2076 */
2077static xmlRelaxNGDefinePtr
2078xmlRelaxNGParseInterleave(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2079 xmlRelaxNGDefinePtr def = NULL;
2080 xmlRelaxNGDefinePtr last = NULL, cur;
2081 xmlNodePtr child;
2082
2083 def = xmlRelaxNGNewDefine(ctxt, node);
2084 if (def == NULL) {
2085 return(NULL);
2086 }
2087 def->type = XML_RELAXNG_INTERLEAVE;
2088
2089 if (ctxt->interleaves == NULL)
2090 ctxt->interleaves = xmlHashCreate(10);
2091 if (ctxt->interleaves == NULL) {
2092 if (ctxt->error != NULL)
2093 ctxt->error(ctxt->userData,
2094 "Failed to create interleaves hash table\n");
2095 ctxt->nbErrors++;
2096 } else {
2097 char name[32];
2098
2099 snprintf(name, 32, "interleave%d", ctxt->nbInterleaves++);
2100 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST name, def) < 0) {
2101 if (ctxt->error != NULL)
2102 ctxt->error(ctxt->userData,
2103 "Failed to add %s to hash table\n", name);
2104 ctxt->nbErrors++;
2105 }
2106 }
2107 child = node->children;
2108 while (child != NULL) {
2109 if (IS_RELAXNG(child, "element")) {
2110 cur = xmlRelaxNGParseElement(ctxt, child);
2111 } else {
2112 cur = xmlRelaxNGParsePattern(ctxt, child);
2113 }
2114 if (cur != NULL) {
2115 cur->parent = def;
2116 if (last == NULL) {
2117 def->content = last = cur;
2118 } else {
2119 last->next = cur;
2120 last = cur;
2121 }
2122 }
2123 child = child->next;
2124 }
2125
2126 return(def);
2127}
Daniel Veillard6eadf632003-01-23 18:29:16 +00002128
2129/**
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002130 * xmlRelaxNGParseInclude:
2131 * @ctxt: a Relax-NG parser context
2132 * @node: the include node
2133 *
2134 * Integrate the content of an include node in the current grammar
2135 *
2136 * Returns 0 in case of success or -1 in case of error
2137 */
2138static int
2139xmlRelaxNGParseInclude(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2140 xmlRelaxNGIncludePtr incl;
2141 xmlNodePtr root;
2142 int ret = 0, tmp;
2143
2144 incl = node->_private;
2145 if (incl == NULL) {
2146 if (ctxt->error != NULL)
2147 ctxt->error(ctxt->userData,
2148 "Include node has no data\n");
2149 ctxt->nbErrors++;
2150 return(-1);
2151 }
2152 root = xmlDocGetRootElement(incl->doc);
2153 if (root == NULL) {
2154 if (ctxt->error != NULL)
2155 ctxt->error(ctxt->userData,
2156 "Include document is empty\n");
2157 ctxt->nbErrors++;
2158 return(-1);
2159 }
2160 if (!xmlStrEqual(root->name, BAD_CAST "grammar")) {
2161 if (ctxt->error != NULL)
2162 ctxt->error(ctxt->userData,
2163 "Include document root is not a grammar\n");
2164 ctxt->nbErrors++;
2165 return(-1);
2166 }
2167
2168 /*
2169 * Merge the definition from both the include and the internal list
2170 */
2171 if (root->children != NULL) {
2172 tmp = xmlRelaxNGParseGrammarContent(ctxt, root->children);
2173 if (tmp != 0)
2174 ret = -1;
2175 }
2176 if (node->children != NULL) {
2177 tmp = xmlRelaxNGParseGrammarContent(ctxt, node->children);
2178 if (tmp != 0)
2179 ret = -1;
2180 }
2181 return(ret);
2182}
2183
2184/**
Daniel Veillard276be4a2003-01-24 01:03:34 +00002185 * xmlRelaxNGParseDefine:
2186 * @ctxt: a Relax-NG parser context
2187 * @node: the define node
2188 *
2189 * parse the content of a RelaxNG define element node.
2190 *
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002191 * Returns 0 in case of success or -1 in case of error
Daniel Veillard276be4a2003-01-24 01:03:34 +00002192 */
2193static int
2194xmlRelaxNGParseDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2195 xmlChar *name;
2196 int ret = 0, tmp;
2197 xmlRelaxNGDefinePtr def;
2198 const xmlChar *olddefine;
2199
2200 name = xmlGetProp(node, BAD_CAST "name");
2201 if (name == NULL) {
2202 if (ctxt->error != NULL)
2203 ctxt->error(ctxt->userData,
2204 "define has no name\n");
2205 ctxt->nbErrors++;
2206 } else {
2207 def = xmlRelaxNGNewDefine(ctxt, node);
2208 if (def == NULL) {
2209 xmlFree(name);
2210 return(-1);
2211 }
2212 def->type = XML_RELAXNG_DEF;
2213 def->name = name;
2214 if (node->children == NULL) {
2215 if (ctxt->error != NULL)
2216 ctxt->error(ctxt->userData,
2217 "define has no children\n");
2218 ctxt->nbErrors++;
2219 } else {
2220 olddefine = ctxt->define;
2221 ctxt->define = name;
Daniel Veillard154877e2003-01-30 12:17:05 +00002222 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard276be4a2003-01-24 01:03:34 +00002223 ctxt->define = olddefine;
2224 }
2225 if (ctxt->grammar->defs == NULL)
2226 ctxt->grammar->defs = xmlHashCreate(10);
2227 if (ctxt->grammar->defs == NULL) {
2228 if (ctxt->error != NULL)
2229 ctxt->error(ctxt->userData,
2230 "Could not create definition hash\n");
2231 ctxt->nbErrors++;
2232 ret = -1;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002233 } else {
2234 tmp = xmlHashAddEntry(ctxt->grammar->defs, name, def);
2235 if (tmp < 0) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002236 xmlRelaxNGDefinePtr prev;
2237
2238 prev = xmlHashLookup(ctxt->grammar->defs, name);
2239 if (prev == NULL) {
2240 if (ctxt->error != NULL)
2241 ctxt->error(ctxt->userData,
2242 "Internal error on define aggregation of %s\n",
2243 name);
2244 ctxt->nbErrors++;
2245 ret = -1;
Daniel Veillard154877e2003-01-30 12:17:05 +00002246 } else {
2247 while (prev->nextHash != NULL)
2248 prev = prev->nextHash;
2249 prev->nextHash = def;
2250 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002251 }
2252 }
2253 }
2254 return(ret);
2255}
2256
2257/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00002258 * xmlRelaxNGParsePattern:
2259 * @ctxt: a Relax-NG parser context
2260 * @node: the pattern node.
2261 *
2262 * parse the content of a RelaxNG pattern node.
2263 *
Daniel Veillard276be4a2003-01-24 01:03:34 +00002264 * Returns the definition pointer or NULL in case of error or if no
2265 * pattern is generated.
Daniel Veillard6eadf632003-01-23 18:29:16 +00002266 */
2267static xmlRelaxNGDefinePtr
2268xmlRelaxNGParsePattern(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2269 xmlRelaxNGDefinePtr def = NULL;
2270
2271 if (IS_RELAXNG(node, "element")) {
2272 def = xmlRelaxNGParseElement(ctxt, node);
2273 } else if (IS_RELAXNG(node, "attribute")) {
2274 def = xmlRelaxNGParseAttribute(ctxt, node);
2275 } else if (IS_RELAXNG(node, "empty")) {
2276 def = xmlRelaxNGNewDefine(ctxt, node);
2277 if (def == NULL)
2278 return(NULL);
2279 def->type = XML_RELAXNG_EMPTY;
2280 } else if (IS_RELAXNG(node, "text")) {
2281 def = xmlRelaxNGNewDefine(ctxt, node);
2282 if (def == NULL)
2283 return(NULL);
2284 def->type = XML_RELAXNG_TEXT;
2285 if (node->children != NULL) {
2286 if (ctxt->error != NULL)
2287 ctxt->error(ctxt->userData, "text: had a child node\n");
2288 ctxt->nbErrors++;
2289 }
2290 } else if (IS_RELAXNG(node, "zeroOrMore")) {
2291 def = xmlRelaxNGNewDefine(ctxt, node);
2292 if (def == NULL)
2293 return(NULL);
2294 def->type = XML_RELAXNG_ZEROORMORE;
Daniel Veillard154877e2003-01-30 12:17:05 +00002295 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002296 } else if (IS_RELAXNG(node, "oneOrMore")) {
2297 def = xmlRelaxNGNewDefine(ctxt, node);
2298 if (def == NULL)
2299 return(NULL);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002300 def->type = XML_RELAXNG_ONEORMORE;
Daniel Veillard154877e2003-01-30 12:17:05 +00002301 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002302 } else if (IS_RELAXNG(node, "optional")) {
2303 def = xmlRelaxNGNewDefine(ctxt, node);
2304 if (def == NULL)
2305 return(NULL);
2306 def->type = XML_RELAXNG_OPTIONAL;
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, "choice")) {
2309 def = xmlRelaxNGNewDefine(ctxt, node);
2310 if (def == NULL)
2311 return(NULL);
2312 def->type = XML_RELAXNG_CHOICE;
Daniel Veillard154877e2003-01-30 12:17:05 +00002313 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002314 } else if (IS_RELAXNG(node, "group")) {
2315 def = xmlRelaxNGNewDefine(ctxt, node);
2316 if (def == NULL)
2317 return(NULL);
2318 def->type = XML_RELAXNG_GROUP;
Daniel Veillard154877e2003-01-30 12:17:05 +00002319 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002320 } else if (IS_RELAXNG(node, "ref")) {
2321 def = xmlRelaxNGNewDefine(ctxt, node);
2322 if (def == NULL)
2323 return(NULL);
2324 def->type = XML_RELAXNG_REF;
2325 def->name = xmlGetProp(node, BAD_CAST "name");
2326 if (def->name == NULL) {
2327 if (ctxt->error != NULL)
2328 ctxt->error(ctxt->userData,
2329 "ref has no name\n");
2330 ctxt->nbErrors++;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002331 } else {
2332 if ((ctxt->define != NULL) &&
2333 (xmlStrEqual(ctxt->define, def->name))) {
2334 if (ctxt->error != NULL)
2335 ctxt->error(ctxt->userData,
2336 "Recursive reference to %s not in an element\n",
2337 def->name);
2338 ctxt->nbErrors++;
2339 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002340 }
2341 if (node->children != NULL) {
2342 if (ctxt->error != NULL)
2343 ctxt->error(ctxt->userData,
2344 "ref is not empty\n");
2345 ctxt->nbErrors++;
2346 }
2347 if (ctxt->grammar->refs == NULL)
2348 ctxt->grammar->refs = xmlHashCreate(10);
2349 if (ctxt->grammar->refs == NULL) {
2350 if (ctxt->error != NULL)
2351 ctxt->error(ctxt->userData,
2352 "Could not create references hash\n");
2353 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002354 def = NULL;
2355 } else {
2356 int tmp;
2357
2358 tmp = xmlHashAddEntry(ctxt->grammar->refs, def->name, def);
2359 if (tmp < 0) {
2360 xmlRelaxNGDefinePtr prev;
2361
2362 prev = (xmlRelaxNGDefinePtr)
2363 xmlHashLookup(ctxt->grammar->refs, def->name);
2364 if (prev == NULL) {
2365 if (ctxt->error != NULL)
2366 ctxt->error(ctxt->userData,
2367 "Internal error refs definitions '%s'\n",
2368 def->name);
2369 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002370 def = NULL;
2371 } else {
2372 def->nextHash = prev->nextHash;
2373 prev->nextHash = def;
2374 }
2375 }
2376 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002377 } else if (IS_RELAXNG(node, "data")) {
2378 def = xmlRelaxNGParseData(ctxt, node);
Daniel Veillard276be4a2003-01-24 01:03:34 +00002379 } else if (IS_RELAXNG(node, "define")) {
2380 xmlRelaxNGParseDefine(ctxt, node);
2381 def = NULL;
Daniel Veillardedc91922003-01-26 00:52:04 +00002382 } else if (IS_RELAXNG(node, "value")) {
2383 def = xmlRelaxNGParseValue(ctxt, node);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002384 } else if (IS_RELAXNG(node, "list")) {
2385 def = xmlRelaxNGNewDefine(ctxt, node);
2386 if (def == NULL)
2387 return(NULL);
2388 def->type = XML_RELAXNG_LIST;
Daniel Veillard154877e2003-01-30 12:17:05 +00002389 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002390 } else if (IS_RELAXNG(node, "interleave")) {
2391 def = xmlRelaxNGParseInterleave(ctxt, node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002392 } else if (IS_RELAXNG(node, "externalRef")) {
2393 xmlRelaxNGDocumentPtr docu;
2394 xmlNodePtr root;
2395
2396 docu = node->_private;
2397 if (docu != NULL) {
2398 def = xmlRelaxNGNewDefine(ctxt, node);
2399 if (def == NULL)
2400 return(NULL);
2401 def->type = XML_RELAXNG_EXTERNALREF;
2402
2403 if (docu->content == NULL) {
2404 /*
2405 * Then do the parsing for good
2406 */
2407 root = xmlDocGetRootElement(docu->doc);
2408 if (root == NULL) {
2409 if (ctxt->error != NULL)
2410 ctxt->error(ctxt->userData,
2411 "xmlRelaxNGParse: %s is empty\n",
2412 ctxt->URL);
2413 ctxt->nbErrors++;
2414 return (NULL);
2415 }
2416 docu->schema = xmlRelaxNGParseDocument(ctxt, root);
2417 if ((docu->schema != NULL) &&
2418 (docu->schema->topgrammar != NULL)) {
2419 docu->content = docu->schema->topgrammar->start;
2420 }
2421 }
2422 def->content = docu->content;
2423 } else {
2424 def = NULL;
2425 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002426 } else if (IS_RELAXNG(node, "notAllowed")) {
2427 def = xmlRelaxNGNewDefine(ctxt, node);
2428 if (def == NULL)
2429 return(NULL);
2430 def->type = XML_RELAXNG_NOT_ALLOWED;
2431 if (node->children != NULL) {
2432 if (ctxt->error != NULL)
2433 ctxt->error(ctxt->userData,
2434 "xmlRelaxNGParse: notAllowed element is not empty\n");
2435 ctxt->nbErrors++;
2436 }
Daniel Veillard419a7682003-02-03 23:22:49 +00002437 } else if (IS_RELAXNG(node, "grammar")) {
2438 xmlRelaxNGGrammarPtr grammar, old;
2439 xmlRelaxNGGrammarPtr oldparent;
2440
2441 oldparent = ctxt->parentgrammar;
2442 old = ctxt->grammar;
2443 ctxt->parentgrammar = old;
2444 grammar = xmlRelaxNGParseGrammar(ctxt, node->children);
2445 if (old != NULL) {
2446 ctxt->grammar = old;
2447 ctxt->parentgrammar = oldparent;
2448 if (grammar != NULL) {
2449 grammar->next = old->next;
2450 old->next = grammar;
2451 }
2452 }
2453 if (grammar != NULL)
2454 def = grammar->start;
2455 else
2456 def = NULL;
2457 } else if (IS_RELAXNG(node, "parentRef")) {
2458 if (ctxt->parentgrammar == NULL) {
2459 if (ctxt->error != NULL)
2460 ctxt->error(ctxt->userData,
2461 "Use of parentRef without a parent grammar\n");
2462 ctxt->nbErrors++;
2463 return(NULL);
2464 }
2465 def = xmlRelaxNGNewDefine(ctxt, node);
2466 if (def == NULL)
2467 return(NULL);
2468 def->type = XML_RELAXNG_PARENTREF;
2469 def->name = xmlGetProp(node, BAD_CAST "name");
2470 if (def->name == NULL) {
2471 if (ctxt->error != NULL)
2472 ctxt->error(ctxt->userData,
2473 "parentRef has no name\n");
2474 ctxt->nbErrors++;
2475 }
2476 if (node->children != NULL) {
2477 if (ctxt->error != NULL)
2478 ctxt->error(ctxt->userData,
2479 "parentRef is not empty\n");
2480 ctxt->nbErrors++;
2481 }
2482 if (ctxt->parentgrammar->refs == NULL)
2483 ctxt->parentgrammar->refs = xmlHashCreate(10);
2484 if (ctxt->parentgrammar->refs == NULL) {
2485 if (ctxt->error != NULL)
2486 ctxt->error(ctxt->userData,
2487 "Could not create references hash\n");
2488 ctxt->nbErrors++;
2489 def = NULL;
2490 } else {
2491 int tmp;
2492
2493 tmp = xmlHashAddEntry(ctxt->parentgrammar->refs, def->name, def);
2494 if (tmp < 0) {
2495 xmlRelaxNGDefinePtr prev;
2496
2497 prev = (xmlRelaxNGDefinePtr)
2498 xmlHashLookup(ctxt->parentgrammar->refs, def->name);
2499 if (prev == NULL) {
2500 if (ctxt->error != NULL)
2501 ctxt->error(ctxt->userData,
2502 "Internal error parentRef definitions '%s'\n",
2503 def->name);
2504 ctxt->nbErrors++;
2505 def = NULL;
2506 } else {
2507 def->nextHash = prev->nextHash;
2508 prev->nextHash = def;
2509 }
2510 }
2511 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002512 } else {
2513 TODO
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002514 def = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002515 }
2516 return(def);
2517}
2518
2519/**
2520 * xmlRelaxNGParseAttribute:
2521 * @ctxt: a Relax-NG parser context
2522 * @node: the element node
2523 *
2524 * parse the content of a RelaxNG attribute node.
2525 *
2526 * Returns the definition pointer or NULL in case of error.
2527 */
2528static xmlRelaxNGDefinePtr
2529xmlRelaxNGParseAttribute(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2530 xmlRelaxNGDefinePtr ret, cur, last;
2531 xmlNodePtr child;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002532 int old_flags;
2533
2534 ret = xmlRelaxNGNewDefine(ctxt, node);
2535 if (ret == NULL)
2536 return(NULL);
2537 ret->type = XML_RELAXNG_ATTRIBUTE;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002538 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002539 child = node->children;
2540 if (child == NULL) {
2541 if (ctxt->error != NULL)
2542 ctxt->error(ctxt->userData,
2543 "xmlRelaxNGParseattribute: attribute has no children\n");
2544 ctxt->nbErrors++;
2545 return(ret);
2546 }
2547 old_flags = ctxt->flags;
2548 ctxt->flags |= XML_RELAXNG_IN_ATTRIBUTE;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002549 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
2550 if (cur != NULL)
2551 child = child->next;
2552
Daniel Veillard6eadf632003-01-23 18:29:16 +00002553 last = NULL;
2554 while (child != NULL) {
2555 cur = xmlRelaxNGParsePattern(ctxt, child);
2556 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002557 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002558 switch (cur->type) {
2559 case XML_RELAXNG_EMPTY:
2560 case XML_RELAXNG_NOT_ALLOWED:
2561 case XML_RELAXNG_TEXT:
2562 case XML_RELAXNG_ELEMENT:
2563 case XML_RELAXNG_DATATYPE:
2564 case XML_RELAXNG_VALUE:
2565 case XML_RELAXNG_LIST:
2566 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00002567 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002568 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00002569 case XML_RELAXNG_DEF:
2570 case XML_RELAXNG_ONEORMORE:
2571 case XML_RELAXNG_ZEROORMORE:
2572 case XML_RELAXNG_OPTIONAL:
2573 case XML_RELAXNG_CHOICE:
2574 case XML_RELAXNG_GROUP:
2575 case XML_RELAXNG_INTERLEAVE:
2576 if (last == NULL) {
2577 ret->content = last = cur;
2578 } else {
2579 if ((last->type == XML_RELAXNG_ELEMENT) &&
2580 (ret->content == last)) {
2581 ret->content = xmlRelaxNGNewDefine(ctxt, node);
2582 if (ret->content != NULL) {
2583 ret->content->type = XML_RELAXNG_GROUP;
2584 ret->content->content = last;
2585 } else {
2586 ret->content = last;
2587 }
2588 }
2589 last->next = cur;
2590 last = cur;
2591 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002592 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002593 break;
2594 case XML_RELAXNG_ATTRIBUTE:
2595 cur->next = ret->attrs;
2596 ret->attrs = cur;
2597 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002598 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00002599 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002600 TODO
2601 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002602 }
2603 }
2604 child = child->next;
2605 }
2606 ctxt->flags = old_flags;
2607 return(ret);
2608}
2609
2610/**
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002611 * xmlRelaxNGParseExceptNameClass:
2612 * @ctxt: a Relax-NG parser context
2613 * @node: the except node
Daniel Veillard144fae12003-02-03 13:17:57 +00002614 * @attr: 1 if within an attribute, 0 if within an element
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002615 *
2616 * parse the content of a RelaxNG nameClass node.
2617 *
2618 * Returns the definition pointer or NULL in case of error.
2619 */
2620static xmlRelaxNGDefinePtr
Daniel Veillard144fae12003-02-03 13:17:57 +00002621xmlRelaxNGParseExceptNameClass(xmlRelaxNGParserCtxtPtr ctxt,
2622 xmlNodePtr node, int attr) {
2623 xmlRelaxNGDefinePtr ret, cur, last = NULL;
2624 xmlNodePtr child;
2625
2626 if (!IS_RELAXNG(node, "except"))
2627 return(NULL);
2628 if (node->children == NULL) {
2629 if (ctxt->error != NULL)
2630 ctxt->error(ctxt->userData,
2631 "except has no content\n");
2632 ctxt->nbErrors++;
2633 return(NULL);
2634 }
2635
2636 ret = xmlRelaxNGNewDefine(ctxt, node);
2637 if (ret == NULL)
2638 return(NULL);
2639 ret->type = XML_RELAXNG_EXCEPT;
2640 child = node->children;
2641 while (child != NULL) {
2642 cur = xmlRelaxNGNewDefine(ctxt, child);
2643 if (cur == NULL)
2644 break;
2645 if (attr)
2646 cur->type = XML_RELAXNG_ATTRIBUTE;
2647 else
2648 cur->type = XML_RELAXNG_ELEMENT;
2649
Daniel Veillard419a7682003-02-03 23:22:49 +00002650 if (xmlRelaxNGParseNameClass(ctxt, child, cur) != NULL) {
Daniel Veillard144fae12003-02-03 13:17:57 +00002651 if (last == NULL) {
2652 ret->content = cur;
2653 } else {
2654 last->next = cur;
2655 }
2656 last = cur;
2657 }
2658 child = child->next;
2659 }
2660
2661 return(ret);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002662}
2663
2664/**
2665 * xmlRelaxNGParseNameClass:
2666 * @ctxt: a Relax-NG parser context
2667 * @node: the nameClass node
2668 * @def: the current definition
2669 *
2670 * parse the content of a RelaxNG nameClass node.
2671 *
2672 * Returns the definition pointer or NULL in case of error.
2673 */
2674static xmlRelaxNGDefinePtr
2675xmlRelaxNGParseNameClass(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
2676 xmlRelaxNGDefinePtr def) {
2677 xmlRelaxNGDefinePtr ret = def;
2678 xmlChar *val;
2679
2680 if (IS_RELAXNG(node, "name")) {
2681 val = xmlNodeGetContent(node);
2682 ret->name = val;
2683 val = xmlGetProp(node, BAD_CAST "ns");
2684 ret->ns = val;
2685 } else if (IS_RELAXNG(node, "anyName")) {
2686 ret->name = NULL;
2687 ret->ns = NULL;
2688 if (node->children != NULL) {
2689 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00002690 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
2691 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002692 }
2693 } else if (IS_RELAXNG(node, "nsName")) {
2694 ret->name = NULL;
2695 ret->ns = xmlGetProp(node, BAD_CAST "ns");
2696 if (ret->ns == NULL) {
2697 if (ctxt->error != NULL)
2698 ctxt->error(ctxt->userData,
2699 "nsName has no ns attribute\n");
2700 ctxt->nbErrors++;
2701 }
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, "choice")) {
2708 TODO
2709 } else {
2710 if (ctxt->error != NULL)
2711 ctxt->error(ctxt->userData,
2712 "expecting name, anyName, nsName or choice : got %s\n",
2713 node->name);
2714 ctxt->nbErrors++;
2715 return(NULL);
2716 }
2717 return(ret);
2718}
2719
2720/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00002721 * xmlRelaxNGParseElement:
2722 * @ctxt: a Relax-NG parser context
2723 * @node: the element node
2724 *
2725 * parse the content of a RelaxNG element node.
2726 *
2727 * Returns the definition pointer or NULL in case of error.
2728 */
2729static xmlRelaxNGDefinePtr
2730xmlRelaxNGParseElement(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2731 xmlRelaxNGDefinePtr ret, cur, last;
2732 xmlNodePtr child;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002733 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002734
2735 ret = xmlRelaxNGNewDefine(ctxt, node);
2736 if (ret == NULL)
2737 return(NULL);
2738 ret->type = XML_RELAXNG_ELEMENT;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002739 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002740 child = node->children;
2741 if (child == NULL) {
2742 if (ctxt->error != NULL)
2743 ctxt->error(ctxt->userData,
2744 "xmlRelaxNGParseElement: element has no children\n");
2745 ctxt->nbErrors++;
2746 return(ret);
2747 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002748 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
2749 if (cur != NULL)
2750 child = child->next;
2751
Daniel Veillard6eadf632003-01-23 18:29:16 +00002752 if (child == NULL) {
2753 if (ctxt->error != NULL)
2754 ctxt->error(ctxt->userData,
2755 "xmlRelaxNGParseElement: element has no content\n");
2756 ctxt->nbErrors++;
2757 return(ret);
2758 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002759 olddefine = ctxt->define;
2760 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002761 last = NULL;
2762 while (child != NULL) {
2763 cur = xmlRelaxNGParsePattern(ctxt, child);
2764 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002765 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002766 switch (cur->type) {
2767 case XML_RELAXNG_EMPTY:
2768 case XML_RELAXNG_NOT_ALLOWED:
2769 case XML_RELAXNG_TEXT:
2770 case XML_RELAXNG_ELEMENT:
2771 case XML_RELAXNG_DATATYPE:
2772 case XML_RELAXNG_VALUE:
2773 case XML_RELAXNG_LIST:
2774 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00002775 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002776 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00002777 case XML_RELAXNG_DEF:
2778 case XML_RELAXNG_ZEROORMORE:
2779 case XML_RELAXNG_ONEORMORE:
2780 case XML_RELAXNG_OPTIONAL:
2781 case XML_RELAXNG_CHOICE:
2782 case XML_RELAXNG_GROUP:
2783 case XML_RELAXNG_INTERLEAVE:
2784 if (last == NULL) {
2785 ret->content = last = cur;
2786 } else {
2787 if ((last->type == XML_RELAXNG_ELEMENT) &&
2788 (ret->content == last)) {
2789 ret->content = xmlRelaxNGNewDefine(ctxt, node);
2790 if (ret->content != NULL) {
2791 ret->content->type = XML_RELAXNG_GROUP;
2792 ret->content->content = last;
2793 } else {
2794 ret->content = last;
2795 }
2796 }
2797 last->next = cur;
2798 last = cur;
2799 }
2800 break;
2801 case XML_RELAXNG_ATTRIBUTE:
2802 cur->next = ret->attrs;
2803 ret->attrs = cur;
2804 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002805 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00002806 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002807 TODO
2808 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002809 }
2810 }
2811 child = child->next;
2812 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002813 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002814 return(ret);
2815}
2816
2817/**
2818 * xmlRelaxNGParsePatterns:
2819 * @ctxt: a Relax-NG parser context
2820 * @nodes: list of nodes
Daniel Veillard154877e2003-01-30 12:17:05 +00002821 * @group: use an implicit <group> for elements
Daniel Veillard6eadf632003-01-23 18:29:16 +00002822 *
2823 * parse the content of a RelaxNG start node.
2824 *
2825 * Returns the definition pointer or NULL in case of error.
2826 */
2827static xmlRelaxNGDefinePtr
Daniel Veillard154877e2003-01-30 12:17:05 +00002828xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes,
2829 int group) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002830 xmlRelaxNGDefinePtr def = NULL, last = NULL, cur, parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002831
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002832 parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002833 while (nodes != NULL) {
2834 if (IS_RELAXNG(nodes, "element")) {
2835 cur = xmlRelaxNGParseElement(ctxt, nodes);
2836 if (def == NULL) {
2837 def = last = cur;
2838 } else {
Daniel Veillard154877e2003-01-30 12:17:05 +00002839 if ((group == 1) && (def->type == XML_RELAXNG_ELEMENT) &&
2840 (def == last)) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00002841 def = xmlRelaxNGNewDefine(ctxt, nodes);
2842 def->type = XML_RELAXNG_GROUP;
2843 def->content = last;
2844 }
2845 last->next = cur;
2846 last = cur;
2847 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002848 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002849 } else {
2850 cur = xmlRelaxNGParsePattern(ctxt, nodes);
Daniel Veillard419a7682003-02-03 23:22:49 +00002851 if (cur != NULL) {
2852 if (def == NULL) {
2853 def = last = cur;
2854 } else {
2855 last->next = cur;
2856 last = cur;
2857 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002858 }
2859 }
2860 nodes = nodes->next;
2861 }
2862 return(def);
2863}
2864
2865/**
2866 * xmlRelaxNGParseStart:
2867 * @ctxt: a Relax-NG parser context
2868 * @nodes: start children nodes
2869 *
2870 * parse the content of a RelaxNG start node.
2871 *
2872 * Returns 0 in case of success, -1 in case of error
2873 */
2874static int
2875xmlRelaxNGParseStart(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
2876 int ret = 0;
2877 xmlRelaxNGDefinePtr def = NULL;
2878
2879 while (nodes != NULL) {
2880 if (IS_RELAXNG(nodes, "empty")) {
2881 TODO
2882 xmlElemDump(stdout, nodes->doc, nodes);
2883 } else if (IS_RELAXNG(nodes, "notAllowed")) {
2884 TODO
2885 xmlElemDump(stdout, nodes->doc, nodes);
2886 } else {
Daniel Veillard154877e2003-01-30 12:17:05 +00002887 def = xmlRelaxNGParsePatterns(ctxt, nodes, 1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002888 ctxt->grammar->start = def;
2889 }
2890 nodes = nodes->next;
2891 }
2892 return(ret);
2893}
2894
2895/**
2896 * xmlRelaxNGParseGrammarContent:
2897 * @ctxt: a Relax-NG parser context
2898 * @nodes: grammar children nodes
2899 *
2900 * parse the content of a RelaxNG grammar node.
2901 *
2902 * Returns 0 in case of success, -1 in case of error
2903 */
2904static int
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002905xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes)
Daniel Veillard6eadf632003-01-23 18:29:16 +00002906{
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002907 int ret = 0, tmp;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002908
2909 if (nodes == NULL) {
2910 if (ctxt->error != NULL)
2911 ctxt->error(ctxt->userData,
2912 "grammar has no children\n");
2913 ctxt->nbErrors++;
2914 return(-1);
2915 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002916 while (nodes != NULL) {
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002917 if (IS_RELAXNG(nodes, "start")) {
2918 if (nodes->children == NULL) {
2919 if (ctxt->error != NULL)
2920 ctxt->error(ctxt->userData,
2921 "grammar has no children\n");
2922 ctxt->nbErrors++;
2923 } else {
2924 tmp = xmlRelaxNGParseStart(ctxt, nodes->children);
2925 if (tmp != 0)
2926 ret = -1;
2927 }
2928 } else if (IS_RELAXNG(nodes, "define")) {
2929 tmp = xmlRelaxNGParseDefine(ctxt, nodes);
2930 if (tmp != 0)
2931 ret = -1;
2932 } else if (IS_RELAXNG(nodes, "include")) {
2933 tmp = xmlRelaxNGParseInclude(ctxt, nodes);
2934 if (tmp != 0)
2935 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002936 } else {
2937 if (ctxt->error != NULL)
2938 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002939 "grammar has unexpected child %s\n", nodes->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002940 ctxt->nbErrors++;
2941 ret = -1;
2942 }
2943 nodes = nodes->next;
2944 }
2945 return (ret);
2946}
2947
2948/**
2949 * xmlRelaxNGCheckReference:
2950 * @ref: the ref
2951 * @ctxt: a Relax-NG parser context
2952 * @name: the name associated to the defines
2953 *
2954 * Applies the 4.17. combine attribute rule for all the define
2955 * element of a given grammar using the same name.
2956 */
2957static void
2958xmlRelaxNGCheckReference(xmlRelaxNGDefinePtr ref,
2959 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
2960 xmlRelaxNGGrammarPtr grammar;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002961 xmlRelaxNGDefinePtr def, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002962
2963 grammar = ctxt->grammar;
2964 if (grammar == NULL) {
2965 if (ctxt->error != NULL)
2966 ctxt->error(ctxt->userData,
2967 "Internal error: no grammar in CheckReference %s\n",
2968 name);
2969 ctxt->nbErrors++;
2970 return;
2971 }
2972 if (ref->content != NULL) {
2973 if (ctxt->error != NULL)
2974 ctxt->error(ctxt->userData,
2975 "Internal error: reference has content in CheckReference %s\n",
2976 name);
2977 ctxt->nbErrors++;
2978 return;
2979 }
2980 if (grammar->defs != NULL) {
2981 def = xmlHashLookup(grammar->defs, name);
2982 if (def != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00002983 cur = ref;
2984 while (cur != NULL) {
2985 cur->content = def;
2986 cur = cur->nextHash;
2987 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002988 } else {
2989 TODO
2990 }
2991 }
2992 /*
2993 * TODO: make a closure and verify there is no loop !
2994 */
2995}
2996
2997/**
2998 * xmlRelaxNGCheckCombine:
2999 * @define: the define(s) list
3000 * @ctxt: a Relax-NG parser context
3001 * @name: the name associated to the defines
3002 *
3003 * Applies the 4.17. combine attribute rule for all the define
3004 * element of a given grammar using the same name.
3005 */
3006static void
3007xmlRelaxNGCheckCombine(xmlRelaxNGDefinePtr define,
3008 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
3009 xmlChar *combine;
3010 int choiceOrInterleave = -1;
3011 int missing = 0;
3012 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
3013
3014 if (define->nextHash == NULL)
3015 return;
3016 cur = define;
3017 while (cur != NULL) {
3018 combine = xmlGetProp(cur->node, BAD_CAST "combine");
3019 if (combine != NULL) {
3020 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3021 if (choiceOrInterleave == -1)
3022 choiceOrInterleave = 1;
3023 else if (choiceOrInterleave == 0) {
3024 if (ctxt->error != NULL)
3025 ctxt->error(ctxt->userData,
3026 "Defines for %s use both 'choice' and 'interleave'\n",
3027 name);
3028 ctxt->nbErrors++;
3029 }
Daniel Veillard154877e2003-01-30 12:17:05 +00003030 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003031 if (choiceOrInterleave == -1)
3032 choiceOrInterleave = 0;
3033 else if (choiceOrInterleave == 1) {
3034 if (ctxt->error != NULL)
3035 ctxt->error(ctxt->userData,
3036 "Defines for %s use both 'choice' and 'interleave'\n",
3037 name);
3038 ctxt->nbErrors++;
3039 }
3040 } else {
3041 if (ctxt->error != NULL)
3042 ctxt->error(ctxt->userData,
3043 "Defines for %s use unknown combine value '%s''\n",
3044 name, combine);
3045 ctxt->nbErrors++;
3046 }
3047 xmlFree(combine);
3048 } else {
3049 if (missing == 0)
3050 missing = 1;
3051 else {
3052 if (ctxt->error != NULL)
3053 ctxt->error(ctxt->userData,
3054 "Some defines for %s lacks the combine attribute\n",
3055 name);
3056 ctxt->nbErrors++;
3057 }
3058 }
3059
3060 cur = cur->nextHash;
3061 }
3062#ifdef DEBUG
3063 xmlGenericError(xmlGenericErrorContext,
3064 "xmlRelaxNGCheckCombine(): merging %s defines: %d\n",
3065 name, choiceOrInterleave);
3066#endif
3067 if (choiceOrInterleave == -1)
3068 choiceOrInterleave = 0;
3069 cur = xmlRelaxNGNewDefine(ctxt, define->node);
3070 if (cur == NULL)
3071 return;
3072 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003073 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard154877e2003-01-30 12:17:05 +00003074 else
3075 cur->type = XML_RELAXNG_CHOICE;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003076 tmp = define;
3077 last = NULL;
3078 while (tmp != NULL) {
3079 if (tmp->content != NULL) {
3080 if (tmp->content->next != NULL) {
3081 /*
3082 * we need first to create a wrapper.
3083 */
3084 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
3085 if (tmp2 == NULL)
3086 break;
3087 tmp2->type = XML_RELAXNG_GROUP;
3088 tmp2->content = tmp->content;
3089 } else {
3090 tmp2 = tmp->content;
3091 }
3092 if (last == NULL) {
3093 cur->content = tmp2;
3094 } else {
3095 last->next = tmp2;
3096 }
3097 last = tmp2;
3098 tmp->content = NULL;
3099 }
3100 tmp = tmp->nextHash;
3101 }
3102 define->content = cur;
Daniel Veillard154877e2003-01-30 12:17:05 +00003103 if (choiceOrInterleave == 0) {
3104 if (ctxt->interleaves == NULL)
3105 ctxt->interleaves = xmlHashCreate(10);
3106 if (ctxt->interleaves == NULL) {
3107 if (ctxt->error != NULL)
3108 ctxt->error(ctxt->userData,
3109 "Failed to create interleaves hash table\n");
3110 ctxt->nbErrors++;
3111 } else {
3112 char tmpname[32];
3113
3114 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
3115 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
3116 if (ctxt->error != NULL)
3117 ctxt->error(ctxt->userData,
3118 "Failed to add %s to hash table\n", tmpname);
3119 ctxt->nbErrors++;
3120 }
3121 }
3122 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003123}
3124
3125/**
3126 * xmlRelaxNGCombineStart:
3127 * @ctxt: a Relax-NG parser context
3128 * @grammar: the grammar
3129 *
3130 * Applies the 4.17. combine rule for all the start
3131 * element of a given grammar.
3132 */
3133static void
3134xmlRelaxNGCombineStart(xmlRelaxNGParserCtxtPtr ctxt,
3135 xmlRelaxNGGrammarPtr grammar) {
3136 xmlRelaxNGDefinePtr starts;
3137 xmlChar *combine;
3138 int choiceOrInterleave = -1;
3139 int missing = 0;
3140 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
3141
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003142 starts = grammar->startList;
3143 if ((starts == NULL) || (starts->nextHash == NULL))
Daniel Veillard6eadf632003-01-23 18:29:16 +00003144 return;
3145 cur = starts;
3146 while (cur != NULL) {
3147 combine = xmlGetProp(cur->node, BAD_CAST "combine");
3148 if (combine != NULL) {
3149 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3150 if (choiceOrInterleave == -1)
3151 choiceOrInterleave = 1;
3152 else if (choiceOrInterleave == 0) {
3153 if (ctxt->error != NULL)
3154 ctxt->error(ctxt->userData,
3155 "<start> use both 'choice' and 'interleave'\n");
3156 ctxt->nbErrors++;
3157 }
3158 } else if (xmlStrEqual(combine, BAD_CAST "choice")) {
3159 if (choiceOrInterleave == -1)
3160 choiceOrInterleave = 0;
3161 else if (choiceOrInterleave == 1) {
3162 if (ctxt->error != NULL)
3163 ctxt->error(ctxt->userData,
3164 "<start> use both 'choice' and 'interleave'\n");
3165 ctxt->nbErrors++;
3166 }
3167 } else {
3168 if (ctxt->error != NULL)
3169 ctxt->error(ctxt->userData,
3170 "<start> uses unknown combine value '%s''\n", combine);
3171 ctxt->nbErrors++;
3172 }
3173 xmlFree(combine);
3174 } else {
3175 if (missing == 0)
3176 missing = 1;
3177 else {
3178 if (ctxt->error != NULL)
3179 ctxt->error(ctxt->userData,
3180 "Some <start> elements lacks the combine attribute\n");
3181 ctxt->nbErrors++;
3182 }
3183 }
3184
3185 cur = cur->nextHash;
3186 }
3187#ifdef DEBUG
3188 xmlGenericError(xmlGenericErrorContext,
3189 "xmlRelaxNGCombineStart(): merging <start>: %d\n",
3190 choiceOrInterleave);
3191#endif
3192 if (choiceOrInterleave == -1)
3193 choiceOrInterleave = 0;
3194 cur = xmlRelaxNGNewDefine(ctxt, starts->node);
3195 if (cur == NULL)
3196 return;
3197 if (choiceOrInterleave == 0)
3198 cur->type = XML_RELAXNG_CHOICE;
3199 else
3200 cur->type = XML_RELAXNG_INTERLEAVE;
3201 tmp = starts;
3202 last = NULL;
3203 while (tmp != NULL) {
3204 if (tmp->content != NULL) {
3205 if (tmp->content->next != NULL) {
3206 /*
3207 * we need first to create a wrapper.
3208 */
3209 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
3210 if (tmp2 == NULL)
3211 break;
3212 tmp2->type = XML_RELAXNG_GROUP;
3213 tmp2->content = tmp->content;
3214 } else {
3215 tmp2 = tmp->content;
3216 }
3217 if (last == NULL) {
3218 cur->content = tmp2;
3219 } else {
3220 last->next = tmp2;
3221 }
3222 last = tmp2;
3223 tmp->content = NULL;
3224 }
3225 tmp = tmp->nextHash;
3226 }
3227 starts->content = cur;
3228}
3229
3230/**
3231 * xmlRelaxNGParseGrammar:
3232 * @ctxt: a Relax-NG parser context
3233 * @nodes: grammar children nodes
3234 *
3235 * parse a Relax-NG <grammar> node
3236 *
3237 * Returns the internal xmlRelaxNGGrammarPtr built or
3238 * NULL in case of error
3239 */
3240static xmlRelaxNGGrammarPtr
3241xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
3242 xmlRelaxNGGrammarPtr ret, tmp, old;
3243
Daniel Veillard6eadf632003-01-23 18:29:16 +00003244 ret = xmlRelaxNGNewGrammar(ctxt);
3245 if (ret == NULL)
3246 return(NULL);
3247
3248 /*
3249 * Link the new grammar in the tree
3250 */
3251 ret->parent = ctxt->grammar;
3252 if (ctxt->grammar != NULL) {
3253 tmp = ctxt->grammar->children;
3254 if (tmp == NULL) {
3255 ctxt->grammar->children = ret;
3256 } else {
3257 while (tmp->next != NULL)
3258 tmp = tmp->next;
3259 tmp->next = ret;
3260 }
3261 }
3262
3263 old = ctxt->grammar;
3264 ctxt->grammar = ret;
3265 xmlRelaxNGParseGrammarContent(ctxt, nodes);
3266 ctxt->grammar = ret;
3267
3268 /*
3269 * Apply 4.17 mergingd rules to defines and starts
3270 */
3271 xmlRelaxNGCombineStart(ctxt, ret);
3272 if (ret->defs != NULL) {
3273 xmlHashScan(ret->defs, (xmlHashScanner) xmlRelaxNGCheckCombine,
3274 ctxt);
3275 }
3276
3277 /*
3278 * link together defines and refs in this grammar
3279 */
3280 if (ret->refs != NULL) {
3281 xmlHashScan(ret->refs, (xmlHashScanner) xmlRelaxNGCheckReference,
3282 ctxt);
3283 }
3284 ctxt->grammar = old;
3285 return(ret);
3286}
3287
3288/**
3289 * xmlRelaxNGParseDocument:
3290 * @ctxt: a Relax-NG parser context
3291 * @node: the root node of the RelaxNG schema
3292 *
3293 * parse a Relax-NG definition resource and build an internal
3294 * xmlRelaxNG struture which can be used to validate instances.
3295 *
3296 * Returns the internal XML RelaxNG structure built or
3297 * NULL in case of error
3298 */
3299static xmlRelaxNGPtr
3300xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3301 xmlRelaxNGPtr schema = NULL;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003302 const xmlChar *olddefine;
Daniel Veillarde431a272003-01-29 23:02:33 +00003303 xmlRelaxNGGrammarPtr old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003304
3305 if ((ctxt == NULL) || (node == NULL))
3306 return (NULL);
3307
3308 schema = xmlRelaxNGNewRelaxNG(ctxt);
3309 if (schema == NULL)
3310 return(NULL);
3311
Daniel Veillard276be4a2003-01-24 01:03:34 +00003312 olddefine = ctxt->define;
3313 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003314 if (IS_RELAXNG(node, "grammar")) {
3315 schema->topgrammar = xmlRelaxNGParseGrammar(ctxt, node->children);
3316 } else {
3317 schema->topgrammar = xmlRelaxNGNewGrammar(ctxt);
3318 if (schema->topgrammar == NULL) {
3319 return(schema);
3320 }
3321 schema->topgrammar->parent = NULL;
Daniel Veillarde431a272003-01-29 23:02:33 +00003322 old = ctxt->grammar;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003323 ctxt->grammar = schema->topgrammar;
3324 xmlRelaxNGParseStart(ctxt, node);
Daniel Veillarde431a272003-01-29 23:02:33 +00003325 if (old != NULL)
3326 ctxt->grammar = old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003327 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003328 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003329
3330#ifdef DEBUG
3331 if (schema == NULL)
3332 xmlGenericError(xmlGenericErrorContext,
3333 "xmlRelaxNGParseDocument() failed\n");
3334#endif
3335
3336 return (schema);
3337}
3338
3339/************************************************************************
3340 * *
3341 * Reading RelaxNGs *
3342 * *
3343 ************************************************************************/
3344
3345/**
3346 * xmlRelaxNGNewParserCtxt:
3347 * @URL: the location of the schema
3348 *
3349 * Create an XML RelaxNGs parse context for that file/resource expected
3350 * to contain an XML RelaxNGs file.
3351 *
3352 * Returns the parser context or NULL in case of error
3353 */
3354xmlRelaxNGParserCtxtPtr
3355xmlRelaxNGNewParserCtxt(const char *URL) {
3356 xmlRelaxNGParserCtxtPtr ret;
3357
3358 if (URL == NULL)
3359 return(NULL);
3360
3361 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
3362 if (ret == NULL) {
3363 xmlGenericError(xmlGenericErrorContext,
3364 "Failed to allocate new schama parser context for %s\n", URL);
3365 return (NULL);
3366 }
3367 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
3368 ret->URL = xmlStrdup((const xmlChar *)URL);
3369 return (ret);
3370}
3371
3372/**
3373 * xmlRelaxNGNewMemParserCtxt:
3374 * @buffer: a pointer to a char array containing the schemas
3375 * @size: the size of the array
3376 *
3377 * Create an XML RelaxNGs parse context for that memory buffer expected
3378 * to contain an XML RelaxNGs file.
3379 *
3380 * Returns the parser context or NULL in case of error
3381 */
3382xmlRelaxNGParserCtxtPtr
3383xmlRelaxNGNewMemParserCtxt(const char *buffer, int size) {
3384 xmlRelaxNGParserCtxtPtr ret;
3385
3386 if ((buffer == NULL) || (size <= 0))
3387 return(NULL);
3388
3389 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
3390 if (ret == NULL) {
3391 xmlGenericError(xmlGenericErrorContext,
3392 "Failed to allocate new schama parser context\n");
3393 return (NULL);
3394 }
3395 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
3396 ret->buffer = buffer;
3397 ret->size = size;
3398 return (ret);
3399}
3400
3401/**
3402 * xmlRelaxNGFreeParserCtxt:
3403 * @ctxt: the schema parser context
3404 *
3405 * Free the resources associated to the schema parser context
3406 */
3407void
3408xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxtPtr ctxt) {
3409 if (ctxt == NULL)
3410 return;
3411 if (ctxt->URL != NULL)
3412 xmlFree(ctxt->URL);
3413 if (ctxt->doc != NULL)
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003414 xmlFreeDoc(ctxt->document);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003415 if (ctxt->interleaves != NULL)
3416 xmlHashFree(ctxt->interleaves, NULL);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003417 if (ctxt->documents != NULL)
3418 xmlHashFree(ctxt->documents, (xmlHashDeallocator)
3419 xmlRelaxNGFreeDocument);
3420 if (ctxt->docTab != NULL)
3421 xmlFree(ctxt->docTab);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003422 if (ctxt->incTab != NULL)
3423 xmlFree(ctxt->incTab);
Daniel Veillard419a7682003-02-03 23:22:49 +00003424 if (ctxt->defTab != NULL) {
3425 int i;
3426
3427 for (i = 0;i < ctxt->defNr;i++)
3428 xmlRelaxNGFreeDefine(ctxt->defTab[i]);
3429 xmlFree(ctxt->defTab);
3430 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003431 xmlFree(ctxt);
3432}
3433
Daniel Veillard6eadf632003-01-23 18:29:16 +00003434/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003435 * xmlRelaxNGCleanupDoc:
3436 * @ctxt: a Relax-NG parser context
3437 * @doc: an xmldocPtr document pointer
Daniel Veillard6eadf632003-01-23 18:29:16 +00003438 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003439 * Cleanup the document from unwanted nodes for parsing, resolve
3440 * Include and externalRef lookups.
Daniel Veillard6eadf632003-01-23 18:29:16 +00003441 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003442 * Returns the cleaned up document or NULL in case of error
Daniel Veillard6eadf632003-01-23 18:29:16 +00003443 */
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003444static xmlDocPtr
3445xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt, xmlDocPtr doc) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003446 xmlNodePtr root, cur, delete;
3447
Daniel Veillard6eadf632003-01-23 18:29:16 +00003448 /*
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003449 * Extract the root
Daniel Veillard6eadf632003-01-23 18:29:16 +00003450 */
3451 root = xmlDocGetRootElement(doc);
3452 if (root == NULL) {
3453 if (ctxt->error != NULL)
3454 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
3455 ctxt->URL);
3456 ctxt->nbErrors++;
3457 return (NULL);
3458 }
3459
3460 /*
3461 * Remove all the blank text nodes
3462 */
3463 delete = NULL;
3464 cur = root;
3465 while (cur != NULL) {
3466 if (delete != NULL) {
3467 xmlUnlinkNode(delete);
3468 xmlFreeNode(delete);
3469 delete = NULL;
3470 }
3471 if (cur->type == XML_ELEMENT_NODE) {
3472 /*
3473 * Simplification 4.1. Annotations
3474 */
3475 if ((cur->ns == NULL) ||
3476 (!xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
3477 delete = cur;
3478 goto skip_children;
3479 } else {
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003480 if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003481 xmlChar *href, *ns, *base, *URL;
3482 xmlRelaxNGDocumentPtr docu;
3483
3484 ns = xmlGetProp(cur, BAD_CAST "ns");
3485 href = xmlGetProp(cur, BAD_CAST "href");
3486 if (href == NULL) {
3487 if (ctxt->error != NULL)
3488 ctxt->error(ctxt->userData,
3489 "xmlRelaxNGParse: externalRef has no href attribute\n");
3490 ctxt->nbErrors++;
3491 delete = cur;
3492 goto skip_children;
3493 }
3494 base = xmlNodeGetBase(cur->doc, cur);
3495 URL = xmlBuildURI(href, base);
3496 if (URL == NULL) {
3497 if (ctxt->error != NULL)
3498 ctxt->error(ctxt->userData,
3499 "Failed to compute URL for externalRef %s\n", href);
3500 ctxt->nbErrors++;
3501 if (href != NULL)
3502 xmlFree(href);
3503 if (base != NULL)
3504 xmlFree(base);
3505 delete = cur;
3506 goto skip_children;
3507 }
3508 if (href != NULL)
3509 xmlFree(href);
3510 if (base != NULL)
3511 xmlFree(base);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003512 docu = xmlRelaxNGLoadExternalRef(ctxt, URL, ns);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003513 if (docu == NULL) {
3514 if (ctxt->error != NULL)
3515 ctxt->error(ctxt->userData,
3516 "Failed to load externalRef %s\n", URL);
3517 ctxt->nbErrors++;
3518 xmlFree(URL);
3519 delete = cur;
3520 goto skip_children;
3521 }
3522 xmlFree(URL);
3523 cur->_private = docu;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003524 } else if (xmlStrEqual(cur->name, BAD_CAST "include")) {
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003525 xmlChar *href, *base, *URL;
3526 xmlRelaxNGIncludePtr incl;
3527
3528 href = xmlGetProp(cur, BAD_CAST "href");
3529 if (href == NULL) {
3530 if (ctxt->error != NULL)
3531 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003532 "xmlRelaxNGParse: include has no href attribute\n");
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003533 ctxt->nbErrors++;
3534 delete = cur;
3535 goto skip_children;
3536 }
3537 base = xmlNodeGetBase(cur->doc, cur);
3538 URL = xmlBuildURI(href, base);
3539 if (URL == NULL) {
3540 if (ctxt->error != NULL)
3541 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003542 "Failed to compute URL for include %s\n", href);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003543 ctxt->nbErrors++;
3544 if (href != NULL)
3545 xmlFree(href);
3546 if (base != NULL)
3547 xmlFree(base);
3548 delete = cur;
3549 goto skip_children;
3550 }
3551 if (href != NULL)
3552 xmlFree(href);
3553 if (base != NULL)
3554 xmlFree(base);
3555 incl = xmlRelaxNGLoadInclude(ctxt, URL, cur);
3556 if (incl == NULL) {
3557 if (ctxt->error != NULL)
3558 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003559 "Failed to load include %s\n", URL);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003560 ctxt->nbErrors++;
3561 xmlFree(URL);
3562 delete = cur;
3563 goto skip_children;
3564 }
3565 xmlFree(URL);
3566 cur->_private = incl;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003567 } else if ((xmlStrEqual(cur->name, BAD_CAST "element")) ||
3568 (xmlStrEqual(cur->name, BAD_CAST "attribute"))) {
3569 xmlChar *name;
3570 xmlNodePtr text = NULL;
3571
3572 /*
3573 * Simplification 4.8. name attribute of element
3574 * and attribute elements
3575 */
3576 name = xmlGetProp(cur, BAD_CAST "name");
3577 if (name != NULL) {
3578 if (cur->children == NULL) {
3579 text = xmlNewChild(cur, cur->ns, BAD_CAST "name",
3580 name);
3581 } else {
3582 xmlNodePtr node;
3583 node = xmlNewNode(cur->ns, BAD_CAST "name");
3584 if (node != NULL) {
3585 xmlAddPrevSibling(cur->children, node);
3586 text = xmlNewText(name);
3587 xmlAddChild(node, text);
3588 text = node;
3589 }
3590 }
3591 xmlUnsetProp(cur, BAD_CAST "name");
3592 xmlFree(name);
3593 }
3594 if (xmlStrEqual(cur->name, BAD_CAST "attribute")) {
3595 if (text == NULL) {
3596 text = cur->children;
3597 while (text != NULL) {
3598 if ((text->type == XML_ELEMENT_NODE) &&
3599 (xmlStrEqual(text->name, BAD_CAST "name")))
3600 break;
3601 text = text->next;
3602 }
3603 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003604 if (text != NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003605 xmlSetProp(text, BAD_CAST "ns", BAD_CAST "");
3606 }
3607 }
3608 } else if ((xmlStrEqual(cur->name, BAD_CAST "name")) ||
3609 (xmlStrEqual(cur->name, BAD_CAST "nsName")) ||
3610 (xmlStrEqual(cur->name, BAD_CAST "value"))) {
3611 /*
3612 * Simplification 4.8. name attribute of element
3613 * and attribute elements
3614 */
3615 if (xmlHasProp(cur, BAD_CAST "ns") == NULL) {
3616 xmlNodePtr node;
3617 xmlChar *ns = NULL;
3618
3619 node = cur->parent;
3620 while ((node != NULL) &&
3621 (node->type == XML_ELEMENT_NODE)) {
3622 ns = xmlGetProp(node, BAD_CAST "ns");
3623 if (ns != NULL) {
3624 break;
3625 }
3626 node = node->parent;
3627 }
3628 if (ns == NULL) {
3629 xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
3630 } else {
3631 xmlSetProp(cur, BAD_CAST "ns", ns);
3632 xmlFree(ns);
3633 }
3634 }
3635 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
3636 xmlChar *name, *local, *prefix;
3637
3638 /*
3639 * Simplification: 4.10. QNames
3640 */
3641 name = xmlNodeGetContent(cur);
3642 if (name != NULL) {
3643 local = xmlSplitQName2(name, &prefix);
3644 if (local != NULL) {
3645 xmlNsPtr ns;
3646
3647 ns = xmlSearchNs(cur->doc, cur, prefix);
3648 if (ns == NULL) {
3649 if (ctxt->error != NULL)
3650 ctxt->error(ctxt->userData,
3651 "xmlRelaxNGParse: no namespace for prefix %s\n", prefix);
3652 ctxt->nbErrors++;
3653 } else {
3654 xmlSetProp(cur, BAD_CAST "ns", ns->href);
3655 xmlNodeSetContent(cur, local);
3656 }
3657 xmlFree(local);
3658 xmlFree(prefix);
3659 }
3660 xmlFree(name);
3661 }
3662 }
3663 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003664 /*
3665 * Thisd is not an else since "include" is transformed
3666 * into a div
3667 */
3668 if (xmlStrEqual(cur->name, BAD_CAST "div")) {
3669 /*
3670 * implements rule 4.11
3671 */
3672 xmlNodePtr child, ins, tmp;
3673
3674 child = cur->children;
3675 ins = child;
3676 while (child != NULL) {
3677 tmp = child->next;
3678 xmlUnlinkNode(child);
3679 ins = xmlAddNextSibling(ins, child);
3680 child = tmp;
3681 }
3682 delete = cur;
3683 goto skip_children;
3684 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003685 }
3686 }
3687 /*
3688 * Simplification 4.2 whitespaces
3689 */
3690 else if (cur->type == XML_TEXT_NODE) {
3691 if (IS_BLANK_NODE(cur)) {
3692 if (cur->parent->type == XML_ELEMENT_NODE) {
3693 if ((!xmlStrEqual(cur->parent->name, BAD_CAST "value")) &&
3694 (!xmlStrEqual(cur->parent->name, BAD_CAST "param")))
3695 delete = cur;
3696 } else {
3697 delete = cur;
3698 goto skip_children;
3699 }
3700 }
3701 } else if (cur->type != XML_CDATA_SECTION_NODE) {
3702 delete = cur;
3703 goto skip_children;
3704 }
3705
3706 /*
3707 * Skip to next node
3708 */
3709 if (cur->children != NULL) {
3710 if ((cur->children->type != XML_ENTITY_DECL) &&
3711 (cur->children->type != XML_ENTITY_REF_NODE) &&
3712 (cur->children->type != XML_ENTITY_NODE)) {
3713 cur = cur->children;
3714 continue;
3715 }
3716 }
3717skip_children:
3718 if (cur->next != NULL) {
3719 cur = cur->next;
3720 continue;
3721 }
3722
3723 do {
3724 cur = cur->parent;
3725 if (cur == NULL)
3726 break;
3727 if (cur == root) {
3728 cur = NULL;
3729 break;
3730 }
3731 if (cur->next != NULL) {
3732 cur = cur->next;
3733 break;
3734 }
3735 } while (cur != NULL);
3736 }
3737 if (delete != NULL) {
3738 xmlUnlinkNode(delete);
3739 xmlFreeNode(delete);
3740 delete = NULL;
3741 }
3742
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003743 return(doc);
3744}
3745
3746/**
3747 * xmlRelaxNGParse:
3748 * @ctxt: a Relax-NG parser context
3749 *
3750 * parse a schema definition resource and build an internal
3751 * XML Shema struture which can be used to validate instances.
3752 * *WARNING* this interface is highly subject to change
3753 *
3754 * Returns the internal XML RelaxNG structure built from the resource or
3755 * NULL in case of error
3756 */
3757xmlRelaxNGPtr
3758xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt)
3759{
3760 xmlRelaxNGPtr ret = NULL;
3761 xmlDocPtr doc;
3762 xmlNodePtr root;
3763
3764 xmlRelaxNGInitTypes();
3765
3766 if (ctxt == NULL)
3767 return (NULL);
3768
3769 /*
3770 * First step is to parse the input document into an DOM/Infoset
3771 */
3772 if (ctxt->URL != NULL) {
3773 doc = xmlParseFile((const char *) ctxt->URL);
3774 if (doc == NULL) {
3775 if (ctxt->error != NULL)
3776 ctxt->error(ctxt->userData,
3777 "xmlRelaxNGParse: could not load %s\n", ctxt->URL);
3778 ctxt->nbErrors++;
3779 return (NULL);
3780 }
3781 } else if (ctxt->buffer != NULL) {
3782 doc = xmlParseMemory(ctxt->buffer, ctxt->size);
3783 if (doc == NULL) {
3784 if (ctxt->error != NULL)
3785 ctxt->error(ctxt->userData,
3786 "xmlRelaxNGParse: could not parse schemas\n");
3787 ctxt->nbErrors++;
3788 return (NULL);
3789 }
3790 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
3791 ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
3792 } else {
3793 if (ctxt->error != NULL)
3794 ctxt->error(ctxt->userData,
3795 "xmlRelaxNGParse: nothing to parse\n");
3796 ctxt->nbErrors++;
3797 return (NULL);
3798 }
3799 ctxt->document = doc;
3800
3801 /*
3802 * Some preprocessing of the document content
3803 */
3804 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
3805 if (doc == NULL) {
3806 xmlFreeDoc(ctxt->document);
3807 ctxt->document = NULL;
3808 return(NULL);
3809 }
3810
Daniel Veillard6eadf632003-01-23 18:29:16 +00003811 /*
3812 * Then do the parsing for good
3813 */
3814 root = xmlDocGetRootElement(doc);
3815 if (root == NULL) {
3816 if (ctxt->error != NULL)
3817 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
3818 ctxt->URL);
3819 ctxt->nbErrors++;
3820 return (NULL);
3821 }
3822 ret = xmlRelaxNGParseDocument(ctxt, root);
3823 if (ret == NULL)
3824 return(NULL);
3825
3826 /*
3827 * Check the ref/defines links
3828 */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003829 /*
3830 * try to preprocess interleaves
3831 */
3832 if (ctxt->interleaves != NULL) {
3833 xmlHashScan(ctxt->interleaves,
3834 (xmlHashScanner)xmlRelaxNGComputeInterleaves, ctxt);
3835 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003836
3837 /*
3838 * if there was a parsing error return NULL
3839 */
3840 if (ctxt->nbErrors > 0) {
3841 xmlRelaxNGFree(ret);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003842 ctxt->document = NULL;
3843 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003844 return(NULL);
3845 }
3846
3847 /*
3848 * Transfer the pointer for cleanup at the schema level.
3849 */
3850 ret->doc = doc;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003851 ctxt->document = NULL;
3852 ret->documents = ctxt->documents;
3853 ctxt->documents = NULL;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003854 ret->includes = ctxt->includes;
3855 ctxt->includes = NULL;
Daniel Veillard419a7682003-02-03 23:22:49 +00003856 ret->defNr = ctxt->defNr;
3857 ret->defTab = ctxt->defTab;
3858 ctxt->defTab = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003859
3860 return (ret);
3861}
3862
3863/**
3864 * xmlRelaxNGSetParserErrors:
3865 * @ctxt: a Relax-NG validation context
3866 * @err: the error callback
3867 * @warn: the warning callback
3868 * @ctx: contextual data for the callbacks
3869 *
3870 * Set the callback functions used to handle errors for a validation context
3871 */
3872void
3873xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
3874 xmlRelaxNGValidityErrorFunc err,
3875 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
3876 if (ctxt == NULL)
3877 return;
3878 ctxt->error = err;
3879 ctxt->warning = warn;
3880 ctxt->userData = ctx;
3881}
3882/************************************************************************
3883 * *
3884 * Dump back a compiled form *
3885 * *
3886 ************************************************************************/
3887static void xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define);
3888
3889/**
3890 * xmlRelaxNGDumpDefines:
3891 * @output: the file output
3892 * @defines: a list of define structures
3893 *
3894 * Dump a RelaxNG structure back
3895 */
3896static void
3897xmlRelaxNGDumpDefines(FILE * output, xmlRelaxNGDefinePtr defines) {
3898 while (defines != NULL) {
3899 xmlRelaxNGDumpDefine(output, defines);
3900 defines = defines->next;
3901 }
3902}
3903
3904/**
3905 * xmlRelaxNGDumpDefine:
3906 * @output: the file output
3907 * @define: a define structure
3908 *
3909 * Dump a RelaxNG structure back
3910 */
3911static void
3912xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define) {
3913 if (define == NULL)
3914 return;
3915 switch(define->type) {
3916 case XML_RELAXNG_EMPTY:
3917 fprintf(output, "<empty/>\n");
3918 break;
3919 case XML_RELAXNG_NOT_ALLOWED:
3920 fprintf(output, "<notAllowed/>\n");
3921 break;
3922 case XML_RELAXNG_TEXT:
3923 fprintf(output, "<text/>\n");
3924 break;
3925 case XML_RELAXNG_ELEMENT:
3926 fprintf(output, "<element>\n");
3927 if (define->name != NULL) {
3928 fprintf(output, "<name");
3929 if (define->ns != NULL)
3930 fprintf(output, " ns=\"%s\"", define->ns);
3931 fprintf(output, ">%s</name>\n", define->name);
3932 }
3933 xmlRelaxNGDumpDefines(output, define->attrs);
3934 xmlRelaxNGDumpDefines(output, define->content);
3935 fprintf(output, "</element>\n");
3936 break;
3937 case XML_RELAXNG_LIST:
3938 fprintf(output, "<list>\n");
3939 xmlRelaxNGDumpDefines(output, define->content);
3940 fprintf(output, "</list>\n");
3941 break;
3942 case XML_RELAXNG_ONEORMORE:
3943 fprintf(output, "<oneOrMore>\n");
3944 xmlRelaxNGDumpDefines(output, define->content);
3945 fprintf(output, "</oneOrMore>\n");
3946 break;
3947 case XML_RELAXNG_ZEROORMORE:
3948 fprintf(output, "<zeroOrMore>\n");
3949 xmlRelaxNGDumpDefines(output, define->content);
3950 fprintf(output, "</zeroOrMore>\n");
3951 break;
3952 case XML_RELAXNG_CHOICE:
3953 fprintf(output, "<choice>\n");
3954 xmlRelaxNGDumpDefines(output, define->content);
3955 fprintf(output, "</choice>\n");
3956 break;
3957 case XML_RELAXNG_GROUP:
3958 fprintf(output, "<group>\n");
3959 xmlRelaxNGDumpDefines(output, define->content);
3960 fprintf(output, "</group>\n");
3961 break;
3962 case XML_RELAXNG_INTERLEAVE:
3963 fprintf(output, "<interleave>\n");
3964 xmlRelaxNGDumpDefines(output, define->content);
3965 fprintf(output, "</interleave>\n");
3966 break;
3967 case XML_RELAXNG_OPTIONAL:
3968 fprintf(output, "<optional>\n");
3969 xmlRelaxNGDumpDefines(output, define->content);
3970 fprintf(output, "</optional>\n");
3971 break;
3972 case XML_RELAXNG_ATTRIBUTE:
3973 fprintf(output, "<attribute>\n");
3974 xmlRelaxNGDumpDefines(output, define->content);
3975 fprintf(output, "</attribute>\n");
3976 break;
3977 case XML_RELAXNG_DEF:
3978 fprintf(output, "<define");
3979 if (define->name != NULL)
3980 fprintf(output, " name=\"%s\"", define->name);
3981 fprintf(output, ">\n");
3982 xmlRelaxNGDumpDefines(output, define->content);
3983 fprintf(output, "</define>\n");
3984 break;
3985 case XML_RELAXNG_REF:
3986 fprintf(output, "<ref");
3987 if (define->name != NULL)
3988 fprintf(output, " name=\"%s\"", define->name);
3989 fprintf(output, ">\n");
3990 xmlRelaxNGDumpDefines(output, define->content);
3991 fprintf(output, "</ref>\n");
3992 break;
Daniel Veillard419a7682003-02-03 23:22:49 +00003993 case XML_RELAXNG_PARENTREF:
3994 fprintf(output, "<parentRef");
3995 if (define->name != NULL)
3996 fprintf(output, " name=\"%s\"", define->name);
3997 fprintf(output, ">\n");
3998 xmlRelaxNGDumpDefines(output, define->content);
3999 fprintf(output, "</parentRef>\n");
4000 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004001 case XML_RELAXNG_EXTERNALREF:
Daniel Veillarde431a272003-01-29 23:02:33 +00004002 fprintf(output, "<externalRef");
4003 xmlRelaxNGDumpDefines(output, define->content);
4004 fprintf(output, "</externalRef>\n");
4005 break;
4006 case XML_RELAXNG_DATATYPE:
Daniel Veillard6eadf632003-01-23 18:29:16 +00004007 case XML_RELAXNG_VALUE:
4008 TODO
4009 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004010 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00004011 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004012 TODO
4013 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004014 }
4015}
4016
4017/**
4018 * xmlRelaxNGDumpGrammar:
4019 * @output: the file output
4020 * @grammar: a grammar structure
4021 * @top: is this a top grammar
4022 *
4023 * Dump a RelaxNG structure back
4024 */
4025static void
4026xmlRelaxNGDumpGrammar(FILE * output, xmlRelaxNGGrammarPtr grammar, int top)
4027{
4028 if (grammar == NULL)
4029 return;
4030
4031 fprintf(output, "<grammar");
4032 if (top)
4033 fprintf(output,
4034 " xmlns=\"http://relaxng.org/ns/structure/1.0\"");
4035 switch(grammar->combine) {
4036 case XML_RELAXNG_COMBINE_UNDEFINED:
4037 break;
4038 case XML_RELAXNG_COMBINE_CHOICE:
4039 fprintf(output, " combine=\"choice\"");
4040 break;
4041 case XML_RELAXNG_COMBINE_INTERLEAVE:
4042 fprintf(output, " combine=\"interleave\"");
4043 break;
4044 default:
4045 fprintf(output, " <!-- invalid combine value -->");
4046 }
4047 fprintf(output, ">\n");
4048 if (grammar->start == NULL) {
4049 fprintf(output, " <!-- grammar had no start -->");
4050 } else {
4051 fprintf(output, "<start>\n");
4052 xmlRelaxNGDumpDefine(output, grammar->start);
4053 fprintf(output, "</start>\n");
4054 }
4055 /* TODO ? Dump the defines ? */
4056 fprintf(output, "</grammar>\n");
4057}
4058
4059/**
4060 * xmlRelaxNGDump:
4061 * @output: the file output
4062 * @schema: a schema structure
4063 *
4064 * Dump a RelaxNG structure back
4065 */
4066void
4067xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema)
4068{
4069 if (schema == NULL) {
4070 fprintf(output, "RelaxNG empty or failed to compile\n");
4071 return;
4072 }
4073 fprintf(output, "RelaxNG: ");
4074 if (schema->doc == NULL) {
4075 fprintf(output, "no document\n");
4076 } else if (schema->doc->URL != NULL) {
4077 fprintf(output, "%s\n", schema->doc->URL);
4078 } else {
4079 fprintf(output, "\n");
4080 }
4081 if (schema->topgrammar == NULL) {
4082 fprintf(output, "RelaxNG has no top grammar\n");
4083 return;
4084 }
4085 xmlRelaxNGDumpGrammar(output, schema->topgrammar, 1);
4086}
4087
4088/************************************************************************
4089 * *
4090 * Validation implementation *
4091 * *
4092 ************************************************************************/
4093static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
4094 xmlRelaxNGDefinePtr define);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004095static int xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
4096 xmlRelaxNGDefinePtr define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004097
4098/**
4099 * xmlRelaxNGSkipIgnored:
4100 * @ctxt: a schema validation context
4101 * @node: the top node.
4102 *
4103 * Skip ignorable nodes in that context
4104 *
4105 * Returns the new sibling or NULL in case of error.
4106 */
4107static xmlNodePtr
4108xmlRelaxNGSkipIgnored(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
4109 xmlNodePtr node) {
4110 /*
4111 * TODO complete and handle entities
4112 */
4113 while ((node != NULL) &&
4114 ((node->type == XML_COMMENT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004115 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00004116 ((node->type == XML_TEXT_NODE) &&
4117 (IS_BLANK_NODE(node))))) {
4118 node = node->next;
4119 }
4120 return(node);
4121}
4122
4123/**
Daniel Veillardedc91922003-01-26 00:52:04 +00004124 * xmlRelaxNGNormalize:
4125 * @ctxt: a schema validation context
4126 * @str: the string to normalize
4127 *
4128 * Implements the normalizeWhiteSpace( s ) function from
4129 * section 6.2.9 of the spec
4130 *
4131 * Returns the new string or NULL in case of error.
4132 */
4133static xmlChar *
4134xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *str) {
4135 xmlChar *ret, *p;
4136 const xmlChar *tmp;
4137 int len;
4138
4139 if (str == NULL)
4140 return(NULL);
4141 tmp = str;
4142 while (*tmp != 0) tmp++;
4143 len = tmp - str;
4144
4145 ret = (xmlChar *) xmlMalloc((len + 1) * sizeof(xmlChar));
4146 if (ret == NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00004147 if (ctxt != NULL) {
4148 VALID_CTXT();
4149 VALID_ERROR("xmlRelaxNGNormalize: out of memory\n");
4150 } else {
4151 xmlGenericError(xmlGenericErrorContext,
4152 "xmlRelaxNGNormalize: out of memory\n");
4153 }
Daniel Veillardedc91922003-01-26 00:52:04 +00004154 return(NULL);
4155 }
4156 p = ret;
4157 while (IS_BLANK(*str)) str++;
4158 while (*str != 0) {
4159 if (IS_BLANK(*str)) {
4160 while (IS_BLANK(*str)) str++;
4161 if (*str == 0)
4162 break;
4163 *p++ = ' ';
4164 } else
4165 *p++ = *str++;
4166 }
4167 *p = 0;
4168 return(ret);
4169}
4170
4171/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004172 * xmlRelaxNGValidateDatatype:
4173 * @ctxt: a Relax-NG validation context
4174 * @value: the string value
4175 * @type: the datatype definition
4176 *
4177 * Validate the given value against the dataype
4178 *
4179 * Returns 0 if the validation succeeded or an error code.
4180 */
4181static int
4182xmlRelaxNGValidateDatatype(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *value,
4183 xmlRelaxNGDefinePtr define) {
4184 int ret;
4185 xmlRelaxNGTypeLibraryPtr lib;
4186
4187 if ((define == NULL) || (define->data == NULL)) {
4188 return(-1);
4189 }
4190 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
4191 if (lib->check != NULL)
4192 ret = lib->check(lib->data, define->name, value);
4193 else
4194 ret = -1;
4195 if (ret < 0) {
4196 VALID_CTXT();
4197 VALID_ERROR("Internal: failed to validate type %s\n", define->name);
4198 return(-1);
4199 } else if (ret == 1) {
4200 ret = 0;
4201 } else {
4202 VALID_CTXT();
4203 VALID_ERROR("Type %s doesn't allow value %s\n", define->name, value);
4204 return(-1);
4205 ret = -1;
4206 }
4207 return(ret);
4208}
4209
4210/**
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004211 * xmlRelaxNGNextValue:
4212 * @ctxt: a Relax-NG validation context
4213 *
4214 * Skip to the next value when validating within a list
4215 *
4216 * Returns 0 if the operation succeeded or an error code.
4217 */
4218static int
4219xmlRelaxNGNextValue(xmlRelaxNGValidCtxtPtr ctxt) {
4220 xmlChar *cur;
4221
4222 cur = ctxt->state->value;
4223 if ((cur == NULL) || (ctxt->state->endvalue == NULL)) {
4224 ctxt->state->value = NULL;
Daniel Veillarde5b110b2003-02-04 14:43:39 +00004225 ctxt->state->endvalue = NULL;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004226 return(0);
4227 }
4228 while (*cur != 0) cur++;
4229 while ((cur != ctxt->state->endvalue) && (*cur == 0)) cur++;
4230 if (cur == ctxt->state->endvalue)
4231 ctxt->state->value = NULL;
4232 else
4233 ctxt->state->value = cur;
4234 return(0);
4235}
4236
4237/**
4238 * xmlRelaxNGValidateValueList:
4239 * @ctxt: a Relax-NG validation context
4240 * @defines: the list of definitions to verify
4241 *
4242 * Validate the given set of definitions for the current value
4243 *
4244 * Returns 0 if the validation succeeded or an error code.
4245 */
4246static int
4247xmlRelaxNGValidateValueList(xmlRelaxNGValidCtxtPtr ctxt,
4248 xmlRelaxNGDefinePtr defines) {
4249 int ret = 0;
4250
4251 while (defines != NULL) {
4252 ret = xmlRelaxNGValidateValue(ctxt, defines);
4253 if (ret != 0)
4254 break;
4255 defines = defines->next;
4256 }
4257 return(ret);
4258}
4259
4260/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004261 * xmlRelaxNGValidateValue:
4262 * @ctxt: a Relax-NG validation context
4263 * @define: the definition to verify
4264 *
4265 * Validate the given definition for the current value
4266 *
4267 * Returns 0 if the validation succeeded or an error code.
4268 */
4269static int
4270xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
4271 xmlRelaxNGDefinePtr define) {
Daniel Veillardedc91922003-01-26 00:52:04 +00004272 int ret = 0, oldflags;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004273 xmlChar *value;
4274
4275 value = ctxt->state->value;
4276 switch (define->type) {
4277 case XML_RELAXNG_EMPTY:
4278 if ((value != NULL) && (value[0] != '0'))
4279 ret = -1;
4280 break;
4281 case XML_RELAXNG_TEXT:
4282 break;
Daniel Veillardedc91922003-01-26 00:52:04 +00004283 case XML_RELAXNG_VALUE: {
4284 if (!xmlStrEqual(value, define->value)) {
4285 if (define->name != NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00004286 xmlRelaxNGTypeLibraryPtr lib;
4287
4288 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
4289 if ((lib != NULL) && (lib->comp != NULL))
4290 ret = lib->comp(lib->data, define->name, value,
4291 define->value);
4292 else
4293 ret = -1;
4294 if (ret < 0) {
4295 VALID_CTXT();
4296 VALID_ERROR("Internal: failed to compare type %s\n",
4297 define->name);
4298 return(-1);
4299 } else if (ret == 1) {
4300 ret = 0;
4301 } else {
4302 ret = -1;
4303 }
Daniel Veillardedc91922003-01-26 00:52:04 +00004304 } else {
4305 xmlChar *nval, *nvalue;
4306
4307 /*
4308 * TODO: trivial optimizations are possible by
4309 * computing at compile-time
4310 */
4311 nval = xmlRelaxNGNormalize(ctxt, define->value);
4312 nvalue = xmlRelaxNGNormalize(ctxt, value);
4313
Daniel Veillardea3f3982003-01-26 19:45:18 +00004314 if ((nval == NULL) || (nvalue == NULL) ||
4315 (!xmlStrEqual(nval, nvalue)))
Daniel Veillardedc91922003-01-26 00:52:04 +00004316 ret = -1;
4317 if (nval != NULL)
4318 xmlFree(nval);
4319 if (nvalue != NULL)
4320 xmlFree(nvalue);
4321 }
4322 }
4323 break;
4324 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004325 case XML_RELAXNG_DATATYPE: {
4326 ret = xmlRelaxNGValidateDatatype(ctxt, value, define);
4327 if (ret == 0)
4328 xmlRelaxNGNextValue(ctxt);
4329
4330 break;
4331 }
4332 case XML_RELAXNG_CHOICE: {
4333 xmlRelaxNGDefinePtr list = define->content;
4334 xmlChar *oldvalue;
4335
4336 oldflags = ctxt->flags;
4337 ctxt->flags |= FLAGS_IGNORABLE;
4338
4339 oldvalue = ctxt->state->value;
4340 while (list != NULL) {
4341 ret = xmlRelaxNGValidateValue(ctxt, list);
4342 if (ret == 0) {
4343 break;
4344 }
4345 ctxt->state->value = oldvalue;
4346 list = list->next;
4347 }
4348 ctxt->flags = oldflags;
4349 break;
4350 }
4351 case XML_RELAXNG_LIST: {
4352 xmlRelaxNGDefinePtr list = define->content;
4353 xmlChar *oldvalue, *oldend, *val, *cur;
4354
4355 oldvalue = ctxt->state->value;
4356 oldend = ctxt->state->endvalue;
4357
4358 val = xmlStrdup(oldvalue);
4359 if (val == NULL) {
4360 VALID_CTXT();
4361 VALID_ERROR("Internal: no state\n");
4362 return(-1);
4363 }
4364 cur = val;
4365 while (*cur != 0) {
4366 if (IS_BLANK(*cur))
4367 *cur = 0;
4368 cur++;
4369 }
4370 ctxt->state->endvalue = cur;
4371 cur = val;
4372 while ((*cur == 0) && (cur != ctxt->state->endvalue)) cur++;
4373
4374 ctxt->state->value = cur;
4375
4376 while (list != NULL) {
4377 ret = xmlRelaxNGValidateValue(ctxt, list);
4378 if (ret != 0) {
4379 break;
4380 }
4381 list = list->next;
4382 }
4383 if ((ret == 0) && (ctxt->state->value != NULL) &&
4384 (ctxt->state->value != ctxt->state->endvalue)) {
4385 VALID_CTXT();
4386 VALID_ERROR("Extra data in list: %s\n", ctxt->state->value);
4387 ret = -1;
4388 }
4389 xmlFree(val);
4390 ctxt->state->value = oldvalue;
4391 ctxt->state->endvalue = oldend;
4392 break;
4393 }
4394 case XML_RELAXNG_ONEORMORE:
4395 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
4396 if (ret != 0) {
4397 break;
4398 }
4399 /* no break on purpose */
4400 case XML_RELAXNG_ZEROORMORE: {
4401 xmlChar *cur, *temp;
4402
4403 oldflags = ctxt->flags;
4404 ctxt->flags |= FLAGS_IGNORABLE;
4405 cur = ctxt->state->value;
4406 temp = NULL;
4407 while ((cur != NULL) && (cur != ctxt->state->endvalue) &&
4408 (temp != cur)) {
4409 temp = cur;
4410 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
4411 if (ret != 0) {
4412 ctxt->state->value = temp;
4413 ret = 0;
4414 break;
4415 }
4416 cur = ctxt->state->value;
4417 }
4418 ctxt->flags = oldflags;
4419 break;
4420 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004421 default:
4422 TODO
4423 ret = -1;
4424 }
4425 return(ret);
4426}
4427
4428/**
4429 * xmlRelaxNGValidateValueContent:
4430 * @ctxt: a Relax-NG validation context
4431 * @defines: the list of definitions to verify
4432 *
4433 * Validate the given definitions for the current value
4434 *
4435 * Returns 0 if the validation succeeded or an error code.
4436 */
4437static int
4438xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt,
4439 xmlRelaxNGDefinePtr defines) {
4440 int ret = 0;
4441
4442 while (defines != NULL) {
4443 ret = xmlRelaxNGValidateValue(ctxt, defines);
4444 if (ret != 0)
4445 break;
4446 defines = defines->next;
4447 }
4448 return(ret);
4449}
4450
4451/**
Daniel Veillard144fae12003-02-03 13:17:57 +00004452 * xmlRelaxNGAttributeMatch:
4453 * @ctxt: a Relax-NG validation context
4454 * @define: the definition to check
4455 * @prop: the attribute
4456 *
4457 * Check if the attribute matches the definition nameClass
4458 *
4459 * Returns 1 if the attribute matches, 0 if no, or -1 in case of error
4460 */
4461static int
4462xmlRelaxNGAttributeMatch(xmlRelaxNGValidCtxtPtr ctxt,
4463 xmlRelaxNGDefinePtr define,
4464 xmlAttrPtr prop) {
4465 int ret;
4466
4467 if (define->name != NULL) {
4468 if (!xmlStrEqual(define->name, prop->name))
4469 return(0);
4470 }
4471 if (define->ns != NULL) {
4472 if (define->ns[0] == 0) {
4473 if (prop->ns != NULL)
4474 return(0);
4475 } else {
4476 if ((prop->ns == NULL) ||
4477 (!xmlStrEqual(define->ns, prop->ns->href)))
4478 return(0);
4479 }
4480 }
4481 if (define->nameClass == NULL)
4482 return(1);
4483 define = define->nameClass;
4484 if (define->type == XML_RELAXNG_EXCEPT) {
4485 xmlRelaxNGDefinePtr list;
4486
4487 list = define->content;
4488 while (list != NULL) {
4489 ret = xmlRelaxNGAttributeMatch(ctxt, list, prop);
4490 if (ret == 1)
4491 return(0);
4492 if (ret < 0)
4493 return(ret);
4494 list = list->next;
4495 }
4496 } else {
4497 TODO
4498 }
4499 return(1);
4500}
4501
4502/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004503 * xmlRelaxNGValidateAttribute:
4504 * @ctxt: a Relax-NG validation context
4505 * @define: the definition to verify
4506 *
4507 * Validate the given attribute definition for that node
4508 *
4509 * Returns 0 if the validation succeeded or an error code.
4510 */
4511static int
4512xmlRelaxNGValidateAttribute(xmlRelaxNGValidCtxtPtr ctxt,
4513 xmlRelaxNGDefinePtr define) {
4514 int ret = 0, i;
4515 xmlChar *value, *oldvalue;
4516 xmlAttrPtr prop = NULL, tmp;
4517
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004518 if (ctxt->state->nbAttrLeft <= 0)
4519 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004520 if (define->name != NULL) {
4521 for (i = 0;i < ctxt->state->nbAttrs;i++) {
4522 tmp = ctxt->state->attrs[i];
4523 if ((tmp != NULL) && (xmlStrEqual(define->name, tmp->name))) {
4524 if ((((define->ns == NULL) || (define->ns[0] == 0)) &&
4525 (tmp->ns == NULL)) ||
4526 ((tmp->ns != NULL) &&
4527 (xmlStrEqual(define->ns, tmp->ns->href)))) {
4528 prop = tmp;
4529 break;
4530 }
4531 }
4532 }
4533 if (prop != NULL) {
4534 value = xmlNodeListGetString(prop->doc, prop->children, 1);
4535 oldvalue = ctxt->state->value;
4536 ctxt->state->value = value;
Daniel Veillard231d7912003-02-09 14:22:17 +00004537 ctxt->state->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004538 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00004539 if (ctxt->state->value != NULL)
4540 value = ctxt->state->value;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004541 if (value != NULL)
4542 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00004543 ctxt->state->value = oldvalue;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004544 if (ret == 0) {
4545 /*
4546 * flag the attribute as processed
4547 */
4548 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004549 ctxt->state->nbAttrLeft--;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004550 }
4551 } else {
4552 ret = -1;
4553 }
4554#ifdef DEBUG
4555 xmlGenericError(xmlGenericErrorContext,
4556 "xmlRelaxNGValidateAttribute(%s): %d\n", define->name, ret);
4557#endif
4558 } else {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004559 for (i = 0;i < ctxt->state->nbAttrs;i++) {
4560 tmp = ctxt->state->attrs[i];
Daniel Veillard144fae12003-02-03 13:17:57 +00004561 if ((tmp != NULL) &&
4562 (xmlRelaxNGAttributeMatch(ctxt, define, tmp) == 1)) {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004563 prop = tmp;
4564 break;
4565 }
4566 }
4567 if (prop != NULL) {
4568 value = xmlNodeListGetString(prop->doc, prop->children, 1);
4569 oldvalue = ctxt->state->value;
4570 ctxt->state->value = value;
4571 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00004572 if (ctxt->state->value != NULL)
4573 value = ctxt->state->value;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004574 if (value != NULL)
4575 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00004576 ctxt->state->value = oldvalue;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004577 if (ret == 0) {
4578 /*
4579 * flag the attribute as processed
4580 */
4581 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004582 ctxt->state->nbAttrLeft--;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004583 }
4584 } else {
4585 ret = -1;
4586 }
4587#ifdef DEBUG
Daniel Veillard144fae12003-02-03 13:17:57 +00004588 if (define->ns != NULL) {
4589 xmlGenericError(xmlGenericErrorContext,
4590 "xmlRelaxNGValidateAttribute(nsName ns = %s): %d\n",
4591 define->ns, ret);
4592 } else {
4593 xmlGenericError(xmlGenericErrorContext,
4594 "xmlRelaxNGValidateAttribute(anyName): %d\n",
4595 ret);
4596 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004597#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00004598 }
4599
4600 return(ret);
4601}
4602
4603/**
4604 * xmlRelaxNGValidateAttributeList:
4605 * @ctxt: a Relax-NG validation context
4606 * @define: the list of definition to verify
4607 *
4608 * Validate the given node against the list of attribute definitions
4609 *
4610 * Returns 0 if the validation succeeded or an error code.
4611 */
4612static int
4613xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt,
4614 xmlRelaxNGDefinePtr defines) {
4615 int ret = 0;
4616 while (defines != NULL) {
4617 if (xmlRelaxNGValidateAttribute(ctxt, defines) != 0)
4618 ret = -1;
4619 defines = defines->next;
4620 }
4621 return(ret);
4622}
4623
4624/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004625 * xmlRelaxNGValidateTryPermutation:
4626 * @ctxt: a Relax-NG validation context
4627 * @groups: the array of groups
4628 * @nbgroups: the number of groups in the array
4629 * @array: the permutation to try
4630 * @len: the size of the set
4631 *
4632 * Try to validate a permutation for the group of definitions.
4633 *
4634 * Returns 0 if the validation succeeded or an error code.
4635 */
4636static int
4637xmlRelaxNGValidateTryPermutation(xmlRelaxNGValidCtxtPtr ctxt,
4638 xmlRelaxNGDefinePtr rule,
4639 xmlNodePtr *array, int len) {
4640 int i, ret;
4641
4642 if (len > 0) {
4643 /*
4644 * One only need the next pointer set-up to do the validation
4645 */
4646 for (i = 0;i < (len - 1);i++)
4647 array[i]->next = array[i + 1];
4648 array[i]->next = NULL;
4649
4650 /*
4651 * Now try to validate the sequence
4652 */
4653 ctxt->state->seq = array[0];
4654 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
4655 } else {
4656 ctxt->state->seq = NULL;
4657 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
4658 }
4659
4660 /*
4661 * the sequence must be fully consumed
4662 */
4663 if (ctxt->state->seq != NULL)
4664 return(-1);
4665
4666 return(ret);
4667}
4668
4669/**
4670 * xmlRelaxNGValidateWalkPermutations:
4671 * @ctxt: a Relax-NG validation context
4672 * @groups: the array of groups
4673 * @nbgroups: the number of groups in the array
4674 * @nodes: the set of nodes
4675 * @array: the current state of the parmutation
4676 * @len: the size of the set
4677 * @level: a pointer to the level variable
4678 * @k: the index in the array to fill
4679 *
4680 * Validate a set of nodes for a groups of definitions, will try the
4681 * full set of permutations
4682 *
4683 * Returns 0 if the validation succeeded or an error code.
4684 */
4685static int
4686xmlRelaxNGValidateWalkPermutations(xmlRelaxNGValidCtxtPtr ctxt,
4687 xmlRelaxNGDefinePtr rule, xmlNodePtr *nodes,
4688 xmlNodePtr *array, int len,
4689 int *level, int k) {
4690 int i, ret;
4691
4692 if ((k >= 0) && (k < len))
4693 array[k] = nodes[*level];
4694 *level = *level + 1;
4695 if (*level == len) {
4696 ret = xmlRelaxNGValidateTryPermutation(ctxt, rule, array, len);
4697 if (ret == 0)
4698 return(0);
4699 } else {
4700 for (i = 0;i < len;i++) {
4701 if (array[i] == NULL) {
4702 ret = xmlRelaxNGValidateWalkPermutations(ctxt, rule,
4703 nodes, array, len, level, i);
4704 if (ret == 0)
4705 return(0);
4706 }
4707 }
4708 }
4709 *level = *level - 1;
4710 array[k] = NULL;
4711 return(-1);
4712}
4713
4714/**
4715 * xmlRelaxNGNodeMatchesList:
4716 * @node: the node
4717 * @list: a NULL terminated array of definitions
4718 *
4719 * Check if a node can be matched by one of the definitions
4720 *
4721 * Returns 1 if matches 0 otherwise
4722 */
4723static int
4724xmlRelaxNGNodeMatchesList(xmlNodePtr node, xmlRelaxNGDefinePtr *list) {
4725 xmlRelaxNGDefinePtr cur;
4726 int i = 0;
4727
4728 if ((node == NULL) || (list == NULL))
4729 return(0);
4730
4731 cur = list[i++];
4732 while (cur != NULL) {
4733 if ((node->type == XML_ELEMENT_NODE) &&
4734 (cur->type == XML_RELAXNG_ELEMENT)) {
4735 if (cur->name == NULL) {
4736 if ((node->ns != NULL) &&
4737 (xmlStrEqual(node->ns->href, cur->ns)))
4738 return(1);
4739 } else if (xmlStrEqual(cur->name, node->name)) {
4740 if ((cur->ns == NULL) || (cur->ns[0] == 0)) {
4741 if (node->ns == NULL)
4742 return(1);
4743 } else {
4744 if ((node->ns != NULL) &&
4745 (xmlStrEqual(node->ns->href, cur->ns)))
4746 return(1);
4747 }
4748 }
4749 } else if ((node->type == XML_TEXT_NODE) &&
4750 (cur->type == XML_RELAXNG_TEXT)) {
4751 return(1);
4752 }
4753 cur = list[i++];
4754 }
4755 return(0);
4756}
4757
4758/**
4759 * xmlRelaxNGValidatePartGroup:
4760 * @ctxt: a Relax-NG validation context
4761 * @groups: the array of groups
4762 * @nbgroups: the number of groups in the array
4763 * @nodes: the set of nodes
4764 * @len: the size of the set of nodes
4765 *
4766 * Validate a set of nodes for a groups of definitions
4767 *
4768 * Returns 0 if the validation succeeded or an error code.
4769 */
4770static int
4771xmlRelaxNGValidatePartGroup(xmlRelaxNGValidCtxtPtr ctxt,
4772 xmlRelaxNGInterleaveGroupPtr *groups,
4773 int nbgroups, xmlNodePtr *nodes, int len) {
Daniel Veillard231d7912003-02-09 14:22:17 +00004774 int level, ret = -1, i, j, k, top_j, max_j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004775 xmlNodePtr *array = NULL, *list, oldseq;
4776 xmlRelaxNGInterleaveGroupPtr group;
4777
4778 list = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
4779 if (list == NULL) {
4780 return(-1);
4781 }
4782 array = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
4783 if (array == NULL) {
4784 xmlFree(list);
4785 return(-1);
4786 }
4787 memset(array, 0, len * sizeof(xmlNodePtr));
4788
4789 /*
4790 * Partition the elements and validate the subsets.
4791 */
4792 oldseq = ctxt->state->seq;
Daniel Veillard231d7912003-02-09 14:22:17 +00004793 max_j = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004794 for (i = 0;i < nbgroups;i++) {
4795 group = groups[i];
4796 if (group == NULL)
4797 continue;
4798 k = 0;
Daniel Veillard231d7912003-02-09 14:22:17 +00004799 top_j = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004800 for (j = 0;j < len;j++) {
4801 if (nodes[j] == NULL)
4802 continue;
4803 if (xmlRelaxNGNodeMatchesList(nodes[j], group->defs)) {
4804 list[k++] = nodes[j];
4805 nodes[j] = NULL;
Daniel Veillard231d7912003-02-09 14:22:17 +00004806 top_j = j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004807 }
4808 }
Daniel Veillard231d7912003-02-09 14:22:17 +00004809 if (top_j > max_j)
4810 max_j = top_j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004811 ctxt->state->seq = oldseq;
4812 if (k > 1) {
4813 memset(array, 0, k * sizeof(xmlNodePtr));
Daniel Veillardb08c9812003-01-28 23:09:49 +00004814 level = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004815 ret = xmlRelaxNGValidateWalkPermutations(ctxt, group->rule,
4816 list, array, k, &level, -1);
4817 } else {
4818 ret = xmlRelaxNGValidateTryPermutation(ctxt, group->rule, list, k);
4819 }
4820 if (ret != 0) {
4821 ctxt->state->seq = oldseq;
4822 break;
4823 }
4824 }
4825
Daniel Veillard231d7912003-02-09 14:22:17 +00004826 for (j = 0;j < max_j;j++) {
4827 if (nodes[j] != NULL) {
4828 TODO /* problem, one of the nodes didn't got a match */
4829 }
4830 }
4831 if (ret == 0) {
4832 if (max_j + 1 < len)
4833 ctxt->state->seq = nodes[max_j + 1];
4834 else
4835 ctxt->state->seq = NULL;
4836 }
4837
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004838 xmlFree(list);
4839 xmlFree(array);
4840 return(ret);
4841}
4842
4843/**
4844 * xmlRelaxNGValidateInterleave:
4845 * @ctxt: a Relax-NG validation context
4846 * @define: the definition to verify
4847 *
4848 * Validate an interleave definition for a node.
4849 *
4850 * Returns 0 if the validation succeeded or an error code.
4851 */
4852static int
4853xmlRelaxNGValidateInterleave(xmlRelaxNGValidCtxtPtr ctxt,
4854 xmlRelaxNGDefinePtr define) {
4855 int ret = 0, nbchildren, nbtot, i, j;
4856 xmlRelaxNGPartitionPtr partitions;
4857 xmlNodePtr *children = NULL;
4858 xmlNodePtr *order = NULL;
Daniel Veillard231d7912003-02-09 14:22:17 +00004859 xmlNodePtr cur, oldseq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004860
4861 if (define->data != NULL) {
4862 partitions = (xmlRelaxNGPartitionPtr) define->data;
4863 } else {
4864 VALID_CTXT();
4865 VALID_ERROR("Internal: interleave block has no data\n");
4866 return(-1);
4867 }
4868
4869 /*
4870 * Build the sequence of child and an array preserving the children
4871 * initial order.
4872 */
4873 cur = ctxt->state->seq;
Daniel Veillard231d7912003-02-09 14:22:17 +00004874 oldseq = ctxt->state->seq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004875 nbchildren = 0;
4876 nbtot = 0;
4877 while (cur != NULL) {
4878 if ((cur->type == XML_COMMENT_NODE) ||
4879 (cur->type == XML_PI_NODE) ||
4880 ((cur->type == XML_TEXT_NODE) &&
4881 (IS_BLANK_NODE(cur)))) {
4882 nbtot++;
4883 } else {
4884 nbchildren++;
4885 nbtot++;
4886 }
4887 cur = cur->next;
4888 }
4889 children = (xmlNodePtr *) xmlMalloc(nbchildren * sizeof(xmlNodePtr));
4890 if (children == NULL)
4891 goto error;
4892 order = (xmlNodePtr *) xmlMalloc(nbtot * sizeof(xmlNodePtr));
4893 if (order == NULL)
4894 goto error;
4895 cur = ctxt->state->seq;
4896 i = 0;
4897 j = 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 order[j++] = cur;
4904 } else {
4905 order[j++] = cur;
4906 children[i++] = cur;
4907 }
4908 cur = cur->next;
4909 }
4910
4911 /* TODO: retry with a maller set of child if there is a next... */
4912 ret = xmlRelaxNGValidatePartGroup(ctxt, partitions->groups,
4913 partitions->nbgroups, children, nbchildren);
Daniel Veillard231d7912003-02-09 14:22:17 +00004914 if (ret != 0)
4915 ctxt->state->seq = oldseq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004916
4917 /*
4918 * Cleanup: rebuid the child sequence and free the structure
4919 */
4920 if (order != NULL) {
4921 for (i = 0;i < nbtot;i++) {
4922 if (i == 0)
4923 order[i]->prev = NULL;
4924 else
4925 order[i]->prev = order[i - 1];
4926 if (i == nbtot - 1)
4927 order[i]->next = NULL;
4928 else
4929 order[i]->next = order[i + 1];
4930 }
4931 xmlFree(order);
4932 }
4933 if (children != NULL)
4934 xmlFree(children);
4935
4936 return(ret);
4937
4938error:
4939 if (order != NULL) {
4940 for (i = 0;i < nbtot;i++) {
4941 if (i == 0)
4942 order[i]->prev = NULL;
4943 else
4944 order[i]->prev = order[i - 1];
4945 if (i == nbtot - 1)
4946 order[i]->next = NULL;
4947 else
4948 order[i]->next = order[i + 1];
4949 }
4950 xmlFree(order);
4951 }
4952 if (children != NULL)
4953 xmlFree(children);
4954 return(-1);
4955}
4956
4957/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004958 * xmlRelaxNGValidateElementContent:
4959 * @ctxt: a Relax-NG validation context
4960 * @define: the list of definition to verify
4961 *
4962 * Validate the given node content against the (list) of definitions
4963 *
4964 * Returns 0 if the validation succeeded or an error code.
4965 */
4966static int
4967xmlRelaxNGValidateElementContent(xmlRelaxNGValidCtxtPtr ctxt,
4968 xmlRelaxNGDefinePtr defines) {
4969 int ret = 0, res;
4970
4971 if (ctxt->state == NULL) {
4972 VALID_CTXT();
4973 VALID_ERROR("Internal: no state\n");
4974 return(-1);
4975 }
4976 while (defines != NULL) {
4977 res = xmlRelaxNGValidateDefinition(ctxt, defines);
4978 if (res < 0)
4979 ret = -1;
4980 defines = defines->next;
4981 }
4982
4983 return(ret);
4984}
4985
4986/**
4987 * xmlRelaxNGValidateDefinition:
4988 * @ctxt: a Relax-NG validation context
4989 * @define: the definition to verify
4990 *
4991 * Validate the current node against the definition
4992 *
4993 * Returns 0 if the validation succeeded or an error code.
4994 */
4995static int
4996xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
4997 xmlRelaxNGDefinePtr define) {
4998 xmlNodePtr node;
4999 int ret = 0, i, tmp, oldflags;
5000 xmlRelaxNGValidStatePtr oldstate, state;
5001
5002 if (define == NULL) {
5003 VALID_CTXT();
5004 VALID_ERROR("internal error: define == NULL\n");
5005 return(-1);
5006 }
5007 if (ctxt->state != NULL) {
5008 node = ctxt->state->seq;
5009 } else {
5010 node = NULL;
5011 }
Daniel Veillard231d7912003-02-09 14:22:17 +00005012#ifdef DEBUG
5013 for (i = 0;i < ctxt->depth;i++)
5014 xmlGenericError(xmlGenericErrorContext, " ");
5015 xmlGenericError(xmlGenericErrorContext,
5016 "Start validating %s ", xmlRelaxNGDefName(define));
5017 if (define->name != NULL)
5018 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
5019 if ((node != NULL) && (node->name != NULL))
5020 xmlGenericError(xmlGenericErrorContext, "on %s\n", node->name);
5021 else
5022 xmlGenericError(xmlGenericErrorContext, "\n");
5023#endif
5024 ctxt->depth++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005025 switch (define->type) {
5026 case XML_RELAXNG_EMPTY:
5027 if (node != NULL) {
5028 VALID_CTXT();
5029 VALID_ERROR("Expecting an empty element\n");
Daniel Veillard231d7912003-02-09 14:22:17 +00005030 ret = -1;
5031 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005032 }
Daniel Veillard231d7912003-02-09 14:22:17 +00005033 ret = 0;
5034 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005035 case XML_RELAXNG_NOT_ALLOWED:
Daniel Veillard231d7912003-02-09 14:22:17 +00005036 ret = -1;
5037 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005038 case XML_RELAXNG_TEXT:
Daniel Veillard231d7912003-02-09 14:22:17 +00005039 if (node == NULL) {
5040 ret = 0;
5041 break;
5042 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005043 while ((node != NULL) &&
5044 ((node->type == XML_TEXT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005045 (node->type == XML_COMMENT_NODE) ||
5046 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00005047 (node->type == XML_CDATA_SECTION_NODE)))
5048 node = node->next;
Daniel Veillard276be4a2003-01-24 01:03:34 +00005049 if (node == ctxt->state->seq) {
5050 VALID_CTXT();
5051 VALID_ERROR("Expecting text content\n");
5052 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005053 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00005054 ctxt->state->seq = node;
5055 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005056 case XML_RELAXNG_ELEMENT:
5057 node = xmlRelaxNGSkipIgnored(ctxt, node);
Daniel Veillard231d7912003-02-09 14:22:17 +00005058 if (node == NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005059 VALID_CTXT();
Daniel Veillard231d7912003-02-09 14:22:17 +00005060 VALID_ERROR("Expecting an element, got empty\n");
5061 ret = -1;
5062 break;
5063 }
5064 if (node->type != XML_ELEMENT_NODE) {
5065 VALID_CTXT();
5066 VALID_ERROR("Expecting an element got %d type\n", node->type);
5067 ret = -1;
5068 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005069 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00005070 /*
5071 * This node was already validated successfully against
5072 * this definition.
5073 */
5074 if (node->_private == define)
5075 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005076 if (define->name != NULL) {
5077 if (!xmlStrEqual(node->name, define->name)) {
5078 VALID_CTXT();
5079 VALID_ERROR("Expecting element %s, got %s\n",
5080 define->name, node->name);
Daniel Veillard231d7912003-02-09 14:22:17 +00005081 ret = -1;
5082 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005083 }
5084 }
5085 if ((define->ns != NULL) && (define->ns[0] != 0)) {
5086 if (node->ns == NULL) {
5087 VALID_CTXT();
5088 VALID_ERROR("Expecting a namespace for element %s\n",
5089 node->name);
Daniel Veillard231d7912003-02-09 14:22:17 +00005090 ret = -1;
5091 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005092 } else if (!xmlStrEqual(node->ns->href, define->ns)) {
5093 VALID_CTXT();
5094 VALID_ERROR("Expecting element %s has wrong namespace: expecting %s\n",
5095 node->name, define->ns);
Daniel Veillard231d7912003-02-09 14:22:17 +00005096 ret = -1;
5097 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005098 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005099 } else if (define->name != NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005100 if (node->ns != NULL) {
5101 VALID_CTXT();
5102 VALID_ERROR("Expecting no namespace for element %s\n",
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005103 define->name);
Daniel Veillard231d7912003-02-09 14:22:17 +00005104 ret = -1;
5105 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005106 }
5107 }
5108
5109 state = xmlRelaxNGNewValidState(ctxt, node);
5110 if (state == NULL) {
Daniel Veillard231d7912003-02-09 14:22:17 +00005111 ret = -1;
5112 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005113 }
5114
5115 oldstate = ctxt->state;
5116 ctxt->state = state;
5117 if (define->attrs != NULL) {
5118 tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
Daniel Veillard231d7912003-02-09 14:22:17 +00005119 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005120 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00005121#ifdef DEBUG
5122 xmlGenericError(xmlGenericErrorContext,
5123 "E: Element %s failed to validate attributes\n",
5124 node->name);
5125#endif
5126 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005127 }
5128 if (define->content != NULL) {
5129 tmp = xmlRelaxNGValidateElementContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00005130 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005131 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00005132#ifdef DEBUG
5133 xmlGenericError(xmlGenericErrorContext,
5134 "E: Element %s failed to validate element content\n",
5135 node->name);
5136#endif
5137 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005138 }
5139 state = ctxt->state;
5140 if (state->seq != NULL) {
5141 state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq);
5142 if (state->seq != NULL) {
5143 VALID_CTXT();
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005144 VALID_ERROR("Extra content for element %s: %s\n",
5145 node->name, state->seq->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005146 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00005147#ifdef DEBUG
5148 xmlGenericError(xmlGenericErrorContext,
5149 "E: Element %s has extra content: %s\n",
5150 node->name, state->seq->name);
5151#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00005152 }
5153 }
5154 for (i = 0;i < state->nbAttrs;i++) {
5155 if (state->attrs[i] != NULL) {
5156 VALID_CTXT();
Daniel Veillardea3f3982003-01-26 19:45:18 +00005157 VALID_ERROR("Invalid attribute %s for element %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00005158 state->attrs[i]->name, node->name);
5159 ret = -1;
5160 }
5161 }
5162 ctxt->state = oldstate;
5163 xmlRelaxNGFreeValidState(state);
5164 if (oldstate != NULL)
Daniel Veillarde5b110b2003-02-04 14:43:39 +00005165 oldstate->seq = xmlRelaxNGSkipIgnored(ctxt, node->next);
5166 if (ret == 0)
5167 node->_private = define;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005168
5169
5170#ifdef DEBUG
5171 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard231d7912003-02-09 14:22:17 +00005172 "xmlRelaxNGValidateDefinition(): validated %s : %d",
Daniel Veillard6eadf632003-01-23 18:29:16 +00005173 node->name, ret);
Daniel Veillard231d7912003-02-09 14:22:17 +00005174 if (oldstate == NULL)
5175 xmlGenericError(xmlGenericErrorContext, ": no state\n");
5176 else if (oldstate->seq == NULL)
5177 xmlGenericError(xmlGenericErrorContext, ": done\n");
5178 else if (oldstate->seq->type == XML_ELEMENT_NODE)
5179 xmlGenericError(xmlGenericErrorContext, ": next elem %s\n",
5180 oldstate->seq->name);
5181 else
5182 xmlGenericError(xmlGenericErrorContext, ": next %s %d\n",
5183 oldstate->seq->name, oldstate->seq->type);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005184#endif
5185 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005186 case XML_RELAXNG_OPTIONAL:
5187 oldflags = ctxt->flags;
5188 ctxt->flags |= FLAGS_IGNORABLE;
5189 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5190 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5191 if (ret != 0) {
5192 xmlRelaxNGFreeValidState(ctxt->state);
5193 ctxt->state = oldstate;
5194 ret = 0;
5195 break;
5196 }
5197 xmlRelaxNGFreeValidState(oldstate);
5198 ctxt->flags = oldflags;
5199 ret = 0;
5200 break;
5201 case XML_RELAXNG_ONEORMORE:
5202 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5203 if (ret != 0) {
5204 break;
5205 }
5206 /* no break on purpose */
Daniel Veillard276be4a2003-01-24 01:03:34 +00005207 case XML_RELAXNG_ZEROORMORE: {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005208 oldflags = ctxt->flags;
5209 ctxt->flags |= FLAGS_IGNORABLE;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005210 while (ctxt->state->nbAttrLeft != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005211 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5212 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5213 if (ret != 0) {
5214 xmlRelaxNGFreeValidState(ctxt->state);
5215 ctxt->state = oldstate;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005216 break;
5217 }
5218 xmlRelaxNGFreeValidState(oldstate);
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005219 }
5220 if (ret == 0) {
5221 /*
5222 * There is no attribute left to be consumed,
5223 * we can check the closure by looking at ctxt->state->seq
5224 */
5225 xmlNodePtr cur, temp;
5226
Daniel Veillard276be4a2003-01-24 01:03:34 +00005227 cur = ctxt->state->seq;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005228 temp = NULL;
5229 while ((cur != NULL) && (temp != cur)) {
5230 temp = cur;
5231 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5232 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5233 if (ret != 0) {
5234 xmlRelaxNGFreeValidState(ctxt->state);
5235 ctxt->state = oldstate;
5236 break;
5237 }
5238 xmlRelaxNGFreeValidState(oldstate);
5239 cur = ctxt->state->seq;
5240 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005241 }
5242 ctxt->flags = oldflags;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005243 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005244 break;
Daniel Veillard276be4a2003-01-24 01:03:34 +00005245 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005246 case XML_RELAXNG_CHOICE: {
5247 xmlRelaxNGDefinePtr list = define->content;
5248
5249 oldflags = ctxt->flags;
5250 ctxt->flags |= FLAGS_IGNORABLE;
5251
5252 while (list != NULL) {
5253 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5254 ret = xmlRelaxNGValidateDefinition(ctxt, list);
5255 if (ret == 0) {
5256 xmlRelaxNGFreeValidState(oldstate);
5257 break;
5258 }
5259 xmlRelaxNGFreeValidState(ctxt->state);
5260 ctxt->state = oldstate;
5261 list = list->next;
5262 }
5263 ctxt->flags = oldflags;
5264 break;
5265 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005266 case XML_RELAXNG_DEF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00005267 case XML_RELAXNG_GROUP: {
5268 xmlRelaxNGDefinePtr list = define->content;
5269
5270 while (list != NULL) {
5271 ret = xmlRelaxNGValidateDefinition(ctxt, list);
5272 if (ret != 0)
5273 break;
5274 list = list->next;
5275 }
5276 break;
5277 }
5278 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005279 ret = xmlRelaxNGValidateInterleave(ctxt, define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005280 break;
5281 case XML_RELAXNG_ATTRIBUTE:
5282 ret = xmlRelaxNGValidateAttribute(ctxt, define);
5283 break;
5284 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00005285 case XML_RELAXNG_PARENTREF:
5286 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00005287 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5288 break;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005289 case XML_RELAXNG_DATATYPE: {
5290 xmlChar *content;
5291
5292 content = xmlNodeGetContent(node);
5293 ret = xmlRelaxNGValidateDatatype(ctxt, content, define);
5294 if (ret == -1) {
5295 VALID_CTXT();
5296 VALID_ERROR("internal error validating %s\n", define->name);
5297 } else if (ret == 0) {
5298 ctxt->state->seq = node->next;
5299 }
5300 /*
5301 * TODO cover the problems with
5302 * <p>12<!-- comment -->34</p>
5303 * TODO detect full element coverage at compilation time.
5304 */
5305 if ((node != NULL) && (node->next != NULL)) {
5306 VALID_CTXT();
5307 VALID_ERROR("The data does not cover the full element %s\n",
5308 node->parent->name);
5309 ret = -1;
5310 }
5311 if (content != NULL)
5312 xmlFree(content);
5313 break;
5314 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00005315 case XML_RELAXNG_VALUE: {
5316 xmlChar *content;
5317 xmlChar *oldvalue;
5318
5319 content = xmlNodeGetContent(node);
5320 oldvalue = ctxt->state->value;
5321 ctxt->state->value = content;
5322 ret = xmlRelaxNGValidateValue(ctxt, define);
5323 ctxt->state->value = oldvalue;
5324 if (ret == -1) {
5325 VALID_CTXT();
5326 VALID_ERROR("internal error validating %s\n", define->name);
5327 } else if (ret == 0) {
5328 ctxt->state->seq = node->next;
5329 }
5330 /*
5331 * TODO cover the problems with
5332 * <p>12<!-- comment -->34</p>
5333 * TODO detect full element coverage at compilation time.
5334 */
5335 if ((node != NULL) && (node->next != NULL)) {
5336 VALID_CTXT();
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005337 VALID_ERROR("The value does not cover the full element %s\n",
Daniel Veillardea3f3982003-01-26 19:45:18 +00005338 node->parent->name);
5339 ret = -1;
5340 }
5341 if (content != NULL)
5342 xmlFree(content);
5343 break;
5344 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005345 case XML_RELAXNG_LIST: {
5346 xmlChar *content;
5347 xmlChar *oldvalue, *oldendvalue;
5348 int len;
Daniel Veillardea3f3982003-01-26 19:45:18 +00005349
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005350 content = xmlNodeGetContent(node);
5351 len = xmlStrlen(content);
5352 oldvalue = ctxt->state->value;
5353 oldendvalue = ctxt->state->endvalue;
5354 ctxt->state->value = content;
5355 ctxt->state->endvalue = content + len;
5356 ret = xmlRelaxNGValidateValue(ctxt, define);
5357 ctxt->state->value = oldvalue;
5358 ctxt->state->endvalue = oldendvalue;
5359 if (ret == -1) {
5360 VALID_CTXT();
5361 VALID_ERROR("internal error validating list\n");
5362 } else if (ret == 0) {
5363 ctxt->state->seq = node->next;
5364 }
5365 /*
5366 * TODO cover the problems with
5367 * <p>12<!-- comment -->34</p>
5368 * TODO detect full element coverage at compilation time.
5369 */
5370 if ((node != NULL) && (node->next != NULL)) {
5371 VALID_CTXT();
5372 VALID_ERROR("The list does not cover the full element %s\n",
5373 node->parent->name);
5374 ret = -1;
5375 }
5376 if (content != NULL)
5377 xmlFree(content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005378 break;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005379 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005380 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00005381 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005382 TODO
5383 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005384 }
Daniel Veillard231d7912003-02-09 14:22:17 +00005385 ctxt->depth--;
5386#ifdef DEBUG
5387 for (i = 0;i < ctxt->depth;i++)
5388 xmlGenericError(xmlGenericErrorContext, " ");
5389 xmlGenericError(xmlGenericErrorContext,
5390 "Validating %s ", xmlRelaxNGDefName(define));
5391 if (define->name != NULL)
5392 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
5393 if (ret == 0)
5394 xmlGenericError(xmlGenericErrorContext, "suceeded\n");
5395 else
5396 xmlGenericError(xmlGenericErrorContext, "failed\n");
5397#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00005398 return(ret);
5399}
5400
5401/**
5402 * xmlRelaxNGValidateDocument:
5403 * @ctxt: a Relax-NG validation context
5404 * @doc: the document
5405 *
5406 * Validate the given document
5407 *
5408 * Returns 0 if the validation succeeded or an error code.
5409 */
5410static int
5411xmlRelaxNGValidateDocument(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
5412 int ret;
5413 xmlRelaxNGPtr schema;
5414 xmlRelaxNGGrammarPtr grammar;
5415 xmlRelaxNGValidStatePtr state;
5416
5417 if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL))
5418 return(-1);
5419
5420 schema = ctxt->schema;
5421 grammar = schema->topgrammar;
5422 if (grammar == NULL) {
5423 VALID_CTXT();
5424 VALID_ERROR("No top grammar defined\n");
5425 return(-1);
5426 }
5427 state = xmlRelaxNGNewValidState(ctxt, NULL);
5428 ctxt->state = state;
5429 ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start);
5430 state = ctxt->state;
5431 if ((state != NULL) && (state->seq != NULL)) {
5432 xmlNodePtr node;
5433
5434 node = state->seq;
5435 node = xmlRelaxNGSkipIgnored(ctxt, node);
5436 if (node != NULL) {
5437 VALID_CTXT();
5438 VALID_ERROR("extra data on the document\n");
5439 ret = -1;
5440 }
5441 }
5442 xmlRelaxNGFreeValidState(state);
5443
5444 return(ret);
5445}
5446
5447/************************************************************************
5448 * *
5449 * Validation interfaces *
5450 * *
5451 ************************************************************************/
5452/**
5453 * xmlRelaxNGNewValidCtxt:
5454 * @schema: a precompiled XML RelaxNGs
5455 *
5456 * Create an XML RelaxNGs validation context based on the given schema
5457 *
5458 * Returns the validation context or NULL in case of error
5459 */
5460xmlRelaxNGValidCtxtPtr
5461xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema) {
5462 xmlRelaxNGValidCtxtPtr ret;
5463
5464 ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt));
5465 if (ret == NULL) {
5466 xmlGenericError(xmlGenericErrorContext,
5467 "Failed to allocate new schama validation context\n");
5468 return (NULL);
5469 }
5470 memset(ret, 0, sizeof(xmlRelaxNGValidCtxt));
5471 ret->schema = schema;
5472 return (ret);
5473}
5474
5475/**
5476 * xmlRelaxNGFreeValidCtxt:
5477 * @ctxt: the schema validation context
5478 *
5479 * Free the resources associated to the schema validation context
5480 */
5481void
5482xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt) {
5483 if (ctxt == NULL)
5484 return;
5485 xmlFree(ctxt);
5486}
5487
5488/**
5489 * xmlRelaxNGSetValidErrors:
5490 * @ctxt: a Relax-NG validation context
5491 * @err: the error function
5492 * @warn: the warning function
5493 * @ctx: the functions context
5494 *
5495 * Set the error and warning callback informations
5496 */
5497void
5498xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
5499 xmlRelaxNGValidityErrorFunc err,
5500 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
5501 if (ctxt == NULL)
5502 return;
5503 ctxt->error = err;
5504 ctxt->warning = warn;
5505 ctxt->userData = ctx;
5506}
5507
5508/**
5509 * xmlRelaxNGValidateDoc:
5510 * @ctxt: a Relax-NG validation context
5511 * @doc: a parsed document tree
5512 *
5513 * Validate a document tree in memory.
5514 *
5515 * Returns 0 if the document is valid, a positive error code
5516 * number otherwise and -1 in case of internal or API error.
5517 */
5518int
5519xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
5520 int ret;
5521
5522 if ((ctxt == NULL) || (doc == NULL))
5523 return(-1);
5524
5525 ctxt->doc = doc;
5526
5527 ret = xmlRelaxNGValidateDocument(ctxt, doc);
Daniel Veillard71531f32003-02-05 13:19:53 +00005528 /*
5529 * TODO: build error codes
5530 */
5531 if (ret == -1)
5532 return(1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005533 return(ret);
5534}
5535
5536#endif /* LIBXML_SCHEMAS_ENABLED */
5537