blob: 84ca17c03704fd0b7cee09e01b926662a82d5c0f [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 Veillardf4b4f982003-02-13 11:02:08 +000016 * - add support for DTD compatibility spec
17 * http://www.oasis-open.org/committees/relax-ng/compatibility-20011203.html
Daniel Veillardd41f4f42003-01-29 21:07:52 +000018 */
19
Daniel Veillard6eadf632003-01-23 18:29:16 +000020#define IN_LIBXML
21#include "libxml.h"
22
23#ifdef LIBXML_SCHEMAS_ENABLED
24
25#include <string.h>
26#include <stdio.h>
27#include <libxml/xmlmemory.h>
28#include <libxml/parser.h>
29#include <libxml/parserInternals.h>
30#include <libxml/hash.h>
31#include <libxml/uri.h>
32
33#include <libxml/relaxng.h>
34
35#include <libxml/xmlschemastypes.h>
36#include <libxml/xmlautomata.h>
37#include <libxml/xmlregexp.h>
Daniel Veillardc6e997c2003-01-27 12:35:42 +000038#include <libxml/xmlschemastypes.h>
Daniel Veillard6eadf632003-01-23 18:29:16 +000039
40/*
41 * The Relax-NG namespace
42 */
43static const xmlChar *xmlRelaxNGNs = (const xmlChar *)
44 "http://relaxng.org/ns/structure/1.0";
45
46#define IS_RELAXNG(node, type) \
47 ((node != NULL) && (node->ns != NULL) && \
48 (xmlStrEqual(node->name, (const xmlChar *) type)) && \
49 (xmlStrEqual(node->ns->href, xmlRelaxNGNs)))
50
51
Daniel Veillard71531f32003-02-05 13:19:53 +000052/* #define DEBUG 1 */ /* very verbose output */
53/* #define DEBUG_CONTENT 1 */
54/* #define DEBUG_TYPE 1 */
55/* #define DEBUG_VALID 1 */
Daniel Veillarde5b110b2003-02-04 14:43:39 +000056/* #define DEBUG_INTERLEAVE 1 */
Daniel Veillard416589a2003-02-17 17:25:42 +000057/* #define DEBUG_LIST 1 */
Daniel Veillard6eadf632003-01-23 18:29:16 +000058
59#define UNBOUNDED (1 << 30)
60#define TODO \
61 xmlGenericError(xmlGenericErrorContext, \
62 "Unimplemented block at %s:%d\n", \
63 __FILE__, __LINE__);
64
65typedef struct _xmlRelaxNGSchema xmlRelaxNGSchema;
66typedef xmlRelaxNGSchema *xmlRelaxNGSchemaPtr;
67
68typedef struct _xmlRelaxNGDefine xmlRelaxNGDefine;
69typedef xmlRelaxNGDefine *xmlRelaxNGDefinePtr;
70
Daniel Veillardd41f4f42003-01-29 21:07:52 +000071typedef struct _xmlRelaxNGDocument xmlRelaxNGDocument;
72typedef xmlRelaxNGDocument *xmlRelaxNGDocumentPtr;
73
Daniel Veillarda9d912d2003-02-01 17:43:10 +000074typedef struct _xmlRelaxNGInclude xmlRelaxNGInclude;
75typedef xmlRelaxNGInclude *xmlRelaxNGIncludePtr;
76
Daniel Veillard6eadf632003-01-23 18:29:16 +000077typedef enum {
78 XML_RELAXNG_COMBINE_UNDEFINED = 0, /* undefined */
79 XML_RELAXNG_COMBINE_CHOICE, /* choice */
80 XML_RELAXNG_COMBINE_INTERLEAVE /* interleave */
81} xmlRelaxNGCombine;
82
Daniel Veillard4c5cf702003-02-21 15:40:34 +000083typedef enum {
84 XML_RELAXNG_CONTENT_ERROR = -1,
85 XML_RELAXNG_CONTENT_EMPTY = 0,
86 XML_RELAXNG_CONTENT_SIMPLE,
87 XML_RELAXNG_CONTENT_COMPLEX
88} xmlRelaxNGContentType;
89
Daniel Veillard6eadf632003-01-23 18:29:16 +000090typedef struct _xmlRelaxNGGrammar xmlRelaxNGGrammar;
91typedef xmlRelaxNGGrammar *xmlRelaxNGGrammarPtr;
92
93struct _xmlRelaxNGGrammar {
94 xmlRelaxNGGrammarPtr parent;/* the parent grammar if any */
95 xmlRelaxNGGrammarPtr children;/* the children grammar if any */
96 xmlRelaxNGGrammarPtr next; /* the next grammar if any */
97 xmlRelaxNGDefinePtr start; /* <start> content */
98 xmlRelaxNGCombine combine; /* the default combine value */
Daniel Veillardd41f4f42003-01-29 21:07:52 +000099 xmlRelaxNGDefinePtr startList;/* list of <start> definitions */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000100 xmlHashTablePtr defs; /* define* */
101 xmlHashTablePtr refs; /* references */
102};
103
104
Daniel Veillard6eadf632003-01-23 18:29:16 +0000105typedef enum {
Daniel Veillard77648bb2003-02-20 15:03:22 +0000106 XML_RELAXNG_NOOP = -1, /* a no operation from simplification */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000107 XML_RELAXNG_EMPTY = 0, /* an empty pattern */
108 XML_RELAXNG_NOT_ALLOWED, /* not allowed top */
Daniel Veillard144fae12003-02-03 13:17:57 +0000109 XML_RELAXNG_EXCEPT, /* except present in nameclass defs */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000110 XML_RELAXNG_TEXT, /* textual content */
111 XML_RELAXNG_ELEMENT, /* an element */
112 XML_RELAXNG_DATATYPE, /* extenal data type definition */
Daniel Veillard8fe98712003-02-19 00:19:14 +0000113 XML_RELAXNG_PARAM, /* extenal data type parameter */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000114 XML_RELAXNG_VALUE, /* value from an extenal data type definition */
115 XML_RELAXNG_LIST, /* a list of patterns */
116 XML_RELAXNG_ATTRIBUTE, /* an attrbute following a pattern */
117 XML_RELAXNG_DEF, /* a definition */
118 XML_RELAXNG_REF, /* reference to a definition */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000119 XML_RELAXNG_EXTERNALREF, /* reference to an external def */
Daniel Veillard419a7682003-02-03 23:22:49 +0000120 XML_RELAXNG_PARENTREF, /* reference to a def in the parent grammar */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000121 XML_RELAXNG_OPTIONAL, /* optional patterns */
122 XML_RELAXNG_ZEROORMORE, /* zero or more non empty patterns */
123 XML_RELAXNG_ONEORMORE, /* one or more non empty patterns */
124 XML_RELAXNG_CHOICE, /* a choice between non empty patterns */
125 XML_RELAXNG_GROUP, /* a pair/group of non empty patterns */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000126 XML_RELAXNG_INTERLEAVE, /* interleaving choice of non-empty patterns */
127 XML_RELAXNG_START /* Used to keep track of starts on grammars */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000128} xmlRelaxNGType;
129
130struct _xmlRelaxNGDefine {
131 xmlRelaxNGType type; /* the type of definition */
132 xmlNodePtr node; /* the node in the source */
133 xmlChar *name; /* the element local name if present */
134 xmlChar *ns; /* the namespace local name if present */
Daniel Veillardedc91922003-01-26 00:52:04 +0000135 xmlChar *value; /* value when available */
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000136 void *data; /* data lib or specific pointer */
Daniel Veillardd4310742003-02-18 21:12:46 +0000137 int depth; /* used for the cycle detection */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000138 xmlRelaxNGDefinePtr content;/* the expected content */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000139 xmlRelaxNGDefinePtr parent; /* the parent definition, if any */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000140 xmlRelaxNGDefinePtr next; /* list within grouping sequences */
141 xmlRelaxNGDefinePtr attrs; /* list of attributes for elements */
Daniel Veillard3b2e4e12003-02-03 08:52:58 +0000142 xmlRelaxNGDefinePtr nameClass;/* the nameClass definition if any */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000143 xmlRelaxNGDefinePtr nextHash;/* next define in defs/refs hash tables */
144};
145
146/**
147 * _xmlRelaxNG:
148 *
149 * A RelaxNGs definition
150 */
151struct _xmlRelaxNG {
152 xmlRelaxNGGrammarPtr topgrammar;
153 xmlDocPtr doc;
154
155 xmlHashTablePtr defs; /* define */
156 xmlHashTablePtr refs; /* references */
Daniel Veillard419a7682003-02-03 23:22:49 +0000157 xmlHashTablePtr documents; /* all the documents loaded */
158 xmlHashTablePtr includes; /* all the includes loaded */
159 int defNr; /* number of defines used */
160 xmlRelaxNGDefinePtr *defTab;/* pointer to the allocated definitions */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000161 void *_private; /* unused by the library for users or bindings */
162};
163
164typedef enum {
165 XML_RELAXNG_ERR_OK = 0,
166 XML_RELAXNG_ERR_NOROOT = 1,
167 XML_RELAXNG_ERR_
168} xmlRelaxNGValidError;
169
Daniel Veillard77648bb2003-02-20 15:03:22 +0000170#define XML_RELAXNG_IN_ATTRIBUTE (1 << 0)
171#define XML_RELAXNG_IN_ONEORMORE (1 << 1)
172#define XML_RELAXNG_IN_LIST (1 << 2)
173#define XML_RELAXNG_IN_DATAEXCEPT (1 << 3)
174#define XML_RELAXNG_IN_START (1 << 4)
175#define XML_RELAXNG_IN_OOMGROUP (1 << 5)
176#define XML_RELAXNG_IN_OOMINTERLEAVE (1 << 6)
177#define XML_RELAXNG_IN_EXTERNALREF (1 << 7)
Daniel Veillardc5312d72003-02-21 17:14:10 +0000178#define XML_RELAXNG_IN_ANYEXCEPT (1 << 8)
179#define XML_RELAXNG_IN_NSEXCEPT (1 << 9)
Daniel Veillard6eadf632003-01-23 18:29:16 +0000180
181struct _xmlRelaxNGParserCtxt {
182 void *userData; /* user specific data block */
183 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
184 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
185 xmlRelaxNGValidError err;
186
187 xmlRelaxNGPtr schema; /* The schema in use */
188 xmlRelaxNGGrammarPtr grammar; /* the current grammar */
Daniel Veillard419a7682003-02-03 23:22:49 +0000189 xmlRelaxNGGrammarPtr parentgrammar;/* the parent grammar */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000190 int flags; /* parser flags */
191 int nbErrors; /* number of errors at parse time */
192 int nbWarnings; /* number of warnings at parse time */
Daniel Veillard276be4a2003-01-24 01:03:34 +0000193 const xmlChar *define; /* the current define scope */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000194 xmlRelaxNGDefinePtr def; /* the current define */
195
196 int nbInterleaves;
197 xmlHashTablePtr interleaves; /* keep track of all the interleaves */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000198
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000199 xmlHashTablePtr documents; /* all the documents loaded */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000200 xmlHashTablePtr includes; /* all the includes loaded */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000201 xmlChar *URL;
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000202 xmlDocPtr document;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000203
Daniel Veillard419a7682003-02-03 23:22:49 +0000204 int defNr; /* number of defines used */
205 int defMax; /* number of defines aloocated */
206 xmlRelaxNGDefinePtr *defTab; /* pointer to the allocated definitions */
207
Daniel Veillard6eadf632003-01-23 18:29:16 +0000208 const char *buffer;
209 int size;
210
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000211 /* the document stack */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000212 xmlRelaxNGDocumentPtr doc; /* Current parsed external ref */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000213 int docNr; /* Depth of the parsing stack */
214 int docMax; /* Max depth of the parsing stack */
215 xmlRelaxNGDocumentPtr *docTab; /* array of docs */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000216
217 /* the include stack */
Daniel Veillardd4310742003-02-18 21:12:46 +0000218 xmlRelaxNGIncludePtr inc; /* Current parsed include */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000219 int incNr; /* Depth of the include parsing stack */
220 int incMax; /* Max depth of the parsing stack */
221 xmlRelaxNGIncludePtr *incTab; /* array of incs */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000222};
223
224#define FLAGS_IGNORABLE 1
225#define FLAGS_NEGATIVE 2
226
227/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000228 * xmlRelaxNGInterleaveGroup:
229 *
230 * A RelaxNGs partition set associated to lists of definitions
231 */
232typedef struct _xmlRelaxNGInterleaveGroup xmlRelaxNGInterleaveGroup;
233typedef xmlRelaxNGInterleaveGroup *xmlRelaxNGInterleaveGroupPtr;
234struct _xmlRelaxNGInterleaveGroup {
235 xmlRelaxNGDefinePtr rule; /* the rule to satisfy */
236 xmlRelaxNGDefinePtr *defs; /* the array of element definitions */
Daniel Veillard44e1dd02003-02-21 23:23:28 +0000237 xmlRelaxNGDefinePtr *attrs; /* the array of attributes definitions */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000238};
239
240/**
241 * xmlRelaxNGPartitions:
242 *
243 * A RelaxNGs partition associated to an interleave group
244 */
245typedef struct _xmlRelaxNGPartition xmlRelaxNGPartition;
246typedef xmlRelaxNGPartition *xmlRelaxNGPartitionPtr;
247struct _xmlRelaxNGPartition {
248 int nbgroups; /* number of groups in the partitions */
249 xmlRelaxNGInterleaveGroupPtr *groups;
250};
251
252/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000253 * xmlRelaxNGValidState:
254 *
255 * A RelaxNGs validation state
256 */
257#define MAX_ATTR 20
258typedef struct _xmlRelaxNGValidState xmlRelaxNGValidState;
259typedef xmlRelaxNGValidState *xmlRelaxNGValidStatePtr;
260struct _xmlRelaxNGValidState {
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000261 xmlNodePtr node; /* the current node */
262 xmlNodePtr seq; /* the sequence of children left to validate */
263 int nbAttrs; /* the number of attributes */
Daniel Veillard1ed7f362003-02-03 10:57:45 +0000264 int nbAttrLeft; /* the number of attributes left to validate */
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000265 xmlChar *value; /* the value when operating on string */
266 xmlChar *endvalue; /* the end value when operating on string */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000267 xmlAttrPtr attrs[1]; /* the array of attributes */
268};
269
270/**
271 * xmlRelaxNGValidCtxt:
272 *
273 * A RelaxNGs validation context
274 */
275
276struct _xmlRelaxNGValidCtxt {
277 void *userData; /* user specific data block */
278 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
279 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
280
281 xmlRelaxNGPtr schema; /* The schema in use */
282 xmlDocPtr doc; /* the document being validated */
283 xmlRelaxNGValidStatePtr state; /* the current validation state */
284 int flags; /* validation flags */
Daniel Veillard231d7912003-02-09 14:22:17 +0000285 int depth; /* validation depth */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000286};
287
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000288/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000289 * xmlRelaxNGInclude:
290 *
291 * Structure associated to a RelaxNGs document element
292 */
293struct _xmlRelaxNGInclude {
294 xmlChar *href; /* the normalized href value */
295 xmlDocPtr doc; /* the associated XML document */
296 xmlRelaxNGDefinePtr content;/* the definitions */
297 xmlRelaxNGPtr schema; /* the schema */
298};
299
300/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000301 * xmlRelaxNGDocument:
302 *
303 * Structure associated to a RelaxNGs document element
304 */
305struct _xmlRelaxNGDocument {
306 xmlChar *href; /* the normalized href value */
307 xmlDocPtr doc; /* the associated XML document */
308 xmlRelaxNGDefinePtr content;/* the definitions */
309 xmlRelaxNGPtr schema; /* the schema */
310};
311
Daniel Veillard6eadf632003-01-23 18:29:16 +0000312/************************************************************************
313 * *
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000314 * Preliminary type checking interfaces *
315 * *
316 ************************************************************************/
317/**
318 * xmlRelaxNGTypeHave:
319 * @data: data needed for the library
320 * @type: the type name
321 * @value: the value to check
322 *
323 * Function provided by a type library to check if a type is exported
324 *
325 * Returns 1 if yes, 0 if no and -1 in case of error.
326 */
327typedef int (*xmlRelaxNGTypeHave) (void *data, const xmlChar *type);
328
329/**
330 * xmlRelaxNGTypeCheck:
331 * @data: data needed for the library
332 * @type: the type name
333 * @value: the value to check
334 *
335 * Function provided by a type library to check if a value match a type
336 *
337 * Returns 1 if yes, 0 if no and -1 in case of error.
338 */
339typedef int (*xmlRelaxNGTypeCheck) (void *data, const xmlChar *type,
340 const xmlChar *value);
341
342/**
343 * xmlRelaxNGTypeCompare:
344 * @data: data needed for the library
345 * @type: the type name
346 * @value1: the first value
347 * @value2: the second value
348 *
349 * Function provided by a type library to compare two values accordingly
350 * to a type.
351 *
352 * Returns 1 if yes, 0 if no and -1 in case of error.
353 */
354typedef int (*xmlRelaxNGTypeCompare) (void *data, const xmlChar *type,
355 const xmlChar *value1,
356 const xmlChar *value2);
357typedef struct _xmlRelaxNGTypeLibrary xmlRelaxNGTypeLibrary;
358typedef xmlRelaxNGTypeLibrary *xmlRelaxNGTypeLibraryPtr;
359struct _xmlRelaxNGTypeLibrary {
360 const xmlChar *namespace; /* the datatypeLibrary value */
361 void *data; /* data needed for the library */
362 xmlRelaxNGTypeHave have; /* the export function */
363 xmlRelaxNGTypeCheck check; /* the checking function */
364 xmlRelaxNGTypeCompare comp; /* the compare function */
365};
366
367/************************************************************************
368 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +0000369 * Allocation functions *
370 * *
371 ************************************************************************/
Daniel Veillard6eadf632003-01-23 18:29:16 +0000372static void xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar);
373static void xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define);
Daniel Veillardd2298792003-02-14 16:54:11 +0000374static void xmlRelaxNGNormExtSpace(xmlChar *value);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000375
376/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000377 * xmlRelaxNGFreeDocument:
378 * @docu: a document structure
379 *
380 * Deallocate a RelaxNG document structure.
381 */
382static void
383xmlRelaxNGFreeDocument(xmlRelaxNGDocumentPtr docu)
384{
385 if (docu == NULL)
386 return;
387
388 if (docu->href != NULL)
389 xmlFree(docu->href);
390 if (docu->doc != NULL)
391 xmlFreeDoc(docu->doc);
392 if (docu->schema != NULL)
393 xmlRelaxNGFree(docu->schema);
394 xmlFree(docu);
395}
396
397/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000398 * xmlRelaxNGFreeInclude:
399 * @incl: a include structure
400 *
401 * Deallocate a RelaxNG include structure.
402 */
403static void
404xmlRelaxNGFreeInclude(xmlRelaxNGIncludePtr incl)
405{
406 if (incl == NULL)
407 return;
408
409 if (incl->href != NULL)
410 xmlFree(incl->href);
411 if (incl->doc != NULL)
412 xmlFreeDoc(incl->doc);
413 if (incl->schema != NULL)
414 xmlRelaxNGFree(incl->schema);
415 xmlFree(incl);
416}
417
418/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000419 * xmlRelaxNGNewRelaxNG:
420 * @ctxt: a Relax-NG validation context (optional)
421 *
422 * Allocate a new RelaxNG structure.
423 *
424 * Returns the newly allocated structure or NULL in case or error
425 */
426static xmlRelaxNGPtr
427xmlRelaxNGNewRelaxNG(xmlRelaxNGParserCtxtPtr ctxt)
428{
429 xmlRelaxNGPtr ret;
430
431 ret = (xmlRelaxNGPtr) xmlMalloc(sizeof(xmlRelaxNG));
432 if (ret == NULL) {
433 if ((ctxt != NULL) && (ctxt->error != NULL))
434 ctxt->error(ctxt->userData, "Out of memory\n");
435 ctxt->nbErrors++;
436 return (NULL);
437 }
438 memset(ret, 0, sizeof(xmlRelaxNG));
439
440 return (ret);
441}
442
443/**
444 * xmlRelaxNGFree:
445 * @schema: a schema structure
446 *
447 * Deallocate a RelaxNG structure.
448 */
449void
450xmlRelaxNGFree(xmlRelaxNGPtr schema)
451{
452 if (schema == NULL)
453 return;
454
Daniel Veillard6eadf632003-01-23 18:29:16 +0000455 if (schema->topgrammar != NULL)
456 xmlRelaxNGFreeGrammar(schema->topgrammar);
457 if (schema->doc != NULL)
458 xmlFreeDoc(schema->doc);
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000459 if (schema->documents != NULL)
460 xmlHashFree(schema->documents, (xmlHashDeallocator)
461 xmlRelaxNGFreeDocument);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000462 if (schema->includes != NULL)
463 xmlHashFree(schema->includes, (xmlHashDeallocator)
464 xmlRelaxNGFreeInclude);
Daniel Veillard419a7682003-02-03 23:22:49 +0000465 if (schema->defTab != NULL) {
466 int i;
467
468 for (i = 0;i < schema->defNr;i++)
469 xmlRelaxNGFreeDefine(schema->defTab[i]);
470 xmlFree(schema->defTab);
471 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000472
473 xmlFree(schema);
474}
475
476/**
477 * xmlRelaxNGNewGrammar:
478 * @ctxt: a Relax-NG validation context (optional)
479 *
480 * Allocate a new RelaxNG grammar.
481 *
482 * Returns the newly allocated structure or NULL in case or error
483 */
484static xmlRelaxNGGrammarPtr
485xmlRelaxNGNewGrammar(xmlRelaxNGParserCtxtPtr ctxt)
486{
487 xmlRelaxNGGrammarPtr ret;
488
489 ret = (xmlRelaxNGGrammarPtr) xmlMalloc(sizeof(xmlRelaxNGGrammar));
490 if (ret == NULL) {
491 if ((ctxt != NULL) && (ctxt->error != NULL))
492 ctxt->error(ctxt->userData, "Out of memory\n");
493 ctxt->nbErrors++;
494 return (NULL);
495 }
496 memset(ret, 0, sizeof(xmlRelaxNGGrammar));
497
498 return (ret);
499}
500
501/**
502 * xmlRelaxNGFreeGrammar:
503 * @grammar: a grammar structure
504 *
505 * Deallocate a RelaxNG grammar structure.
506 */
507static void
508xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar)
509{
510 if (grammar == NULL)
511 return;
512
Daniel Veillard419a7682003-02-03 23:22:49 +0000513 if (grammar->next != NULL) {
514 xmlRelaxNGFreeGrammar(grammar->next);
515 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000516 if (grammar->refs != NULL) {
517 xmlHashFree(grammar->refs, NULL);
518 }
519 if (grammar->defs != NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000520 xmlHashFree(grammar->defs, NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000521 }
522
523 xmlFree(grammar);
524}
525
526/**
527 * xmlRelaxNGNewDefine:
528 * @ctxt: a Relax-NG validation context
529 * @node: the node in the input document.
530 *
531 * Allocate a new RelaxNG define.
532 *
533 * Returns the newly allocated structure or NULL in case or error
534 */
535static xmlRelaxNGDefinePtr
536xmlRelaxNGNewDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
537{
538 xmlRelaxNGDefinePtr ret;
539
Daniel Veillard419a7682003-02-03 23:22:49 +0000540 if (ctxt->defMax == 0) {
541 ctxt->defMax = 16;
542 ctxt->defNr = 0;
543 ctxt->defTab = (xmlRelaxNGDefinePtr *)
544 xmlMalloc(ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
545 if (ctxt->defTab == NULL) {
546 if ((ctxt != NULL) && (ctxt->error != NULL))
547 ctxt->error(ctxt->userData, "Out of memory\n");
548 ctxt->nbErrors++;
549 return (NULL);
550 }
551 } else if (ctxt->defMax <= ctxt->defNr) {
552 xmlRelaxNGDefinePtr *tmp;
553 ctxt->defMax *= 2;
554 tmp = (xmlRelaxNGDefinePtr *) xmlRealloc(ctxt->defTab,
555 ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
556 if (tmp == NULL) {
557 if ((ctxt != NULL) && (ctxt->error != NULL))
558 ctxt->error(ctxt->userData, "Out of memory\n");
559 ctxt->nbErrors++;
560 return (NULL);
561 }
562 ctxt->defTab = tmp;
563 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000564 ret = (xmlRelaxNGDefinePtr) xmlMalloc(sizeof(xmlRelaxNGDefine));
565 if (ret == NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000566 if ((ctxt != NULL) && (ctxt->error != NULL))
567 ctxt->error(ctxt->userData, "Out of memory\n");
Daniel Veillard6eadf632003-01-23 18:29:16 +0000568 ctxt->nbErrors++;
Daniel Veillard419a7682003-02-03 23:22:49 +0000569 return(NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000570 }
571 memset(ret, 0, sizeof(xmlRelaxNGDefine));
Daniel Veillard419a7682003-02-03 23:22:49 +0000572 ctxt->defTab[ctxt->defNr++] = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000573 ret->node = node;
Daniel Veillardd4310742003-02-18 21:12:46 +0000574 ret->depth = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000575 return (ret);
576}
577
578/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000579 * xmlRelaxNGFreePartition:
580 * @partitions: a partition set structure
581 *
582 * Deallocate RelaxNG partition set structures.
583 */
584static void
585xmlRelaxNGFreePartition(xmlRelaxNGPartitionPtr partitions) {
586 xmlRelaxNGInterleaveGroupPtr group;
587 int j;
588
589 if (partitions != NULL) {
590 if (partitions->groups != NULL) {
591 for (j = 0;j < partitions->nbgroups;j++) {
592 group = partitions->groups[j];
593 if (group != NULL) {
594 if (group->defs != NULL)
595 xmlFree(group->defs);
Daniel Veillard44e1dd02003-02-21 23:23:28 +0000596 if (group->attrs != NULL)
597 xmlFree(group->attrs);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000598 xmlFree(group);
599 }
600 }
601 xmlFree(partitions->groups);
602 }
603 xmlFree(partitions);
604 }
605}
606/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000607 * xmlRelaxNGFreeDefine:
608 * @define: a define structure
609 *
610 * Deallocate a RelaxNG define structure.
611 */
612static void
613xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define)
614{
615 if (define == NULL)
616 return;
617
Daniel Veillard419a7682003-02-03 23:22:49 +0000618 if ((define->data != NULL) &&
619 (define->type == XML_RELAXNG_INTERLEAVE))
620 xmlRelaxNGFreePartition((xmlRelaxNGPartitionPtr) define->data);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000621 if (define->name != NULL)
622 xmlFree(define->name);
623 if (define->ns != NULL)
624 xmlFree(define->ns);
Daniel Veillardedc91922003-01-26 00:52:04 +0000625 if (define->value != NULL)
626 xmlFree(define->value);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000627 xmlFree(define);
628}
629
630/**
631 * xmlRelaxNGNewValidState:
632 * @ctxt: a Relax-NG validation context
633 * @node: the current node or NULL for the document
634 *
635 * Allocate a new RelaxNG validation state
636 *
637 * Returns the newly allocated structure or NULL in case or error
638 */
639static xmlRelaxNGValidStatePtr
640xmlRelaxNGNewValidState(xmlRelaxNGValidCtxtPtr ctxt, xmlNodePtr node)
641{
642 xmlRelaxNGValidStatePtr ret;
643 xmlAttrPtr attr;
644 xmlAttrPtr attrs[MAX_ATTR];
645 int nbAttrs = 0;
646 xmlNodePtr root = NULL;
647
648 if (node == NULL) {
649 root = xmlDocGetRootElement(ctxt->doc);
650 if (root == NULL)
651 return(NULL);
652 } else {
653 attr = node->properties;
654 while (attr != NULL) {
655 if (nbAttrs < MAX_ATTR)
656 attrs[nbAttrs++] = attr;
657 else
658 nbAttrs++;
659 attr = attr->next;
660 }
661 }
662
663 if (nbAttrs < MAX_ATTR)
664 attrs[nbAttrs] = NULL;
665 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(sizeof(xmlRelaxNGValidState) +
666 nbAttrs * sizeof(xmlAttrPtr));
667 if (ret == NULL) {
668 if ((ctxt != NULL) && (ctxt->error != NULL))
669 ctxt->error(ctxt->userData, "Out of memory\n");
670 return (NULL);
671 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +0000672 ret->value = NULL;
673 ret->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000674 if (node == NULL) {
675 ret->node = (xmlNodePtr) ctxt->doc;
676 ret->seq = root;
677 ret->nbAttrs = 0;
678 } else {
679 ret->node = node;
680 ret->seq = node->children;
681 ret->nbAttrs = nbAttrs;
682 if (nbAttrs > 0) {
683 if (nbAttrs < MAX_ATTR) {
684 memcpy(&(ret->attrs[0]), attrs,
685 sizeof(xmlAttrPtr) * (nbAttrs + 1));
686 } else {
687 attr = node->properties;
688 nbAttrs = 0;
689 while (attr != NULL) {
690 ret->attrs[nbAttrs++] = attr;
691 attr = attr->next;
692 }
693 ret->attrs[nbAttrs] = NULL;
694 }
695 }
696 }
Daniel Veillard1ed7f362003-02-03 10:57:45 +0000697 ret->nbAttrLeft = ret->nbAttrs;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000698 return (ret);
699}
700
701/**
702 * xmlRelaxNGCopyValidState:
703 * @ctxt: a Relax-NG validation context
704 * @state: a validation state
705 *
706 * Copy the validation state
707 *
708 * Returns the newly allocated structure or NULL in case or error
709 */
710static xmlRelaxNGValidStatePtr
711xmlRelaxNGCopyValidState(xmlRelaxNGValidCtxtPtr ctxt,
712 xmlRelaxNGValidStatePtr state)
713{
714 xmlRelaxNGValidStatePtr ret;
715 unsigned int size;
716
717 if (state == NULL)
718 return(NULL);
719
720 size = sizeof(xmlRelaxNGValidState) +
721 state->nbAttrs * sizeof(xmlAttrPtr);
722 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(size);
723 if (ret == NULL) {
724 if ((ctxt != NULL) && (ctxt->error != NULL))
725 ctxt->error(ctxt->userData, "Out of memory\n");
726 return (NULL);
727 }
728 memcpy(ret, state, size);
729 return(ret);
730}
731
732/**
Daniel Veillardce14fa52003-02-19 17:32:48 +0000733 * xmlRelaxNGEqualValidState:
734 * @ctxt: a Relax-NG validation context
735 * @state1: a validation state
736 * @state2: a validation state
737 *
738 * Compare the validation states for equality
739 *
740 * Returns 1 if equald, 0 otherwise
741 */
742static int
743xmlRelaxNGEqualValidState(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
744 xmlRelaxNGValidStatePtr state1,
745 xmlRelaxNGValidStatePtr state2)
746{
747 int i;
748
749 if ((state1 == NULL) || (state2 == NULL))
750 return(0);
751 if (state1 == state2)
752 return(1);
753 if (state1->node != state2->node)
754 return(0);
755 if (state1->seq != state2->seq)
756 return(0);
757 if (state1->nbAttrLeft != state2->nbAttrLeft)
758 return(0);
759 if (state1->nbAttrs != state2->nbAttrs)
760 return(0);
761 if (state1->endvalue != state2->endvalue)
762 return(0);
763 if ((state1->value != state2->value) &&
764 (!xmlStrEqual(state1->value, state2->value)))
765 return(0);
766 for (i = 0;i < state1->nbAttrs;i++) {
767 if (state1->attrs[i] != state2->attrs[i])
768 return(0);
769 }
770 return(1);
771}
772
773/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000774 * xmlRelaxNGFreeValidState:
775 * @state: a validation state structure
776 *
777 * Deallocate a RelaxNG validation state structure.
778 */
779static void
780xmlRelaxNGFreeValidState(xmlRelaxNGValidStatePtr state)
781{
782 if (state == NULL)
783 return;
784
785 xmlFree(state);
786}
787
788/************************************************************************
789 * *
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000790 * Document functions *
791 * *
792 ************************************************************************/
793static xmlDocPtr xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt,
794 xmlDocPtr doc);
795
796/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000797 * xmlRelaxNGIncludePush:
798 * @ctxt: the parser context
799 * @value: the element doc
800 *
801 * Pushes a new include on top of the include stack
802 *
803 * Returns 0 in case of error, the index in the stack otherwise
804 */
805static int
806xmlRelaxNGIncludePush(xmlRelaxNGParserCtxtPtr ctxt,
807 xmlRelaxNGIncludePtr value)
808{
809 if (ctxt->incTab == NULL) {
810 ctxt->incMax = 4;
811 ctxt->incNr = 0;
812 ctxt->incTab = (xmlRelaxNGIncludePtr *) xmlMalloc(
813 ctxt->incMax * sizeof(ctxt->incTab[0]));
814 if (ctxt->incTab == NULL) {
815 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
816 return (0);
817 }
818 }
819 if (ctxt->incNr >= ctxt->incMax) {
820 ctxt->incMax *= 2;
821 ctxt->incTab =
822 (xmlRelaxNGIncludePtr *) xmlRealloc(ctxt->incTab,
823 ctxt->incMax *
824 sizeof(ctxt->incTab[0]));
825 if (ctxt->incTab == NULL) {
826 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
827 return (0);
828 }
829 }
830 ctxt->incTab[ctxt->incNr] = value;
831 ctxt->inc = value;
832 return (ctxt->incNr++);
833}
834
835/**
836 * xmlRelaxNGIncludePop:
837 * @ctxt: the parser context
838 *
839 * Pops the top include from the include stack
840 *
841 * Returns the include just removed
842 */
843static xmlRelaxNGIncludePtr
844xmlRelaxNGIncludePop(xmlRelaxNGParserCtxtPtr ctxt)
845{
846 xmlRelaxNGIncludePtr ret;
847
848 if (ctxt->incNr <= 0)
849 return (0);
850 ctxt->incNr--;
851 if (ctxt->incNr > 0)
852 ctxt->inc = ctxt->incTab[ctxt->incNr - 1];
853 else
854 ctxt->inc = NULL;
855 ret = ctxt->incTab[ctxt->incNr];
856 ctxt->incTab[ctxt->incNr] = 0;
857 return (ret);
858}
859
860/**
861 * xmlRelaxNGLoadInclude:
862 * @ctxt: the parser context
863 * @URL: the normalized URL
864 * @node: the include node.
Daniel Veillard416589a2003-02-17 17:25:42 +0000865 * @ns: the namespace passed from the context.
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000866 *
867 * First lookup if the document is already loaded into the parser context,
868 * check against recursion. If not found the resource is loaded and
869 * the content is preprocessed before being returned back to the caller.
870 *
871 * Returns the xmlRelaxNGIncludePtr or NULL in case of error
872 */
873static xmlRelaxNGIncludePtr
874xmlRelaxNGLoadInclude(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillard416589a2003-02-17 17:25:42 +0000875 xmlNodePtr node, const xmlChar *ns) {
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000876 xmlRelaxNGIncludePtr ret = NULL;
877 xmlDocPtr doc;
878 int i;
879 xmlNodePtr root, tmp, tmp2, cur;
880
881 /*
882 * check against recursion in the stack
883 */
884 for (i = 0;i < ctxt->incNr;i++) {
885 if (xmlStrEqual(ctxt->incTab[i]->href, URL)) {
886 if (ctxt->error != NULL)
887 ctxt->error(ctxt->userData,
888 "Detected an externalRef recursion for %s\n",
889 URL);
890 ctxt->nbErrors++;
891 return(NULL);
892 }
893 }
894
895 /*
896 * Lookup in the hash table
897 */
898 if (ctxt->includes == NULL) {
899 ctxt->includes = xmlHashCreate(10);
900 if (ctxt->includes == NULL) {
901 if (ctxt->error != NULL)
902 ctxt->error(ctxt->userData,
903 "Failed to allocate hash table for document\n");
904 ctxt->nbErrors++;
905 return(NULL);
906 }
907 } else {
Daniel Veillard416589a2003-02-17 17:25:42 +0000908 if (ns == NULL)
909 ret = xmlHashLookup2(ctxt->includes, BAD_CAST "", URL);
910 else
911 ret = xmlHashLookup2(ctxt->includes, ns, URL);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000912 if (ret != NULL)
913 return(ret);
914 }
915
916
917 /*
918 * load the document
919 */
920 doc = xmlParseFile((const char *) URL);
921 if (doc == NULL) {
922 if (ctxt->error != NULL)
923 ctxt->error(ctxt->userData,
924 "xmlRelaxNG: could not load %s\n", URL);
925 ctxt->nbErrors++;
926 return (NULL);
927 }
928
929 /*
930 * Allocate the document structures and register it first.
931 */
932 ret = (xmlRelaxNGIncludePtr) xmlMalloc(sizeof(xmlRelaxNGInclude));
933 if (ret == NULL) {
934 if (ctxt->error != NULL)
935 ctxt->error(ctxt->userData,
936 "xmlRelaxNG: allocate memory for doc %s\n", URL);
937 ctxt->nbErrors++;
938 xmlFreeDoc(doc);
939 return (NULL);
940 }
941 memset(ret, 0, sizeof(xmlRelaxNGInclude));
942 ret->doc = doc;
943 ret->href = xmlStrdup(URL);
944
945 /*
Daniel Veillard416589a2003-02-17 17:25:42 +0000946 * transmit the ns if needed
947 */
948 if (ns != NULL) {
949 root = xmlDocGetRootElement(doc);
950 if (root != NULL) {
951 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
952 xmlSetProp(root, BAD_CAST"ns", ns);
953 }
954 }
955 }
956
957 /*
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000958 * push it on the stack and register it in the hash table
959 */
Daniel Veillard416589a2003-02-17 17:25:42 +0000960 if (ns == NULL)
961 xmlHashAddEntry2(ctxt->includes, BAD_CAST "", URL, ret);
962 else
963 xmlHashAddEntry2(ctxt->includes, ns, URL, ret);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000964 xmlRelaxNGIncludePush(ctxt, ret);
965
966 /*
967 * Some preprocessing of the document content, this include recursing
968 * in the include stack.
969 */
970 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
971 if (doc == NULL) {
972 /* xmlFreeDoc(ctxt->include); */
973 ctxt->inc = NULL;
974 return(NULL);
975 }
976
977 /*
978 * Pop up the include from the stack
979 */
980 xmlRelaxNGIncludePop(ctxt);
981
982 /*
983 * Check that the top element is a grammar
984 */
985 root = xmlDocGetRootElement(doc);
986 if (root == NULL) {
987 if (ctxt->error != NULL)
988 ctxt->error(ctxt->userData,
989 "xmlRelaxNG: included document is empty %s\n", URL);
990 ctxt->nbErrors++;
991 xmlFreeDoc(doc);
992 return (NULL);
993 }
994 if (!IS_RELAXNG(root, "grammar")) {
995 if (ctxt->error != NULL)
996 ctxt->error(ctxt->userData,
997 "xmlRelaxNG: included document %s root is not a grammar\n",
998 URL);
999 ctxt->nbErrors++;
1000 xmlFreeDoc(doc);
1001 return (NULL);
1002 }
1003
1004 /*
1005 * Elimination of redefined rules in the include.
1006 */
1007 cur = node->children;
1008 while (cur != NULL) {
1009 if (IS_RELAXNG(cur, "start")) {
1010 int found = 0;
1011
1012 tmp = root->children;
1013 while (tmp != NULL) {
1014 tmp2 = tmp->next;
1015 if (IS_RELAXNG(tmp, "start")) {
1016 found = 1;
1017 xmlUnlinkNode(tmp);
1018 xmlFreeNode(tmp);
1019 }
1020 tmp = tmp2;
1021 }
1022 if (!found) {
1023 if (ctxt->error != NULL)
1024 ctxt->error(ctxt->userData,
1025 "xmlRelaxNG: include %s has a start but not the included grammar\n",
1026 URL);
1027 ctxt->nbErrors++;
1028 }
1029 } else if (IS_RELAXNG(cur, "define")) {
1030 xmlChar *name, *name2;
1031
1032 name = xmlGetProp(cur, BAD_CAST "name");
1033 if (name == NULL) {
1034 if (ctxt->error != NULL)
1035 ctxt->error(ctxt->userData,
1036 "xmlRelaxNG: include %s has define without name\n",
1037 URL);
1038 ctxt->nbErrors++;
1039 } else {
1040 int found = 0;
1041
Daniel Veillardd2298792003-02-14 16:54:11 +00001042 xmlRelaxNGNormExtSpace(name);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001043 tmp = root->children;
1044 while (tmp != NULL) {
1045 tmp2 = tmp->next;
1046 if (IS_RELAXNG(tmp, "define")) {
1047 name2 = xmlGetProp(tmp, BAD_CAST "name");
Daniel Veillardd2298792003-02-14 16:54:11 +00001048 xmlRelaxNGNormExtSpace(name2);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001049 if (name2 != NULL) {
1050 if (xmlStrEqual(name, name2)) {
1051 found = 1;
1052 xmlUnlinkNode(tmp);
1053 xmlFreeNode(tmp);
1054 }
1055 xmlFree(name2);
1056 }
1057 }
1058 tmp = tmp2;
1059 }
1060 if (!found) {
1061 if (ctxt->error != NULL)
1062 ctxt->error(ctxt->userData,
1063 "xmlRelaxNG: include %s has a define %s but not the included grammar\n",
1064 URL, name);
1065 ctxt->nbErrors++;
1066 }
1067 xmlFree(name);
1068 }
1069 }
1070 cur = cur->next;
1071 }
1072
1073
1074 return(ret);
1075}
1076
1077/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001078 * xmlRelaxNGDocumentPush:
1079 * @ctxt: the parser context
1080 * @value: the element doc
1081 *
1082 * Pushes a new doc on top of the doc stack
1083 *
1084 * Returns 0 in case of error, the index in the stack otherwise
1085 */
1086static int
1087xmlRelaxNGDocumentPush(xmlRelaxNGParserCtxtPtr ctxt,
1088 xmlRelaxNGDocumentPtr value)
1089{
1090 if (ctxt->docTab == NULL) {
1091 ctxt->docMax = 4;
1092 ctxt->docNr = 0;
1093 ctxt->docTab = (xmlRelaxNGDocumentPtr *) xmlMalloc(
1094 ctxt->docMax * sizeof(ctxt->docTab[0]));
1095 if (ctxt->docTab == NULL) {
1096 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
1097 return (0);
1098 }
1099 }
1100 if (ctxt->docNr >= ctxt->docMax) {
1101 ctxt->docMax *= 2;
1102 ctxt->docTab =
1103 (xmlRelaxNGDocumentPtr *) xmlRealloc(ctxt->docTab,
1104 ctxt->docMax *
1105 sizeof(ctxt->docTab[0]));
1106 if (ctxt->docTab == NULL) {
1107 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
1108 return (0);
1109 }
1110 }
1111 ctxt->docTab[ctxt->docNr] = value;
1112 ctxt->doc = value;
1113 return (ctxt->docNr++);
1114}
1115
1116/**
1117 * xmlRelaxNGDocumentPop:
1118 * @ctxt: the parser context
1119 *
1120 * Pops the top doc from the doc stack
1121 *
1122 * Returns the doc just removed
1123 */
1124static xmlRelaxNGDocumentPtr
1125xmlRelaxNGDocumentPop(xmlRelaxNGParserCtxtPtr ctxt)
1126{
1127 xmlRelaxNGDocumentPtr ret;
1128
1129 if (ctxt->docNr <= 0)
1130 return (0);
1131 ctxt->docNr--;
1132 if (ctxt->docNr > 0)
1133 ctxt->doc = ctxt->docTab[ctxt->docNr - 1];
1134 else
1135 ctxt->doc = NULL;
1136 ret = ctxt->docTab[ctxt->docNr];
1137 ctxt->docTab[ctxt->docNr] = 0;
1138 return (ret);
1139}
1140
1141/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001142 * xmlRelaxNGLoadExternalRef:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001143 * @ctxt: the parser context
1144 * @URL: the normalized URL
1145 * @ns: the inherited ns if any
1146 *
1147 * First lookup if the document is already loaded into the parser context,
1148 * check against recursion. If not found the resource is loaded and
1149 * the content is preprocessed before being returned back to the caller.
1150 *
1151 * Returns the xmlRelaxNGDocumentPtr or NULL in case of error
1152 */
1153static xmlRelaxNGDocumentPtr
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001154xmlRelaxNGLoadExternalRef(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001155 const xmlChar *ns) {
1156 xmlRelaxNGDocumentPtr ret = NULL;
1157 xmlDocPtr doc;
1158 xmlNodePtr root;
1159 int i;
1160
1161 /*
1162 * check against recursion in the stack
1163 */
1164 for (i = 0;i < ctxt->docNr;i++) {
1165 if (xmlStrEqual(ctxt->docTab[i]->href, URL)) {
1166 if (ctxt->error != NULL)
1167 ctxt->error(ctxt->userData,
1168 "Detected an externalRef recursion for %s\n",
1169 URL);
1170 ctxt->nbErrors++;
1171 return(NULL);
1172 }
1173 }
1174
1175 /*
1176 * Lookup in the hash table
1177 */
1178 if (ctxt->documents == NULL) {
1179 ctxt->documents = xmlHashCreate(10);
1180 if (ctxt->documents == NULL) {
1181 if (ctxt->error != NULL)
1182 ctxt->error(ctxt->userData,
1183 "Failed to allocate hash table for document\n");
1184 ctxt->nbErrors++;
1185 return(NULL);
1186 }
1187 } else {
1188 if (ns == NULL)
1189 ret = xmlHashLookup2(ctxt->documents, BAD_CAST "", URL);
1190 else
1191 ret = xmlHashLookup2(ctxt->documents, ns, URL);
1192 if (ret != NULL)
1193 return(ret);
1194 }
1195
1196
1197 /*
1198 * load the document
1199 */
1200 doc = xmlParseFile((const char *) URL);
1201 if (doc == NULL) {
1202 if (ctxt->error != NULL)
1203 ctxt->error(ctxt->userData,
1204 "xmlRelaxNG: could not load %s\n", URL);
1205 ctxt->nbErrors++;
1206 return (NULL);
1207 }
1208
1209 /*
1210 * Allocate the document structures and register it first.
1211 */
1212 ret = (xmlRelaxNGDocumentPtr) xmlMalloc(sizeof(xmlRelaxNGDocument));
1213 if (ret == NULL) {
1214 if (ctxt->error != NULL)
1215 ctxt->error(ctxt->userData,
1216 "xmlRelaxNG: allocate memory for doc %s\n", URL);
1217 ctxt->nbErrors++;
1218 xmlFreeDoc(doc);
1219 return (NULL);
1220 }
1221 memset(ret, 0, sizeof(xmlRelaxNGDocument));
1222 ret->doc = doc;
1223 ret->href = xmlStrdup(URL);
1224
1225 /*
1226 * transmit the ns if needed
1227 */
1228 if (ns != NULL) {
1229 root = xmlDocGetRootElement(doc);
1230 if (root != NULL) {
1231 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
1232 xmlSetProp(root, BAD_CAST"ns", ns);
1233 }
1234 }
1235 }
1236
1237 /*
1238 * push it on the stack and register it in the hash table
1239 */
1240 if (ns == NULL)
1241 xmlHashAddEntry2(ctxt->documents, BAD_CAST "", URL, ret);
1242 else
1243 xmlHashAddEntry2(ctxt->documents, ns, URL, ret);
1244 xmlRelaxNGDocumentPush(ctxt, ret);
1245
1246 /*
1247 * Some preprocessing of the document content
1248 */
1249 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
1250 if (doc == NULL) {
1251 xmlFreeDoc(ctxt->document);
1252 ctxt->doc = NULL;
1253 return(NULL);
1254 }
1255
1256 xmlRelaxNGDocumentPop(ctxt);
1257
1258 return(ret);
1259}
1260
1261/************************************************************************
1262 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +00001263 * Error functions *
1264 * *
1265 ************************************************************************/
1266
1267#define VALID_CTXT() \
Daniel Veillard231d7912003-02-09 14:22:17 +00001268 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1269 xmlGenericError(xmlGenericErrorContext, \
Daniel Veillard6eadf632003-01-23 18:29:16 +00001270 "error detected at %s:%d\n", \
1271 __FILE__, __LINE__);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001272
1273#define VALID_ERROR(a) \
Daniel Veillard231d7912003-02-09 14:22:17 +00001274 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001275 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a)
1276#define VALID_ERROR2(a, b) \
1277 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1278 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a, b)
1279#define VALID_ERROR3(a, b, c) \
1280 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1281 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a, b, c)
Daniel Veillard6eadf632003-01-23 18:29:16 +00001282
Daniel Veillardd2298792003-02-14 16:54:11 +00001283#ifdef DEBUG
Daniel Veillard231d7912003-02-09 14:22:17 +00001284static const char *
1285xmlRelaxNGDefName(xmlRelaxNGDefinePtr def) {
1286 if (def == NULL)
1287 return("none");
1288 switch(def->type) {
1289 case XML_RELAXNG_EMPTY: return("empty");
1290 case XML_RELAXNG_NOT_ALLOWED: return("notAllowed");
1291 case XML_RELAXNG_EXCEPT: return("except");
1292 case XML_RELAXNG_TEXT: return("text");
1293 case XML_RELAXNG_ELEMENT: return("element");
1294 case XML_RELAXNG_DATATYPE: return("datatype");
1295 case XML_RELAXNG_VALUE: return("value");
1296 case XML_RELAXNG_LIST: return("list");
1297 case XML_RELAXNG_ATTRIBUTE: return("attribute");
1298 case XML_RELAXNG_DEF: return("def");
1299 case XML_RELAXNG_REF: return("ref");
1300 case XML_RELAXNG_EXTERNALREF: return("externalRef");
1301 case XML_RELAXNG_PARENTREF: return("parentRef");
1302 case XML_RELAXNG_OPTIONAL: return("optional");
1303 case XML_RELAXNG_ZEROORMORE: return("zeroOrMore");
1304 case XML_RELAXNG_ONEORMORE: return("oneOrMore");
1305 case XML_RELAXNG_CHOICE: return("choice");
1306 case XML_RELAXNG_GROUP: return("group");
1307 case XML_RELAXNG_INTERLEAVE: return("interleave");
1308 case XML_RELAXNG_START: return("start");
1309 }
1310 return("unknown");
1311}
Daniel Veillardd2298792003-02-14 16:54:11 +00001312#endif
1313
Daniel Veillard6eadf632003-01-23 18:29:16 +00001314#if 0
1315/**
1316 * xmlRelaxNGErrorContext:
1317 * @ctxt: the parsing context
1318 * @schema: the schema being built
1319 * @node: the node being processed
1320 * @child: the child being processed
1321 *
1322 * Dump a RelaxNGType structure
1323 */
1324static void
1325xmlRelaxNGErrorContext(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGPtr schema,
1326 xmlNodePtr node, xmlNodePtr child)
1327{
1328 int line = 0;
1329 const xmlChar *file = NULL;
1330 const xmlChar *name = NULL;
1331 const char *type = "error";
1332
1333 if ((ctxt == NULL) || (ctxt->error == NULL))
1334 return;
1335
1336 if (child != NULL)
1337 node = child;
1338
1339 if (node != NULL) {
1340 if ((node->type == XML_DOCUMENT_NODE) ||
1341 (node->type == XML_HTML_DOCUMENT_NODE)) {
1342 xmlDocPtr doc = (xmlDocPtr) node;
1343
1344 file = doc->URL;
1345 } else {
1346 /*
1347 * Try to find contextual informations to report
1348 */
1349 if (node->type == XML_ELEMENT_NODE) {
1350 line = (int) node->content;
1351 } else if ((node->prev != NULL) &&
1352 (node->prev->type == XML_ELEMENT_NODE)) {
1353 line = (int) node->prev->content;
1354 } else if ((node->parent != NULL) &&
1355 (node->parent->type == XML_ELEMENT_NODE)) {
1356 line = (int) node->parent->content;
1357 }
1358 if ((node->doc != NULL) && (node->doc->URL != NULL))
1359 file = node->doc->URL;
1360 if (node->name != NULL)
1361 name = node->name;
1362 }
1363 }
1364
1365 if (ctxt != NULL)
1366 type = "compilation error";
1367 else if (schema != NULL)
1368 type = "runtime error";
1369
1370 if ((file != NULL) && (line != 0) && (name != NULL))
1371 ctxt->error(ctxt->userData, "%s: file %s line %d element %s\n",
1372 type, file, line, name);
1373 else if ((file != NULL) && (name != NULL))
1374 ctxt->error(ctxt->userData, "%s: file %s element %s\n",
1375 type, file, name);
1376 else if ((file != NULL) && (line != 0))
1377 ctxt->error(ctxt->userData, "%s: file %s line %d\n", type, file, line);
1378 else if (file != NULL)
1379 ctxt->error(ctxt->userData, "%s: file %s\n", type, file);
1380 else if (name != NULL)
1381 ctxt->error(ctxt->userData, "%s: element %s\n", type, name);
1382 else
1383 ctxt->error(ctxt->userData, "%s\n", type);
1384}
1385#endif
1386
1387/************************************************************************
1388 * *
1389 * Type library hooks *
1390 * *
1391 ************************************************************************/
Daniel Veillardea3f3982003-01-26 19:45:18 +00001392static xmlChar *xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt,
1393 const xmlChar *str);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001394
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001395/**
1396 * xmlRelaxNGSchemaTypeHave:
1397 * @data: data needed for the library
1398 * @type: the type name
1399 *
1400 * Check if the given type is provided by
1401 * the W3C XMLSchema Datatype library.
1402 *
1403 * Returns 1 if yes, 0 if no and -1 in case of error.
1404 */
1405static int
1406xmlRelaxNGSchemaTypeHave(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001407 const xmlChar *type) {
1408 xmlSchemaTypePtr typ;
1409
1410 if (type == NULL)
1411 return(-1);
1412 typ = xmlSchemaGetPredefinedType(type,
1413 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1414 if (typ == NULL)
1415 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001416 return(1);
1417}
1418
1419/**
1420 * xmlRelaxNGSchemaTypeCheck:
1421 * @data: data needed for the library
1422 * @type: the type name
1423 * @value: the value to check
1424 *
1425 * Check if the given type and value are validated by
1426 * the W3C XMLSchema Datatype library.
1427 *
1428 * Returns 1 if yes, 0 if no and -1 in case of error.
1429 */
1430static int
1431xmlRelaxNGSchemaTypeCheck(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001432 const xmlChar *type,
1433 const xmlChar *value) {
1434 xmlSchemaTypePtr typ;
1435 int ret;
1436
1437 /*
1438 * TODO: the type should be cached ab provided back, interface subject
1439 * to changes.
1440 * TODO: handle facets, may require an additional interface and keep
1441 * the value returned from the validation.
1442 */
1443 if ((type == NULL) || (value == NULL))
1444 return(-1);
1445 typ = xmlSchemaGetPredefinedType(type,
1446 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1447 if (typ == NULL)
1448 return(-1);
1449 ret = xmlSchemaValidatePredefinedType(typ, value, NULL);
1450 if (ret == 0)
1451 return(1);
1452 if (ret > 0)
1453 return(0);
1454 return(-1);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001455}
1456
1457/**
1458 * xmlRelaxNGSchemaTypeCompare:
1459 * @data: data needed for the library
1460 * @type: the type name
1461 * @value1: the first value
1462 * @value2: the second value
1463 *
1464 * Compare two values accordingly a type from the W3C XMLSchema
1465 * Datatype library.
1466 *
1467 * Returns 1 if yes, 0 if no and -1 in case of error.
1468 */
1469static int
1470xmlRelaxNGSchemaTypeCompare(void *data ATTRIBUTE_UNUSED,
1471 const xmlChar *type ATTRIBUTE_UNUSED,
1472 const xmlChar *value1 ATTRIBUTE_UNUSED,
1473 const xmlChar *value2 ATTRIBUTE_UNUSED) {
1474 TODO
1475 return(1);
1476}
1477
1478/**
1479 * xmlRelaxNGDefaultTypeHave:
1480 * @data: data needed for the library
1481 * @type: the type name
1482 *
1483 * Check if the given type is provided by
1484 * the default datatype library.
1485 *
1486 * Returns 1 if yes, 0 if no and -1 in case of error.
1487 */
1488static int
1489xmlRelaxNGDefaultTypeHave(void *data ATTRIBUTE_UNUSED, const xmlChar *type) {
1490 if (type == NULL)
1491 return(-1);
1492 if (xmlStrEqual(type, BAD_CAST "string"))
1493 return(1);
1494 if (xmlStrEqual(type, BAD_CAST "token"))
1495 return(1);
1496 return(0);
1497}
1498
1499/**
1500 * xmlRelaxNGDefaultTypeCheck:
1501 * @data: data needed for the library
1502 * @type: the type name
1503 * @value: the value to check
1504 *
1505 * Check if the given type and value are validated by
1506 * the default datatype library.
1507 *
1508 * Returns 1 if yes, 0 if no and -1 in case of error.
1509 */
1510static int
1511xmlRelaxNGDefaultTypeCheck(void *data ATTRIBUTE_UNUSED,
1512 const xmlChar *type ATTRIBUTE_UNUSED,
1513 const xmlChar *value ATTRIBUTE_UNUSED) {
Daniel Veillardd4310742003-02-18 21:12:46 +00001514 if (value == NULL)
1515 return(-1);
1516 if (xmlStrEqual(type, BAD_CAST "string"))
1517 return(1);
1518 if (xmlStrEqual(type, BAD_CAST "token")) {
1519#if 0
1520 const xmlChar *cur = value;
1521
1522 while (*cur != 0) {
1523 if (!IS_BLANK(*cur))
1524 return(1);
1525 cur++;
1526 }
1527#endif
1528 return(1);
1529 }
1530
1531 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001532}
1533
1534/**
1535 * xmlRelaxNGDefaultTypeCompare:
1536 * @data: data needed for the library
1537 * @type: the type name
1538 * @value1: the first value
1539 * @value2: the second value
1540 *
1541 * Compare two values accordingly a type from the default
1542 * datatype library.
1543 *
1544 * Returns 1 if yes, 0 if no and -1 in case of error.
1545 */
1546static int
1547xmlRelaxNGDefaultTypeCompare(void *data ATTRIBUTE_UNUSED,
1548 const xmlChar *type ATTRIBUTE_UNUSED,
1549 const xmlChar *value1 ATTRIBUTE_UNUSED,
1550 const xmlChar *value2 ATTRIBUTE_UNUSED) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00001551 int ret = -1;
1552
1553 if (xmlStrEqual(type, BAD_CAST "string")) {
1554 ret = xmlStrEqual(value1, value2);
1555 } else if (xmlStrEqual(type, BAD_CAST "token")) {
1556 if (!xmlStrEqual(value1, value2)) {
1557 xmlChar *nval, *nvalue;
1558
1559 /*
1560 * TODO: trivial optimizations are possible by
1561 * computing at compile-time
1562 */
1563 nval = xmlRelaxNGNormalize(NULL, value1);
1564 nvalue = xmlRelaxNGNormalize(NULL, value2);
1565
Daniel Veillardd4310742003-02-18 21:12:46 +00001566 if ((nval == NULL) || (nvalue == NULL))
Daniel Veillardea3f3982003-01-26 19:45:18 +00001567 ret = -1;
Daniel Veillardd4310742003-02-18 21:12:46 +00001568 else if (xmlStrEqual(nval, nvalue))
1569 ret = 1;
1570 else
1571 ret = 0;
Daniel Veillardea3f3982003-01-26 19:45:18 +00001572 if (nval != NULL)
1573 xmlFree(nval);
1574 if (nvalue != NULL)
1575 xmlFree(nvalue);
Daniel Veillardd4310742003-02-18 21:12:46 +00001576 } else
1577 ret = 1;
Daniel Veillardea3f3982003-01-26 19:45:18 +00001578 }
1579 return(ret);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001580}
1581
1582static int xmlRelaxNGTypeInitialized = 0;
1583static xmlHashTablePtr xmlRelaxNGRegisteredTypes = NULL;
1584
1585/**
1586 * xmlRelaxNGFreeTypeLibrary:
1587 * @lib: the type library structure
1588 * @namespace: the URI bound to the library
1589 *
1590 * Free the structure associated to the type library
1591 */
Daniel Veillard6eadf632003-01-23 18:29:16 +00001592static void
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001593xmlRelaxNGFreeTypeLibrary(xmlRelaxNGTypeLibraryPtr lib,
1594 const xmlChar *namespace ATTRIBUTE_UNUSED) {
1595 if (lib == NULL)
1596 return;
1597 if (lib->namespace != NULL)
1598 xmlFree((xmlChar *)lib->namespace);
1599 xmlFree(lib);
1600}
1601
1602/**
1603 * xmlRelaxNGRegisterTypeLibrary:
1604 * @namespace: the URI bound to the library
1605 * @data: data associated to the library
1606 * @have: the provide function
1607 * @check: the checking function
1608 * @comp: the comparison function
1609 *
1610 * Register a new type library
1611 *
1612 * Returns 0 in case of success and -1 in case of error.
1613 */
1614static int
1615xmlRelaxNGRegisterTypeLibrary(const xmlChar *namespace, void *data,
1616 xmlRelaxNGTypeHave have, xmlRelaxNGTypeCheck check,
1617 xmlRelaxNGTypeCompare comp) {
1618 xmlRelaxNGTypeLibraryPtr lib;
1619 int ret;
1620
1621 if ((xmlRelaxNGRegisteredTypes == NULL) || (namespace == NULL) ||
1622 (check == NULL) || (comp == NULL))
1623 return(-1);
1624 if (xmlHashLookup(xmlRelaxNGRegisteredTypes, namespace) != NULL) {
1625 xmlGenericError(xmlGenericErrorContext,
1626 "Relax-NG types library '%s' already registered\n",
1627 namespace);
1628 return(-1);
1629 }
1630 lib = (xmlRelaxNGTypeLibraryPtr) xmlMalloc(sizeof(xmlRelaxNGTypeLibrary));
1631 if (lib == NULL) {
1632 xmlGenericError(xmlGenericErrorContext,
1633 "Relax-NG types library '%s' malloc() failed\n",
1634 namespace);
1635 return (-1);
1636 }
1637 memset(lib, 0, sizeof(xmlRelaxNGTypeLibrary));
1638 lib->namespace = xmlStrdup(namespace);
1639 lib->data = data;
1640 lib->have = have;
1641 lib->comp = comp;
1642 lib->check = check;
1643 ret = xmlHashAddEntry(xmlRelaxNGRegisteredTypes, namespace, lib);
1644 if (ret < 0) {
1645 xmlGenericError(xmlGenericErrorContext,
1646 "Relax-NG types library failed to register '%s'\n",
1647 namespace);
1648 xmlRelaxNGFreeTypeLibrary(lib, namespace);
1649 return(-1);
1650 }
1651 return(0);
1652}
1653
1654/**
1655 * xmlRelaxNGInitTypes:
1656 *
1657 * Initilize the default type libraries.
1658 *
1659 * Returns 0 in case of success and -1 in case of error.
1660 */
1661static int
Daniel Veillard6eadf632003-01-23 18:29:16 +00001662xmlRelaxNGInitTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001663 if (xmlRelaxNGTypeInitialized != 0)
1664 return(0);
1665 xmlRelaxNGRegisteredTypes = xmlHashCreate(10);
1666 if (xmlRelaxNGRegisteredTypes == NULL) {
1667 xmlGenericError(xmlGenericErrorContext,
1668 "Failed to allocate sh table for Relax-NG types\n");
1669 return(-1);
1670 }
1671 xmlRelaxNGRegisterTypeLibrary(
1672 BAD_CAST "http://www.w3.org/2001/XMLSchema-datatypes",
1673 NULL,
1674 xmlRelaxNGSchemaTypeHave,
1675 xmlRelaxNGSchemaTypeCheck,
1676 xmlRelaxNGSchemaTypeCompare);
1677 xmlRelaxNGRegisterTypeLibrary(
1678 xmlRelaxNGNs,
1679 NULL,
1680 xmlRelaxNGDefaultTypeHave,
1681 xmlRelaxNGDefaultTypeCheck,
1682 xmlRelaxNGDefaultTypeCompare);
1683 xmlRelaxNGTypeInitialized = 1;
1684 return(0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001685}
1686
1687/**
1688 * xmlRelaxNGCleanupTypes:
1689 *
1690 * Cleanup the default Schemas type library associated to RelaxNG
1691 */
1692void
1693xmlRelaxNGCleanupTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001694 if (xmlRelaxNGTypeInitialized == 0)
1695 return;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001696 xmlSchemaCleanupTypes();
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001697 xmlHashFree(xmlRelaxNGRegisteredTypes, (xmlHashDeallocator)
1698 xmlRelaxNGFreeTypeLibrary);
1699 xmlRelaxNGTypeInitialized = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001700}
1701
1702/************************************************************************
1703 * *
1704 * Parsing functions *
1705 * *
1706 ************************************************************************/
1707
1708static xmlRelaxNGDefinePtr xmlRelaxNGParseAttribute(
1709 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1710static xmlRelaxNGDefinePtr xmlRelaxNGParseElement(
1711 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1712static xmlRelaxNGDefinePtr xmlRelaxNGParsePatterns(
Daniel Veillard154877e2003-01-30 12:17:05 +00001713 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes, int group);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001714static xmlRelaxNGDefinePtr xmlRelaxNGParsePattern(
1715 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001716static xmlRelaxNGPtr xmlRelaxNGParseDocument(
1717 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00001718static int xmlRelaxNGParseGrammarContent(
1719 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00001720static xmlRelaxNGDefinePtr xmlRelaxNGParseNameClass(
1721 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
1722 xmlRelaxNGDefinePtr def);
Daniel Veillard419a7682003-02-03 23:22:49 +00001723static xmlRelaxNGGrammarPtr xmlRelaxNGParseGrammar(
1724 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001725
1726
1727#define IS_BLANK_NODE(n) \
1728 (((n)->type == XML_TEXT_NODE) && (xmlRelaxNGIsBlank((n)->content)))
1729
1730/**
1731 * xmlRelaxNGIsBlank:
1732 * @str: a string
1733 *
1734 * Check if a string is ignorable c.f. 4.2. Whitespace
1735 *
1736 * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
1737 */
1738static int
1739xmlRelaxNGIsBlank(xmlChar *str) {
1740 if (str == NULL)
1741 return(1);
1742 while (*str != 0) {
1743 if (!(IS_BLANK(*str))) return(0);
1744 str++;
1745 }
1746 return(1);
1747}
1748
Daniel Veillard6eadf632003-01-23 18:29:16 +00001749/**
1750 * xmlRelaxNGGetDataTypeLibrary:
1751 * @ctxt: a Relax-NG parser context
1752 * @node: the current data or value element
1753 *
1754 * Applies algorithm from 4.3. datatypeLibrary attribute
1755 *
1756 * Returns the datatypeLibary value or NULL if not found
1757 */
1758static xmlChar *
1759xmlRelaxNGGetDataTypeLibrary(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1760 xmlNodePtr node) {
1761 xmlChar *ret, *escape;
1762
Daniel Veillard6eadf632003-01-23 18:29:16 +00001763 if ((IS_RELAXNG(node, "data")) || (IS_RELAXNG(node, "value"))) {
1764 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1765 if (ret != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001766 if (ret[0] == 0) {
1767 xmlFree(ret);
1768 return(NULL);
1769 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001770 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +00001771 if (escape == NULL) {
1772 return(ret);
1773 }
1774 xmlFree(ret);
1775 return(escape);
1776 }
1777 }
1778 node = node->parent;
1779 while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) {
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001780 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1781 if (ret != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001782 if (ret[0] == 0) {
1783 xmlFree(ret);
1784 return(NULL);
1785 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001786 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
1787 if (escape == NULL) {
1788 return(ret);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001789 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001790 xmlFree(ret);
1791 return(escape);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001792 }
1793 node = node->parent;
1794 }
1795 return(NULL);
1796}
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001797
1798/**
Daniel Veillardedc91922003-01-26 00:52:04 +00001799 * xmlRelaxNGParseValue:
1800 * @ctxt: a Relax-NG parser context
1801 * @node: the data node.
1802 *
1803 * parse the content of a RelaxNG value node.
1804 *
1805 * Returns the definition pointer or NULL in case of error
1806 */
1807static xmlRelaxNGDefinePtr
1808xmlRelaxNGParseValue(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1809 xmlRelaxNGDefinePtr def = NULL;
1810 xmlRelaxNGTypeLibraryPtr lib;
1811 xmlChar *type;
1812 xmlChar *library;
1813 int tmp;
1814
1815 def = xmlRelaxNGNewDefine(ctxt, node);
1816 if (def == NULL)
1817 return(NULL);
1818 def->type = XML_RELAXNG_VALUE;
1819
1820 type = xmlGetProp(node, BAD_CAST "type");
1821 if (type != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001822 xmlRelaxNGNormExtSpace(type);
1823 if (xmlValidateNCName(type, 0)) {
1824 if (ctxt->error != NULL)
1825 ctxt->error(ctxt->userData,
1826 "value type '%s' is not an NCName\n",
1827 type);
1828 ctxt->nbErrors++;
1829 }
Daniel Veillardedc91922003-01-26 00:52:04 +00001830 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1831 if (library == NULL)
1832 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1833
1834 def->name = type;
1835 def->ns = library;
1836
1837 lib = (xmlRelaxNGTypeLibraryPtr)
1838 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1839 if (lib == NULL) {
1840 if (ctxt->error != NULL)
1841 ctxt->error(ctxt->userData,
1842 "Use of unregistered type library '%s'\n",
1843 library);
1844 ctxt->nbErrors++;
1845 def->data = NULL;
1846 } else {
1847 def->data = lib;
1848 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001849 if (ctxt->error != NULL)
1850 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00001851 "Internal error with type library '%s': no 'have'\n",
1852 library);
1853 ctxt->nbErrors++;
1854 } else {
1855 tmp = lib->have(lib->data, def->name);
1856 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001857 if (ctxt->error != NULL)
1858 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00001859 "Error type '%s' is not exported by type library '%s'\n",
1860 def->name, library);
1861 ctxt->nbErrors++;
1862 }
1863 }
1864 }
1865 }
1866 if (node->children == NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00001867 def->value = xmlStrdup(BAD_CAST "");
Daniel Veillardedc91922003-01-26 00:52:04 +00001868 } else if ((node->children->type != XML_TEXT_NODE) ||
1869 (node->children->next != NULL)) {
1870 if (ctxt->error != NULL)
1871 ctxt->error(ctxt->userData,
1872 "Expecting a single text value for <value>content\n");
1873 ctxt->nbErrors++;
1874 } else {
1875 def->value = xmlNodeGetContent(node);
1876 if (def->value == NULL) {
1877 if (ctxt->error != NULL)
1878 ctxt->error(ctxt->userData,
1879 "Element <value> has no content\n");
1880 ctxt->nbErrors++;
1881 }
1882 }
1883 /* TODO check ahead of time that the value is okay per the type */
1884 return(def);
1885}
1886
1887/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001888 * xmlRelaxNGParseData:
1889 * @ctxt: a Relax-NG parser context
1890 * @node: the data node.
1891 *
1892 * parse the content of a RelaxNG data node.
1893 *
1894 * Returns the definition pointer or NULL in case of error
1895 */
1896static xmlRelaxNGDefinePtr
1897xmlRelaxNGParseData(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
Daniel Veillard416589a2003-02-17 17:25:42 +00001898 xmlRelaxNGDefinePtr def = NULL, except, last = NULL;
Daniel Veillard8fe98712003-02-19 00:19:14 +00001899 xmlRelaxNGDefinePtr param, lastparam = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001900 xmlRelaxNGTypeLibraryPtr lib;
1901 xmlChar *type;
1902 xmlChar *library;
1903 xmlNodePtr content;
1904 int tmp;
1905
1906 type = xmlGetProp(node, BAD_CAST "type");
1907 if (type == NULL) {
1908 if (ctxt->error != NULL)
1909 ctxt->error(ctxt->userData,
1910 "data has no type\n");
1911 ctxt->nbErrors++;
1912 return(NULL);
1913 }
Daniel Veillardd2298792003-02-14 16:54:11 +00001914 xmlRelaxNGNormExtSpace(type);
1915 if (xmlValidateNCName(type, 0)) {
1916 if (ctxt->error != NULL)
1917 ctxt->error(ctxt->userData,
1918 "data type '%s' is not an NCName\n",
1919 type);
1920 ctxt->nbErrors++;
1921 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001922 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1923 if (library == NULL)
1924 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1925
1926 def = xmlRelaxNGNewDefine(ctxt, node);
1927 if (def == NULL) {
1928 xmlFree(type);
1929 return(NULL);
1930 }
1931 def->type = XML_RELAXNG_DATATYPE;
1932 def->name = type;
1933 def->ns = library;
1934
1935 lib = (xmlRelaxNGTypeLibraryPtr)
1936 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1937 if (lib == NULL) {
1938 if (ctxt->error != NULL)
1939 ctxt->error(ctxt->userData,
1940 "Use of unregistered type library '%s'\n",
1941 library);
1942 ctxt->nbErrors++;
1943 def->data = NULL;
1944 } else {
1945 def->data = lib;
1946 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001947 if (ctxt->error != NULL)
1948 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001949 "Internal error with type library '%s': no 'have'\n",
1950 library);
1951 ctxt->nbErrors++;
1952 } else {
1953 tmp = lib->have(lib->data, def->name);
1954 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001955 if (ctxt->error != NULL)
1956 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001957 "Error type '%s' is not exported by type library '%s'\n",
1958 def->name, library);
1959 ctxt->nbErrors++;
1960 }
1961 }
1962 }
1963 content = node->children;
Daniel Veillard416589a2003-02-17 17:25:42 +00001964
1965 /*
1966 * Handle optional params
1967 */
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001968 while (content != NULL) {
Daniel Veillard416589a2003-02-17 17:25:42 +00001969 if (!xmlStrEqual(content->name, BAD_CAST "param"))
1970 break;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00001971 if (xmlStrEqual(library,
1972 BAD_CAST"http://relaxng.org/ns/structure/1.0")) {
1973 if (ctxt->error != NULL)
1974 ctxt->error(ctxt->userData,
1975 "Type library '%s' does not allow type parameters\n",
1976 library);
1977 ctxt->nbErrors++;
1978 content = content->next;
1979 while ((content != NULL) &&
1980 (xmlStrEqual(content->name, BAD_CAST "param")))
1981 content = content->next;
1982 } else {
1983 param = xmlRelaxNGNewDefine(ctxt, node);
1984 if (param != NULL) {
1985 param->type = XML_RELAXNG_PARAM;
1986 param->name = xmlGetProp(content, BAD_CAST "name");
1987 if (param->name == NULL) {
1988 if (ctxt->error != NULL)
1989 ctxt->error(ctxt->userData,
1990 "param has no name\n");
1991 ctxt->nbErrors++;
1992 }
1993 param->value = xmlNodeGetContent(content);
1994 if (lastparam == NULL) {
1995 def->attrs = lastparam = param;
1996 } else {
1997 lastparam->next = param;
1998 lastparam = param;
1999 }
Daniel Veillard8fe98712003-02-19 00:19:14 +00002000 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00002001 content = content->next;
Daniel Veillard8fe98712003-02-19 00:19:14 +00002002 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002003 }
Daniel Veillard416589a2003-02-17 17:25:42 +00002004 /*
2005 * Handle optional except
2006 */
2007 if ((content != NULL) && (xmlStrEqual(content->name, BAD_CAST "except"))) {
2008 xmlNodePtr child;
2009 xmlRelaxNGDefinePtr tmp2, last2 = NULL;
2010
2011 except = xmlRelaxNGNewDefine(ctxt, node);
2012 if (except == NULL) {
2013 return(def);
2014 }
2015 except->type = XML_RELAXNG_EXCEPT;
2016 child = content->children;
2017 if (last == NULL) {
2018 def->content = except;
2019 } else {
2020 last->next = except;
2021 }
2022 if (child == NULL) {
2023 if (ctxt->error != NULL)
2024 ctxt->error(ctxt->userData,
2025 "except has no content\n");
2026 ctxt->nbErrors++;
2027 }
2028 while (child != NULL) {
2029 tmp2 = xmlRelaxNGParsePattern(ctxt, child);
2030 if (tmp2 != NULL) {
2031 if (last2 == NULL) {
2032 except->content = last2 = tmp2;
2033 } else {
2034 last2->next = tmp2;
2035 last2 = tmp2;
2036 }
2037 }
2038 child = child->next;
2039 }
2040 content = content->next;
2041 }
2042 /*
2043 * Check there is no unhandled data
2044 */
2045 if (content != NULL) {
2046 if (ctxt->error != NULL)
2047 ctxt->error(ctxt->userData,
2048 "Element data has unexpected content %s\n", content->name);
2049 ctxt->nbErrors++;
2050 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002051
2052 return(def);
2053}
2054
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002055/**
2056 * xmlRelaxNGCompareElemDefLists:
2057 * @ctxt: a Relax-NG parser context
2058 * @defs1: the first list of element defs
2059 * @defs2: the second list of element defs
2060 *
2061 * Compare the 2 lists of element definitions. The comparison is
2062 * that if both lists do not accept the same QNames, it returns 1
2063 * If the 2 lists can accept the same QName the comparison returns 0
2064 *
2065 * Returns 1 disttinct, 0 if equal
2066 */
2067static int
2068xmlRelaxNGCompareElemDefLists(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
2069 xmlRelaxNGDefinePtr *def1,
2070 xmlRelaxNGDefinePtr *def2) {
2071 xmlRelaxNGDefinePtr *basedef2 = def2;
2072
Daniel Veillard154877e2003-01-30 12:17:05 +00002073 if ((def1 == NULL) || (def2 == NULL))
2074 return(1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002075 if ((*def1 == NULL) || (*def2 == NULL))
2076 return(1);
2077 while (*def1 != NULL) {
2078 while ((*def2) != NULL) {
2079 if ((*def1)->name == NULL) {
2080 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
2081 return(0);
2082 } else if ((*def2)->name == NULL) {
2083 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
2084 return(0);
2085 } else if (xmlStrEqual((*def1)->name, (*def2)->name)) {
2086 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
2087 return(0);
2088 }
2089 def2++;
2090 }
2091 def2 = basedef2;
2092 def1++;
2093 }
2094 return(1);
2095}
2096
2097/**
2098 * xmlRelaxNGGetElements:
2099 * @ctxt: a Relax-NG parser context
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002100 * @def: the definition definition
2101 * @eora: gather elements (0) or attributes (1)
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002102 *
2103 * Compute the list of top elements a definition can generate
2104 *
2105 * Returns a list of elements or NULL if none was found.
2106 */
2107static xmlRelaxNGDefinePtr *
2108xmlRelaxNGGetElements(xmlRelaxNGParserCtxtPtr ctxt,
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002109 xmlRelaxNGDefinePtr def,
2110 int eora) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002111 xmlRelaxNGDefinePtr *ret = NULL, parent, cur, tmp;
2112 int len = 0;
2113 int max = 0;
2114
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002115 /*
2116 * Don't run that check in case of error. Infinite recursion
2117 * becomes possible.
2118 */
2119 if (ctxt->nbErrors != 0)
2120 return(NULL);
2121
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002122 parent = NULL;
2123 cur = def;
2124 while (cur != NULL) {
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002125 if (((eora == 0) && ((cur->type == XML_RELAXNG_ELEMENT) ||
2126 (cur->type == XML_RELAXNG_TEXT))) ||
2127 ((eora == 1) && (cur->type == XML_RELAXNG_ATTRIBUTE))) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002128 if (ret == NULL) {
2129 max = 10;
2130 ret = (xmlRelaxNGDefinePtr *)
2131 xmlMalloc((max + 1) * sizeof(xmlRelaxNGDefinePtr));
2132 if (ret == NULL) {
2133 if (ctxt->error != NULL)
2134 ctxt->error(ctxt->userData,
2135 "Out of memory in element search\n");
2136 ctxt->nbErrors++;
2137 return(NULL);
2138 }
2139 } else if (max <= len) {
2140 max *= 2;
2141 ret = xmlRealloc(ret, (max + 1) * sizeof(xmlRelaxNGDefinePtr));
2142 if (ret == NULL) {
2143 if (ctxt->error != NULL)
2144 ctxt->error(ctxt->userData,
2145 "Out of memory in element search\n");
2146 ctxt->nbErrors++;
2147 return(NULL);
2148 }
2149 }
Daniel Veillardb08c9812003-01-28 23:09:49 +00002150 ret[len++] = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002151 ret[len] = NULL;
2152 } else if ((cur->type == XML_RELAXNG_CHOICE) ||
2153 (cur->type == XML_RELAXNG_INTERLEAVE) ||
2154 (cur->type == XML_RELAXNG_GROUP) ||
2155 (cur->type == XML_RELAXNG_ONEORMORE) ||
Daniel Veillardb08c9812003-01-28 23:09:49 +00002156 (cur->type == XML_RELAXNG_ZEROORMORE) ||
2157 (cur->type == XML_RELAXNG_OPTIONAL) ||
2158 (cur->type == XML_RELAXNG_REF) ||
2159 (cur->type == XML_RELAXNG_DEF)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002160 /*
2161 * Don't go within elements or attributes or string values.
2162 * Just gather the element top list
2163 */
2164 if (cur->content != NULL) {
2165 parent = cur;
2166 cur = cur->content;
2167 tmp = cur;
2168 while (tmp != NULL) {
2169 tmp->parent = parent;
2170 tmp = tmp->next;
2171 }
2172 continue;
2173 }
2174 }
Daniel Veillard154877e2003-01-30 12:17:05 +00002175 if (cur == def)
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002176 break;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002177 if (cur->next != NULL) {
2178 cur = cur->next;
2179 continue;
2180 }
2181 do {
2182 cur = cur->parent;
2183 if (cur == NULL) break;
2184 if (cur == def) return(ret);
2185 if (cur->next != NULL) {
2186 cur = cur->next;
2187 break;
2188 }
2189 } while (cur != NULL);
2190 }
2191 return(ret);
2192}
2193
2194/**
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002195 * xmlRelaxNGCheckGroupAttrs:
2196 * @ctxt: a Relax-NG parser context
2197 * @def: the group definition
2198 *
2199 * Detects violations of rule 7.3
2200 */
2201static void
2202xmlRelaxNGCheckGroupAttrs(xmlRelaxNGParserCtxtPtr ctxt,
2203 xmlRelaxNGDefinePtr def) {
2204 xmlRelaxNGDefinePtr **list;
2205 xmlRelaxNGDefinePtr cur;
2206 int nbchild = 0, i, j, ret;
2207
2208 if ((def == NULL) ||
2209 ((def->type != XML_RELAXNG_GROUP) &&
2210 (def->type != XML_RELAXNG_ELEMENT)))
2211 return;
2212
2213 /*
2214 * Don't run that check in case of error. Infinite recursion
2215 * becomes possible.
2216 */
2217 if (ctxt->nbErrors != 0)
2218 return;
2219
2220 cur = def->attrs;
2221 while (cur != NULL) {
2222 nbchild++;
2223 cur = cur->next;
2224 }
2225 cur = def->content;
2226 while (cur != NULL) {
2227 nbchild++;
2228 cur = cur->next;
2229 }
2230
2231 list = (xmlRelaxNGDefinePtr **) xmlMalloc(nbchild *
2232 sizeof(xmlRelaxNGDefinePtr *));
2233 if (list == NULL) {
2234 if (ctxt->error != NULL)
2235 ctxt->error(ctxt->userData,
2236 "Out of memory in group computation\n");
2237 ctxt->nbErrors++;
2238 return;
2239 }
2240 i = 0;
2241 cur = def->attrs;
2242 while (cur != NULL) {
2243 list[i] = xmlRelaxNGGetElements(ctxt, cur, 1);
2244 i++;
2245 cur = cur->next;
2246 }
2247 cur = def->content;
2248 while (cur != NULL) {
2249 list[i] = xmlRelaxNGGetElements(ctxt, cur, 1);
2250 i++;
2251 cur = cur->next;
2252 }
2253
2254 for (i = 0;i < nbchild;i++) {
2255 if (list[i] == NULL)
2256 continue;
2257 for (j = 0;j < i;j++) {
2258 if (list[j] == NULL)
2259 continue;
2260 ret = xmlRelaxNGCompareElemDefLists(ctxt, list[i], list[j]);
2261 if (ret == 0) {
2262 if (ctxt->error != NULL)
2263 ctxt->error(ctxt->userData,
2264 "Attributes conflicts in group\n");
2265 ctxt->nbErrors++;
2266 }
2267 }
2268 }
2269 for (i = 0;i < nbchild;i++) {
2270 if (list[i] != NULL)
2271 xmlFree(list[i]);
2272 }
2273 xmlFree(list);
2274}
2275
2276/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002277 * xmlRelaxNGComputeInterleaves:
2278 * @def: the interleave definition
2279 * @ctxt: a Relax-NG parser context
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002280 * @name: the definition name
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002281 *
2282 * A lot of work for preprocessing interleave definitions
2283 * is potentially needed to get a decent execution speed at runtime
2284 * - trying to get a total order on the element nodes generated
2285 * by the interleaves, order the list of interleave definitions
2286 * following that order.
2287 * - if <text/> is used to handle mixed content, it is better to
2288 * flag this in the define and simplify the runtime checking
2289 * algorithm
2290 */
2291static void
2292xmlRelaxNGComputeInterleaves(xmlRelaxNGDefinePtr def,
2293 xmlRelaxNGParserCtxtPtr ctxt,
2294 xmlChar *name ATTRIBUTE_UNUSED) {
2295 xmlRelaxNGDefinePtr cur;
2296
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002297 xmlRelaxNGPartitionPtr partitions = NULL;
2298 xmlRelaxNGInterleaveGroupPtr *groups = NULL;
2299 xmlRelaxNGInterleaveGroupPtr group;
2300 int i,j,ret;
2301 int nbgroups = 0;
2302 int nbchild = 0;
2303
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002304 /*
2305 * Don't run that check in case of error. Infinite recursion
2306 * becomes possible.
2307 */
2308 if (ctxt->nbErrors != 0)
2309 return;
2310
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002311#ifdef DEBUG_INTERLEAVE
2312 xmlGenericError(xmlGenericErrorContext,
2313 "xmlRelaxNGComputeInterleaves(%s)\n",
2314 name);
2315#endif
2316 cur = def->content;
2317 while (cur != NULL) {
2318 nbchild++;
2319 cur = cur->next;
2320 }
2321
2322#ifdef DEBUG_INTERLEAVE
2323 xmlGenericError(xmlGenericErrorContext, " %d child\n", nbchild);
2324#endif
2325 groups = (xmlRelaxNGInterleaveGroupPtr *)
2326 xmlMalloc(nbchild * sizeof(xmlRelaxNGInterleaveGroupPtr));
2327 if (groups == NULL)
2328 goto error;
2329 cur = def->content;
2330 while (cur != NULL) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002331 groups[nbgroups] = (xmlRelaxNGInterleaveGroupPtr)
2332 xmlMalloc(sizeof(xmlRelaxNGInterleaveGroup));
2333 if (groups[nbgroups] == NULL)
2334 goto error;
2335 groups[nbgroups]->rule = cur;
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002336 groups[nbgroups]->defs = xmlRelaxNGGetElements(ctxt, cur, 0);
2337 groups[nbgroups]->attrs = xmlRelaxNGGetElements(ctxt, cur, 1);
Daniel Veillard154877e2003-01-30 12:17:05 +00002338 nbgroups++;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002339 cur = cur->next;
2340 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002341#ifdef DEBUG_INTERLEAVE
2342 xmlGenericError(xmlGenericErrorContext, " %d groups\n", nbgroups);
2343#endif
2344
2345 /*
2346 * Let's check that all rules makes a partitions according to 7.4
2347 */
2348 partitions = (xmlRelaxNGPartitionPtr)
2349 xmlMalloc(sizeof(xmlRelaxNGPartition));
2350 if (partitions == NULL)
2351 goto error;
2352 partitions->nbgroups = nbgroups;
2353 for (i = 0;i < nbgroups;i++) {
2354 group = groups[i];
2355 for (j = i+1;j < nbgroups;j++) {
2356 if (groups[j] == NULL)
2357 continue;
2358 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->defs,
2359 groups[j]->defs);
2360 if (ret == 0) {
2361 if (ctxt->error != NULL)
2362 ctxt->error(ctxt->userData,
2363 "Element or text conflicts in interleave\n");
2364 ctxt->nbErrors++;
2365 }
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002366 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->attrs,
2367 groups[j]->attrs);
2368 if (ret == 0) {
2369 if (ctxt->error != NULL)
2370 ctxt->error(ctxt->userData,
2371 "Attributes conflicts in interleave\n");
2372 ctxt->nbErrors++;
2373 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002374 }
2375 }
2376 partitions->groups = groups;
2377
2378 /*
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002379 * and save the partition list back in the def
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002380 */
2381 def->data = partitions;
2382 return;
2383
2384error:
2385 if (ctxt->error != NULL)
2386 ctxt->error(ctxt->userData,
2387 "Out of memory in interleave computation\n");
2388 ctxt->nbErrors++;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002389 if (groups != NULL) {
2390 for (i = 0;i < nbgroups;i++)
2391 if (groups[i] != NULL) {
2392 if (groups[i]->defs != NULL)
2393 xmlFree(groups[i]->defs);
2394 xmlFree(groups[i]);
2395 }
2396 xmlFree(groups);
2397 }
2398 xmlRelaxNGFreePartition(partitions);
2399}
2400
2401/**
2402 * xmlRelaxNGParseInterleave:
2403 * @ctxt: a Relax-NG parser context
2404 * @node: the data node.
2405 *
2406 * parse the content of a RelaxNG interleave node.
2407 *
2408 * Returns the definition pointer or NULL in case of error
2409 */
2410static xmlRelaxNGDefinePtr
2411xmlRelaxNGParseInterleave(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2412 xmlRelaxNGDefinePtr def = NULL;
2413 xmlRelaxNGDefinePtr last = NULL, cur;
2414 xmlNodePtr child;
2415
2416 def = xmlRelaxNGNewDefine(ctxt, node);
2417 if (def == NULL) {
2418 return(NULL);
2419 }
2420 def->type = XML_RELAXNG_INTERLEAVE;
2421
2422 if (ctxt->interleaves == NULL)
2423 ctxt->interleaves = xmlHashCreate(10);
2424 if (ctxt->interleaves == NULL) {
2425 if (ctxt->error != NULL)
2426 ctxt->error(ctxt->userData,
2427 "Failed to create interleaves hash table\n");
2428 ctxt->nbErrors++;
2429 } else {
2430 char name[32];
2431
2432 snprintf(name, 32, "interleave%d", ctxt->nbInterleaves++);
2433 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST name, def) < 0) {
2434 if (ctxt->error != NULL)
2435 ctxt->error(ctxt->userData,
2436 "Failed to add %s to hash table\n", name);
2437 ctxt->nbErrors++;
2438 }
2439 }
2440 child = node->children;
Daniel Veillardd2298792003-02-14 16:54:11 +00002441 if (child == NULL) {
2442 if (ctxt->error != NULL)
2443 ctxt->error(ctxt->userData, "Element interleave is empty\n");
2444 ctxt->nbErrors++;
2445 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002446 while (child != NULL) {
2447 if (IS_RELAXNG(child, "element")) {
2448 cur = xmlRelaxNGParseElement(ctxt, child);
2449 } else {
2450 cur = xmlRelaxNGParsePattern(ctxt, child);
2451 }
2452 if (cur != NULL) {
2453 cur->parent = def;
2454 if (last == NULL) {
2455 def->content = last = cur;
2456 } else {
2457 last->next = cur;
2458 last = cur;
2459 }
2460 }
2461 child = child->next;
2462 }
2463
2464 return(def);
2465}
Daniel Veillard6eadf632003-01-23 18:29:16 +00002466
2467/**
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002468 * xmlRelaxNGParseInclude:
2469 * @ctxt: a Relax-NG parser context
2470 * @node: the include node
2471 *
2472 * Integrate the content of an include node in the current grammar
2473 *
2474 * Returns 0 in case of success or -1 in case of error
2475 */
2476static int
2477xmlRelaxNGParseInclude(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2478 xmlRelaxNGIncludePtr incl;
2479 xmlNodePtr root;
2480 int ret = 0, tmp;
2481
2482 incl = node->_private;
2483 if (incl == NULL) {
2484 if (ctxt->error != NULL)
2485 ctxt->error(ctxt->userData,
2486 "Include node has no data\n");
2487 ctxt->nbErrors++;
2488 return(-1);
2489 }
2490 root = xmlDocGetRootElement(incl->doc);
2491 if (root == NULL) {
2492 if (ctxt->error != NULL)
2493 ctxt->error(ctxt->userData,
2494 "Include document is empty\n");
2495 ctxt->nbErrors++;
2496 return(-1);
2497 }
2498 if (!xmlStrEqual(root->name, BAD_CAST "grammar")) {
2499 if (ctxt->error != NULL)
2500 ctxt->error(ctxt->userData,
2501 "Include document root is not a grammar\n");
2502 ctxt->nbErrors++;
2503 return(-1);
2504 }
2505
2506 /*
2507 * Merge the definition from both the include and the internal list
2508 */
2509 if (root->children != NULL) {
2510 tmp = xmlRelaxNGParseGrammarContent(ctxt, root->children);
2511 if (tmp != 0)
2512 ret = -1;
2513 }
2514 if (node->children != NULL) {
2515 tmp = xmlRelaxNGParseGrammarContent(ctxt, node->children);
2516 if (tmp != 0)
2517 ret = -1;
2518 }
2519 return(ret);
2520}
2521
2522/**
Daniel Veillard276be4a2003-01-24 01:03:34 +00002523 * xmlRelaxNGParseDefine:
2524 * @ctxt: a Relax-NG parser context
2525 * @node: the define node
2526 *
2527 * parse the content of a RelaxNG define element node.
2528 *
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002529 * Returns 0 in case of success or -1 in case of error
Daniel Veillard276be4a2003-01-24 01:03:34 +00002530 */
2531static int
2532xmlRelaxNGParseDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2533 xmlChar *name;
2534 int ret = 0, tmp;
2535 xmlRelaxNGDefinePtr def;
2536 const xmlChar *olddefine;
2537
2538 name = xmlGetProp(node, BAD_CAST "name");
2539 if (name == NULL) {
2540 if (ctxt->error != NULL)
2541 ctxt->error(ctxt->userData,
2542 "define has no name\n");
2543 ctxt->nbErrors++;
2544 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00002545 xmlRelaxNGNormExtSpace(name);
2546 if (xmlValidateNCName(name, 0)) {
2547 if (ctxt->error != NULL)
2548 ctxt->error(ctxt->userData,
2549 "define name '%s' is not an NCName\n",
2550 name);
2551 ctxt->nbErrors++;
2552 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002553 def = xmlRelaxNGNewDefine(ctxt, node);
2554 if (def == NULL) {
2555 xmlFree(name);
2556 return(-1);
2557 }
2558 def->type = XML_RELAXNG_DEF;
2559 def->name = name;
2560 if (node->children == NULL) {
2561 if (ctxt->error != NULL)
2562 ctxt->error(ctxt->userData,
2563 "define has no children\n");
2564 ctxt->nbErrors++;
2565 } else {
2566 olddefine = ctxt->define;
2567 ctxt->define = name;
Daniel Veillard154877e2003-01-30 12:17:05 +00002568 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard276be4a2003-01-24 01:03:34 +00002569 ctxt->define = olddefine;
2570 }
2571 if (ctxt->grammar->defs == NULL)
2572 ctxt->grammar->defs = xmlHashCreate(10);
2573 if (ctxt->grammar->defs == NULL) {
2574 if (ctxt->error != NULL)
2575 ctxt->error(ctxt->userData,
2576 "Could not create definition hash\n");
2577 ctxt->nbErrors++;
2578 ret = -1;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002579 } else {
2580 tmp = xmlHashAddEntry(ctxt->grammar->defs, name, def);
2581 if (tmp < 0) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002582 xmlRelaxNGDefinePtr prev;
2583
2584 prev = xmlHashLookup(ctxt->grammar->defs, name);
2585 if (prev == NULL) {
2586 if (ctxt->error != NULL)
2587 ctxt->error(ctxt->userData,
2588 "Internal error on define aggregation of %s\n",
2589 name);
2590 ctxt->nbErrors++;
2591 ret = -1;
Daniel Veillard154877e2003-01-30 12:17:05 +00002592 } else {
2593 while (prev->nextHash != NULL)
2594 prev = prev->nextHash;
2595 prev->nextHash = def;
2596 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002597 }
2598 }
2599 }
2600 return(ret);
2601}
2602
2603/**
Daniel Veillardfebcca42003-02-16 15:44:18 +00002604 * xmlRelaxNGProcessExternalRef:
2605 * @ctxt: the parser context
2606 * @node: the externlRef node
2607 *
2608 * Process and compile an externlRef node
2609 *
2610 * Returns the xmlRelaxNGDefinePtr or NULL in case of error
2611 */
2612static xmlRelaxNGDefinePtr
2613xmlRelaxNGProcessExternalRef(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2614 xmlRelaxNGDocumentPtr docu;
2615 xmlNodePtr root, tmp;
2616 xmlChar *ns;
Daniel Veillard77648bb2003-02-20 15:03:22 +00002617 int newNs = 0, oldflags;
Daniel Veillardfebcca42003-02-16 15:44:18 +00002618 xmlRelaxNGDefinePtr def;
2619
2620 docu = node->_private;
2621 if (docu != NULL) {
2622 def = xmlRelaxNGNewDefine(ctxt, node);
2623 if (def == NULL)
2624 return(NULL);
2625 def->type = XML_RELAXNG_EXTERNALREF;
2626
2627 if (docu->content == NULL) {
2628 /*
2629 * Then do the parsing for good
2630 */
2631 root = xmlDocGetRootElement(docu->doc);
2632 if (root == NULL) {
2633 if (ctxt->error != NULL)
2634 ctxt->error(ctxt->userData,
2635 "xmlRelaxNGParse: %s is empty\n",
2636 ctxt->URL);
2637 ctxt->nbErrors++;
2638 return (NULL);
2639 }
2640 /*
2641 * ns transmission rules
2642 */
2643 ns = xmlGetProp(root, BAD_CAST "ns");
2644 if (ns == NULL) {
2645 tmp = node;
2646 while ((tmp != NULL) &&
2647 (tmp->type == XML_ELEMENT_NODE)) {
2648 ns = xmlGetProp(tmp, BAD_CAST "ns");
2649 if (ns != NULL) {
2650 break;
2651 }
2652 tmp = tmp->parent;
2653 }
2654 if (ns != NULL) {
2655 xmlSetProp(root, BAD_CAST "ns", ns);
2656 newNs = 1;
2657 xmlFree(ns);
2658 }
2659 } else {
2660 xmlFree(ns);
2661 }
2662
2663 /*
2664 * Parsing to get a precompiled schemas.
2665 */
Daniel Veillard77648bb2003-02-20 15:03:22 +00002666 oldflags = ctxt->flags;
2667 ctxt->flags |= XML_RELAXNG_IN_EXTERNALREF;
Daniel Veillardfebcca42003-02-16 15:44:18 +00002668 docu->schema = xmlRelaxNGParseDocument(ctxt, root);
Daniel Veillard77648bb2003-02-20 15:03:22 +00002669 ctxt->flags = oldflags;
Daniel Veillardfebcca42003-02-16 15:44:18 +00002670 if ((docu->schema != NULL) &&
2671 (docu->schema->topgrammar != NULL)) {
2672 docu->content = docu->schema->topgrammar->start;
2673 }
2674
2675 /*
2676 * the externalRef may be reused in a different ns context
2677 */
2678 if (newNs == 1) {
2679 xmlUnsetProp(root, BAD_CAST "ns");
2680 }
2681 }
2682 def->content = docu->content;
2683 } else {
2684 def = NULL;
2685 }
2686 return(def);
2687}
2688
2689/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00002690 * xmlRelaxNGParsePattern:
2691 * @ctxt: a Relax-NG parser context
2692 * @node: the pattern node.
2693 *
2694 * parse the content of a RelaxNG pattern node.
2695 *
Daniel Veillard276be4a2003-01-24 01:03:34 +00002696 * Returns the definition pointer or NULL in case of error or if no
2697 * pattern is generated.
Daniel Veillard6eadf632003-01-23 18:29:16 +00002698 */
2699static xmlRelaxNGDefinePtr
2700xmlRelaxNGParsePattern(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2701 xmlRelaxNGDefinePtr def = NULL;
2702
Daniel Veillardd2298792003-02-14 16:54:11 +00002703 if (node == NULL) {
2704 return(NULL);
2705 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002706 if (IS_RELAXNG(node, "element")) {
2707 def = xmlRelaxNGParseElement(ctxt, node);
2708 } else if (IS_RELAXNG(node, "attribute")) {
2709 def = xmlRelaxNGParseAttribute(ctxt, node);
2710 } else if (IS_RELAXNG(node, "empty")) {
2711 def = xmlRelaxNGNewDefine(ctxt, node);
2712 if (def == NULL)
2713 return(NULL);
2714 def->type = XML_RELAXNG_EMPTY;
Daniel Veillardd2298792003-02-14 16:54:11 +00002715 if (node->children != NULL) {
2716 if (ctxt->error != NULL)
2717 ctxt->error(ctxt->userData, "empty: had a child node\n");
2718 ctxt->nbErrors++;
2719 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002720 } else if (IS_RELAXNG(node, "text")) {
2721 def = xmlRelaxNGNewDefine(ctxt, node);
2722 if (def == NULL)
2723 return(NULL);
2724 def->type = XML_RELAXNG_TEXT;
2725 if (node->children != NULL) {
2726 if (ctxt->error != NULL)
2727 ctxt->error(ctxt->userData, "text: had a child node\n");
2728 ctxt->nbErrors++;
2729 }
2730 } else if (IS_RELAXNG(node, "zeroOrMore")) {
2731 def = xmlRelaxNGNewDefine(ctxt, node);
2732 if (def == NULL)
2733 return(NULL);
2734 def->type = XML_RELAXNG_ZEROORMORE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002735 if (node->children == NULL) {
2736 if (ctxt->error != NULL)
2737 ctxt->error(ctxt->userData,
2738 "Element %s is empty\n", node->name);
2739 ctxt->nbErrors++;
2740 } else {
2741 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2742 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002743 } else if (IS_RELAXNG(node, "oneOrMore")) {
2744 def = xmlRelaxNGNewDefine(ctxt, node);
2745 if (def == NULL)
2746 return(NULL);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002747 def->type = XML_RELAXNG_ONEORMORE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002748 if (node->children == NULL) {
2749 if (ctxt->error != NULL)
2750 ctxt->error(ctxt->userData,
2751 "Element %s is empty\n", node->name);
2752 ctxt->nbErrors++;
2753 } else {
2754 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2755 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002756 } else if (IS_RELAXNG(node, "optional")) {
2757 def = xmlRelaxNGNewDefine(ctxt, node);
2758 if (def == NULL)
2759 return(NULL);
2760 def->type = XML_RELAXNG_OPTIONAL;
Daniel Veillardd2298792003-02-14 16:54:11 +00002761 if (node->children == NULL) {
2762 if (ctxt->error != NULL)
2763 ctxt->error(ctxt->userData,
2764 "Element %s is empty\n", node->name);
2765 ctxt->nbErrors++;
2766 } else {
2767 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2768 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002769 } else if (IS_RELAXNG(node, "choice")) {
2770 def = xmlRelaxNGNewDefine(ctxt, node);
2771 if (def == NULL)
2772 return(NULL);
2773 def->type = XML_RELAXNG_CHOICE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002774 if (node->children == NULL) {
2775 if (ctxt->error != NULL)
2776 ctxt->error(ctxt->userData,
2777 "Element %s is empty\n", node->name);
2778 ctxt->nbErrors++;
2779 } else {
2780 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2781 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002782 } else if (IS_RELAXNG(node, "group")) {
2783 def = xmlRelaxNGNewDefine(ctxt, node);
2784 if (def == NULL)
2785 return(NULL);
2786 def->type = XML_RELAXNG_GROUP;
Daniel Veillardd2298792003-02-14 16:54:11 +00002787 if (node->children == NULL) {
2788 if (ctxt->error != NULL)
2789 ctxt->error(ctxt->userData,
2790 "Element %s is empty\n", node->name);
2791 ctxt->nbErrors++;
2792 } else {
2793 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2794 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002795 } else if (IS_RELAXNG(node, "ref")) {
2796 def = xmlRelaxNGNewDefine(ctxt, node);
2797 if (def == NULL)
2798 return(NULL);
2799 def->type = XML_RELAXNG_REF;
2800 def->name = xmlGetProp(node, BAD_CAST "name");
2801 if (def->name == NULL) {
2802 if (ctxt->error != NULL)
2803 ctxt->error(ctxt->userData,
2804 "ref has no name\n");
2805 ctxt->nbErrors++;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002806 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00002807 xmlRelaxNGNormExtSpace(def->name);
2808 if (xmlValidateNCName(def->name, 0)) {
2809 if (ctxt->error != NULL)
2810 ctxt->error(ctxt->userData,
2811 "ref name '%s' is not an NCName\n",
2812 def->name);
2813 ctxt->nbErrors++;
2814 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002815 }
2816 if (node->children != NULL) {
2817 if (ctxt->error != NULL)
2818 ctxt->error(ctxt->userData,
2819 "ref is not empty\n");
2820 ctxt->nbErrors++;
2821 }
2822 if (ctxt->grammar->refs == NULL)
2823 ctxt->grammar->refs = xmlHashCreate(10);
2824 if (ctxt->grammar->refs == NULL) {
2825 if (ctxt->error != NULL)
2826 ctxt->error(ctxt->userData,
2827 "Could not create references hash\n");
2828 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002829 def = NULL;
2830 } else {
2831 int tmp;
2832
2833 tmp = xmlHashAddEntry(ctxt->grammar->refs, def->name, def);
2834 if (tmp < 0) {
2835 xmlRelaxNGDefinePtr prev;
2836
2837 prev = (xmlRelaxNGDefinePtr)
2838 xmlHashLookup(ctxt->grammar->refs, def->name);
2839 if (prev == NULL) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00002840 if (def->name != NULL) {
2841 if (ctxt->error != NULL)
2842 ctxt->error(ctxt->userData,
2843 "Error refs definitions '%s'\n",
2844 def->name);
2845 } else {
2846 if (ctxt->error != NULL)
2847 ctxt->error(ctxt->userData,
2848 "Error refs definitions\n");
2849 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002850 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002851 def = NULL;
2852 } else {
2853 def->nextHash = prev->nextHash;
2854 prev->nextHash = def;
2855 }
2856 }
2857 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002858 } else if (IS_RELAXNG(node, "data")) {
2859 def = xmlRelaxNGParseData(ctxt, node);
Daniel Veillardd2298792003-02-14 16:54:11 +00002860#if 0
Daniel Veillard276be4a2003-01-24 01:03:34 +00002861 } else if (IS_RELAXNG(node, "define")) {
2862 xmlRelaxNGParseDefine(ctxt, node);
2863 def = NULL;
Daniel Veillardd2298792003-02-14 16:54:11 +00002864#endif
Daniel Veillardedc91922003-01-26 00:52:04 +00002865 } else if (IS_RELAXNG(node, "value")) {
2866 def = xmlRelaxNGParseValue(ctxt, node);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002867 } else if (IS_RELAXNG(node, "list")) {
2868 def = xmlRelaxNGNewDefine(ctxt, node);
2869 if (def == NULL)
2870 return(NULL);
2871 def->type = XML_RELAXNG_LIST;
Daniel Veillardd2298792003-02-14 16:54:11 +00002872 if (node->children == NULL) {
2873 if (ctxt->error != NULL)
2874 ctxt->error(ctxt->userData,
2875 "Element %s is empty\n", node->name);
2876 ctxt->nbErrors++;
2877 } else {
2878 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2879 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002880 } else if (IS_RELAXNG(node, "interleave")) {
2881 def = xmlRelaxNGParseInterleave(ctxt, node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002882 } else if (IS_RELAXNG(node, "externalRef")) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00002883 def = xmlRelaxNGProcessExternalRef(ctxt, node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002884 } else if (IS_RELAXNG(node, "notAllowed")) {
2885 def = xmlRelaxNGNewDefine(ctxt, node);
2886 if (def == NULL)
2887 return(NULL);
2888 def->type = XML_RELAXNG_NOT_ALLOWED;
2889 if (node->children != NULL) {
2890 if (ctxt->error != NULL)
2891 ctxt->error(ctxt->userData,
2892 "xmlRelaxNGParse: notAllowed element is not empty\n");
2893 ctxt->nbErrors++;
2894 }
Daniel Veillard419a7682003-02-03 23:22:49 +00002895 } else if (IS_RELAXNG(node, "grammar")) {
2896 xmlRelaxNGGrammarPtr grammar, old;
2897 xmlRelaxNGGrammarPtr oldparent;
2898
2899 oldparent = ctxt->parentgrammar;
2900 old = ctxt->grammar;
2901 ctxt->parentgrammar = old;
2902 grammar = xmlRelaxNGParseGrammar(ctxt, node->children);
2903 if (old != NULL) {
2904 ctxt->grammar = old;
2905 ctxt->parentgrammar = oldparent;
2906 if (grammar != NULL) {
2907 grammar->next = old->next;
2908 old->next = grammar;
2909 }
2910 }
2911 if (grammar != NULL)
2912 def = grammar->start;
2913 else
2914 def = NULL;
2915 } else if (IS_RELAXNG(node, "parentRef")) {
2916 if (ctxt->parentgrammar == NULL) {
2917 if (ctxt->error != NULL)
2918 ctxt->error(ctxt->userData,
2919 "Use of parentRef without a parent grammar\n");
2920 ctxt->nbErrors++;
2921 return(NULL);
2922 }
2923 def = xmlRelaxNGNewDefine(ctxt, node);
2924 if (def == NULL)
2925 return(NULL);
2926 def->type = XML_RELAXNG_PARENTREF;
2927 def->name = xmlGetProp(node, BAD_CAST "name");
2928 if (def->name == NULL) {
2929 if (ctxt->error != NULL)
2930 ctxt->error(ctxt->userData,
2931 "parentRef has no name\n");
2932 ctxt->nbErrors++;
Daniel Veillardd2298792003-02-14 16:54:11 +00002933 } else {
2934 xmlRelaxNGNormExtSpace(def->name);
2935 if (xmlValidateNCName(def->name, 0)) {
2936 if (ctxt->error != NULL)
2937 ctxt->error(ctxt->userData,
2938 "parentRef name '%s' is not an NCName\n",
2939 def->name);
2940 ctxt->nbErrors++;
2941 }
Daniel Veillard419a7682003-02-03 23:22:49 +00002942 }
2943 if (node->children != NULL) {
2944 if (ctxt->error != NULL)
2945 ctxt->error(ctxt->userData,
2946 "parentRef is not empty\n");
2947 ctxt->nbErrors++;
2948 }
2949 if (ctxt->parentgrammar->refs == NULL)
2950 ctxt->parentgrammar->refs = xmlHashCreate(10);
2951 if (ctxt->parentgrammar->refs == NULL) {
2952 if (ctxt->error != NULL)
2953 ctxt->error(ctxt->userData,
2954 "Could not create references hash\n");
2955 ctxt->nbErrors++;
2956 def = NULL;
Daniel Veillardd2298792003-02-14 16:54:11 +00002957 } else if (def->name != NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +00002958 int tmp;
2959
2960 tmp = xmlHashAddEntry(ctxt->parentgrammar->refs, def->name, def);
2961 if (tmp < 0) {
2962 xmlRelaxNGDefinePtr prev;
2963
2964 prev = (xmlRelaxNGDefinePtr)
2965 xmlHashLookup(ctxt->parentgrammar->refs, def->name);
2966 if (prev == NULL) {
2967 if (ctxt->error != NULL)
2968 ctxt->error(ctxt->userData,
2969 "Internal error parentRef definitions '%s'\n",
2970 def->name);
2971 ctxt->nbErrors++;
2972 def = NULL;
2973 } else {
2974 def->nextHash = prev->nextHash;
2975 prev->nextHash = def;
2976 }
2977 }
2978 }
Daniel Veillardd2298792003-02-14 16:54:11 +00002979 } else if (IS_RELAXNG(node, "mixed")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00002980 if (node->children == NULL) {
2981 if (ctxt->error != NULL)
2982 ctxt->error(ctxt->userData,
2983 "Mixed is empty\n");
2984 ctxt->nbErrors++;
2985 def = NULL;
Daniel Veillard416589a2003-02-17 17:25:42 +00002986 } else {
2987 def = xmlRelaxNGParseInterleave(ctxt, node);
2988 if (def != NULL) {
2989 xmlRelaxNGDefinePtr tmp;
Daniel Veillard2df2de22003-02-17 23:34:33 +00002990
2991 if ((def->content != NULL) && (def->content->next != NULL)) {
2992 tmp = xmlRelaxNGNewDefine(ctxt, node);
2993 if (tmp != NULL) {
2994 tmp->type = XML_RELAXNG_GROUP;
2995 tmp->content = def->content;
2996 def->content = tmp;
2997 }
2998 }
2999
Daniel Veillard416589a2003-02-17 17:25:42 +00003000 tmp = xmlRelaxNGNewDefine(ctxt, node);
3001 if (tmp == NULL)
3002 return(def);
3003 tmp->type = XML_RELAXNG_TEXT;
3004 tmp->next = def->content;
3005 def->content = tmp;
3006 }
3007 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003008 } else {
3009 if (ctxt->error != NULL)
3010 ctxt->error(ctxt->userData,
3011 "Unexpected node %s is not a pattern\n",
3012 node->name);
3013 ctxt->nbErrors++;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003014 def = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003015 }
3016 return(def);
3017}
3018
3019/**
3020 * xmlRelaxNGParseAttribute:
3021 * @ctxt: a Relax-NG parser context
3022 * @node: the element node
3023 *
3024 * parse the content of a RelaxNG attribute node.
3025 *
3026 * Returns the definition pointer or NULL in case of error.
3027 */
3028static xmlRelaxNGDefinePtr
3029xmlRelaxNGParseAttribute(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
Daniel Veillardd2298792003-02-14 16:54:11 +00003030 xmlRelaxNGDefinePtr ret, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003031 xmlNodePtr child;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003032 int old_flags;
3033
3034 ret = xmlRelaxNGNewDefine(ctxt, node);
3035 if (ret == NULL)
3036 return(NULL);
3037 ret->type = XML_RELAXNG_ATTRIBUTE;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003038 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003039 child = node->children;
3040 if (child == NULL) {
3041 if (ctxt->error != NULL)
3042 ctxt->error(ctxt->userData,
3043 "xmlRelaxNGParseattribute: attribute has no children\n");
3044 ctxt->nbErrors++;
3045 return(ret);
3046 }
3047 old_flags = ctxt->flags;
3048 ctxt->flags |= XML_RELAXNG_IN_ATTRIBUTE;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003049 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
3050 if (cur != NULL)
3051 child = child->next;
3052
Daniel Veillardd2298792003-02-14 16:54:11 +00003053 if (child != NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003054 cur = xmlRelaxNGParsePattern(ctxt, child);
3055 if (cur != NULL) {
3056 switch (cur->type) {
3057 case XML_RELAXNG_EMPTY:
3058 case XML_RELAXNG_NOT_ALLOWED:
3059 case XML_RELAXNG_TEXT:
3060 case XML_RELAXNG_ELEMENT:
3061 case XML_RELAXNG_DATATYPE:
3062 case XML_RELAXNG_VALUE:
3063 case XML_RELAXNG_LIST:
3064 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00003065 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003066 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00003067 case XML_RELAXNG_DEF:
3068 case XML_RELAXNG_ONEORMORE:
3069 case XML_RELAXNG_ZEROORMORE:
3070 case XML_RELAXNG_OPTIONAL:
3071 case XML_RELAXNG_CHOICE:
3072 case XML_RELAXNG_GROUP:
3073 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard77648bb2003-02-20 15:03:22 +00003074 case XML_RELAXNG_ATTRIBUTE:
Daniel Veillardd2298792003-02-14 16:54:11 +00003075 ret->content = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003076 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003077 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003078 case XML_RELAXNG_START:
Daniel Veillard8fe98712003-02-19 00:19:14 +00003079 case XML_RELAXNG_PARAM:
Daniel Veillard144fae12003-02-03 13:17:57 +00003080 case XML_RELAXNG_EXCEPT:
Daniel Veillardd2298792003-02-14 16:54:11 +00003081 if (ctxt->error != NULL)
3082 ctxt->error(ctxt->userData,
3083 "attribute has invalid content\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003084 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003085 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00003086 case XML_RELAXNG_NOOP:
3087 TODO
3088 if (ctxt->error != NULL)
3089 ctxt->error(ctxt->userData,
3090 "Internal error, noop found\n");
3091 ctxt->nbErrors++;
3092 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003093 }
3094 }
3095 child = child->next;
3096 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003097 if (child != NULL) {
3098 if (ctxt->error != NULL)
3099 ctxt->error(ctxt->userData, "attribute has multiple children\n");
3100 ctxt->nbErrors++;
3101 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003102 ctxt->flags = old_flags;
3103 return(ret);
3104}
3105
3106/**
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003107 * xmlRelaxNGParseExceptNameClass:
3108 * @ctxt: a Relax-NG parser context
3109 * @node: the except node
Daniel Veillard144fae12003-02-03 13:17:57 +00003110 * @attr: 1 if within an attribute, 0 if within an element
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003111 *
3112 * parse the content of a RelaxNG nameClass node.
3113 *
3114 * Returns the definition pointer or NULL in case of error.
3115 */
3116static xmlRelaxNGDefinePtr
Daniel Veillard144fae12003-02-03 13:17:57 +00003117xmlRelaxNGParseExceptNameClass(xmlRelaxNGParserCtxtPtr ctxt,
3118 xmlNodePtr node, int attr) {
3119 xmlRelaxNGDefinePtr ret, cur, last = NULL;
3120 xmlNodePtr child;
3121
Daniel Veillardd2298792003-02-14 16:54:11 +00003122 if (!IS_RELAXNG(node, "except")) {
3123 if (ctxt->error != NULL)
3124 ctxt->error(ctxt->userData,
3125 "Expecting an except node\n");
3126 ctxt->nbErrors++;
Daniel Veillard144fae12003-02-03 13:17:57 +00003127 return(NULL);
Daniel Veillardd2298792003-02-14 16:54:11 +00003128 }
3129 if (node->next != NULL) {
3130 if (ctxt->error != NULL)
3131 ctxt->error(ctxt->userData,
3132 "exceptNameClass allows only a single except node\n");
3133 ctxt->nbErrors++;
3134 }
Daniel Veillard144fae12003-02-03 13:17:57 +00003135 if (node->children == NULL) {
3136 if (ctxt->error != NULL)
3137 ctxt->error(ctxt->userData,
3138 "except has no content\n");
3139 ctxt->nbErrors++;
3140 return(NULL);
3141 }
3142
3143 ret = xmlRelaxNGNewDefine(ctxt, node);
3144 if (ret == NULL)
3145 return(NULL);
3146 ret->type = XML_RELAXNG_EXCEPT;
3147 child = node->children;
3148 while (child != NULL) {
3149 cur = xmlRelaxNGNewDefine(ctxt, child);
3150 if (cur == NULL)
3151 break;
3152 if (attr)
3153 cur->type = XML_RELAXNG_ATTRIBUTE;
3154 else
3155 cur->type = XML_RELAXNG_ELEMENT;
3156
Daniel Veillard419a7682003-02-03 23:22:49 +00003157 if (xmlRelaxNGParseNameClass(ctxt, child, cur) != NULL) {
Daniel Veillard144fae12003-02-03 13:17:57 +00003158 if (last == NULL) {
3159 ret->content = cur;
3160 } else {
3161 last->next = cur;
3162 }
3163 last = cur;
3164 }
3165 child = child->next;
3166 }
3167
3168 return(ret);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003169}
3170
3171/**
3172 * xmlRelaxNGParseNameClass:
3173 * @ctxt: a Relax-NG parser context
3174 * @node: the nameClass node
3175 * @def: the current definition
3176 *
3177 * parse the content of a RelaxNG nameClass node.
3178 *
3179 * Returns the definition pointer or NULL in case of error.
3180 */
3181static xmlRelaxNGDefinePtr
3182xmlRelaxNGParseNameClass(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
3183 xmlRelaxNGDefinePtr def) {
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003184 xmlRelaxNGDefinePtr ret, tmp;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003185 xmlChar *val;
3186
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003187 ret = def;
3188 if ((IS_RELAXNG(node, "name")) || (IS_RELAXNG(node, "anyName")) ||
3189 (IS_RELAXNG(node, "nsName"))) {
3190 if ((def->type != XML_RELAXNG_ELEMENT) &&
3191 (def->type != XML_RELAXNG_ATTRIBUTE)) {
3192 ret = xmlRelaxNGNewDefine(ctxt, node);
3193 if (ret == NULL)
3194 return(NULL);
3195 ret->parent = def;
3196 if (ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE)
3197 ret->type = XML_RELAXNG_ATTRIBUTE;
3198 else
3199 ret->type = XML_RELAXNG_ELEMENT;
3200 }
3201 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003202 if (IS_RELAXNG(node, "name")) {
3203 val = xmlNodeGetContent(node);
Daniel Veillardd2298792003-02-14 16:54:11 +00003204 xmlRelaxNGNormExtSpace(val);
3205 if (xmlValidateNCName(val, 0)) {
3206 if (ctxt->error != NULL) {
3207 if (node->parent != NULL)
3208 ctxt->error(ctxt->userData,
3209 "Element %s name '%s' is not an NCName\n",
3210 node->parent->name, val);
3211 else
3212 ctxt->error(ctxt->userData,
3213 "name '%s' is not an NCName\n",
3214 val);
3215 }
3216 ctxt->nbErrors++;
3217 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003218 ret->name = val;
3219 val = xmlGetProp(node, BAD_CAST "ns");
3220 ret->ns = val;
Daniel Veillard416589a2003-02-17 17:25:42 +00003221 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3222 (val != NULL) &&
3223 (xmlStrEqual(val, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
3224 ctxt->error(ctxt->userData,
3225 "Attribute with namespace '%s' is not allowed\n",
3226 val);
3227 ctxt->nbErrors++;
3228 }
3229 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3230 (val != NULL) &&
3231 (val[0] == 0) &&
3232 (xmlStrEqual(ret->name, BAD_CAST "xmlns"))) {
3233 ctxt->error(ctxt->userData,
3234 "Attribute with QName 'xmlns' is not allowed\n",
3235 val);
3236 ctxt->nbErrors++;
3237 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003238 } else if (IS_RELAXNG(node, "anyName")) {
3239 ret->name = NULL;
3240 ret->ns = NULL;
3241 if (node->children != NULL) {
3242 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00003243 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
3244 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003245 }
3246 } else if (IS_RELAXNG(node, "nsName")) {
3247 ret->name = NULL;
3248 ret->ns = xmlGetProp(node, BAD_CAST "ns");
3249 if (ret->ns == NULL) {
3250 if (ctxt->error != NULL)
3251 ctxt->error(ctxt->userData,
3252 "nsName has no ns attribute\n");
3253 ctxt->nbErrors++;
3254 }
Daniel Veillard416589a2003-02-17 17:25:42 +00003255 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3256 (ret->ns != NULL) &&
3257 (xmlStrEqual(ret->ns, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
3258 ctxt->error(ctxt->userData,
3259 "Attribute with namespace '%s' is not allowed\n",
3260 ret->ns);
3261 ctxt->nbErrors++;
3262 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003263 if (node->children != NULL) {
3264 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00003265 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
3266 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003267 }
3268 } else if (IS_RELAXNG(node, "choice")) {
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003269 xmlNodePtr child;
3270 xmlRelaxNGDefinePtr last = NULL;
3271
Daniel Veillardd4310742003-02-18 21:12:46 +00003272 ret = xmlRelaxNGNewDefine(ctxt, node);
3273 if (ret == NULL)
3274 return(NULL);
3275 ret->parent = def;
3276 ret->type = XML_RELAXNG_CHOICE;
3277
Daniel Veillardd2298792003-02-14 16:54:11 +00003278 if (node->children == NULL) {
3279 if (ctxt->error != NULL)
3280 ctxt->error(ctxt->userData,
3281 "Element choice is empty\n");
3282 ctxt->nbErrors++;
3283 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00003284
3285 child = node->children;
3286 while (child != NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00003287 tmp = xmlRelaxNGParseNameClass(ctxt, child, ret);
Daniel Veillardd2298792003-02-14 16:54:11 +00003288 if (tmp != NULL) {
3289 if (last == NULL) {
3290 last = ret->nameClass = tmp;
3291 } else {
3292 last->next = tmp;
3293 last = tmp;
3294 }
3295 }
3296 child = child->next;
3297 }
3298 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003299 } else {
3300 if (ctxt->error != NULL)
3301 ctxt->error(ctxt->userData,
3302 "expecting name, anyName, nsName or choice : got %s\n",
3303 node->name);
3304 ctxt->nbErrors++;
3305 return(NULL);
3306 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003307 if (ret != def) {
3308 if (def->nameClass == NULL) {
3309 def->nameClass = ret;
3310 } else {
3311 tmp = def->nameClass;
3312 while (tmp->next != NULL) {
3313 tmp = tmp->next;
3314 }
3315 tmp->next = ret;
3316 }
3317 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003318 return(ret);
3319}
3320
3321/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00003322 * xmlRelaxNGParseElement:
3323 * @ctxt: a Relax-NG parser context
3324 * @node: the element node
3325 *
3326 * parse the content of a RelaxNG element node.
3327 *
3328 * Returns the definition pointer or NULL in case of error.
3329 */
3330static xmlRelaxNGDefinePtr
3331xmlRelaxNGParseElement(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3332 xmlRelaxNGDefinePtr ret, cur, last;
3333 xmlNodePtr child;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003334 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003335
3336 ret = xmlRelaxNGNewDefine(ctxt, node);
3337 if (ret == NULL)
3338 return(NULL);
3339 ret->type = XML_RELAXNG_ELEMENT;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003340 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003341 child = node->children;
3342 if (child == NULL) {
3343 if (ctxt->error != NULL)
3344 ctxt->error(ctxt->userData,
3345 "xmlRelaxNGParseElement: element has no children\n");
3346 ctxt->nbErrors++;
3347 return(ret);
3348 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003349 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
3350 if (cur != NULL)
3351 child = child->next;
3352
Daniel Veillard6eadf632003-01-23 18:29:16 +00003353 if (child == NULL) {
3354 if (ctxt->error != NULL)
3355 ctxt->error(ctxt->userData,
3356 "xmlRelaxNGParseElement: element has no content\n");
3357 ctxt->nbErrors++;
3358 return(ret);
3359 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003360 olddefine = ctxt->define;
3361 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003362 last = NULL;
3363 while (child != NULL) {
3364 cur = xmlRelaxNGParsePattern(ctxt, child);
3365 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003366 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003367 switch (cur->type) {
3368 case XML_RELAXNG_EMPTY:
3369 case XML_RELAXNG_NOT_ALLOWED:
3370 case XML_RELAXNG_TEXT:
3371 case XML_RELAXNG_ELEMENT:
3372 case XML_RELAXNG_DATATYPE:
3373 case XML_RELAXNG_VALUE:
3374 case XML_RELAXNG_LIST:
3375 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00003376 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003377 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00003378 case XML_RELAXNG_DEF:
3379 case XML_RELAXNG_ZEROORMORE:
3380 case XML_RELAXNG_ONEORMORE:
3381 case XML_RELAXNG_OPTIONAL:
3382 case XML_RELAXNG_CHOICE:
3383 case XML_RELAXNG_GROUP:
3384 case XML_RELAXNG_INTERLEAVE:
3385 if (last == NULL) {
3386 ret->content = last = cur;
3387 } else {
3388 if ((last->type == XML_RELAXNG_ELEMENT) &&
3389 (ret->content == last)) {
3390 ret->content = xmlRelaxNGNewDefine(ctxt, node);
3391 if (ret->content != NULL) {
3392 ret->content->type = XML_RELAXNG_GROUP;
3393 ret->content->content = last;
3394 } else {
3395 ret->content = last;
3396 }
3397 }
3398 last->next = cur;
3399 last = cur;
3400 }
3401 break;
3402 case XML_RELAXNG_ATTRIBUTE:
3403 cur->next = ret->attrs;
3404 ret->attrs = cur;
3405 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003406 case XML_RELAXNG_START:
Daniel Veillard8fe98712003-02-19 00:19:14 +00003407 case XML_RELAXNG_PARAM:
Daniel Veillard144fae12003-02-03 13:17:57 +00003408 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003409 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003410 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003411 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00003412 case XML_RELAXNG_NOOP:
3413 TODO
3414 if (ctxt->error != NULL)
3415 ctxt->error(ctxt->userData,
3416 "Internal error, noop found\n");
3417 ctxt->nbErrors++;
3418 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003419 }
3420 }
3421 child = child->next;
3422 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003423 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003424 return(ret);
3425}
3426
3427/**
3428 * xmlRelaxNGParsePatterns:
3429 * @ctxt: a Relax-NG parser context
3430 * @nodes: list of nodes
Daniel Veillard154877e2003-01-30 12:17:05 +00003431 * @group: use an implicit <group> for elements
Daniel Veillard6eadf632003-01-23 18:29:16 +00003432 *
3433 * parse the content of a RelaxNG start node.
3434 *
3435 * Returns the definition pointer or NULL in case of error.
3436 */
3437static xmlRelaxNGDefinePtr
Daniel Veillard154877e2003-01-30 12:17:05 +00003438xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes,
3439 int group) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003440 xmlRelaxNGDefinePtr def = NULL, last = NULL, cur, parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003441
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003442 parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003443 while (nodes != NULL) {
3444 if (IS_RELAXNG(nodes, "element")) {
3445 cur = xmlRelaxNGParseElement(ctxt, nodes);
3446 if (def == NULL) {
3447 def = last = cur;
3448 } else {
Daniel Veillard154877e2003-01-30 12:17:05 +00003449 if ((group == 1) && (def->type == XML_RELAXNG_ELEMENT) &&
3450 (def == last)) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003451 def = xmlRelaxNGNewDefine(ctxt, nodes);
3452 def->type = XML_RELAXNG_GROUP;
3453 def->content = last;
3454 }
3455 last->next = cur;
3456 last = cur;
3457 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003458 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003459 } else {
3460 cur = xmlRelaxNGParsePattern(ctxt, nodes);
Daniel Veillard419a7682003-02-03 23:22:49 +00003461 if (cur != NULL) {
3462 if (def == NULL) {
3463 def = last = cur;
3464 } else {
3465 last->next = cur;
3466 last = cur;
3467 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003468 }
3469 }
3470 nodes = nodes->next;
3471 }
3472 return(def);
3473}
3474
3475/**
3476 * xmlRelaxNGParseStart:
3477 * @ctxt: a Relax-NG parser context
3478 * @nodes: start children nodes
3479 *
3480 * parse the content of a RelaxNG start node.
3481 *
3482 * Returns 0 in case of success, -1 in case of error
3483 */
3484static int
3485xmlRelaxNGParseStart(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
3486 int ret = 0;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003487 xmlRelaxNGDefinePtr def = NULL, last;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003488
Daniel Veillardd2298792003-02-14 16:54:11 +00003489 if (nodes == NULL) {
3490 if (ctxt->error != NULL)
3491 ctxt->error(ctxt->userData,
3492 "start has no children\n");
3493 ctxt->nbErrors++;
3494 return(-1);
3495 }
3496 if (IS_RELAXNG(nodes, "empty")) {
3497 def = xmlRelaxNGNewDefine(ctxt, nodes);
3498 if (def == NULL)
3499 return(-1);
3500 def->type = XML_RELAXNG_EMPTY;
3501 if (nodes->children != NULL) {
3502 if (ctxt->error != NULL)
3503 ctxt->error(ctxt->userData, "element empty is not empty\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003504 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003505 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003506 } else if (IS_RELAXNG(nodes, "notAllowed")) {
3507 def = xmlRelaxNGNewDefine(ctxt, nodes);
3508 if (def == NULL)
3509 return(-1);
3510 def->type = XML_RELAXNG_NOT_ALLOWED;
3511 if (nodes->children != NULL) {
3512 if (ctxt->error != NULL)
3513 ctxt->error(ctxt->userData,
3514 "element notAllowed is not empty\n");
3515 ctxt->nbErrors++;
3516 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003517 } else {
3518 def = xmlRelaxNGParsePatterns(ctxt, nodes, 1);
Daniel Veillard2df2de22003-02-17 23:34:33 +00003519 }
3520 if (ctxt->grammar->start != NULL) {
3521 last = ctxt->grammar->start;
3522 while (last->next != NULL)
3523 last = last->next;
3524 last->next = def;
3525 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00003526 ctxt->grammar->start = def;
3527 }
3528 nodes = nodes->next;
3529 if (nodes != NULL) {
3530 if (ctxt->error != NULL)
3531 ctxt->error(ctxt->userData,
3532 "start more than one children\n");
3533 ctxt->nbErrors++;
3534 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003535 }
3536 return(ret);
3537}
3538
3539/**
3540 * xmlRelaxNGParseGrammarContent:
3541 * @ctxt: a Relax-NG parser context
3542 * @nodes: grammar children nodes
3543 *
3544 * parse the content of a RelaxNG grammar node.
3545 *
3546 * Returns 0 in case of success, -1 in case of error
3547 */
3548static int
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003549xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003550{
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003551 int ret = 0, tmp;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003552
3553 if (nodes == NULL) {
3554 if (ctxt->error != NULL)
3555 ctxt->error(ctxt->userData,
3556 "grammar has no children\n");
3557 ctxt->nbErrors++;
3558 return(-1);
3559 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003560 while (nodes != NULL) {
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003561 if (IS_RELAXNG(nodes, "start")) {
3562 if (nodes->children == NULL) {
3563 if (ctxt->error != NULL)
3564 ctxt->error(ctxt->userData,
Daniel Veillardd2298792003-02-14 16:54:11 +00003565 "start has no children\n");
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003566 ctxt->nbErrors++;
3567 } else {
3568 tmp = xmlRelaxNGParseStart(ctxt, nodes->children);
3569 if (tmp != 0)
3570 ret = -1;
3571 }
3572 } else if (IS_RELAXNG(nodes, "define")) {
3573 tmp = xmlRelaxNGParseDefine(ctxt, nodes);
3574 if (tmp != 0)
3575 ret = -1;
3576 } else if (IS_RELAXNG(nodes, "include")) {
3577 tmp = xmlRelaxNGParseInclude(ctxt, nodes);
3578 if (tmp != 0)
3579 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003580 } else {
3581 if (ctxt->error != NULL)
3582 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003583 "grammar has unexpected child %s\n", nodes->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003584 ctxt->nbErrors++;
3585 ret = -1;
3586 }
3587 nodes = nodes->next;
3588 }
3589 return (ret);
3590}
3591
3592/**
3593 * xmlRelaxNGCheckReference:
3594 * @ref: the ref
3595 * @ctxt: a Relax-NG parser context
3596 * @name: the name associated to the defines
3597 *
3598 * Applies the 4.17. combine attribute rule for all the define
3599 * element of a given grammar using the same name.
3600 */
3601static void
3602xmlRelaxNGCheckReference(xmlRelaxNGDefinePtr ref,
3603 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
3604 xmlRelaxNGGrammarPtr grammar;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003605 xmlRelaxNGDefinePtr def, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003606
3607 grammar = ctxt->grammar;
3608 if (grammar == NULL) {
3609 if (ctxt->error != NULL)
3610 ctxt->error(ctxt->userData,
3611 "Internal error: no grammar in CheckReference %s\n",
3612 name);
3613 ctxt->nbErrors++;
3614 return;
3615 }
3616 if (ref->content != NULL) {
3617 if (ctxt->error != NULL)
3618 ctxt->error(ctxt->userData,
3619 "Internal error: reference has content in CheckReference %s\n",
3620 name);
3621 ctxt->nbErrors++;
3622 return;
3623 }
3624 if (grammar->defs != NULL) {
3625 def = xmlHashLookup(grammar->defs, name);
3626 if (def != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00003627 cur = ref;
3628 while (cur != NULL) {
3629 cur->content = def;
3630 cur = cur->nextHash;
3631 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003632 } else {
Daniel Veillardd4310742003-02-18 21:12:46 +00003633 if (ctxt->error != NULL)
3634 ctxt->error(ctxt->userData,
3635 "Reference %s has no matching definition\n",
3636 name);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003637 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003638 }
Daniel Veillardd4310742003-02-18 21:12:46 +00003639 } else {
3640 if (ctxt->error != NULL)
3641 ctxt->error(ctxt->userData,
3642 "Reference %s has no matching definition\n",
3643 name);
3644 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003645 }
3646 /*
3647 * TODO: make a closure and verify there is no loop !
3648 */
3649}
3650
3651/**
3652 * xmlRelaxNGCheckCombine:
3653 * @define: the define(s) list
3654 * @ctxt: a Relax-NG parser context
3655 * @name: the name associated to the defines
3656 *
3657 * Applies the 4.17. combine attribute rule for all the define
3658 * element of a given grammar using the same name.
3659 */
3660static void
3661xmlRelaxNGCheckCombine(xmlRelaxNGDefinePtr define,
3662 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
3663 xmlChar *combine;
3664 int choiceOrInterleave = -1;
3665 int missing = 0;
3666 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
3667
3668 if (define->nextHash == NULL)
3669 return;
3670 cur = define;
3671 while (cur != NULL) {
3672 combine = xmlGetProp(cur->node, BAD_CAST "combine");
3673 if (combine != NULL) {
3674 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3675 if (choiceOrInterleave == -1)
3676 choiceOrInterleave = 1;
3677 else if (choiceOrInterleave == 0) {
3678 if (ctxt->error != NULL)
3679 ctxt->error(ctxt->userData,
3680 "Defines for %s use both 'choice' and 'interleave'\n",
3681 name);
3682 ctxt->nbErrors++;
3683 }
Daniel Veillard154877e2003-01-30 12:17:05 +00003684 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003685 if (choiceOrInterleave == -1)
3686 choiceOrInterleave = 0;
3687 else if (choiceOrInterleave == 1) {
3688 if (ctxt->error != NULL)
3689 ctxt->error(ctxt->userData,
3690 "Defines for %s use both 'choice' and 'interleave'\n",
3691 name);
3692 ctxt->nbErrors++;
3693 }
3694 } else {
3695 if (ctxt->error != NULL)
3696 ctxt->error(ctxt->userData,
3697 "Defines for %s use unknown combine value '%s''\n",
3698 name, combine);
3699 ctxt->nbErrors++;
3700 }
3701 xmlFree(combine);
3702 } else {
3703 if (missing == 0)
3704 missing = 1;
3705 else {
3706 if (ctxt->error != NULL)
3707 ctxt->error(ctxt->userData,
3708 "Some defines for %s lacks the combine attribute\n",
3709 name);
3710 ctxt->nbErrors++;
3711 }
3712 }
3713
3714 cur = cur->nextHash;
3715 }
3716#ifdef DEBUG
3717 xmlGenericError(xmlGenericErrorContext,
3718 "xmlRelaxNGCheckCombine(): merging %s defines: %d\n",
3719 name, choiceOrInterleave);
3720#endif
3721 if (choiceOrInterleave == -1)
3722 choiceOrInterleave = 0;
3723 cur = xmlRelaxNGNewDefine(ctxt, define->node);
3724 if (cur == NULL)
3725 return;
3726 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003727 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard154877e2003-01-30 12:17:05 +00003728 else
3729 cur->type = XML_RELAXNG_CHOICE;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003730 tmp = define;
3731 last = NULL;
3732 while (tmp != NULL) {
3733 if (tmp->content != NULL) {
3734 if (tmp->content->next != NULL) {
3735 /*
3736 * we need first to create a wrapper.
3737 */
3738 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
3739 if (tmp2 == NULL)
3740 break;
3741 tmp2->type = XML_RELAXNG_GROUP;
3742 tmp2->content = tmp->content;
3743 } else {
3744 tmp2 = tmp->content;
3745 }
3746 if (last == NULL) {
3747 cur->content = tmp2;
3748 } else {
3749 last->next = tmp2;
3750 }
3751 last = tmp2;
3752 tmp->content = NULL;
3753 }
3754 tmp = tmp->nextHash;
3755 }
3756 define->content = cur;
Daniel Veillard154877e2003-01-30 12:17:05 +00003757 if (choiceOrInterleave == 0) {
3758 if (ctxt->interleaves == NULL)
3759 ctxt->interleaves = xmlHashCreate(10);
3760 if (ctxt->interleaves == NULL) {
3761 if (ctxt->error != NULL)
3762 ctxt->error(ctxt->userData,
3763 "Failed to create interleaves hash table\n");
3764 ctxt->nbErrors++;
3765 } else {
3766 char tmpname[32];
3767
3768 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
3769 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
3770 if (ctxt->error != NULL)
3771 ctxt->error(ctxt->userData,
3772 "Failed to add %s to hash table\n", tmpname);
3773 ctxt->nbErrors++;
3774 }
3775 }
3776 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003777}
3778
3779/**
3780 * xmlRelaxNGCombineStart:
3781 * @ctxt: a Relax-NG parser context
3782 * @grammar: the grammar
3783 *
3784 * Applies the 4.17. combine rule for all the start
3785 * element of a given grammar.
3786 */
3787static void
3788xmlRelaxNGCombineStart(xmlRelaxNGParserCtxtPtr ctxt,
3789 xmlRelaxNGGrammarPtr grammar) {
3790 xmlRelaxNGDefinePtr starts;
3791 xmlChar *combine;
3792 int choiceOrInterleave = -1;
3793 int missing = 0;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003794 xmlRelaxNGDefinePtr cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003795
Daniel Veillard2df2de22003-02-17 23:34:33 +00003796 starts = grammar->start;
3797 if ((starts == NULL) || (starts->next == NULL))
Daniel Veillard6eadf632003-01-23 18:29:16 +00003798 return;
3799 cur = starts;
3800 while (cur != NULL) {
Daniel Veillard2df2de22003-02-17 23:34:33 +00003801 if ((cur->node == NULL) || (cur->node->parent == NULL) ||
3802 (!xmlStrEqual(cur->node->parent->name, BAD_CAST "start"))) {
3803 combine = NULL;
3804 if (ctxt->error != NULL)
3805 ctxt->error(ctxt->userData,
3806 "Internal error: start element not found\n");
3807 ctxt->nbErrors++;
3808 } else {
3809 combine = xmlGetProp(cur->node->parent, BAD_CAST "combine");
3810 }
3811
Daniel Veillard6eadf632003-01-23 18:29:16 +00003812 if (combine != NULL) {
3813 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3814 if (choiceOrInterleave == -1)
3815 choiceOrInterleave = 1;
3816 else if (choiceOrInterleave == 0) {
3817 if (ctxt->error != NULL)
3818 ctxt->error(ctxt->userData,
3819 "<start> use both 'choice' and 'interleave'\n");
3820 ctxt->nbErrors++;
3821 }
Daniel Veillard2df2de22003-02-17 23:34:33 +00003822 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003823 if (choiceOrInterleave == -1)
3824 choiceOrInterleave = 0;
3825 else if (choiceOrInterleave == 1) {
3826 if (ctxt->error != NULL)
3827 ctxt->error(ctxt->userData,
3828 "<start> use both 'choice' and 'interleave'\n");
3829 ctxt->nbErrors++;
3830 }
3831 } else {
3832 if (ctxt->error != NULL)
3833 ctxt->error(ctxt->userData,
3834 "<start> uses unknown combine value '%s''\n", combine);
3835 ctxt->nbErrors++;
3836 }
3837 xmlFree(combine);
3838 } else {
3839 if (missing == 0)
3840 missing = 1;
3841 else {
3842 if (ctxt->error != NULL)
3843 ctxt->error(ctxt->userData,
3844 "Some <start> elements lacks the combine attribute\n");
3845 ctxt->nbErrors++;
3846 }
3847 }
3848
Daniel Veillard2df2de22003-02-17 23:34:33 +00003849 cur = cur->next;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003850 }
3851#ifdef DEBUG
3852 xmlGenericError(xmlGenericErrorContext,
3853 "xmlRelaxNGCombineStart(): merging <start>: %d\n",
3854 choiceOrInterleave);
3855#endif
3856 if (choiceOrInterleave == -1)
3857 choiceOrInterleave = 0;
3858 cur = xmlRelaxNGNewDefine(ctxt, starts->node);
3859 if (cur == NULL)
3860 return;
3861 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003862 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003863 else
3864 cur->type = XML_RELAXNG_CHOICE;
3865 cur->content = grammar->start;
3866 grammar->start = cur;
3867 if (choiceOrInterleave == 0) {
3868 if (ctxt->interleaves == NULL)
3869 ctxt->interleaves = xmlHashCreate(10);
3870 if (ctxt->interleaves == NULL) {
3871 if (ctxt->error != NULL)
3872 ctxt->error(ctxt->userData,
3873 "Failed to create interleaves hash table\n");
3874 ctxt->nbErrors++;
3875 } else {
3876 char tmpname[32];
3877
3878 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
3879 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
3880 if (ctxt->error != NULL)
3881 ctxt->error(ctxt->userData,
3882 "Failed to add %s to hash table\n", tmpname);
3883 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003884 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003885 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003886 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003887}
3888
3889/**
Daniel Veillardd4310742003-02-18 21:12:46 +00003890 * xmlRelaxNGCheckCycles:
3891 * @ctxt: a Relax-NG parser context
3892 * @nodes: grammar children nodes
3893 * @depth: the counter
3894 *
3895 * Check for cycles.
3896 *
3897 * Returns 0 if check passed, and -1 in case of error
3898 */
3899static int
3900xmlRelaxNGCheckCycles(xmlRelaxNGParserCtxtPtr ctxt,
3901 xmlRelaxNGDefinePtr cur, int depth) {
3902 int ret = 0;
3903
3904 while ((ret == 0) && (cur != NULL)) {
3905 if ((cur->type == XML_RELAXNG_REF) ||
3906 (cur->type == XML_RELAXNG_PARENTREF)) {
3907 if (cur->depth == -1) {
3908 cur->depth = depth;
3909 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth);
3910 cur->depth = -2;
3911 } else if (depth == cur->depth) {
3912 if (ctxt->error != NULL)
3913 ctxt->error(ctxt->userData,
3914 "Detected a cycle in %s references\n", cur->name);
3915 ctxt->nbErrors++;
3916 return(-1);
3917 }
3918 } else if (cur->type == XML_RELAXNG_ELEMENT) {
3919 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth + 1);
3920 } else {
3921 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth);
3922 }
3923 cur = cur->next;
3924 }
3925 return(ret);
3926}
3927
3928/**
Daniel Veillard77648bb2003-02-20 15:03:22 +00003929 * xmlRelaxNGTryUnlink:
3930 * @ctxt: a Relax-NG parser context
3931 * @cur: the definition to unlink
3932 * @parent: the parent definition
3933 * @prev: the previous sibling definition
3934 *
3935 * Try to unlink a definition. If not possble make it a NOOP
3936 *
3937 * Returns the new prev definition
3938 */
3939static xmlRelaxNGDefinePtr
3940xmlRelaxNGTryUnlink(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
3941 xmlRelaxNGDefinePtr cur,
3942 xmlRelaxNGDefinePtr parent,
3943 xmlRelaxNGDefinePtr prev) {
3944 if (prev != NULL) {
3945 prev->next = cur->next;
3946 } else {
3947 if (parent != NULL) {
3948 if (parent->content == cur)
3949 parent->content = cur->next;
3950 else if (parent->attrs == cur)
3951 parent->attrs = cur->next;
3952 else if (parent->nameClass == cur)
3953 parent->nameClass = cur->next;
3954 } else {
3955 cur->type = XML_RELAXNG_NOOP;
3956 prev = cur;
3957 }
3958 }
3959 return(prev);
3960}
3961
3962/**
Daniel Veillard4c5cf702003-02-21 15:40:34 +00003963 * xmlRelaxNGSimplify:
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003964 * @ctxt: a Relax-NG parser context
3965 * @nodes: grammar children nodes
3966 *
3967 * Check for simplification of empty and notAllowed
3968 */
3969static void
3970xmlRelaxNGSimplify(xmlRelaxNGParserCtxtPtr ctxt,
3971 xmlRelaxNGDefinePtr cur,
3972 xmlRelaxNGDefinePtr parent) {
3973 xmlRelaxNGDefinePtr prev = NULL;
3974
3975 while (cur != NULL) {
3976 if ((cur->type == XML_RELAXNG_REF) ||
3977 (cur->type == XML_RELAXNG_PARENTREF)) {
3978 if (cur->depth != -3) {
3979 cur->depth = -3;
3980 xmlRelaxNGSimplify(ctxt, cur->content, cur);
3981 }
3982 } else if (cur->type == XML_RELAXNG_NOT_ALLOWED) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00003983 cur->parent = parent;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003984 if ((parent != NULL) &&
3985 ((parent->type == XML_RELAXNG_ATTRIBUTE) ||
3986 (parent->type == XML_RELAXNG_LIST) ||
3987 (parent->type == XML_RELAXNG_GROUP) ||
3988 (parent->type == XML_RELAXNG_INTERLEAVE) ||
3989 (parent->type == XML_RELAXNG_ONEORMORE) ||
3990 (parent->type == XML_RELAXNG_ZEROORMORE))) {
3991 parent->type = XML_RELAXNG_NOT_ALLOWED;
3992 break;
3993 }
3994 if ((parent != NULL) &&
3995 (parent->type == XML_RELAXNG_CHOICE)) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00003996 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003997 } else
3998 prev = cur;
3999 } else if (cur->type == XML_RELAXNG_EMPTY){
Daniel Veillard77648bb2003-02-20 15:03:22 +00004000 cur->parent = parent;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004001 if ((parent != NULL) &&
4002 ((parent->type == XML_RELAXNG_ONEORMORE) ||
4003 (parent->type == XML_RELAXNG_ZEROORMORE))) {
4004 parent->type = XML_RELAXNG_EMPTY;
4005 break;
4006 }
4007 if ((parent != NULL) &&
4008 ((parent->type == XML_RELAXNG_GROUP) ||
4009 (parent->type == XML_RELAXNG_INTERLEAVE))) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004010 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004011 } else
4012 prev = cur;
4013 } else {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004014 cur->parent = parent;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004015 if (cur->content != NULL)
4016 xmlRelaxNGSimplify(ctxt, cur->content, cur);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004017 if (cur->attrs != NULL)
4018 xmlRelaxNGSimplify(ctxt, cur->attrs, cur);
4019 if (cur->nameClass != NULL)
4020 xmlRelaxNGSimplify(ctxt, cur->nameClass, cur);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004021 /*
4022 * This may result in a simplification
4023 */
4024 if ((cur->type == XML_RELAXNG_GROUP) ||
4025 (cur->type == XML_RELAXNG_INTERLEAVE)) {
4026 if (cur->content == NULL)
4027 cur->type = XML_RELAXNG_EMPTY;
4028 else if (cur->content->next == NULL) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004029 if ((parent == NULL) && (prev == NULL)) {
4030 cur->type = XML_RELAXNG_NOOP;
4031 } else if (prev == NULL) {
4032 parent->content = cur->content;
4033 cur->content->next = cur->next;
4034 cur = cur->content;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004035 } else {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004036 cur->content->next = cur->next;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004037 prev->next = cur->content;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004038 cur = cur->content;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004039 }
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004040 }
4041 }
4042 /*
4043 * the current node may have been transformed back
4044 */
4045 if ((cur->type == XML_RELAXNG_EXCEPT) &&
4046 (cur->content != NULL) &&
4047 (cur->content->type == XML_RELAXNG_NOT_ALLOWED)) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004048 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004049 } else if (cur->type == XML_RELAXNG_NOT_ALLOWED) {
4050 if ((parent != NULL) &&
4051 ((parent->type == XML_RELAXNG_ATTRIBUTE) ||
4052 (parent->type == XML_RELAXNG_LIST) ||
4053 (parent->type == XML_RELAXNG_GROUP) ||
4054 (parent->type == XML_RELAXNG_INTERLEAVE) ||
4055 (parent->type == XML_RELAXNG_ONEORMORE) ||
4056 (parent->type == XML_RELAXNG_ZEROORMORE))) {
4057 parent->type = XML_RELAXNG_NOT_ALLOWED;
4058 break;
4059 }
4060 if ((parent != NULL) &&
4061 (parent->type == XML_RELAXNG_CHOICE)) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004062 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004063 } else
4064 prev = cur;
4065 } else if (cur->type == XML_RELAXNG_EMPTY){
4066 if ((parent != NULL) &&
4067 ((parent->type == XML_RELAXNG_ONEORMORE) ||
4068 (parent->type == XML_RELAXNG_ZEROORMORE))) {
4069 parent->type = XML_RELAXNG_EMPTY;
4070 break;
4071 }
4072 if ((parent != NULL) &&
4073 ((parent->type == XML_RELAXNG_GROUP) ||
4074 (parent->type == XML_RELAXNG_INTERLEAVE) ||
4075 (parent->type == XML_RELAXNG_CHOICE))) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004076 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004077 } else
4078 prev = cur;
4079 } else {
4080 prev = cur;
4081 }
4082 }
4083 cur = cur->next;
4084 }
4085}
4086
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004087/**
4088 * xmlRelaxNGGroupContentType:
4089 * @ct1: the first content type
4090 * @ct2: the second content type
4091 *
4092 * Try to group 2 content types
4093 *
4094 * Returns the content type
4095 */
4096static xmlRelaxNGContentType
4097xmlRelaxNGGroupContentType(xmlRelaxNGContentType ct1,
4098 xmlRelaxNGContentType ct2) {
4099 if ((ct1 == XML_RELAXNG_CONTENT_ERROR) ||
4100 (ct2 == XML_RELAXNG_CONTENT_ERROR))
4101 return(XML_RELAXNG_CONTENT_ERROR);
4102 if (ct1 == XML_RELAXNG_CONTENT_EMPTY)
4103 return(ct2);
4104 if (ct2 == XML_RELAXNG_CONTENT_EMPTY)
4105 return(ct1);
4106 if ((ct1 == XML_RELAXNG_CONTENT_COMPLEX) &&
4107 (ct2 == XML_RELAXNG_CONTENT_COMPLEX))
4108 return(XML_RELAXNG_CONTENT_COMPLEX);
4109 return(XML_RELAXNG_CONTENT_ERROR);
4110}
4111
4112/**
4113 * xmlRelaxNGMaxContentType:
4114 * @ct1: the first content type
4115 * @ct2: the second content type
4116 *
4117 * Compute the max content-type
4118 *
4119 * Returns the content type
4120 */
4121static xmlRelaxNGContentType
4122xmlRelaxNGMaxContentType(xmlRelaxNGContentType ct1,
4123 xmlRelaxNGContentType ct2) {
4124 if ((ct1 == XML_RELAXNG_CONTENT_ERROR) ||
4125 (ct2 == XML_RELAXNG_CONTENT_ERROR))
4126 return(XML_RELAXNG_CONTENT_ERROR);
4127 if ((ct1 == XML_RELAXNG_CONTENT_SIMPLE) ||
4128 (ct2 == XML_RELAXNG_CONTENT_SIMPLE))
4129 return(XML_RELAXNG_CONTENT_SIMPLE);
4130 if ((ct1 == XML_RELAXNG_CONTENT_COMPLEX) ||
4131 (ct2 == XML_RELAXNG_CONTENT_COMPLEX))
4132 return(XML_RELAXNG_CONTENT_COMPLEX);
4133 return(XML_RELAXNG_CONTENT_EMPTY);
4134}
Daniel Veillard77648bb2003-02-20 15:03:22 +00004135
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004136/**
4137 * xmlRelaxNGCheckRules:
4138 * @ctxt: a Relax-NG parser context
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004139 * @cur: the current definition
Daniel Veillard77648bb2003-02-20 15:03:22 +00004140 * @flags: some accumulated flags
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004141 * @ptype: the parent type
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004142 *
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004143 * Check for rules in section 7.1 and 7.2
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004144 *
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004145 * Returns the content type of @cur
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004146 */
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004147static xmlRelaxNGContentType
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004148xmlRelaxNGCheckRules(xmlRelaxNGParserCtxtPtr ctxt,
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004149 xmlRelaxNGDefinePtr cur, int flags,
4150 xmlRelaxNGType ptype) {
4151 int nflags = flags;
4152 xmlRelaxNGContentType ret, tmp, val = XML_RELAXNG_CONTENT_EMPTY;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004153
Daniel Veillard77648bb2003-02-20 15:03:22 +00004154 while (cur != NULL) {
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004155 ret = XML_RELAXNG_CONTENT_EMPTY;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004156 if ((cur->type == XML_RELAXNG_REF) ||
4157 (cur->type == XML_RELAXNG_PARENTREF)) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004158 if (flags & XML_RELAXNG_IN_LIST) {
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004159 if (ctxt->error != NULL)
4160 ctxt->error(ctxt->userData,
Daniel Veillard77648bb2003-02-20 15:03:22 +00004161 "Found forbidden pattern list//ref\n");
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004162 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004163 }
4164 if (flags & XML_RELAXNG_IN_ATTRIBUTE) {
4165 if (ctxt->error != NULL)
4166 ctxt->error(ctxt->userData,
4167 "Found forbidden pattern attribute//ref\n");
4168 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004169 }
4170 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4171 if (ctxt->error != NULL)
4172 ctxt->error(ctxt->userData,
4173 "Found forbidden pattern data/except//ref\n");
4174 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004175 }
Daniel Veillardc5312d72003-02-21 17:14:10 +00004176 if (cur->depth > -4) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004177 cur->depth = -4;
Daniel Veillardc5312d72003-02-21 17:14:10 +00004178 ret = xmlRelaxNGCheckRules(ctxt, cur->content,
4179 flags, cur->type);
4180 cur->depth = ret - 15 ;
4181 } else if (cur->depth == -4) {
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004182 ret = XML_RELAXNG_CONTENT_COMPLEX;
Daniel Veillardc5312d72003-02-21 17:14:10 +00004183 } else {
4184 ret = (xmlRelaxNGContentType) cur->depth + 15;
4185 }
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004186 } else if (cur->type == XML_RELAXNG_ELEMENT) {
Daniel Veillard44e1dd02003-02-21 23:23:28 +00004187 /*
4188 * The 7.3 Attribute derivation rule for groups is plugged there
4189 */
4190 xmlRelaxNGCheckGroupAttrs(ctxt, cur);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004191 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4192 if (ctxt->error != NULL)
4193 ctxt->error(ctxt->userData,
4194 "Found forbidden pattern data/except//element(ref)\n");
4195 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004196 }
4197 if (flags & XML_RELAXNG_IN_LIST) {
4198 if (ctxt->error != NULL)
4199 ctxt->error(ctxt->userData,
4200 "Found forbidden pattern list//element(ref)\n");
4201 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004202 }
4203 if (flags & XML_RELAXNG_IN_ATTRIBUTE) {
4204 if (ctxt->error != NULL)
4205 ctxt->error(ctxt->userData,
4206 "Found forbidden pattern attribute//element(ref)\n");
4207 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004208 }
4209 /*
4210 * reset since in the simple form elements are only child
4211 * of grammar/define
4212 */
4213 nflags = 0;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004214 ret = xmlRelaxNGCheckRules(ctxt, cur->attrs, nflags, cur->type);
4215 if (ret != XML_RELAXNG_CONTENT_EMPTY) {
4216 if (ctxt->error != NULL)
4217 ctxt->error(ctxt->userData,
4218 "Element %s attributes have a content type error\n",
4219 cur->name);
4220 ctxt->nbErrors++;
4221 }
4222 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
4223 if (ret == XML_RELAXNG_CONTENT_ERROR) {
4224 if (ctxt->error != NULL)
4225 ctxt->error(ctxt->userData,
4226 "Element %s has a content type error\n",
4227 cur->name);
4228 ctxt->nbErrors++;
4229 } else {
4230 ret = XML_RELAXNG_CONTENT_COMPLEX;
4231 }
Daniel Veillard77648bb2003-02-20 15:03:22 +00004232 } else if (cur->type == XML_RELAXNG_ATTRIBUTE) {
4233 if (flags & XML_RELAXNG_IN_ATTRIBUTE) {
4234 if (ctxt->error != NULL)
4235 ctxt->error(ctxt->userData,
4236 "Found forbidden pattern attribute//attribute\n");
4237 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004238 }
4239 if (flags & XML_RELAXNG_IN_LIST) {
4240 if (ctxt->error != NULL)
4241 ctxt->error(ctxt->userData,
4242 "Found forbidden pattern list//attribute\n");
4243 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004244 }
4245 if (flags & XML_RELAXNG_IN_OOMGROUP) {
4246 if (ctxt->error != NULL)
4247 ctxt->error(ctxt->userData,
4248 "Found forbidden pattern oneOrMore//group//attribute\n");
4249 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004250 }
4251 if (flags & XML_RELAXNG_IN_OOMINTERLEAVE) {
4252 if (ctxt->error != NULL)
4253 ctxt->error(ctxt->userData,
4254 "Found forbidden pattern oneOrMore//interleave//attribute\n");
4255 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004256 }
4257 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4258 if (ctxt->error != NULL)
4259 ctxt->error(ctxt->userData,
4260 "Found forbidden pattern data/except//attribute\n");
4261 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004262 }
4263 if (flags & XML_RELAXNG_IN_START) {
4264 if (ctxt->error != NULL)
4265 ctxt->error(ctxt->userData,
4266 "Found forbidden pattern start//attribute\n");
4267 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004268 }
4269 nflags = flags | XML_RELAXNG_IN_ATTRIBUTE;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004270 xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
4271 ret = XML_RELAXNG_CONTENT_EMPTY;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004272 } else if ((cur->type == XML_RELAXNG_ONEORMORE) ||
4273 (cur->type == XML_RELAXNG_ZEROORMORE)) {
4274 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4275 if (ctxt->error != NULL)
4276 ctxt->error(ctxt->userData,
4277 "Found forbidden pattern data/except//oneOrMore\n");
4278 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004279 }
4280 if (flags & XML_RELAXNG_IN_START) {
4281 if (ctxt->error != NULL)
4282 ctxt->error(ctxt->userData,
4283 "Found forbidden pattern start//oneOrMore\n");
4284 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004285 }
4286 nflags = flags | XML_RELAXNG_IN_ONEORMORE;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004287 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
4288 ret = xmlRelaxNGGroupContentType(ret, ret);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004289 } else if (cur->type == XML_RELAXNG_LIST) {
4290 if (flags & XML_RELAXNG_IN_LIST) {
4291 if (ctxt->error != NULL)
4292 ctxt->error(ctxt->userData,
4293 "Found forbidden pattern list//list\n");
4294 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004295 }
4296 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4297 if (ctxt->error != NULL)
4298 ctxt->error(ctxt->userData,
4299 "Found forbidden pattern data/except//list\n");
4300 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004301 }
4302 if (flags & XML_RELAXNG_IN_START) {
4303 if (ctxt->error != NULL)
4304 ctxt->error(ctxt->userData,
4305 "Found forbidden pattern start//list\n");
4306 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004307 }
4308 nflags = flags | XML_RELAXNG_IN_LIST;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004309 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004310 } else if (cur->type == XML_RELAXNG_GROUP) {
4311 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4312 if (ctxt->error != NULL)
4313 ctxt->error(ctxt->userData,
4314 "Found forbidden pattern data/except//group\n");
4315 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004316 }
4317 if (flags & XML_RELAXNG_IN_START) {
4318 if (ctxt->error != NULL)
4319 ctxt->error(ctxt->userData,
4320 "Found forbidden pattern start//group\n");
4321 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004322 }
4323 if (flags & XML_RELAXNG_IN_ONEORMORE)
4324 nflags = flags | XML_RELAXNG_IN_OOMGROUP;
4325 else
4326 nflags = flags;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004327 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
Daniel Veillard44e1dd02003-02-21 23:23:28 +00004328 /*
4329 * The 7.3 Attribute derivation rule for groups is plugged there
4330 */
4331 xmlRelaxNGCheckGroupAttrs(ctxt, cur);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004332 } else if (cur->type == XML_RELAXNG_INTERLEAVE) {
4333 if (flags & XML_RELAXNG_IN_LIST) {
4334 if (ctxt->error != NULL)
4335 ctxt->error(ctxt->userData,
4336 "Found forbidden pattern list//interleave\n");
4337 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004338 }
4339 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4340 if (ctxt->error != NULL)
4341 ctxt->error(ctxt->userData,
4342 "Found forbidden pattern data/except//interleave\n");
4343 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004344 }
4345 if (flags & XML_RELAXNG_IN_START) {
4346 if (ctxt->error != NULL)
4347 ctxt->error(ctxt->userData,
4348 "Found forbidden pattern start//interleave\n");
4349 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004350 }
4351 if (flags & XML_RELAXNG_IN_ONEORMORE)
4352 nflags = flags | XML_RELAXNG_IN_OOMINTERLEAVE;
4353 else
4354 nflags = flags;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004355 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004356 } else if (cur->type == XML_RELAXNG_EXCEPT) {
4357 if ((cur->parent != NULL) &&
4358 (cur->parent->type == XML_RELAXNG_DATATYPE))
4359 nflags = flags | XML_RELAXNG_IN_DATAEXCEPT;
4360 else
4361 nflags = flags;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004362 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004363 } else if (cur->type == XML_RELAXNG_DATATYPE) {
4364 if (flags & XML_RELAXNG_IN_START) {
4365 if (ctxt->error != NULL)
4366 ctxt->error(ctxt->userData,
4367 "Found forbidden pattern start//data\n");
4368 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004369 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004370 xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type);
4371 ret = XML_RELAXNG_CONTENT_SIMPLE;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004372 } else if (cur->type == XML_RELAXNG_VALUE) {
4373 if (flags & XML_RELAXNG_IN_START) {
4374 if (ctxt->error != NULL)
4375 ctxt->error(ctxt->userData,
4376 "Found forbidden pattern start//value\n");
4377 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004378 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004379 xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type);
4380 ret = XML_RELAXNG_CONTENT_SIMPLE;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004381 } else if (cur->type == XML_RELAXNG_TEXT) {
4382 if (flags & XML_RELAXNG_IN_LIST) {
4383 if (ctxt->error != NULL)
4384 ctxt->error(ctxt->userData,
4385 "Found forbidden pattern list//text\n");
4386 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004387 }
4388 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4389 if (ctxt->error != NULL)
4390 ctxt->error(ctxt->userData,
4391 "Found forbidden pattern data/except//text\n");
4392 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004393 }
4394 if (flags & XML_RELAXNG_IN_START) {
4395 if (ctxt->error != NULL)
4396 ctxt->error(ctxt->userData,
4397 "Found forbidden pattern start//text\n");
4398 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004399 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004400 ret = XML_RELAXNG_CONTENT_COMPLEX;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004401 } else if (cur->type == XML_RELAXNG_EMPTY) {
4402 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4403 if (ctxt->error != NULL)
4404 ctxt->error(ctxt->userData,
4405 "Found forbidden pattern data/except//empty\n");
4406 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004407 }
4408 if (flags & XML_RELAXNG_IN_START) {
4409 if (ctxt->error != NULL)
4410 ctxt->error(ctxt->userData,
4411 "Found forbidden pattern start//empty\n");
4412 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004413 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004414 ret = XML_RELAXNG_CONTENT_EMPTY;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004415 } else {
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004416 ret = xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004417 }
4418 cur = cur->next;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004419 if (ptype == XML_RELAXNG_GROUP) {
4420 val = xmlRelaxNGGroupContentType(val, ret);
4421 } else if (ptype == XML_RELAXNG_INTERLEAVE) {
4422 tmp = xmlRelaxNGGroupContentType(val, ret);
4423 if (tmp != XML_RELAXNG_CONTENT_ERROR)
4424 tmp = xmlRelaxNGMaxContentType(val, ret);
4425 } else if (ptype == XML_RELAXNG_CHOICE) {
4426 val = xmlRelaxNGMaxContentType(val, ret);
4427 } else if (ptype == XML_RELAXNG_LIST) {
4428 val = XML_RELAXNG_CONTENT_SIMPLE;
4429 } else if (ptype == XML_RELAXNG_EXCEPT) {
4430 if (ret == XML_RELAXNG_CONTENT_ERROR)
4431 val = XML_RELAXNG_CONTENT_ERROR;
4432 else
4433 val = XML_RELAXNG_CONTENT_SIMPLE;
4434 } else {
4435 val = xmlRelaxNGGroupContentType(val, ret);
4436 }
4437
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004438 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004439 return(val);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004440}
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004441
4442/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004443 * xmlRelaxNGParseGrammar:
4444 * @ctxt: a Relax-NG parser context
4445 * @nodes: grammar children nodes
4446 *
4447 * parse a Relax-NG <grammar> node
4448 *
4449 * Returns the internal xmlRelaxNGGrammarPtr built or
4450 * NULL in case of error
4451 */
4452static xmlRelaxNGGrammarPtr
4453xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
4454 xmlRelaxNGGrammarPtr ret, tmp, old;
4455
Daniel Veillard6eadf632003-01-23 18:29:16 +00004456 ret = xmlRelaxNGNewGrammar(ctxt);
4457 if (ret == NULL)
4458 return(NULL);
4459
4460 /*
4461 * Link the new grammar in the tree
4462 */
4463 ret->parent = ctxt->grammar;
4464 if (ctxt->grammar != NULL) {
4465 tmp = ctxt->grammar->children;
4466 if (tmp == NULL) {
4467 ctxt->grammar->children = ret;
4468 } else {
4469 while (tmp->next != NULL)
4470 tmp = tmp->next;
4471 tmp->next = ret;
4472 }
4473 }
4474
4475 old = ctxt->grammar;
4476 ctxt->grammar = ret;
4477 xmlRelaxNGParseGrammarContent(ctxt, nodes);
4478 ctxt->grammar = ret;
Daniel Veillard2df2de22003-02-17 23:34:33 +00004479 if (ctxt->grammar == NULL) {
4480 if (ctxt->error != NULL)
4481 ctxt->error(ctxt->userData,
4482 "Failed to parse <grammar> content\n");
4483 ctxt->nbErrors++;
4484 } else if (ctxt->grammar->start == NULL) {
4485 if (ctxt->error != NULL)
4486 ctxt->error(ctxt->userData,
4487 "Element <grammar> has no <start>\n");
4488 ctxt->nbErrors++;
4489 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004490
4491 /*
4492 * Apply 4.17 mergingd rules to defines and starts
4493 */
4494 xmlRelaxNGCombineStart(ctxt, ret);
4495 if (ret->defs != NULL) {
4496 xmlHashScan(ret->defs, (xmlHashScanner) xmlRelaxNGCheckCombine,
4497 ctxt);
4498 }
4499
4500 /*
4501 * link together defines and refs in this grammar
4502 */
4503 if (ret->refs != NULL) {
4504 xmlHashScan(ret->refs, (xmlHashScanner) xmlRelaxNGCheckReference,
4505 ctxt);
4506 }
4507 ctxt->grammar = old;
4508 return(ret);
4509}
4510
4511/**
4512 * xmlRelaxNGParseDocument:
4513 * @ctxt: a Relax-NG parser context
4514 * @node: the root node of the RelaxNG schema
4515 *
4516 * parse a Relax-NG definition resource and build an internal
4517 * xmlRelaxNG struture which can be used to validate instances.
4518 *
4519 * Returns the internal XML RelaxNG structure built or
4520 * NULL in case of error
4521 */
4522static xmlRelaxNGPtr
4523xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
4524 xmlRelaxNGPtr schema = NULL;
Daniel Veillard276be4a2003-01-24 01:03:34 +00004525 const xmlChar *olddefine;
Daniel Veillarde431a272003-01-29 23:02:33 +00004526 xmlRelaxNGGrammarPtr old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004527
4528 if ((ctxt == NULL) || (node == NULL))
4529 return (NULL);
4530
4531 schema = xmlRelaxNGNewRelaxNG(ctxt);
4532 if (schema == NULL)
4533 return(NULL);
4534
Daniel Veillard276be4a2003-01-24 01:03:34 +00004535 olddefine = ctxt->define;
4536 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004537 if (IS_RELAXNG(node, "grammar")) {
4538 schema->topgrammar = xmlRelaxNGParseGrammar(ctxt, node->children);
4539 } else {
4540 schema->topgrammar = xmlRelaxNGNewGrammar(ctxt);
4541 if (schema->topgrammar == NULL) {
4542 return(schema);
4543 }
4544 schema->topgrammar->parent = NULL;
Daniel Veillarde431a272003-01-29 23:02:33 +00004545 old = ctxt->grammar;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004546 ctxt->grammar = schema->topgrammar;
4547 xmlRelaxNGParseStart(ctxt, node);
Daniel Veillarde431a272003-01-29 23:02:33 +00004548 if (old != NULL)
4549 ctxt->grammar = old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004550 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00004551 ctxt->define = olddefine;
Daniel Veillardd4310742003-02-18 21:12:46 +00004552 if (schema->topgrammar->start != NULL) {
4553 xmlRelaxNGCheckCycles(ctxt, schema->topgrammar->start, 0);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004554 if ((ctxt->flags & XML_RELAXNG_IN_EXTERNALREF) == 0) {
4555 xmlRelaxNGSimplify(ctxt, schema->topgrammar->start, NULL);
4556 while ((schema->topgrammar->start != NULL) &&
4557 (schema->topgrammar->start->type == XML_RELAXNG_NOOP) &&
4558 (schema->topgrammar->start->next != NULL))
4559 schema->topgrammar->start = schema->topgrammar->start->content;
4560 xmlRelaxNGCheckRules(ctxt, schema->topgrammar->start,
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004561 XML_RELAXNG_IN_START, XML_RELAXNG_NOOP);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004562 }
Daniel Veillardd4310742003-02-18 21:12:46 +00004563 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004564
4565#ifdef DEBUG
4566 if (schema == NULL)
4567 xmlGenericError(xmlGenericErrorContext,
4568 "xmlRelaxNGParseDocument() failed\n");
4569#endif
4570
4571 return (schema);
4572}
4573
4574/************************************************************************
4575 * *
4576 * Reading RelaxNGs *
4577 * *
4578 ************************************************************************/
4579
4580/**
4581 * xmlRelaxNGNewParserCtxt:
4582 * @URL: the location of the schema
4583 *
4584 * Create an XML RelaxNGs parse context for that file/resource expected
4585 * to contain an XML RelaxNGs file.
4586 *
4587 * Returns the parser context or NULL in case of error
4588 */
4589xmlRelaxNGParserCtxtPtr
4590xmlRelaxNGNewParserCtxt(const char *URL) {
4591 xmlRelaxNGParserCtxtPtr ret;
4592
4593 if (URL == NULL)
4594 return(NULL);
4595
4596 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
4597 if (ret == NULL) {
4598 xmlGenericError(xmlGenericErrorContext,
4599 "Failed to allocate new schama parser context for %s\n", URL);
4600 return (NULL);
4601 }
4602 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
4603 ret->URL = xmlStrdup((const xmlChar *)URL);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004604 ret->error = xmlGenericError;
4605 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004606 return (ret);
4607}
4608
4609/**
4610 * xmlRelaxNGNewMemParserCtxt:
4611 * @buffer: a pointer to a char array containing the schemas
4612 * @size: the size of the array
4613 *
4614 * Create an XML RelaxNGs parse context for that memory buffer expected
4615 * to contain an XML RelaxNGs file.
4616 *
4617 * Returns the parser context or NULL in case of error
4618 */
4619xmlRelaxNGParserCtxtPtr
4620xmlRelaxNGNewMemParserCtxt(const char *buffer, int size) {
4621 xmlRelaxNGParserCtxtPtr ret;
4622
4623 if ((buffer == NULL) || (size <= 0))
4624 return(NULL);
4625
4626 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
4627 if (ret == NULL) {
4628 xmlGenericError(xmlGenericErrorContext,
4629 "Failed to allocate new schama parser context\n");
4630 return (NULL);
4631 }
4632 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
4633 ret->buffer = buffer;
4634 ret->size = size;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004635 ret->error = xmlGenericError;
4636 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004637 return (ret);
4638}
4639
4640/**
4641 * xmlRelaxNGFreeParserCtxt:
4642 * @ctxt: the schema parser context
4643 *
4644 * Free the resources associated to the schema parser context
4645 */
4646void
4647xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxtPtr ctxt) {
4648 if (ctxt == NULL)
4649 return;
4650 if (ctxt->URL != NULL)
4651 xmlFree(ctxt->URL);
4652 if (ctxt->doc != NULL)
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004653 xmlFreeDoc(ctxt->document);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004654 if (ctxt->interleaves != NULL)
4655 xmlHashFree(ctxt->interleaves, NULL);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004656 if (ctxt->documents != NULL)
4657 xmlHashFree(ctxt->documents, (xmlHashDeallocator)
4658 xmlRelaxNGFreeDocument);
4659 if (ctxt->docTab != NULL)
4660 xmlFree(ctxt->docTab);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004661 if (ctxt->incTab != NULL)
4662 xmlFree(ctxt->incTab);
Daniel Veillard419a7682003-02-03 23:22:49 +00004663 if (ctxt->defTab != NULL) {
4664 int i;
4665
4666 for (i = 0;i < ctxt->defNr;i++)
4667 xmlRelaxNGFreeDefine(ctxt->defTab[i]);
4668 xmlFree(ctxt->defTab);
4669 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004670 xmlFree(ctxt);
4671}
4672
Daniel Veillard6eadf632003-01-23 18:29:16 +00004673/**
Daniel Veillardd2298792003-02-14 16:54:11 +00004674 * xmlRelaxNGNormExtSpace:
4675 * @value: a value
4676 *
4677 * Removes the leading and ending spaces of the value
4678 * The string is modified "in situ"
4679 */
4680static void
4681xmlRelaxNGNormExtSpace(xmlChar *value) {
4682 xmlChar *start = value;
4683 xmlChar *cur = value;
4684 if (value == NULL)
4685 return;
4686
4687 while (IS_BLANK(*cur)) cur++;
4688 if (cur == start) {
4689 do {
4690 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
4691 if (*cur == 0)
4692 return;
4693 start = cur;
4694 while (IS_BLANK(*cur)) cur++;
4695 if (*cur == 0) {
4696 *start = 0;
4697 return;
4698 }
4699 } while (1);
4700 } else {
4701 do {
4702 while ((*cur != 0) && (!IS_BLANK(*cur)))
4703 *start++ = *cur++;
4704 if (*cur == 0) {
4705 *start = 0;
4706 return;
4707 }
4708 /* don't try to normalize the inner spaces */
4709 while (IS_BLANK(*cur)) cur++;
4710 *start++ = *cur++;
4711 if (*cur == 0) {
4712 *start = 0;
4713 return;
4714 }
4715 } while (1);
4716 }
4717}
4718
4719/**
4720 * xmlRelaxNGCheckAttributes:
4721 * @ctxt: a Relax-NG parser context
4722 * @node: a Relax-NG node
4723 *
4724 * Check all the attributes on the given node
4725 */
4726static void
4727xmlRelaxNGCleanupAttributes(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
4728 xmlAttrPtr cur, next;
4729
4730 cur = node->properties;
4731 while (cur != NULL) {
4732 next = cur->next;
4733 if ((cur->ns == NULL) ||
4734 (xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
4735 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
4736 if ((!xmlStrEqual(node->name, BAD_CAST "element")) &&
4737 (!xmlStrEqual(node->name, BAD_CAST "attribute")) &&
4738 (!xmlStrEqual(node->name, BAD_CAST "ref")) &&
4739 (!xmlStrEqual(node->name, BAD_CAST "parentRef")) &&
Daniel Veillard2df2de22003-02-17 23:34:33 +00004740 (!xmlStrEqual(node->name, BAD_CAST "param")) &&
Daniel Veillardd2298792003-02-14 16:54:11 +00004741 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
4742 if (ctxt->error != NULL)
4743 ctxt->error(ctxt->userData,
4744 "Attribute %s is not allowed on %s\n",
4745 cur->name, node->name);
4746 ctxt->nbErrors++;
4747 }
4748 } else if (xmlStrEqual(cur->name, BAD_CAST "type")) {
4749 if ((!xmlStrEqual(node->name, BAD_CAST "value")) &&
4750 (!xmlStrEqual(node->name, BAD_CAST "data"))) {
4751 if (ctxt->error != NULL)
4752 ctxt->error(ctxt->userData,
4753 "Attribute %s is not allowed on %s\n",
4754 cur->name, node->name);
4755 ctxt->nbErrors++;
4756 }
4757 } else if (xmlStrEqual(cur->name, BAD_CAST "href")) {
4758 if ((!xmlStrEqual(node->name, BAD_CAST "externalRef")) &&
4759 (!xmlStrEqual(node->name, BAD_CAST "include"))) {
4760 if (ctxt->error != NULL)
4761 ctxt->error(ctxt->userData,
4762 "Attribute %s is not allowed on %s\n",
4763 cur->name, node->name);
4764 ctxt->nbErrors++;
4765 }
4766 } else if (xmlStrEqual(cur->name, BAD_CAST "combine")) {
4767 if ((!xmlStrEqual(node->name, BAD_CAST "start")) &&
4768 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
4769 if (ctxt->error != NULL)
4770 ctxt->error(ctxt->userData,
4771 "Attribute %s is not allowed on %s\n",
4772 cur->name, node->name);
4773 ctxt->nbErrors++;
4774 }
4775 } else if (xmlStrEqual(cur->name, BAD_CAST "datatypeLibrary")) {
4776 xmlChar *val;
4777 xmlURIPtr uri;
4778
4779 val = xmlNodeListGetString(node->doc, cur->children, 1);
4780 if (val != NULL) {
4781 if (val[0] != 0) {
4782 uri = xmlParseURI((const char *) val);
4783 if (uri == NULL) {
4784 if (ctxt->error != NULL)
4785 ctxt->error(ctxt->userData,
4786 "Attribute %s contains invalid URI %s\n",
4787 cur->name, val);
4788 ctxt->nbErrors++;
4789 } else {
4790 if (uri->scheme == NULL) {
4791 if (ctxt->error != NULL)
4792 ctxt->error(ctxt->userData,
4793 "Attribute %s URI %s is not absolute\n",
4794 cur->name, val);
4795 ctxt->nbErrors++;
4796 }
4797 if (uri->fragment != NULL) {
4798 if (ctxt->error != NULL)
4799 ctxt->error(ctxt->userData,
4800 "Attribute %s URI %s has a fragment ID\n",
4801 cur->name, val);
4802 ctxt->nbErrors++;
4803 }
4804 xmlFreeURI(uri);
4805 }
4806 }
4807 xmlFree(val);
4808 }
4809 } else if (!xmlStrEqual(cur->name, BAD_CAST "ns")) {
4810 if (ctxt->error != NULL)
4811 ctxt->error(ctxt->userData,
4812 "Unknown attribute %s on %s\n",
4813 cur->name, node->name);
4814 ctxt->nbErrors++;
4815 }
4816 }
4817 cur = next;
4818 }
4819}
4820
4821/**
Daniel Veillardc5312d72003-02-21 17:14:10 +00004822 * xmlRelaxNGCleanupTree:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004823 * @ctxt: a Relax-NG parser context
Daniel Veillardc5312d72003-02-21 17:14:10 +00004824 * @root: an xmlNodePtr subtree
Daniel Veillard6eadf632003-01-23 18:29:16 +00004825 *
Daniel Veillardc5312d72003-02-21 17:14:10 +00004826 * Cleanup the subtree from unwanted nodes for parsing, resolve
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004827 * Include and externalRef lookups.
Daniel Veillard6eadf632003-01-23 18:29:16 +00004828 */
Daniel Veillardc5312d72003-02-21 17:14:10 +00004829static void
4830xmlRelaxNGCleanupTree(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr root) {
4831 xmlNodePtr cur, delete;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004832
Daniel Veillard6eadf632003-01-23 18:29:16 +00004833 delete = NULL;
4834 cur = root;
4835 while (cur != NULL) {
4836 if (delete != NULL) {
4837 xmlUnlinkNode(delete);
4838 xmlFreeNode(delete);
4839 delete = NULL;
4840 }
4841 if (cur->type == XML_ELEMENT_NODE) {
4842 /*
4843 * Simplification 4.1. Annotations
4844 */
4845 if ((cur->ns == NULL) ||
4846 (!xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
Daniel Veillardd2298792003-02-14 16:54:11 +00004847 if ((cur->parent != NULL) &&
4848 (cur->parent->type == XML_ELEMENT_NODE) &&
4849 ((xmlStrEqual(cur->parent->name, BAD_CAST "name")) ||
4850 (xmlStrEqual(cur->parent->name, BAD_CAST "value")) ||
4851 (xmlStrEqual(cur->parent->name, BAD_CAST "param")))) {
4852 if (ctxt->error != NULL)
4853 ctxt->error(ctxt->userData,
4854 "element %s doesn't allow foreign elements\n",
4855 cur->parent->name);
4856 ctxt->nbErrors++;
4857 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004858 delete = cur;
4859 goto skip_children;
4860 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00004861 xmlRelaxNGCleanupAttributes(ctxt, cur);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004862 if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004863 xmlChar *href, *ns, *base, *URL;
4864 xmlRelaxNGDocumentPtr docu;
Daniel Veillard416589a2003-02-17 17:25:42 +00004865 xmlNodePtr tmp;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004866
4867 ns = xmlGetProp(cur, BAD_CAST "ns");
Daniel Veillard416589a2003-02-17 17:25:42 +00004868 if (ns == NULL) {
4869 tmp = cur->parent;
4870 while ((tmp != NULL) &&
4871 (tmp->type == XML_ELEMENT_NODE)) {
4872 ns = xmlGetProp(tmp, BAD_CAST "ns");
4873 if (ns != NULL)
4874 break;
4875 tmp = tmp->parent;
4876 }
4877 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004878 href = xmlGetProp(cur, BAD_CAST "href");
4879 if (href == NULL) {
4880 if (ctxt->error != NULL)
4881 ctxt->error(ctxt->userData,
4882 "xmlRelaxNGParse: externalRef has no href attribute\n");
4883 ctxt->nbErrors++;
4884 delete = cur;
4885 goto skip_children;
4886 }
4887 base = xmlNodeGetBase(cur->doc, cur);
4888 URL = xmlBuildURI(href, base);
4889 if (URL == NULL) {
4890 if (ctxt->error != NULL)
4891 ctxt->error(ctxt->userData,
4892 "Failed to compute URL for externalRef %s\n", href);
4893 ctxt->nbErrors++;
4894 if (href != NULL)
4895 xmlFree(href);
4896 if (base != NULL)
4897 xmlFree(base);
4898 delete = cur;
4899 goto skip_children;
4900 }
4901 if (href != NULL)
4902 xmlFree(href);
4903 if (base != NULL)
4904 xmlFree(base);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004905 docu = xmlRelaxNGLoadExternalRef(ctxt, URL, ns);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004906 if (docu == NULL) {
4907 if (ctxt->error != NULL)
4908 ctxt->error(ctxt->userData,
4909 "Failed to load externalRef %s\n", URL);
4910 ctxt->nbErrors++;
4911 xmlFree(URL);
4912 delete = cur;
4913 goto skip_children;
4914 }
4915 xmlFree(URL);
4916 cur->_private = docu;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004917 } else if (xmlStrEqual(cur->name, BAD_CAST "include")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00004918 xmlChar *href, *ns, *base, *URL;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004919 xmlRelaxNGIncludePtr incl;
Daniel Veillard416589a2003-02-17 17:25:42 +00004920 xmlNodePtr tmp;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004921
4922 href = xmlGetProp(cur, BAD_CAST "href");
4923 if (href == NULL) {
4924 if (ctxt->error != NULL)
4925 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004926 "xmlRelaxNGParse: include has no href attribute\n");
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004927 ctxt->nbErrors++;
4928 delete = cur;
4929 goto skip_children;
4930 }
4931 base = xmlNodeGetBase(cur->doc, cur);
4932 URL = xmlBuildURI(href, base);
4933 if (URL == NULL) {
4934 if (ctxt->error != NULL)
4935 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004936 "Failed to compute URL for include %s\n", href);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004937 ctxt->nbErrors++;
4938 if (href != NULL)
4939 xmlFree(href);
4940 if (base != NULL)
4941 xmlFree(base);
4942 delete = cur;
4943 goto skip_children;
4944 }
4945 if (href != NULL)
4946 xmlFree(href);
4947 if (base != NULL)
4948 xmlFree(base);
Daniel Veillard416589a2003-02-17 17:25:42 +00004949 ns = xmlGetProp(cur, BAD_CAST "ns");
4950 if (ns == NULL) {
4951 tmp = cur->parent;
4952 while ((tmp != NULL) &&
4953 (tmp->type == XML_ELEMENT_NODE)) {
4954 ns = xmlGetProp(tmp, BAD_CAST "ns");
4955 if (ns != NULL)
4956 break;
4957 tmp = tmp->parent;
4958 }
4959 }
4960 incl = xmlRelaxNGLoadInclude(ctxt, URL, cur, ns);
4961 if (ns != NULL)
4962 xmlFree(ns);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004963 if (incl == NULL) {
4964 if (ctxt->error != NULL)
4965 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004966 "Failed to load include %s\n", URL);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004967 ctxt->nbErrors++;
4968 xmlFree(URL);
4969 delete = cur;
4970 goto skip_children;
4971 }
4972 xmlFree(URL);
4973 cur->_private = incl;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004974 } else if ((xmlStrEqual(cur->name, BAD_CAST "element")) ||
4975 (xmlStrEqual(cur->name, BAD_CAST "attribute"))) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00004976 xmlChar *name, *ns;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004977 xmlNodePtr text = NULL;
4978
4979 /*
4980 * Simplification 4.8. name attribute of element
4981 * and attribute elements
4982 */
4983 name = xmlGetProp(cur, BAD_CAST "name");
4984 if (name != NULL) {
4985 if (cur->children == NULL) {
4986 text = xmlNewChild(cur, cur->ns, BAD_CAST "name",
4987 name);
4988 } else {
4989 xmlNodePtr node;
4990 node = xmlNewNode(cur->ns, BAD_CAST "name");
4991 if (node != NULL) {
4992 xmlAddPrevSibling(cur->children, node);
4993 text = xmlNewText(name);
4994 xmlAddChild(node, text);
4995 text = node;
4996 }
4997 }
Daniel Veillardfebcca42003-02-16 15:44:18 +00004998 if (text == NULL) {
4999 if (ctxt->error != NULL)
5000 ctxt->error(ctxt->userData,
5001 "Failed to create a name %s element\n", name);
5002 ctxt->nbErrors++;
5003 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005004 xmlUnsetProp(cur, BAD_CAST "name");
5005 xmlFree(name);
Daniel Veillardfebcca42003-02-16 15:44:18 +00005006 ns = xmlGetProp(cur, BAD_CAST "ns");
5007 if (ns != NULL) {
5008 if (text != NULL) {
5009 xmlSetProp(text, BAD_CAST "ns", ns);
5010 /* xmlUnsetProp(cur, BAD_CAST "ns"); */
Daniel Veillard6eadf632003-01-23 18:29:16 +00005011 }
Daniel Veillardfebcca42003-02-16 15:44:18 +00005012 xmlFree(ns);
5013 } else if (xmlStrEqual(cur->name,
5014 BAD_CAST "attribute")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005015 xmlSetProp(text, BAD_CAST "ns", BAD_CAST "");
5016 }
5017 }
5018 } else if ((xmlStrEqual(cur->name, BAD_CAST "name")) ||
5019 (xmlStrEqual(cur->name, BAD_CAST "nsName")) ||
5020 (xmlStrEqual(cur->name, BAD_CAST "value"))) {
5021 /*
5022 * Simplification 4.8. name attribute of element
5023 * and attribute elements
5024 */
5025 if (xmlHasProp(cur, BAD_CAST "ns") == NULL) {
5026 xmlNodePtr node;
5027 xmlChar *ns = NULL;
5028
5029 node = cur->parent;
5030 while ((node != NULL) &&
5031 (node->type == XML_ELEMENT_NODE)) {
5032 ns = xmlGetProp(node, BAD_CAST "ns");
5033 if (ns != NULL) {
5034 break;
5035 }
5036 node = node->parent;
5037 }
5038 if (ns == NULL) {
5039 xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
5040 } else {
5041 xmlSetProp(cur, BAD_CAST "ns", ns);
5042 xmlFree(ns);
5043 }
5044 }
5045 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
5046 xmlChar *name, *local, *prefix;
5047
5048 /*
5049 * Simplification: 4.10. QNames
5050 */
5051 name = xmlNodeGetContent(cur);
5052 if (name != NULL) {
5053 local = xmlSplitQName2(name, &prefix);
5054 if (local != NULL) {
5055 xmlNsPtr ns;
5056
5057 ns = xmlSearchNs(cur->doc, cur, prefix);
5058 if (ns == NULL) {
5059 if (ctxt->error != NULL)
5060 ctxt->error(ctxt->userData,
5061 "xmlRelaxNGParse: no namespace for prefix %s\n", prefix);
5062 ctxt->nbErrors++;
5063 } else {
5064 xmlSetProp(cur, BAD_CAST "ns", ns->href);
5065 xmlNodeSetContent(cur, local);
5066 }
5067 xmlFree(local);
5068 xmlFree(prefix);
5069 }
5070 xmlFree(name);
5071 }
5072 }
Daniel Veillard44e1dd02003-02-21 23:23:28 +00005073 /*
5074 * 4.16
5075 */
Daniel Veillardc5312d72003-02-21 17:14:10 +00005076 if (xmlStrEqual(cur->name, BAD_CAST "nsName")) {
5077 if (ctxt->flags & XML_RELAXNG_IN_NSEXCEPT) {
5078 if (ctxt->error != NULL)
5079 ctxt->error(ctxt->userData,
5080 "Found nsName/except//nsName forbidden construct\n");
5081 ctxt->nbErrors++;
5082 }
5083 }
5084 } else if ((xmlStrEqual(cur->name, BAD_CAST "except")) &&
5085 (cur != root)) {
5086 int oldflags = ctxt->flags;
5087
Daniel Veillard44e1dd02003-02-21 23:23:28 +00005088 /*
5089 * 4.16
5090 */
Daniel Veillardc5312d72003-02-21 17:14:10 +00005091 if ((cur->parent != NULL) &&
5092 (xmlStrEqual(cur->parent->name, BAD_CAST "anyName"))) {
5093 ctxt->flags |= XML_RELAXNG_IN_ANYEXCEPT;
5094 xmlRelaxNGCleanupTree(ctxt, cur);
5095 ctxt->flags = oldflags;
5096 goto skip_children;
5097 } else if ((cur->parent != NULL) &&
5098 (xmlStrEqual(cur->parent->name, BAD_CAST "nsName"))) {
5099 ctxt->flags |= XML_RELAXNG_IN_NSEXCEPT;
5100 xmlRelaxNGCleanupTree(ctxt, cur);
5101 ctxt->flags = oldflags;
5102 goto skip_children;
5103 }
5104 } else if (xmlStrEqual(cur->name, BAD_CAST "anyName")) {
Daniel Veillard44e1dd02003-02-21 23:23:28 +00005105 /*
5106 * 4.16
5107 */
Daniel Veillardc5312d72003-02-21 17:14:10 +00005108 if (ctxt->flags & XML_RELAXNG_IN_ANYEXCEPT) {
5109 if (ctxt->error != NULL)
5110 ctxt->error(ctxt->userData,
5111 "Found anyName/except//anyName forbidden construct\n");
5112 ctxt->nbErrors++;
5113 } else if (ctxt->flags & XML_RELAXNG_IN_NSEXCEPT) {
5114 if (ctxt->error != NULL)
5115 ctxt->error(ctxt->userData,
5116 "Found nsName/except//anyName forbidden construct\n");
5117 ctxt->nbErrors++;
5118 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005119 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005120 /*
5121 * Thisd is not an else since "include" is transformed
5122 * into a div
5123 */
5124 if (xmlStrEqual(cur->name, BAD_CAST "div")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005125 xmlChar *ns;
5126 xmlNodePtr child, ins, tmp;
5127
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005128 /*
5129 * implements rule 4.11
5130 */
Daniel Veillard416589a2003-02-17 17:25:42 +00005131
5132 ns = xmlGetProp(cur, BAD_CAST "ns");
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005133
5134 child = cur->children;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005135 ins = cur;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005136 while (child != NULL) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005137 if (ns != NULL) {
5138 if (!xmlHasProp(child, BAD_CAST "ns")) {
5139 xmlSetProp(child, BAD_CAST "ns", ns);
5140 }
5141 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005142 tmp = child->next;
5143 xmlUnlinkNode(child);
5144 ins = xmlAddNextSibling(ins, child);
5145 child = tmp;
5146 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005147 if (ns != NULL)
5148 xmlFree(ns);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005149 delete = cur;
5150 goto skip_children;
5151 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005152 }
5153 }
5154 /*
5155 * Simplification 4.2 whitespaces
5156 */
5157 else if (cur->type == XML_TEXT_NODE) {
5158 if (IS_BLANK_NODE(cur)) {
5159 if (cur->parent->type == XML_ELEMENT_NODE) {
5160 if ((!xmlStrEqual(cur->parent->name, BAD_CAST "value")) &&
5161 (!xmlStrEqual(cur->parent->name, BAD_CAST "param")))
5162 delete = cur;
5163 } else {
5164 delete = cur;
5165 goto skip_children;
5166 }
5167 }
5168 } else if (cur->type != XML_CDATA_SECTION_NODE) {
5169 delete = cur;
5170 goto skip_children;
5171 }
5172
5173 /*
5174 * Skip to next node
5175 */
5176 if (cur->children != NULL) {
5177 if ((cur->children->type != XML_ENTITY_DECL) &&
5178 (cur->children->type != XML_ENTITY_REF_NODE) &&
5179 (cur->children->type != XML_ENTITY_NODE)) {
5180 cur = cur->children;
5181 continue;
5182 }
5183 }
5184skip_children:
5185 if (cur->next != NULL) {
5186 cur = cur->next;
5187 continue;
5188 }
5189
5190 do {
5191 cur = cur->parent;
5192 if (cur == NULL)
5193 break;
5194 if (cur == root) {
5195 cur = NULL;
5196 break;
5197 }
5198 if (cur->next != NULL) {
5199 cur = cur->next;
5200 break;
5201 }
5202 } while (cur != NULL);
5203 }
5204 if (delete != NULL) {
5205 xmlUnlinkNode(delete);
5206 xmlFreeNode(delete);
5207 delete = NULL;
5208 }
Daniel Veillardc5312d72003-02-21 17:14:10 +00005209}
Daniel Veillard6eadf632003-01-23 18:29:16 +00005210
Daniel Veillardc5312d72003-02-21 17:14:10 +00005211/**
5212 * xmlRelaxNGCleanupDoc:
5213 * @ctxt: a Relax-NG parser context
5214 * @doc: an xmldocPtr document pointer
5215 *
5216 * Cleanup the document from unwanted nodes for parsing, resolve
5217 * Include and externalRef lookups.
5218 *
5219 * Returns the cleaned up document or NULL in case of error
5220 */
5221static xmlDocPtr
5222xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt, xmlDocPtr doc) {
5223 xmlNodePtr root;
5224
5225 /*
5226 * Extract the root
5227 */
5228 root = xmlDocGetRootElement(doc);
5229 if (root == NULL) {
5230 if (ctxt->error != NULL)
5231 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
5232 ctxt->URL);
5233 ctxt->nbErrors++;
5234 return (NULL);
5235 }
5236 xmlRelaxNGCleanupTree(ctxt, root);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005237 return(doc);
5238}
5239
5240/**
5241 * xmlRelaxNGParse:
5242 * @ctxt: a Relax-NG parser context
5243 *
5244 * parse a schema definition resource and build an internal
5245 * XML Shema struture which can be used to validate instances.
5246 * *WARNING* this interface is highly subject to change
5247 *
5248 * Returns the internal XML RelaxNG structure built from the resource or
5249 * NULL in case of error
5250 */
5251xmlRelaxNGPtr
5252xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt)
5253{
5254 xmlRelaxNGPtr ret = NULL;
5255 xmlDocPtr doc;
5256 xmlNodePtr root;
5257
5258 xmlRelaxNGInitTypes();
5259
5260 if (ctxt == NULL)
5261 return (NULL);
5262
5263 /*
5264 * First step is to parse the input document into an DOM/Infoset
5265 */
5266 if (ctxt->URL != NULL) {
5267 doc = xmlParseFile((const char *) ctxt->URL);
5268 if (doc == NULL) {
5269 if (ctxt->error != NULL)
5270 ctxt->error(ctxt->userData,
5271 "xmlRelaxNGParse: could not load %s\n", ctxt->URL);
5272 ctxt->nbErrors++;
5273 return (NULL);
5274 }
5275 } else if (ctxt->buffer != NULL) {
5276 doc = xmlParseMemory(ctxt->buffer, ctxt->size);
5277 if (doc == NULL) {
5278 if (ctxt->error != NULL)
5279 ctxt->error(ctxt->userData,
5280 "xmlRelaxNGParse: could not parse schemas\n");
5281 ctxt->nbErrors++;
5282 return (NULL);
5283 }
5284 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
5285 ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
5286 } else {
5287 if (ctxt->error != NULL)
5288 ctxt->error(ctxt->userData,
5289 "xmlRelaxNGParse: nothing to parse\n");
5290 ctxt->nbErrors++;
5291 return (NULL);
5292 }
5293 ctxt->document = doc;
5294
5295 /*
5296 * Some preprocessing of the document content
5297 */
5298 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
5299 if (doc == NULL) {
5300 xmlFreeDoc(ctxt->document);
5301 ctxt->document = NULL;
5302 return(NULL);
5303 }
5304
Daniel Veillard6eadf632003-01-23 18:29:16 +00005305 /*
5306 * Then do the parsing for good
5307 */
5308 root = xmlDocGetRootElement(doc);
5309 if (root == NULL) {
5310 if (ctxt->error != NULL)
5311 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
5312 ctxt->URL);
5313 ctxt->nbErrors++;
5314 return (NULL);
5315 }
5316 ret = xmlRelaxNGParseDocument(ctxt, root);
5317 if (ret == NULL)
5318 return(NULL);
5319
5320 /*
5321 * Check the ref/defines links
5322 */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005323 /*
5324 * try to preprocess interleaves
5325 */
5326 if (ctxt->interleaves != NULL) {
5327 xmlHashScan(ctxt->interleaves,
5328 (xmlHashScanner)xmlRelaxNGComputeInterleaves, ctxt);
5329 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005330
5331 /*
5332 * if there was a parsing error return NULL
5333 */
5334 if (ctxt->nbErrors > 0) {
5335 xmlRelaxNGFree(ret);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005336 ctxt->document = NULL;
5337 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005338 return(NULL);
5339 }
5340
5341 /*
5342 * Transfer the pointer for cleanup at the schema level.
5343 */
5344 ret->doc = doc;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005345 ctxt->document = NULL;
5346 ret->documents = ctxt->documents;
5347 ctxt->documents = NULL;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005348 ret->includes = ctxt->includes;
5349 ctxt->includes = NULL;
Daniel Veillard419a7682003-02-03 23:22:49 +00005350 ret->defNr = ctxt->defNr;
5351 ret->defTab = ctxt->defTab;
5352 ctxt->defTab = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005353
5354 return (ret);
5355}
5356
5357/**
5358 * xmlRelaxNGSetParserErrors:
5359 * @ctxt: a Relax-NG validation context
5360 * @err: the error callback
5361 * @warn: the warning callback
5362 * @ctx: contextual data for the callbacks
5363 *
5364 * Set the callback functions used to handle errors for a validation context
5365 */
5366void
5367xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
5368 xmlRelaxNGValidityErrorFunc err,
5369 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
5370 if (ctxt == NULL)
5371 return;
5372 ctxt->error = err;
5373 ctxt->warning = warn;
5374 ctxt->userData = ctx;
5375}
5376/************************************************************************
5377 * *
5378 * Dump back a compiled form *
5379 * *
5380 ************************************************************************/
5381static void xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define);
5382
5383/**
5384 * xmlRelaxNGDumpDefines:
5385 * @output: the file output
5386 * @defines: a list of define structures
5387 *
5388 * Dump a RelaxNG structure back
5389 */
5390static void
5391xmlRelaxNGDumpDefines(FILE * output, xmlRelaxNGDefinePtr defines) {
5392 while (defines != NULL) {
5393 xmlRelaxNGDumpDefine(output, defines);
5394 defines = defines->next;
5395 }
5396}
5397
5398/**
5399 * xmlRelaxNGDumpDefine:
5400 * @output: the file output
5401 * @define: a define structure
5402 *
5403 * Dump a RelaxNG structure back
5404 */
5405static void
5406xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define) {
5407 if (define == NULL)
5408 return;
5409 switch(define->type) {
5410 case XML_RELAXNG_EMPTY:
5411 fprintf(output, "<empty/>\n");
5412 break;
5413 case XML_RELAXNG_NOT_ALLOWED:
5414 fprintf(output, "<notAllowed/>\n");
5415 break;
5416 case XML_RELAXNG_TEXT:
5417 fprintf(output, "<text/>\n");
5418 break;
5419 case XML_RELAXNG_ELEMENT:
5420 fprintf(output, "<element>\n");
5421 if (define->name != NULL) {
5422 fprintf(output, "<name");
5423 if (define->ns != NULL)
5424 fprintf(output, " ns=\"%s\"", define->ns);
5425 fprintf(output, ">%s</name>\n", define->name);
5426 }
5427 xmlRelaxNGDumpDefines(output, define->attrs);
5428 xmlRelaxNGDumpDefines(output, define->content);
5429 fprintf(output, "</element>\n");
5430 break;
5431 case XML_RELAXNG_LIST:
5432 fprintf(output, "<list>\n");
5433 xmlRelaxNGDumpDefines(output, define->content);
5434 fprintf(output, "</list>\n");
5435 break;
5436 case XML_RELAXNG_ONEORMORE:
5437 fprintf(output, "<oneOrMore>\n");
5438 xmlRelaxNGDumpDefines(output, define->content);
5439 fprintf(output, "</oneOrMore>\n");
5440 break;
5441 case XML_RELAXNG_ZEROORMORE:
5442 fprintf(output, "<zeroOrMore>\n");
5443 xmlRelaxNGDumpDefines(output, define->content);
5444 fprintf(output, "</zeroOrMore>\n");
5445 break;
5446 case XML_RELAXNG_CHOICE:
5447 fprintf(output, "<choice>\n");
5448 xmlRelaxNGDumpDefines(output, define->content);
5449 fprintf(output, "</choice>\n");
5450 break;
5451 case XML_RELAXNG_GROUP:
5452 fprintf(output, "<group>\n");
5453 xmlRelaxNGDumpDefines(output, define->content);
5454 fprintf(output, "</group>\n");
5455 break;
5456 case XML_RELAXNG_INTERLEAVE:
5457 fprintf(output, "<interleave>\n");
5458 xmlRelaxNGDumpDefines(output, define->content);
5459 fprintf(output, "</interleave>\n");
5460 break;
5461 case XML_RELAXNG_OPTIONAL:
5462 fprintf(output, "<optional>\n");
5463 xmlRelaxNGDumpDefines(output, define->content);
5464 fprintf(output, "</optional>\n");
5465 break;
5466 case XML_RELAXNG_ATTRIBUTE:
5467 fprintf(output, "<attribute>\n");
5468 xmlRelaxNGDumpDefines(output, define->content);
5469 fprintf(output, "</attribute>\n");
5470 break;
5471 case XML_RELAXNG_DEF:
5472 fprintf(output, "<define");
5473 if (define->name != NULL)
5474 fprintf(output, " name=\"%s\"", define->name);
5475 fprintf(output, ">\n");
5476 xmlRelaxNGDumpDefines(output, define->content);
5477 fprintf(output, "</define>\n");
5478 break;
5479 case XML_RELAXNG_REF:
5480 fprintf(output, "<ref");
5481 if (define->name != NULL)
5482 fprintf(output, " name=\"%s\"", define->name);
5483 fprintf(output, ">\n");
5484 xmlRelaxNGDumpDefines(output, define->content);
5485 fprintf(output, "</ref>\n");
5486 break;
Daniel Veillard419a7682003-02-03 23:22:49 +00005487 case XML_RELAXNG_PARENTREF:
5488 fprintf(output, "<parentRef");
5489 if (define->name != NULL)
5490 fprintf(output, " name=\"%s\"", define->name);
5491 fprintf(output, ">\n");
5492 xmlRelaxNGDumpDefines(output, define->content);
5493 fprintf(output, "</parentRef>\n");
5494 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005495 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard416589a2003-02-17 17:25:42 +00005496 fprintf(output, "<externalRef>");
Daniel Veillarde431a272003-01-29 23:02:33 +00005497 xmlRelaxNGDumpDefines(output, define->content);
5498 fprintf(output, "</externalRef>\n");
5499 break;
5500 case XML_RELAXNG_DATATYPE:
Daniel Veillard6eadf632003-01-23 18:29:16 +00005501 case XML_RELAXNG_VALUE:
5502 TODO
5503 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005504 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00005505 case XML_RELAXNG_EXCEPT:
Daniel Veillard8fe98712003-02-19 00:19:14 +00005506 case XML_RELAXNG_PARAM:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005507 TODO
5508 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00005509 case XML_RELAXNG_NOOP:
5510 xmlRelaxNGDumpDefines(output, define->content);
5511 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005512 }
5513}
5514
5515/**
5516 * xmlRelaxNGDumpGrammar:
5517 * @output: the file output
5518 * @grammar: a grammar structure
5519 * @top: is this a top grammar
5520 *
5521 * Dump a RelaxNG structure back
5522 */
5523static void
5524xmlRelaxNGDumpGrammar(FILE * output, xmlRelaxNGGrammarPtr grammar, int top)
5525{
5526 if (grammar == NULL)
5527 return;
5528
5529 fprintf(output, "<grammar");
5530 if (top)
5531 fprintf(output,
5532 " xmlns=\"http://relaxng.org/ns/structure/1.0\"");
5533 switch(grammar->combine) {
5534 case XML_RELAXNG_COMBINE_UNDEFINED:
5535 break;
5536 case XML_RELAXNG_COMBINE_CHOICE:
5537 fprintf(output, " combine=\"choice\"");
5538 break;
5539 case XML_RELAXNG_COMBINE_INTERLEAVE:
5540 fprintf(output, " combine=\"interleave\"");
5541 break;
5542 default:
5543 fprintf(output, " <!-- invalid combine value -->");
5544 }
5545 fprintf(output, ">\n");
5546 if (grammar->start == NULL) {
5547 fprintf(output, " <!-- grammar had no start -->");
5548 } else {
5549 fprintf(output, "<start>\n");
5550 xmlRelaxNGDumpDefine(output, grammar->start);
5551 fprintf(output, "</start>\n");
5552 }
5553 /* TODO ? Dump the defines ? */
5554 fprintf(output, "</grammar>\n");
5555}
5556
5557/**
5558 * xmlRelaxNGDump:
5559 * @output: the file output
5560 * @schema: a schema structure
5561 *
5562 * Dump a RelaxNG structure back
5563 */
5564void
5565xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema)
5566{
5567 if (schema == NULL) {
5568 fprintf(output, "RelaxNG empty or failed to compile\n");
5569 return;
5570 }
5571 fprintf(output, "RelaxNG: ");
5572 if (schema->doc == NULL) {
5573 fprintf(output, "no document\n");
5574 } else if (schema->doc->URL != NULL) {
5575 fprintf(output, "%s\n", schema->doc->URL);
5576 } else {
5577 fprintf(output, "\n");
5578 }
5579 if (schema->topgrammar == NULL) {
5580 fprintf(output, "RelaxNG has no top grammar\n");
5581 return;
5582 }
5583 xmlRelaxNGDumpGrammar(output, schema->topgrammar, 1);
5584}
5585
Daniel Veillardfebcca42003-02-16 15:44:18 +00005586/**
5587 * xmlRelaxNGDumpTree:
5588 * @output: the file output
5589 * @schema: a schema structure
5590 *
5591 * Dump the transformed RelaxNG tree.
5592 */
5593void
5594xmlRelaxNGDumpTree(FILE * output, xmlRelaxNGPtr schema)
5595{
5596 if (schema == NULL) {
5597 fprintf(output, "RelaxNG empty or failed to compile\n");
5598 return;
5599 }
5600 if (schema->doc == NULL) {
5601 fprintf(output, "no document\n");
5602 } else {
5603 xmlDocDump(output, schema->doc);
5604 }
5605}
5606
Daniel Veillard6eadf632003-01-23 18:29:16 +00005607/************************************************************************
5608 * *
5609 * Validation implementation *
5610 * *
5611 ************************************************************************/
5612static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
5613 xmlRelaxNGDefinePtr define);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005614static int xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
5615 xmlRelaxNGDefinePtr define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005616
5617/**
5618 * xmlRelaxNGSkipIgnored:
5619 * @ctxt: a schema validation context
5620 * @node: the top node.
5621 *
5622 * Skip ignorable nodes in that context
5623 *
5624 * Returns the new sibling or NULL in case of error.
5625 */
5626static xmlNodePtr
5627xmlRelaxNGSkipIgnored(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
5628 xmlNodePtr node) {
5629 /*
5630 * TODO complete and handle entities
5631 */
5632 while ((node != NULL) &&
5633 ((node->type == XML_COMMENT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005634 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00005635 ((node->type == XML_TEXT_NODE) &&
5636 (IS_BLANK_NODE(node))))) {
5637 node = node->next;
5638 }
5639 return(node);
5640}
5641
5642/**
Daniel Veillardedc91922003-01-26 00:52:04 +00005643 * xmlRelaxNGNormalize:
5644 * @ctxt: a schema validation context
5645 * @str: the string to normalize
5646 *
5647 * Implements the normalizeWhiteSpace( s ) function from
5648 * section 6.2.9 of the spec
5649 *
5650 * Returns the new string or NULL in case of error.
5651 */
5652static xmlChar *
5653xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *str) {
5654 xmlChar *ret, *p;
5655 const xmlChar *tmp;
5656 int len;
5657
5658 if (str == NULL)
5659 return(NULL);
5660 tmp = str;
5661 while (*tmp != 0) tmp++;
5662 len = tmp - str;
5663
5664 ret = (xmlChar *) xmlMalloc((len + 1) * sizeof(xmlChar));
5665 if (ret == NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00005666 if (ctxt != NULL) {
5667 VALID_CTXT();
5668 VALID_ERROR("xmlRelaxNGNormalize: out of memory\n");
5669 } else {
5670 xmlGenericError(xmlGenericErrorContext,
5671 "xmlRelaxNGNormalize: out of memory\n");
5672 }
Daniel Veillardedc91922003-01-26 00:52:04 +00005673 return(NULL);
5674 }
5675 p = ret;
5676 while (IS_BLANK(*str)) str++;
5677 while (*str != 0) {
5678 if (IS_BLANK(*str)) {
5679 while (IS_BLANK(*str)) str++;
5680 if (*str == 0)
5681 break;
5682 *p++ = ' ';
5683 } else
5684 *p++ = *str++;
5685 }
5686 *p = 0;
5687 return(ret);
5688}
5689
5690/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005691 * xmlRelaxNGValidateDatatype:
5692 * @ctxt: a Relax-NG validation context
5693 * @value: the string value
5694 * @type: the datatype definition
5695 *
5696 * Validate the given value against the dataype
5697 *
5698 * Returns 0 if the validation succeeded or an error code.
5699 */
5700static int
5701xmlRelaxNGValidateDatatype(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *value,
5702 xmlRelaxNGDefinePtr define) {
5703 int ret;
5704 xmlRelaxNGTypeLibraryPtr lib;
5705
5706 if ((define == NULL) || (define->data == NULL)) {
5707 return(-1);
5708 }
5709 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
5710 if (lib->check != NULL)
5711 ret = lib->check(lib->data, define->name, value);
5712 else
5713 ret = -1;
5714 if (ret < 0) {
5715 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005716 VALID_ERROR2("Internal: failed to validate type %s\n", define->name);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005717 return(-1);
5718 } else if (ret == 1) {
5719 ret = 0;
5720 } else {
5721 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005722 VALID_ERROR3("Type %s doesn't allow value %s\n", define->name, value);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005723 return(-1);
5724 ret = -1;
5725 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005726 if ((ret == 0) && (define->content != NULL)) {
5727 const xmlChar *oldvalue, *oldendvalue;
5728
5729 oldvalue = ctxt->state->value;
5730 oldendvalue = ctxt->state->endvalue;
5731 ctxt->state->value = (xmlChar *) value;
5732 ctxt->state->endvalue = NULL;
5733 ret = xmlRelaxNGValidateValue(ctxt, define->content);
5734 ctxt->state->value = (xmlChar *) oldvalue;
5735 ctxt->state->endvalue = (xmlChar *) oldendvalue;
5736 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005737 return(ret);
5738}
5739
5740/**
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005741 * xmlRelaxNGNextValue:
5742 * @ctxt: a Relax-NG validation context
5743 *
5744 * Skip to the next value when validating within a list
5745 *
5746 * Returns 0 if the operation succeeded or an error code.
5747 */
5748static int
5749xmlRelaxNGNextValue(xmlRelaxNGValidCtxtPtr ctxt) {
5750 xmlChar *cur;
5751
5752 cur = ctxt->state->value;
5753 if ((cur == NULL) || (ctxt->state->endvalue == NULL)) {
5754 ctxt->state->value = NULL;
Daniel Veillarde5b110b2003-02-04 14:43:39 +00005755 ctxt->state->endvalue = NULL;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005756 return(0);
5757 }
5758 while (*cur != 0) cur++;
5759 while ((cur != ctxt->state->endvalue) && (*cur == 0)) cur++;
5760 if (cur == ctxt->state->endvalue)
5761 ctxt->state->value = NULL;
5762 else
5763 ctxt->state->value = cur;
5764 return(0);
5765}
5766
5767/**
5768 * xmlRelaxNGValidateValueList:
5769 * @ctxt: a Relax-NG validation context
5770 * @defines: the list of definitions to verify
5771 *
5772 * Validate the given set of definitions for the current value
5773 *
5774 * Returns 0 if the validation succeeded or an error code.
5775 */
5776static int
5777xmlRelaxNGValidateValueList(xmlRelaxNGValidCtxtPtr ctxt,
5778 xmlRelaxNGDefinePtr defines) {
5779 int ret = 0;
5780
5781 while (defines != NULL) {
5782 ret = xmlRelaxNGValidateValue(ctxt, defines);
5783 if (ret != 0)
5784 break;
5785 defines = defines->next;
5786 }
5787 return(ret);
5788}
5789
5790/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00005791 * xmlRelaxNGValidateValue:
5792 * @ctxt: a Relax-NG validation context
5793 * @define: the definition to verify
5794 *
5795 * Validate the given definition for the current value
5796 *
5797 * Returns 0 if the validation succeeded or an error code.
5798 */
5799static int
5800xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
5801 xmlRelaxNGDefinePtr define) {
Daniel Veillardedc91922003-01-26 00:52:04 +00005802 int ret = 0, oldflags;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005803 xmlChar *value;
5804
5805 value = ctxt->state->value;
5806 switch (define->type) {
Daniel Veillardd4310742003-02-18 21:12:46 +00005807 case XML_RELAXNG_EMPTY: {
5808 if ((value != NULL) && (value[0] != 0)) {
5809 int idx = 0;
5810
5811 while (IS_BLANK(value[idx]))
5812 idx++;
5813 if (value[idx] != 0)
5814 ret = -1;
5815 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005816 break;
Daniel Veillardd4310742003-02-18 21:12:46 +00005817 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005818 case XML_RELAXNG_TEXT:
5819 break;
Daniel Veillardedc91922003-01-26 00:52:04 +00005820 case XML_RELAXNG_VALUE: {
5821 if (!xmlStrEqual(value, define->value)) {
5822 if (define->name != NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00005823 xmlRelaxNGTypeLibraryPtr lib;
5824
5825 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
5826 if ((lib != NULL) && (lib->comp != NULL))
5827 ret = lib->comp(lib->data, define->name, value,
5828 define->value);
5829 else
5830 ret = -1;
5831 if (ret < 0) {
5832 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005833 VALID_ERROR2("Internal: failed to compare type %s\n",
Daniel Veillardea3f3982003-01-26 19:45:18 +00005834 define->name);
5835 return(-1);
5836 } else if (ret == 1) {
5837 ret = 0;
5838 } else {
5839 ret = -1;
5840 }
Daniel Veillardedc91922003-01-26 00:52:04 +00005841 } else {
5842 xmlChar *nval, *nvalue;
5843
5844 /*
5845 * TODO: trivial optimizations are possible by
5846 * computing at compile-time
5847 */
5848 nval = xmlRelaxNGNormalize(ctxt, define->value);
5849 nvalue = xmlRelaxNGNormalize(ctxt, value);
5850
Daniel Veillardea3f3982003-01-26 19:45:18 +00005851 if ((nval == NULL) || (nvalue == NULL) ||
5852 (!xmlStrEqual(nval, nvalue)))
Daniel Veillardedc91922003-01-26 00:52:04 +00005853 ret = -1;
5854 if (nval != NULL)
5855 xmlFree(nval);
5856 if (nvalue != NULL)
5857 xmlFree(nvalue);
5858 }
5859 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005860 if (ret == 0)
5861 xmlRelaxNGNextValue(ctxt);
Daniel Veillardedc91922003-01-26 00:52:04 +00005862 break;
5863 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005864 case XML_RELAXNG_DATATYPE: {
5865 ret = xmlRelaxNGValidateDatatype(ctxt, value, define);
5866 if (ret == 0)
5867 xmlRelaxNGNextValue(ctxt);
5868
5869 break;
5870 }
5871 case XML_RELAXNG_CHOICE: {
5872 xmlRelaxNGDefinePtr list = define->content;
5873 xmlChar *oldvalue;
5874
5875 oldflags = ctxt->flags;
5876 ctxt->flags |= FLAGS_IGNORABLE;
5877
5878 oldvalue = ctxt->state->value;
5879 while (list != NULL) {
5880 ret = xmlRelaxNGValidateValue(ctxt, list);
5881 if (ret == 0) {
5882 break;
5883 }
5884 ctxt->state->value = oldvalue;
5885 list = list->next;
5886 }
5887 ctxt->flags = oldflags;
Daniel Veillard416589a2003-02-17 17:25:42 +00005888 if (ret == 0)
5889 xmlRelaxNGNextValue(ctxt);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005890 break;
5891 }
5892 case XML_RELAXNG_LIST: {
5893 xmlRelaxNGDefinePtr list = define->content;
5894 xmlChar *oldvalue, *oldend, *val, *cur;
Daniel Veillard416589a2003-02-17 17:25:42 +00005895#ifdef DEBUG_LIST
5896 int nb_values = 0;
5897#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005898
5899 oldvalue = ctxt->state->value;
5900 oldend = ctxt->state->endvalue;
5901
5902 val = xmlStrdup(oldvalue);
5903 if (val == NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00005904 val = xmlStrdup(BAD_CAST "");
5905 }
5906 if (val == NULL) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005907 VALID_CTXT();
5908 VALID_ERROR("Internal: no state\n");
5909 return(-1);
5910 }
5911 cur = val;
5912 while (*cur != 0) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005913 if (IS_BLANK(*cur)) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005914 *cur = 0;
Daniel Veillard416589a2003-02-17 17:25:42 +00005915 cur++;
5916#ifdef DEBUG_LIST
5917 nb_values++;
5918#endif
5919 while (IS_BLANK(*cur))
5920 *cur++ = 0;
5921 } else
5922 cur++;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005923 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005924#ifdef DEBUG_LIST
5925 xmlGenericError(xmlGenericErrorContext,
5926 "list value: '%s' found %d items\n", oldvalue, nb_values);
5927 nb_values = 0;
5928#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005929 ctxt->state->endvalue = cur;
5930 cur = val;
5931 while ((*cur == 0) && (cur != ctxt->state->endvalue)) cur++;
5932
5933 ctxt->state->value = cur;
5934
5935 while (list != NULL) {
Daniel Veillard2e9b1652003-02-19 13:29:45 +00005936 if (ctxt->state->value == ctxt->state->endvalue)
5937 ctxt->state->value = NULL;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005938 ret = xmlRelaxNGValidateValue(ctxt, list);
5939 if (ret != 0) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005940#ifdef DEBUG_LIST
5941 xmlGenericError(xmlGenericErrorContext,
5942 "Failed to validate value: '%s' with %d rule\n",
5943 ctxt->state->value, nb_values);
5944#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005945 break;
5946 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005947#ifdef DEBUG_LIST
5948 nb_values++;
5949#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005950 list = list->next;
5951 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00005952
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005953 if ((ret == 0) && (ctxt->state->value != NULL) &&
5954 (ctxt->state->value != ctxt->state->endvalue)) {
5955 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005956 VALID_ERROR2("Extra data in list: %s\n", ctxt->state->value);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005957 ret = -1;
5958 }
5959 xmlFree(val);
5960 ctxt->state->value = oldvalue;
5961 ctxt->state->endvalue = oldend;
5962 break;
5963 }
5964 case XML_RELAXNG_ONEORMORE:
5965 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
5966 if (ret != 0) {
5967 break;
5968 }
5969 /* no break on purpose */
5970 case XML_RELAXNG_ZEROORMORE: {
5971 xmlChar *cur, *temp;
5972
5973 oldflags = ctxt->flags;
5974 ctxt->flags |= FLAGS_IGNORABLE;
5975 cur = ctxt->state->value;
5976 temp = NULL;
5977 while ((cur != NULL) && (cur != ctxt->state->endvalue) &&
5978 (temp != cur)) {
5979 temp = cur;
5980 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
5981 if (ret != 0) {
5982 ctxt->state->value = temp;
5983 ret = 0;
5984 break;
5985 }
5986 cur = ctxt->state->value;
5987 }
5988 ctxt->flags = oldflags;
5989 break;
5990 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005991 case XML_RELAXNG_EXCEPT: {
5992 xmlRelaxNGDefinePtr list;
5993
5994 list = define->content;
5995 while (list != NULL) {
5996 ret = xmlRelaxNGValidateValue(ctxt, list);
5997 if (ret == 0) {
5998 ret = -1;
5999 break;
6000 } else
6001 ret = 0;
6002 list = list->next;
6003 }
6004 break;
6005 }
Daniel Veillardd4310742003-02-18 21:12:46 +00006006 case XML_RELAXNG_GROUP: {
6007 xmlRelaxNGDefinePtr list;
6008
6009 list = define->content;
6010 while (list != NULL) {
6011 ret = xmlRelaxNGValidateValue(ctxt, list);
6012 if (ret != 0) {
6013 ret = -1;
6014 break;
6015 } else
6016 ret = 0;
6017 list = list->next;
6018 }
6019 break;
6020 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006021 default:
6022 TODO
6023 ret = -1;
6024 }
6025 return(ret);
6026}
6027
6028/**
6029 * xmlRelaxNGValidateValueContent:
6030 * @ctxt: a Relax-NG validation context
6031 * @defines: the list of definitions to verify
6032 *
6033 * Validate the given definitions for the current value
6034 *
6035 * Returns 0 if the validation succeeded or an error code.
6036 */
6037static int
6038xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt,
6039 xmlRelaxNGDefinePtr defines) {
6040 int ret = 0;
6041
6042 while (defines != NULL) {
6043 ret = xmlRelaxNGValidateValue(ctxt, defines);
6044 if (ret != 0)
6045 break;
6046 defines = defines->next;
6047 }
6048 return(ret);
6049}
6050
6051/**
Daniel Veillard144fae12003-02-03 13:17:57 +00006052 * xmlRelaxNGAttributeMatch:
6053 * @ctxt: a Relax-NG validation context
6054 * @define: the definition to check
6055 * @prop: the attribute
6056 *
6057 * Check if the attribute matches the definition nameClass
6058 *
6059 * Returns 1 if the attribute matches, 0 if no, or -1 in case of error
6060 */
6061static int
6062xmlRelaxNGAttributeMatch(xmlRelaxNGValidCtxtPtr ctxt,
6063 xmlRelaxNGDefinePtr define,
6064 xmlAttrPtr prop) {
6065 int ret;
6066
6067 if (define->name != NULL) {
6068 if (!xmlStrEqual(define->name, prop->name))
6069 return(0);
6070 }
6071 if (define->ns != NULL) {
6072 if (define->ns[0] == 0) {
6073 if (prop->ns != NULL)
6074 return(0);
6075 } else {
6076 if ((prop->ns == NULL) ||
6077 (!xmlStrEqual(define->ns, prop->ns->href)))
6078 return(0);
6079 }
6080 }
6081 if (define->nameClass == NULL)
6082 return(1);
6083 define = define->nameClass;
6084 if (define->type == XML_RELAXNG_EXCEPT) {
6085 xmlRelaxNGDefinePtr list;
6086
6087 list = define->content;
6088 while (list != NULL) {
6089 ret = xmlRelaxNGAttributeMatch(ctxt, list, prop);
6090 if (ret == 1)
6091 return(0);
6092 if (ret < 0)
6093 return(ret);
6094 list = list->next;
6095 }
6096 } else {
6097 TODO
6098 }
6099 return(1);
6100}
6101
6102/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00006103 * xmlRelaxNGValidateAttribute:
6104 * @ctxt: a Relax-NG validation context
6105 * @define: the definition to verify
6106 *
6107 * Validate the given attribute definition for that node
6108 *
6109 * Returns 0 if the validation succeeded or an error code.
6110 */
6111static int
6112xmlRelaxNGValidateAttribute(xmlRelaxNGValidCtxtPtr ctxt,
6113 xmlRelaxNGDefinePtr define) {
6114 int ret = 0, i;
6115 xmlChar *value, *oldvalue;
6116 xmlAttrPtr prop = NULL, tmp;
6117
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006118 if (ctxt->state->nbAttrLeft <= 0)
6119 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006120 if (define->name != NULL) {
6121 for (i = 0;i < ctxt->state->nbAttrs;i++) {
6122 tmp = ctxt->state->attrs[i];
6123 if ((tmp != NULL) && (xmlStrEqual(define->name, tmp->name))) {
6124 if ((((define->ns == NULL) || (define->ns[0] == 0)) &&
6125 (tmp->ns == NULL)) ||
6126 ((tmp->ns != NULL) &&
6127 (xmlStrEqual(define->ns, tmp->ns->href)))) {
6128 prop = tmp;
6129 break;
6130 }
6131 }
6132 }
6133 if (prop != NULL) {
6134 value = xmlNodeListGetString(prop->doc, prop->children, 1);
6135 oldvalue = ctxt->state->value;
6136 ctxt->state->value = value;
Daniel Veillard231d7912003-02-09 14:22:17 +00006137 ctxt->state->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006138 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00006139 if (ctxt->state->value != NULL)
6140 value = ctxt->state->value;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006141 if (value != NULL)
6142 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00006143 ctxt->state->value = oldvalue;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006144 if (ret == 0) {
6145 /*
6146 * flag the attribute as processed
6147 */
6148 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006149 ctxt->state->nbAttrLeft--;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006150 }
6151 } else {
6152 ret = -1;
6153 }
6154#ifdef DEBUG
6155 xmlGenericError(xmlGenericErrorContext,
6156 "xmlRelaxNGValidateAttribute(%s): %d\n", define->name, ret);
6157#endif
6158 } else {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006159 for (i = 0;i < ctxt->state->nbAttrs;i++) {
6160 tmp = ctxt->state->attrs[i];
Daniel Veillard144fae12003-02-03 13:17:57 +00006161 if ((tmp != NULL) &&
6162 (xmlRelaxNGAttributeMatch(ctxt, define, tmp) == 1)) {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006163 prop = tmp;
6164 break;
6165 }
6166 }
6167 if (prop != NULL) {
6168 value = xmlNodeListGetString(prop->doc, prop->children, 1);
6169 oldvalue = ctxt->state->value;
6170 ctxt->state->value = value;
6171 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00006172 if (ctxt->state->value != NULL)
6173 value = ctxt->state->value;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006174 if (value != NULL)
6175 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00006176 ctxt->state->value = oldvalue;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006177 if (ret == 0) {
6178 /*
6179 * flag the attribute as processed
6180 */
6181 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006182 ctxt->state->nbAttrLeft--;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006183 }
6184 } else {
6185 ret = -1;
6186 }
6187#ifdef DEBUG
Daniel Veillard144fae12003-02-03 13:17:57 +00006188 if (define->ns != NULL) {
6189 xmlGenericError(xmlGenericErrorContext,
6190 "xmlRelaxNGValidateAttribute(nsName ns = %s): %d\n",
6191 define->ns, ret);
6192 } else {
6193 xmlGenericError(xmlGenericErrorContext,
6194 "xmlRelaxNGValidateAttribute(anyName): %d\n",
6195 ret);
6196 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006197#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00006198 }
6199
6200 return(ret);
6201}
6202
6203/**
6204 * xmlRelaxNGValidateAttributeList:
6205 * @ctxt: a Relax-NG validation context
6206 * @define: the list of definition to verify
6207 *
6208 * Validate the given node against the list of attribute definitions
6209 *
6210 * Returns 0 if the validation succeeded or an error code.
6211 */
6212static int
6213xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt,
6214 xmlRelaxNGDefinePtr defines) {
6215 int ret = 0;
6216 while (defines != NULL) {
6217 if (xmlRelaxNGValidateAttribute(ctxt, defines) != 0)
6218 ret = -1;
6219 defines = defines->next;
6220 }
6221 return(ret);
6222}
6223
6224/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006225 * xmlRelaxNGValidateTryPermutation:
6226 * @ctxt: a Relax-NG validation context
6227 * @groups: the array of groups
6228 * @nbgroups: the number of groups in the array
6229 * @array: the permutation to try
6230 * @len: the size of the set
6231 *
6232 * Try to validate a permutation for the group of definitions.
6233 *
6234 * Returns 0 if the validation succeeded or an error code.
6235 */
6236static int
6237xmlRelaxNGValidateTryPermutation(xmlRelaxNGValidCtxtPtr ctxt,
6238 xmlRelaxNGDefinePtr rule,
6239 xmlNodePtr *array, int len) {
6240 int i, ret;
6241
6242 if (len > 0) {
6243 /*
6244 * One only need the next pointer set-up to do the validation
6245 */
6246 for (i = 0;i < (len - 1);i++)
6247 array[i]->next = array[i + 1];
6248 array[i]->next = NULL;
6249
6250 /*
6251 * Now try to validate the sequence
6252 */
6253 ctxt->state->seq = array[0];
6254 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
6255 } else {
6256 ctxt->state->seq = NULL;
6257 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
6258 }
6259
6260 /*
6261 * the sequence must be fully consumed
6262 */
6263 if (ctxt->state->seq != NULL)
6264 return(-1);
6265
6266 return(ret);
6267}
6268
6269/**
6270 * xmlRelaxNGValidateWalkPermutations:
6271 * @ctxt: a Relax-NG validation context
6272 * @groups: the array of groups
6273 * @nbgroups: the number of groups in the array
6274 * @nodes: the set of nodes
6275 * @array: the current state of the parmutation
6276 * @len: the size of the set
6277 * @level: a pointer to the level variable
6278 * @k: the index in the array to fill
6279 *
6280 * Validate a set of nodes for a groups of definitions, will try the
6281 * full set of permutations
6282 *
6283 * Returns 0 if the validation succeeded or an error code.
6284 */
6285static int
6286xmlRelaxNGValidateWalkPermutations(xmlRelaxNGValidCtxtPtr ctxt,
6287 xmlRelaxNGDefinePtr rule, xmlNodePtr *nodes,
6288 xmlNodePtr *array, int len,
6289 int *level, int k) {
6290 int i, ret;
6291
6292 if ((k >= 0) && (k < len))
6293 array[k] = nodes[*level];
6294 *level = *level + 1;
6295 if (*level == len) {
6296 ret = xmlRelaxNGValidateTryPermutation(ctxt, rule, array, len);
6297 if (ret == 0)
6298 return(0);
6299 } else {
6300 for (i = 0;i < len;i++) {
6301 if (array[i] == NULL) {
6302 ret = xmlRelaxNGValidateWalkPermutations(ctxt, rule,
6303 nodes, array, len, level, i);
6304 if (ret == 0)
6305 return(0);
6306 }
6307 }
6308 }
6309 *level = *level - 1;
6310 array[k] = NULL;
6311 return(-1);
6312}
6313
6314/**
6315 * xmlRelaxNGNodeMatchesList:
6316 * @node: the node
6317 * @list: a NULL terminated array of definitions
6318 *
6319 * Check if a node can be matched by one of the definitions
6320 *
6321 * Returns 1 if matches 0 otherwise
6322 */
6323static int
6324xmlRelaxNGNodeMatchesList(xmlNodePtr node, xmlRelaxNGDefinePtr *list) {
6325 xmlRelaxNGDefinePtr cur;
6326 int i = 0;
6327
6328 if ((node == NULL) || (list == NULL))
6329 return(0);
6330
6331 cur = list[i++];
6332 while (cur != NULL) {
6333 if ((node->type == XML_ELEMENT_NODE) &&
6334 (cur->type == XML_RELAXNG_ELEMENT)) {
6335 if (cur->name == NULL) {
6336 if ((node->ns != NULL) &&
6337 (xmlStrEqual(node->ns->href, cur->ns)))
6338 return(1);
6339 } else if (xmlStrEqual(cur->name, node->name)) {
6340 if ((cur->ns == NULL) || (cur->ns[0] == 0)) {
6341 if (node->ns == NULL)
6342 return(1);
6343 } else {
6344 if ((node->ns != NULL) &&
6345 (xmlStrEqual(node->ns->href, cur->ns)))
6346 return(1);
6347 }
6348 }
6349 } else if ((node->type == XML_TEXT_NODE) &&
6350 (cur->type == XML_RELAXNG_TEXT)) {
6351 return(1);
6352 }
6353 cur = list[i++];
6354 }
6355 return(0);
6356}
6357
6358/**
6359 * xmlRelaxNGValidatePartGroup:
6360 * @ctxt: a Relax-NG validation context
6361 * @groups: the array of groups
6362 * @nbgroups: the number of groups in the array
6363 * @nodes: the set of nodes
6364 * @len: the size of the set of nodes
6365 *
6366 * Validate a set of nodes for a groups of definitions
6367 *
6368 * Returns 0 if the validation succeeded or an error code.
6369 */
6370static int
6371xmlRelaxNGValidatePartGroup(xmlRelaxNGValidCtxtPtr ctxt,
6372 xmlRelaxNGInterleaveGroupPtr *groups,
6373 int nbgroups, xmlNodePtr *nodes, int len) {
Daniel Veillard231d7912003-02-09 14:22:17 +00006374 int level, ret = -1, i, j, k, top_j, max_j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006375 xmlNodePtr *array = NULL, *list, oldseq;
6376 xmlRelaxNGInterleaveGroupPtr group;
6377
6378 list = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
6379 if (list == NULL) {
6380 return(-1);
6381 }
6382 array = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
6383 if (array == NULL) {
6384 xmlFree(list);
6385 return(-1);
6386 }
6387 memset(array, 0, len * sizeof(xmlNodePtr));
6388
6389 /*
6390 * Partition the elements and validate the subsets.
6391 */
6392 oldseq = ctxt->state->seq;
Daniel Veillard231d7912003-02-09 14:22:17 +00006393 max_j = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006394 for (i = 0;i < nbgroups;i++) {
6395 group = groups[i];
6396 if (group == NULL)
6397 continue;
6398 k = 0;
Daniel Veillard231d7912003-02-09 14:22:17 +00006399 top_j = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006400 for (j = 0;j < len;j++) {
6401 if (nodes[j] == NULL)
6402 continue;
6403 if (xmlRelaxNGNodeMatchesList(nodes[j], group->defs)) {
6404 list[k++] = nodes[j];
6405 nodes[j] = NULL;
Daniel Veillard231d7912003-02-09 14:22:17 +00006406 top_j = j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006407 }
6408 }
Daniel Veillard231d7912003-02-09 14:22:17 +00006409 if (top_j > max_j)
6410 max_j = top_j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006411 ctxt->state->seq = oldseq;
6412 if (k > 1) {
6413 memset(array, 0, k * sizeof(xmlNodePtr));
Daniel Veillardb08c9812003-01-28 23:09:49 +00006414 level = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006415 ret = xmlRelaxNGValidateWalkPermutations(ctxt, group->rule,
6416 list, array, k, &level, -1);
6417 } else {
6418 ret = xmlRelaxNGValidateTryPermutation(ctxt, group->rule, list, k);
6419 }
6420 if (ret != 0) {
6421 ctxt->state->seq = oldseq;
6422 break;
6423 }
6424 }
6425
Daniel Veillard231d7912003-02-09 14:22:17 +00006426 for (j = 0;j < max_j;j++) {
6427 if (nodes[j] != NULL) {
6428 TODO /* problem, one of the nodes didn't got a match */
6429 }
6430 }
6431 if (ret == 0) {
6432 if (max_j + 1 < len)
6433 ctxt->state->seq = nodes[max_j + 1];
6434 else
6435 ctxt->state->seq = NULL;
6436 }
6437
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006438 xmlFree(list);
6439 xmlFree(array);
6440 return(ret);
6441}
6442
6443/**
6444 * xmlRelaxNGValidateInterleave:
6445 * @ctxt: a Relax-NG validation context
6446 * @define: the definition to verify
6447 *
6448 * Validate an interleave definition for a node.
6449 *
6450 * Returns 0 if the validation succeeded or an error code.
6451 */
6452static int
6453xmlRelaxNGValidateInterleave(xmlRelaxNGValidCtxtPtr ctxt,
6454 xmlRelaxNGDefinePtr define) {
6455 int ret = 0, nbchildren, nbtot, i, j;
6456 xmlRelaxNGPartitionPtr partitions;
6457 xmlNodePtr *children = NULL;
6458 xmlNodePtr *order = NULL;
Daniel Veillard231d7912003-02-09 14:22:17 +00006459 xmlNodePtr cur, oldseq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006460
6461 if (define->data != NULL) {
6462 partitions = (xmlRelaxNGPartitionPtr) define->data;
6463 } else {
6464 VALID_CTXT();
6465 VALID_ERROR("Internal: interleave block has no data\n");
6466 return(-1);
6467 }
6468
6469 /*
6470 * Build the sequence of child and an array preserving the children
6471 * initial order.
6472 */
6473 cur = ctxt->state->seq;
Daniel Veillard231d7912003-02-09 14:22:17 +00006474 oldseq = ctxt->state->seq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006475 nbchildren = 0;
6476 nbtot = 0;
6477 while (cur != NULL) {
6478 if ((cur->type == XML_COMMENT_NODE) ||
6479 (cur->type == XML_PI_NODE) ||
6480 ((cur->type == XML_TEXT_NODE) &&
6481 (IS_BLANK_NODE(cur)))) {
6482 nbtot++;
6483 } else {
6484 nbchildren++;
6485 nbtot++;
6486 }
6487 cur = cur->next;
6488 }
6489 children = (xmlNodePtr *) xmlMalloc(nbchildren * sizeof(xmlNodePtr));
6490 if (children == NULL)
6491 goto error;
6492 order = (xmlNodePtr *) xmlMalloc(nbtot * sizeof(xmlNodePtr));
6493 if (order == NULL)
6494 goto error;
6495 cur = ctxt->state->seq;
6496 i = 0;
6497 j = 0;
6498 while (cur != NULL) {
6499 if ((cur->type == XML_COMMENT_NODE) ||
6500 (cur->type == XML_PI_NODE) ||
6501 ((cur->type == XML_TEXT_NODE) &&
6502 (IS_BLANK_NODE(cur)))) {
6503 order[j++] = cur;
6504 } else {
6505 order[j++] = cur;
6506 children[i++] = cur;
6507 }
6508 cur = cur->next;
6509 }
6510
6511 /* TODO: retry with a maller set of child if there is a next... */
6512 ret = xmlRelaxNGValidatePartGroup(ctxt, partitions->groups,
6513 partitions->nbgroups, children, nbchildren);
Daniel Veillard231d7912003-02-09 14:22:17 +00006514 if (ret != 0)
6515 ctxt->state->seq = oldseq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006516
6517 /*
6518 * Cleanup: rebuid the child sequence and free the structure
6519 */
6520 if (order != NULL) {
6521 for (i = 0;i < nbtot;i++) {
6522 if (i == 0)
6523 order[i]->prev = NULL;
6524 else
6525 order[i]->prev = order[i - 1];
6526 if (i == nbtot - 1)
6527 order[i]->next = NULL;
6528 else
6529 order[i]->next = order[i + 1];
6530 }
6531 xmlFree(order);
6532 }
6533 if (children != NULL)
6534 xmlFree(children);
6535
6536 return(ret);
6537
6538error:
6539 if (order != NULL) {
6540 for (i = 0;i < nbtot;i++) {
6541 if (i == 0)
6542 order[i]->prev = NULL;
6543 else
6544 order[i]->prev = order[i - 1];
6545 if (i == nbtot - 1)
6546 order[i]->next = NULL;
6547 else
6548 order[i]->next = order[i + 1];
6549 }
6550 xmlFree(order);
6551 }
6552 if (children != NULL)
6553 xmlFree(children);
6554 return(-1);
6555}
6556
6557/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00006558 * xmlRelaxNGValidateElementContent:
6559 * @ctxt: a Relax-NG validation context
6560 * @define: the list of definition to verify
6561 *
6562 * Validate the given node content against the (list) of definitions
6563 *
6564 * Returns 0 if the validation succeeded or an error code.
6565 */
6566static int
6567xmlRelaxNGValidateElementContent(xmlRelaxNGValidCtxtPtr ctxt,
6568 xmlRelaxNGDefinePtr defines) {
6569 int ret = 0, res;
6570
6571 if (ctxt->state == NULL) {
6572 VALID_CTXT();
6573 VALID_ERROR("Internal: no state\n");
6574 return(-1);
6575 }
6576 while (defines != NULL) {
6577 res = xmlRelaxNGValidateDefinition(ctxt, defines);
6578 if (res < 0)
6579 ret = -1;
6580 defines = defines->next;
6581 }
6582
6583 return(ret);
6584}
6585
6586/**
Daniel Veillard416589a2003-02-17 17:25:42 +00006587 * xmlRelaxNGElementMatch:
6588 * @ctxt: a Relax-NG validation context
6589 * @define: the definition to check
6590 * @elem: the element
6591 *
6592 * Check if the element matches the definition nameClass
6593 *
6594 * Returns 1 if the element matches, 0 if no, or -1 in case of error
6595 */
6596static int
6597xmlRelaxNGElementMatch(xmlRelaxNGValidCtxtPtr ctxt,
6598 xmlRelaxNGDefinePtr define,
6599 xmlNodePtr elem) {
6600 int ret, oldflags;
6601
6602 if (define->name != NULL) {
6603 if (!xmlStrEqual(elem->name, define->name)) {
6604 VALID_CTXT();
6605 VALID_ERROR3("Expecting element %s, got %s\n",
6606 define->name, elem->name);
6607 return(0);
6608 }
6609 }
6610 if ((define->ns != NULL) && (define->ns[0] != 0)) {
6611 if (elem->ns == NULL) {
6612 VALID_CTXT();
6613 VALID_ERROR2("Expecting a namespace for element %s\n",
6614 elem->name);
6615 return(0);
6616 } else if (!xmlStrEqual(elem->ns->href, define->ns)) {
6617 VALID_CTXT();
6618 VALID_ERROR3("Expecting element %s has wrong namespace: expecting %s\n",
6619 elem->name, define->ns);
6620 return(0);
6621 }
Daniel Veillard8fe98712003-02-19 00:19:14 +00006622 } else if ((elem->ns != NULL) && (define->ns != NULL) &&
6623 (define->name == NULL)) {
6624 VALID_CTXT();
6625 VALID_ERROR2("Expecting no namespace for element %s\n",
6626 define->name);
6627 return(0);
6628 } else if ((elem->ns != NULL) && (define->name != NULL)) {
6629 VALID_CTXT();
6630 VALID_ERROR2("Expecting no namespace for element %s\n",
6631 define->name);
6632 return(0);
Daniel Veillard416589a2003-02-17 17:25:42 +00006633 }
6634
6635 if (define->nameClass == NULL)
6636 return(1);
6637
6638 define = define->nameClass;
6639 if (define->type == XML_RELAXNG_EXCEPT) {
6640 xmlRelaxNGDefinePtr list;
6641 oldflags = ctxt->flags;
6642 ctxt->flags |= FLAGS_IGNORABLE;
6643
6644 list = define->content;
6645 while (list != NULL) {
6646 ret = xmlRelaxNGElementMatch(ctxt, list, elem);
6647 if (ret == 1) {
6648 ctxt->flags = oldflags;
6649 return(0);
6650 }
6651 if (ret < 0) {
6652 ctxt->flags = oldflags;
6653 return(ret);
6654 }
6655 list = list->next;
6656 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006657 ret = 1;
6658 ctxt->flags = oldflags;
6659 } else if (define->type == XML_RELAXNG_CHOICE) {
6660 xmlRelaxNGDefinePtr list;
6661 oldflags = ctxt->flags;
6662 ctxt->flags |= FLAGS_IGNORABLE;
6663
6664 list = define->nameClass;
6665 while (list != NULL) {
6666 ret = xmlRelaxNGElementMatch(ctxt, list, elem);
6667 if (ret == 1) {
6668 ctxt->flags = oldflags;
6669 return(1);
6670 }
6671 if (ret < 0) {
6672 ctxt->flags = oldflags;
6673 return(ret);
6674 }
6675 list = list->next;
6676 }
6677 ret = 0;
Daniel Veillard416589a2003-02-17 17:25:42 +00006678 ctxt->flags = oldflags;
6679 } else {
6680 TODO
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006681 ret = -1;
Daniel Veillard416589a2003-02-17 17:25:42 +00006682 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006683 return(ret);
Daniel Veillard416589a2003-02-17 17:25:42 +00006684}
6685
6686/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00006687 * xmlRelaxNGValidateDefinition:
6688 * @ctxt: a Relax-NG validation context
6689 * @define: the definition to verify
6690 *
6691 * Validate the current node against the definition
6692 *
6693 * Returns 0 if the validation succeeded or an error code.
6694 */
6695static int
6696xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
6697 xmlRelaxNGDefinePtr define) {
6698 xmlNodePtr node;
6699 int ret = 0, i, tmp, oldflags;
6700 xmlRelaxNGValidStatePtr oldstate, state;
6701
6702 if (define == NULL) {
6703 VALID_CTXT();
6704 VALID_ERROR("internal error: define == NULL\n");
6705 return(-1);
6706 }
6707 if (ctxt->state != NULL) {
6708 node = ctxt->state->seq;
6709 } else {
6710 node = NULL;
6711 }
Daniel Veillard231d7912003-02-09 14:22:17 +00006712#ifdef DEBUG
6713 for (i = 0;i < ctxt->depth;i++)
6714 xmlGenericError(xmlGenericErrorContext, " ");
6715 xmlGenericError(xmlGenericErrorContext,
6716 "Start validating %s ", xmlRelaxNGDefName(define));
6717 if (define->name != NULL)
6718 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
6719 if ((node != NULL) && (node->name != NULL))
6720 xmlGenericError(xmlGenericErrorContext, "on %s\n", node->name);
6721 else
6722 xmlGenericError(xmlGenericErrorContext, "\n");
6723#endif
6724 ctxt->depth++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006725 switch (define->type) {
6726 case XML_RELAXNG_EMPTY:
Daniel Veillardd4310742003-02-18 21:12:46 +00006727 node = xmlRelaxNGSkipIgnored(ctxt, node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006728 if (node != NULL) {
6729 VALID_CTXT();
6730 VALID_ERROR("Expecting an empty element\n");
Daniel Veillard231d7912003-02-09 14:22:17 +00006731 ret = -1;
6732 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006733 }
Daniel Veillard231d7912003-02-09 14:22:17 +00006734 ret = 0;
6735 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006736 case XML_RELAXNG_NOT_ALLOWED:
Daniel Veillard231d7912003-02-09 14:22:17 +00006737 ret = -1;
6738 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006739 case XML_RELAXNG_TEXT:
Daniel Veillardce14fa52003-02-19 17:32:48 +00006740#if 0
Daniel Veillard231d7912003-02-09 14:22:17 +00006741 if (node == NULL) {
6742 ret = 0;
6743 break;
6744 }
Daniel Veillardce14fa52003-02-19 17:32:48 +00006745#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00006746 while ((node != NULL) &&
6747 ((node->type == XML_TEXT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006748 (node->type == XML_COMMENT_NODE) ||
6749 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00006750 (node->type == XML_CDATA_SECTION_NODE)))
6751 node = node->next;
Daniel Veillardce14fa52003-02-19 17:32:48 +00006752#if 0
Daniel Veillard276be4a2003-01-24 01:03:34 +00006753 if (node == ctxt->state->seq) {
6754 VALID_CTXT();
6755 VALID_ERROR("Expecting text content\n");
6756 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006757 }
Daniel Veillardce14fa52003-02-19 17:32:48 +00006758#endif
Daniel Veillard276be4a2003-01-24 01:03:34 +00006759 ctxt->state->seq = node;
6760 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006761 case XML_RELAXNG_ELEMENT:
6762 node = xmlRelaxNGSkipIgnored(ctxt, node);
Daniel Veillard231d7912003-02-09 14:22:17 +00006763 if (node == NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006764 VALID_CTXT();
Daniel Veillard231d7912003-02-09 14:22:17 +00006765 VALID_ERROR("Expecting an element, got empty\n");
6766 ret = -1;
6767 break;
6768 }
6769 if (node->type != XML_ELEMENT_NODE) {
6770 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006771 VALID_ERROR2("Expecting an element got %d type\n", node->type);
Daniel Veillard231d7912003-02-09 14:22:17 +00006772 ret = -1;
6773 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006774 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00006775 /*
6776 * This node was already validated successfully against
6777 * this definition.
6778 */
6779 if (node->_private == define)
6780 break;
Daniel Veillard416589a2003-02-17 17:25:42 +00006781
6782 ret = xmlRelaxNGElementMatch(ctxt, define, node);
6783 if (ret <= 0) {
6784 ret = -1;
6785 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006786 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006787 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006788
6789 state = xmlRelaxNGNewValidState(ctxt, node);
6790 if (state == NULL) {
Daniel Veillard231d7912003-02-09 14:22:17 +00006791 ret = -1;
6792 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006793 }
6794
6795 oldstate = ctxt->state;
6796 ctxt->state = state;
6797 if (define->attrs != NULL) {
6798 tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
Daniel Veillard231d7912003-02-09 14:22:17 +00006799 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006800 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00006801#ifdef DEBUG
6802 xmlGenericError(xmlGenericErrorContext,
6803 "E: Element %s failed to validate attributes\n",
6804 node->name);
6805#endif
6806 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006807 }
6808 if (define->content != NULL) {
6809 tmp = xmlRelaxNGValidateElementContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00006810 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006811 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00006812#ifdef DEBUG
6813 xmlGenericError(xmlGenericErrorContext,
6814 "E: Element %s failed to validate element content\n",
6815 node->name);
6816#endif
6817 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006818 }
6819 state = ctxt->state;
6820 if (state->seq != NULL) {
6821 state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq);
6822 if (state->seq != NULL) {
6823 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006824 VALID_ERROR3("Extra content for element %s: %s\n",
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006825 node->name, state->seq->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006826 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00006827#ifdef DEBUG
6828 xmlGenericError(xmlGenericErrorContext,
6829 "E: Element %s has extra content: %s\n",
6830 node->name, state->seq->name);
6831#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00006832 }
6833 }
6834 for (i = 0;i < state->nbAttrs;i++) {
6835 if (state->attrs[i] != NULL) {
6836 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006837 VALID_ERROR3("Invalid attribute %s for element %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00006838 state->attrs[i]->name, node->name);
6839 ret = -1;
6840 }
6841 }
6842 ctxt->state = oldstate;
6843 xmlRelaxNGFreeValidState(state);
6844 if (oldstate != NULL)
Daniel Veillarde5b110b2003-02-04 14:43:39 +00006845 oldstate->seq = xmlRelaxNGSkipIgnored(ctxt, node->next);
6846 if (ret == 0)
6847 node->_private = define;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006848
6849
6850#ifdef DEBUG
6851 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard231d7912003-02-09 14:22:17 +00006852 "xmlRelaxNGValidateDefinition(): validated %s : %d",
Daniel Veillard6eadf632003-01-23 18:29:16 +00006853 node->name, ret);
Daniel Veillard231d7912003-02-09 14:22:17 +00006854 if (oldstate == NULL)
6855 xmlGenericError(xmlGenericErrorContext, ": no state\n");
6856 else if (oldstate->seq == NULL)
6857 xmlGenericError(xmlGenericErrorContext, ": done\n");
6858 else if (oldstate->seq->type == XML_ELEMENT_NODE)
6859 xmlGenericError(xmlGenericErrorContext, ": next elem %s\n",
6860 oldstate->seq->name);
6861 else
6862 xmlGenericError(xmlGenericErrorContext, ": next %s %d\n",
6863 oldstate->seq->name, oldstate->seq->type);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006864#endif
6865 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006866 case XML_RELAXNG_OPTIONAL:
6867 oldflags = ctxt->flags;
6868 ctxt->flags |= FLAGS_IGNORABLE;
6869 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6870 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6871 if (ret != 0) {
6872 xmlRelaxNGFreeValidState(ctxt->state);
6873 ctxt->state = oldstate;
6874 ret = 0;
6875 break;
6876 }
6877 xmlRelaxNGFreeValidState(oldstate);
6878 ctxt->flags = oldflags;
6879 ret = 0;
6880 break;
6881 case XML_RELAXNG_ONEORMORE:
6882 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6883 if (ret != 0) {
6884 break;
6885 }
6886 /* no break on purpose */
Daniel Veillard276be4a2003-01-24 01:03:34 +00006887 case XML_RELAXNG_ZEROORMORE: {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006888 oldflags = ctxt->flags;
6889 ctxt->flags |= FLAGS_IGNORABLE;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006890 while (ctxt->state->nbAttrLeft != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006891 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6892 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6893 if (ret != 0) {
6894 xmlRelaxNGFreeValidState(ctxt->state);
6895 ctxt->state = oldstate;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006896 break;
6897 }
6898 xmlRelaxNGFreeValidState(oldstate);
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006899 }
6900 if (ret == 0) {
6901 /*
6902 * There is no attribute left to be consumed,
6903 * we can check the closure by looking at ctxt->state->seq
6904 */
6905 xmlNodePtr cur, temp;
6906
Daniel Veillard276be4a2003-01-24 01:03:34 +00006907 cur = ctxt->state->seq;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006908 temp = NULL;
6909 while ((cur != NULL) && (temp != cur)) {
6910 temp = cur;
6911 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6912 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6913 if (ret != 0) {
6914 xmlRelaxNGFreeValidState(ctxt->state);
6915 ctxt->state = oldstate;
6916 break;
6917 }
6918 xmlRelaxNGFreeValidState(oldstate);
6919 cur = ctxt->state->seq;
6920 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006921 }
6922 ctxt->flags = oldflags;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006923 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006924 break;
Daniel Veillard276be4a2003-01-24 01:03:34 +00006925 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006926 case XML_RELAXNG_CHOICE: {
6927 xmlRelaxNGDefinePtr list = define->content;
Daniel Veillardce14fa52003-02-19 17:32:48 +00006928 int success = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006929
6930 oldflags = ctxt->flags;
6931 ctxt->flags |= FLAGS_IGNORABLE;
6932
6933 while (list != NULL) {
6934 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6935 ret = xmlRelaxNGValidateDefinition(ctxt, list);
6936 if (ret == 0) {
Daniel Veillardce14fa52003-02-19 17:32:48 +00006937 if (xmlRelaxNGEqualValidState(ctxt, ctxt->state, oldstate)){
6938 /*
6939 * if that pattern was nullable flag it but try
6940 * to make more progresses
6941 */
6942 success = 1;
6943 } else {
6944 xmlRelaxNGFreeValidState(oldstate);
6945 break;
6946 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006947 }
6948 xmlRelaxNGFreeValidState(ctxt->state);
6949 ctxt->state = oldstate;
6950 list = list->next;
6951 }
6952 ctxt->flags = oldflags;
Daniel Veillardce14fa52003-02-19 17:32:48 +00006953 if (success == 1)
6954 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006955 break;
6956 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00006957 case XML_RELAXNG_DEF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00006958 case XML_RELAXNG_GROUP: {
6959 xmlRelaxNGDefinePtr list = define->content;
6960
6961 while (list != NULL) {
6962 ret = xmlRelaxNGValidateDefinition(ctxt, list);
6963 if (ret != 0)
6964 break;
6965 list = list->next;
6966 }
6967 break;
6968 }
6969 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006970 ret = xmlRelaxNGValidateInterleave(ctxt, define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006971 break;
6972 case XML_RELAXNG_ATTRIBUTE:
6973 ret = xmlRelaxNGValidateAttribute(ctxt, define);
6974 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00006975 case XML_RELAXNG_NOOP:
Daniel Veillard6eadf632003-01-23 18:29:16 +00006976 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00006977 case XML_RELAXNG_PARENTREF:
6978 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00006979 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6980 break;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006981 case XML_RELAXNG_DATATYPE: {
Daniel Veillardd4310742003-02-18 21:12:46 +00006982 xmlNodePtr child;
6983 xmlChar *content = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006984
Daniel Veillardd4310742003-02-18 21:12:46 +00006985 child = node;
6986 while (child != NULL) {
6987 if (child->type == XML_ELEMENT_NODE) {
6988 VALID_CTXT();
6989 VALID_ERROR2("Element %s has child elements\n",
6990 node->parent->name);
6991 ret = -1;
6992 break;
6993 } else if ((child->type == XML_TEXT_NODE) ||
6994 (child->type == XML_CDATA_SECTION_NODE)) {
6995 content = xmlStrcat(content, child->content);
6996 }
6997 /* TODO: handle entities ... */
6998 child = child->next;
6999 }
7000 if (ret == -1) {
7001 if (content != NULL)
7002 xmlFree(content);
7003 break;
7004 }
7005 if (content == NULL) {
7006 content = xmlStrdup(BAD_CAST "");
7007 if (content == NULL) {
7008 VALID_CTXT();
7009 VALID_ERROR("Allocation failure\n");
7010 ret = -1;
7011 break;
7012 }
7013 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00007014 ret = xmlRelaxNGValidateDatatype(ctxt, content, define);
7015 if (ret == -1) {
7016 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00007017 VALID_ERROR2("internal error validating %s\n", define->name);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00007018 } else if (ret == 0) {
Daniel Veillardd4310742003-02-18 21:12:46 +00007019 ctxt->state->seq = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00007020 }
7021 if (content != NULL)
7022 xmlFree(content);
7023 break;
7024 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00007025 case XML_RELAXNG_VALUE: {
Daniel Veillardd4310742003-02-18 21:12:46 +00007026 xmlChar *content = NULL;
Daniel Veillardea3f3982003-01-26 19:45:18 +00007027 xmlChar *oldvalue;
Daniel Veillardd4310742003-02-18 21:12:46 +00007028 xmlNodePtr child;
Daniel Veillardea3f3982003-01-26 19:45:18 +00007029
Daniel Veillardd4310742003-02-18 21:12:46 +00007030 child = node;
7031 while (child != NULL) {
7032 if (child->type == XML_ELEMENT_NODE) {
7033 VALID_CTXT();
7034 VALID_ERROR2("Element %s has child elements\n",
7035 node->parent->name);
7036 ret = -1;
7037 break;
7038 } else if ((child->type == XML_TEXT_NODE) ||
7039 (child->type == XML_CDATA_SECTION_NODE)) {
7040 content = xmlStrcat(content, child->content);
7041 }
7042 /* TODO: handle entities ... */
7043 child = child->next;
7044 }
7045 if (ret == -1) {
7046 if (content != NULL)
7047 xmlFree(content);
7048 break;
7049 }
7050 if (content == NULL) {
7051 content = xmlStrdup(BAD_CAST "");
7052 if (content == NULL) {
7053 VALID_CTXT();
7054 VALID_ERROR("Allocation failure\n");
7055 ret = -1;
7056 break;
7057 }
7058 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00007059 oldvalue = ctxt->state->value;
7060 ctxt->state->value = content;
7061 ret = xmlRelaxNGValidateValue(ctxt, define);
7062 ctxt->state->value = oldvalue;
7063 if (ret == -1) {
7064 VALID_CTXT();
Daniel Veillardd2298792003-02-14 16:54:11 +00007065 if (define->name != NULL) {
7066 VALID_ERROR2("error validating value %s\n", define->name);
7067 } else {
7068 VALID_ERROR("error validating value\n");
7069 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00007070 } else if (ret == 0) {
Daniel Veillardd4310742003-02-18 21:12:46 +00007071 ctxt->state->seq = NULL;
Daniel Veillardea3f3982003-01-26 19:45:18 +00007072 }
7073 if (content != NULL)
7074 xmlFree(content);
7075 break;
7076 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007077 case XML_RELAXNG_LIST: {
7078 xmlChar *content;
Daniel Veillardd4310742003-02-18 21:12:46 +00007079 xmlNodePtr child;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007080 xmlChar *oldvalue, *oldendvalue;
7081 int len;
Daniel Veillardea3f3982003-01-26 19:45:18 +00007082
Daniel Veillardd4310742003-02-18 21:12:46 +00007083 /*
7084 * Make sure it's only text nodes
7085 */
7086
7087 content = NULL;
7088 child = node;
7089 while (child != NULL) {
7090 if (child->type == XML_ELEMENT_NODE) {
7091 VALID_CTXT();
7092 VALID_ERROR2("Element %s has child elements\n",
7093 node->parent->name);
7094 ret = -1;
7095 break;
7096 } else if ((child->type == XML_TEXT_NODE) ||
7097 (child->type == XML_CDATA_SECTION_NODE)) {
7098 content = xmlStrcat(content, child->content);
7099 }
7100 /* TODO: handle entities ... */
7101 child = child->next;
7102 }
7103 if (ret == -1) {
7104 if (content != NULL)
7105 xmlFree(content);
7106 break;
7107 }
7108 if (content == NULL) {
7109 content = xmlStrdup(BAD_CAST "");
7110 if (content == NULL) {
7111 VALID_CTXT();
7112 VALID_ERROR("Allocation failure\n");
7113 ret = -1;
7114 break;
7115 }
7116 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007117 len = xmlStrlen(content);
7118 oldvalue = ctxt->state->value;
7119 oldendvalue = ctxt->state->endvalue;
7120 ctxt->state->value = content;
7121 ctxt->state->endvalue = content + len;
7122 ret = xmlRelaxNGValidateValue(ctxt, define);
7123 ctxt->state->value = oldvalue;
7124 ctxt->state->endvalue = oldendvalue;
7125 if (ret == -1) {
7126 VALID_CTXT();
Daniel Veillard2e9b1652003-02-19 13:29:45 +00007127 VALID_ERROR("error validating list\n");
Daniel Veillardd4310742003-02-18 21:12:46 +00007128 } else if ((ret == 0) && (node != NULL)) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007129 ctxt->state->seq = node->next;
7130 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007131 if (content != NULL)
7132 xmlFree(content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00007133 break;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007134 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00007135 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00007136 case XML_RELAXNG_EXCEPT:
Daniel Veillard8fe98712003-02-19 00:19:14 +00007137 case XML_RELAXNG_PARAM:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00007138 TODO
Daniel Veillard416589a2003-02-17 17:25:42 +00007139 ret = -1;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00007140 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007141 }
Daniel Veillard231d7912003-02-09 14:22:17 +00007142 ctxt->depth--;
7143#ifdef DEBUG
7144 for (i = 0;i < ctxt->depth;i++)
7145 xmlGenericError(xmlGenericErrorContext, " ");
7146 xmlGenericError(xmlGenericErrorContext,
7147 "Validating %s ", xmlRelaxNGDefName(define));
7148 if (define->name != NULL)
7149 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
7150 if (ret == 0)
7151 xmlGenericError(xmlGenericErrorContext, "suceeded\n");
7152 else
7153 xmlGenericError(xmlGenericErrorContext, "failed\n");
7154#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00007155 return(ret);
7156}
7157
7158/**
7159 * xmlRelaxNGValidateDocument:
7160 * @ctxt: a Relax-NG validation context
7161 * @doc: the document
7162 *
7163 * Validate the given document
7164 *
7165 * Returns 0 if the validation succeeded or an error code.
7166 */
7167static int
7168xmlRelaxNGValidateDocument(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
7169 int ret;
7170 xmlRelaxNGPtr schema;
7171 xmlRelaxNGGrammarPtr grammar;
7172 xmlRelaxNGValidStatePtr state;
7173
7174 if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL))
7175 return(-1);
7176
7177 schema = ctxt->schema;
7178 grammar = schema->topgrammar;
7179 if (grammar == NULL) {
7180 VALID_CTXT();
7181 VALID_ERROR("No top grammar defined\n");
7182 return(-1);
7183 }
7184 state = xmlRelaxNGNewValidState(ctxt, NULL);
7185 ctxt->state = state;
7186 ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start);
7187 state = ctxt->state;
7188 if ((state != NULL) && (state->seq != NULL)) {
7189 xmlNodePtr node;
7190
7191 node = state->seq;
7192 node = xmlRelaxNGSkipIgnored(ctxt, node);
7193 if (node != NULL) {
7194 VALID_CTXT();
7195 VALID_ERROR("extra data on the document\n");
7196 ret = -1;
7197 }
7198 }
7199 xmlRelaxNGFreeValidState(state);
7200
7201 return(ret);
7202}
7203
7204/************************************************************************
7205 * *
7206 * Validation interfaces *
7207 * *
7208 ************************************************************************/
7209/**
7210 * xmlRelaxNGNewValidCtxt:
7211 * @schema: a precompiled XML RelaxNGs
7212 *
7213 * Create an XML RelaxNGs validation context based on the given schema
7214 *
7215 * Returns the validation context or NULL in case of error
7216 */
7217xmlRelaxNGValidCtxtPtr
7218xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema) {
7219 xmlRelaxNGValidCtxtPtr ret;
7220
7221 ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt));
7222 if (ret == NULL) {
7223 xmlGenericError(xmlGenericErrorContext,
7224 "Failed to allocate new schama validation context\n");
7225 return (NULL);
7226 }
7227 memset(ret, 0, sizeof(xmlRelaxNGValidCtxt));
7228 ret->schema = schema;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00007229 ret->error = xmlGenericError;
7230 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007231 return (ret);
7232}
7233
7234/**
7235 * xmlRelaxNGFreeValidCtxt:
7236 * @ctxt: the schema validation context
7237 *
7238 * Free the resources associated to the schema validation context
7239 */
7240void
7241xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt) {
7242 if (ctxt == NULL)
7243 return;
7244 xmlFree(ctxt);
7245}
7246
7247/**
7248 * xmlRelaxNGSetValidErrors:
7249 * @ctxt: a Relax-NG validation context
7250 * @err: the error function
7251 * @warn: the warning function
7252 * @ctx: the functions context
7253 *
7254 * Set the error and warning callback informations
7255 */
7256void
7257xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
7258 xmlRelaxNGValidityErrorFunc err,
7259 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
7260 if (ctxt == NULL)
7261 return;
7262 ctxt->error = err;
7263 ctxt->warning = warn;
7264 ctxt->userData = ctx;
7265}
7266
7267/**
7268 * xmlRelaxNGValidateDoc:
7269 * @ctxt: a Relax-NG validation context
7270 * @doc: a parsed document tree
7271 *
7272 * Validate a document tree in memory.
7273 *
7274 * Returns 0 if the document is valid, a positive error code
7275 * number otherwise and -1 in case of internal or API error.
7276 */
7277int
7278xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
7279 int ret;
7280
7281 if ((ctxt == NULL) || (doc == NULL))
7282 return(-1);
7283
7284 ctxt->doc = doc;
7285
7286 ret = xmlRelaxNGValidateDocument(ctxt, doc);
Daniel Veillard71531f32003-02-05 13:19:53 +00007287 /*
7288 * TODO: build error codes
7289 */
7290 if (ret == -1)
7291 return(1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00007292 return(ret);
7293}
7294
7295#endif /* LIBXML_SCHEMAS_ENABLED */
7296