blob: 2f91afed615e94113eac5bcc964840a07cc49041 [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 Veillard3ebc7d42003-02-24 17:17:58 +0000312
Daniel Veillard6eadf632003-01-23 18:29:16 +0000313/************************************************************************
314 * *
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000315 * Preliminary type checking interfaces *
316 * *
317 ************************************************************************/
318/**
319 * xmlRelaxNGTypeHave:
320 * @data: data needed for the library
321 * @type: the type name
322 * @value: the value to check
323 *
324 * Function provided by a type library to check if a type is exported
325 *
326 * Returns 1 if yes, 0 if no and -1 in case of error.
327 */
328typedef int (*xmlRelaxNGTypeHave) (void *data, const xmlChar *type);
329
330/**
331 * xmlRelaxNGTypeCheck:
332 * @data: data needed for the library
333 * @type: the type name
334 * @value: the value to check
335 *
336 * Function provided by a type library to check if a value match a type
337 *
338 * Returns 1 if yes, 0 if no and -1 in case of error.
339 */
340typedef int (*xmlRelaxNGTypeCheck) (void *data, const xmlChar *type,
341 const xmlChar *value);
342
343/**
344 * xmlRelaxNGTypeCompare:
345 * @data: data needed for the library
346 * @type: the type name
347 * @value1: the first value
348 * @value2: the second value
349 *
350 * Function provided by a type library to compare two values accordingly
351 * to a type.
352 *
353 * Returns 1 if yes, 0 if no and -1 in case of error.
354 */
355typedef int (*xmlRelaxNGTypeCompare) (void *data, const xmlChar *type,
356 const xmlChar *value1,
357 const xmlChar *value2);
358typedef struct _xmlRelaxNGTypeLibrary xmlRelaxNGTypeLibrary;
359typedef xmlRelaxNGTypeLibrary *xmlRelaxNGTypeLibraryPtr;
360struct _xmlRelaxNGTypeLibrary {
361 const xmlChar *namespace; /* the datatypeLibrary value */
362 void *data; /* data needed for the library */
363 xmlRelaxNGTypeHave have; /* the export function */
364 xmlRelaxNGTypeCheck check; /* the checking function */
365 xmlRelaxNGTypeCompare comp; /* the compare function */
366};
367
368/************************************************************************
369 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +0000370 * Allocation functions *
371 * *
372 ************************************************************************/
Daniel Veillard6eadf632003-01-23 18:29:16 +0000373static void xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar);
374static void xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define);
Daniel Veillardd2298792003-02-14 16:54:11 +0000375static void xmlRelaxNGNormExtSpace(xmlChar *value);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000376
377/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000378 * xmlRelaxNGFreeDocument:
379 * @docu: a document structure
380 *
381 * Deallocate a RelaxNG document structure.
382 */
383static void
384xmlRelaxNGFreeDocument(xmlRelaxNGDocumentPtr docu)
385{
386 if (docu == NULL)
387 return;
388
389 if (docu->href != NULL)
390 xmlFree(docu->href);
391 if (docu->doc != NULL)
392 xmlFreeDoc(docu->doc);
393 if (docu->schema != NULL)
394 xmlRelaxNGFree(docu->schema);
395 xmlFree(docu);
396}
397
398/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000399 * xmlRelaxNGFreeInclude:
400 * @incl: a include structure
401 *
402 * Deallocate a RelaxNG include structure.
403 */
404static void
405xmlRelaxNGFreeInclude(xmlRelaxNGIncludePtr incl)
406{
407 if (incl == NULL)
408 return;
409
410 if (incl->href != NULL)
411 xmlFree(incl->href);
412 if (incl->doc != NULL)
413 xmlFreeDoc(incl->doc);
414 if (incl->schema != NULL)
415 xmlRelaxNGFree(incl->schema);
416 xmlFree(incl);
417}
418
419/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000420 * xmlRelaxNGNewRelaxNG:
421 * @ctxt: a Relax-NG validation context (optional)
422 *
423 * Allocate a new RelaxNG structure.
424 *
425 * Returns the newly allocated structure or NULL in case or error
426 */
427static xmlRelaxNGPtr
428xmlRelaxNGNewRelaxNG(xmlRelaxNGParserCtxtPtr ctxt)
429{
430 xmlRelaxNGPtr ret;
431
432 ret = (xmlRelaxNGPtr) xmlMalloc(sizeof(xmlRelaxNG));
433 if (ret == NULL) {
434 if ((ctxt != NULL) && (ctxt->error != NULL))
435 ctxt->error(ctxt->userData, "Out of memory\n");
436 ctxt->nbErrors++;
437 return (NULL);
438 }
439 memset(ret, 0, sizeof(xmlRelaxNG));
440
441 return (ret);
442}
443
444/**
445 * xmlRelaxNGFree:
446 * @schema: a schema structure
447 *
448 * Deallocate a RelaxNG structure.
449 */
450void
451xmlRelaxNGFree(xmlRelaxNGPtr schema)
452{
453 if (schema == NULL)
454 return;
455
Daniel Veillard6eadf632003-01-23 18:29:16 +0000456 if (schema->topgrammar != NULL)
457 xmlRelaxNGFreeGrammar(schema->topgrammar);
458 if (schema->doc != NULL)
459 xmlFreeDoc(schema->doc);
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000460 if (schema->documents != NULL)
461 xmlHashFree(schema->documents, (xmlHashDeallocator)
462 xmlRelaxNGFreeDocument);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000463 if (schema->includes != NULL)
464 xmlHashFree(schema->includes, (xmlHashDeallocator)
465 xmlRelaxNGFreeInclude);
Daniel Veillard419a7682003-02-03 23:22:49 +0000466 if (schema->defTab != NULL) {
467 int i;
468
469 for (i = 0;i < schema->defNr;i++)
470 xmlRelaxNGFreeDefine(schema->defTab[i]);
471 xmlFree(schema->defTab);
472 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000473
474 xmlFree(schema);
475}
476
477/**
478 * xmlRelaxNGNewGrammar:
479 * @ctxt: a Relax-NG validation context (optional)
480 *
481 * Allocate a new RelaxNG grammar.
482 *
483 * Returns the newly allocated structure or NULL in case or error
484 */
485static xmlRelaxNGGrammarPtr
486xmlRelaxNGNewGrammar(xmlRelaxNGParserCtxtPtr ctxt)
487{
488 xmlRelaxNGGrammarPtr ret;
489
490 ret = (xmlRelaxNGGrammarPtr) xmlMalloc(sizeof(xmlRelaxNGGrammar));
491 if (ret == NULL) {
492 if ((ctxt != NULL) && (ctxt->error != NULL))
493 ctxt->error(ctxt->userData, "Out of memory\n");
494 ctxt->nbErrors++;
495 return (NULL);
496 }
497 memset(ret, 0, sizeof(xmlRelaxNGGrammar));
498
499 return (ret);
500}
501
502/**
503 * xmlRelaxNGFreeGrammar:
504 * @grammar: a grammar structure
505 *
506 * Deallocate a RelaxNG grammar structure.
507 */
508static void
509xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar)
510{
511 if (grammar == NULL)
512 return;
513
Daniel Veillard419a7682003-02-03 23:22:49 +0000514 if (grammar->next != NULL) {
515 xmlRelaxNGFreeGrammar(grammar->next);
516 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000517 if (grammar->refs != NULL) {
518 xmlHashFree(grammar->refs, NULL);
519 }
520 if (grammar->defs != NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000521 xmlHashFree(grammar->defs, NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000522 }
523
524 xmlFree(grammar);
525}
526
527/**
528 * xmlRelaxNGNewDefine:
529 * @ctxt: a Relax-NG validation context
530 * @node: the node in the input document.
531 *
532 * Allocate a new RelaxNG define.
533 *
534 * Returns the newly allocated structure or NULL in case or error
535 */
536static xmlRelaxNGDefinePtr
537xmlRelaxNGNewDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
538{
539 xmlRelaxNGDefinePtr ret;
540
Daniel Veillard419a7682003-02-03 23:22:49 +0000541 if (ctxt->defMax == 0) {
542 ctxt->defMax = 16;
543 ctxt->defNr = 0;
544 ctxt->defTab = (xmlRelaxNGDefinePtr *)
545 xmlMalloc(ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
546 if (ctxt->defTab == NULL) {
547 if ((ctxt != NULL) && (ctxt->error != NULL))
548 ctxt->error(ctxt->userData, "Out of memory\n");
549 ctxt->nbErrors++;
550 return (NULL);
551 }
552 } else if (ctxt->defMax <= ctxt->defNr) {
553 xmlRelaxNGDefinePtr *tmp;
554 ctxt->defMax *= 2;
555 tmp = (xmlRelaxNGDefinePtr *) xmlRealloc(ctxt->defTab,
556 ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
557 if (tmp == NULL) {
558 if ((ctxt != NULL) && (ctxt->error != NULL))
559 ctxt->error(ctxt->userData, "Out of memory\n");
560 ctxt->nbErrors++;
561 return (NULL);
562 }
563 ctxt->defTab = tmp;
564 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000565 ret = (xmlRelaxNGDefinePtr) xmlMalloc(sizeof(xmlRelaxNGDefine));
566 if (ret == NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000567 if ((ctxt != NULL) && (ctxt->error != NULL))
568 ctxt->error(ctxt->userData, "Out of memory\n");
Daniel Veillard6eadf632003-01-23 18:29:16 +0000569 ctxt->nbErrors++;
Daniel Veillard419a7682003-02-03 23:22:49 +0000570 return(NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000571 }
572 memset(ret, 0, sizeof(xmlRelaxNGDefine));
Daniel Veillard419a7682003-02-03 23:22:49 +0000573 ctxt->defTab[ctxt->defNr++] = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000574 ret->node = node;
Daniel Veillardd4310742003-02-18 21:12:46 +0000575 ret->depth = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000576 return (ret);
577}
578
579/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000580 * xmlRelaxNGFreePartition:
581 * @partitions: a partition set structure
582 *
583 * Deallocate RelaxNG partition set structures.
584 */
585static void
586xmlRelaxNGFreePartition(xmlRelaxNGPartitionPtr partitions) {
587 xmlRelaxNGInterleaveGroupPtr group;
588 int j;
589
590 if (partitions != NULL) {
591 if (partitions->groups != NULL) {
592 for (j = 0;j < partitions->nbgroups;j++) {
593 group = partitions->groups[j];
594 if (group != NULL) {
595 if (group->defs != NULL)
596 xmlFree(group->defs);
Daniel Veillard44e1dd02003-02-21 23:23:28 +0000597 if (group->attrs != NULL)
598 xmlFree(group->attrs);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000599 xmlFree(group);
600 }
601 }
602 xmlFree(partitions->groups);
603 }
604 xmlFree(partitions);
605 }
606}
607/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000608 * xmlRelaxNGFreeDefine:
609 * @define: a define structure
610 *
611 * Deallocate a RelaxNG define structure.
612 */
613static void
614xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define)
615{
616 if (define == NULL)
617 return;
618
Daniel Veillard419a7682003-02-03 23:22:49 +0000619 if ((define->data != NULL) &&
620 (define->type == XML_RELAXNG_INTERLEAVE))
621 xmlRelaxNGFreePartition((xmlRelaxNGPartitionPtr) define->data);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000622 if (define->name != NULL)
623 xmlFree(define->name);
624 if (define->ns != NULL)
625 xmlFree(define->ns);
Daniel Veillardedc91922003-01-26 00:52:04 +0000626 if (define->value != NULL)
627 xmlFree(define->value);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000628 xmlFree(define);
629}
630
631/**
632 * xmlRelaxNGNewValidState:
633 * @ctxt: a Relax-NG validation context
634 * @node: the current node or NULL for the document
635 *
636 * Allocate a new RelaxNG validation state
637 *
638 * Returns the newly allocated structure or NULL in case or error
639 */
640static xmlRelaxNGValidStatePtr
641xmlRelaxNGNewValidState(xmlRelaxNGValidCtxtPtr ctxt, xmlNodePtr node)
642{
643 xmlRelaxNGValidStatePtr ret;
644 xmlAttrPtr attr;
645 xmlAttrPtr attrs[MAX_ATTR];
646 int nbAttrs = 0;
647 xmlNodePtr root = NULL;
648
649 if (node == NULL) {
650 root = xmlDocGetRootElement(ctxt->doc);
651 if (root == NULL)
652 return(NULL);
653 } else {
654 attr = node->properties;
655 while (attr != NULL) {
656 if (nbAttrs < MAX_ATTR)
657 attrs[nbAttrs++] = attr;
658 else
659 nbAttrs++;
660 attr = attr->next;
661 }
662 }
663
664 if (nbAttrs < MAX_ATTR)
665 attrs[nbAttrs] = NULL;
666 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(sizeof(xmlRelaxNGValidState) +
667 nbAttrs * sizeof(xmlAttrPtr));
668 if (ret == NULL) {
669 if ((ctxt != NULL) && (ctxt->error != NULL))
670 ctxt->error(ctxt->userData, "Out of memory\n");
671 return (NULL);
672 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +0000673 ret->value = NULL;
674 ret->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000675 if (node == NULL) {
676 ret->node = (xmlNodePtr) ctxt->doc;
677 ret->seq = root;
678 ret->nbAttrs = 0;
679 } else {
680 ret->node = node;
681 ret->seq = node->children;
682 ret->nbAttrs = nbAttrs;
683 if (nbAttrs > 0) {
684 if (nbAttrs < MAX_ATTR) {
685 memcpy(&(ret->attrs[0]), attrs,
686 sizeof(xmlAttrPtr) * (nbAttrs + 1));
687 } else {
688 attr = node->properties;
689 nbAttrs = 0;
690 while (attr != NULL) {
691 ret->attrs[nbAttrs++] = attr;
692 attr = attr->next;
693 }
694 ret->attrs[nbAttrs] = NULL;
695 }
696 }
697 }
Daniel Veillard1ed7f362003-02-03 10:57:45 +0000698 ret->nbAttrLeft = ret->nbAttrs;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000699 return (ret);
700}
701
702/**
703 * xmlRelaxNGCopyValidState:
704 * @ctxt: a Relax-NG validation context
705 * @state: a validation state
706 *
707 * Copy the validation state
708 *
709 * Returns the newly allocated structure or NULL in case or error
710 */
711static xmlRelaxNGValidStatePtr
712xmlRelaxNGCopyValidState(xmlRelaxNGValidCtxtPtr ctxt,
713 xmlRelaxNGValidStatePtr state)
714{
715 xmlRelaxNGValidStatePtr ret;
716 unsigned int size;
717
718 if (state == NULL)
719 return(NULL);
720
721 size = sizeof(xmlRelaxNGValidState) +
722 state->nbAttrs * sizeof(xmlAttrPtr);
723 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(size);
724 if (ret == NULL) {
725 if ((ctxt != NULL) && (ctxt->error != NULL))
726 ctxt->error(ctxt->userData, "Out of memory\n");
727 return (NULL);
728 }
729 memcpy(ret, state, size);
730 return(ret);
731}
732
733/**
Daniel Veillardce14fa52003-02-19 17:32:48 +0000734 * xmlRelaxNGEqualValidState:
735 * @ctxt: a Relax-NG validation context
736 * @state1: a validation state
737 * @state2: a validation state
738 *
739 * Compare the validation states for equality
740 *
741 * Returns 1 if equald, 0 otherwise
742 */
743static int
744xmlRelaxNGEqualValidState(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
745 xmlRelaxNGValidStatePtr state1,
746 xmlRelaxNGValidStatePtr state2)
747{
748 int i;
749
750 if ((state1 == NULL) || (state2 == NULL))
751 return(0);
752 if (state1 == state2)
753 return(1);
754 if (state1->node != state2->node)
755 return(0);
756 if (state1->seq != state2->seq)
757 return(0);
758 if (state1->nbAttrLeft != state2->nbAttrLeft)
759 return(0);
760 if (state1->nbAttrs != state2->nbAttrs)
761 return(0);
762 if (state1->endvalue != state2->endvalue)
763 return(0);
764 if ((state1->value != state2->value) &&
765 (!xmlStrEqual(state1->value, state2->value)))
766 return(0);
767 for (i = 0;i < state1->nbAttrs;i++) {
768 if (state1->attrs[i] != state2->attrs[i])
769 return(0);
770 }
771 return(1);
772}
773
774/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000775 * xmlRelaxNGFreeValidState:
776 * @state: a validation state structure
777 *
778 * Deallocate a RelaxNG validation state structure.
779 */
780static void
781xmlRelaxNGFreeValidState(xmlRelaxNGValidStatePtr state)
782{
783 if (state == NULL)
784 return;
785
786 xmlFree(state);
787}
788
789/************************************************************************
790 * *
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000791 * Document functions *
792 * *
793 ************************************************************************/
794static xmlDocPtr xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt,
795 xmlDocPtr doc);
796
797/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000798 * xmlRelaxNGIncludePush:
799 * @ctxt: the parser context
800 * @value: the element doc
801 *
802 * Pushes a new include on top of the include stack
803 *
804 * Returns 0 in case of error, the index in the stack otherwise
805 */
806static int
807xmlRelaxNGIncludePush(xmlRelaxNGParserCtxtPtr ctxt,
808 xmlRelaxNGIncludePtr value)
809{
810 if (ctxt->incTab == NULL) {
811 ctxt->incMax = 4;
812 ctxt->incNr = 0;
813 ctxt->incTab = (xmlRelaxNGIncludePtr *) xmlMalloc(
814 ctxt->incMax * sizeof(ctxt->incTab[0]));
815 if (ctxt->incTab == NULL) {
816 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
817 return (0);
818 }
819 }
820 if (ctxt->incNr >= ctxt->incMax) {
821 ctxt->incMax *= 2;
822 ctxt->incTab =
823 (xmlRelaxNGIncludePtr *) xmlRealloc(ctxt->incTab,
824 ctxt->incMax *
825 sizeof(ctxt->incTab[0]));
826 if (ctxt->incTab == NULL) {
827 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
828 return (0);
829 }
830 }
831 ctxt->incTab[ctxt->incNr] = value;
832 ctxt->inc = value;
833 return (ctxt->incNr++);
834}
835
836/**
837 * xmlRelaxNGIncludePop:
838 * @ctxt: the parser context
839 *
840 * Pops the top include from the include stack
841 *
842 * Returns the include just removed
843 */
844static xmlRelaxNGIncludePtr
845xmlRelaxNGIncludePop(xmlRelaxNGParserCtxtPtr ctxt)
846{
847 xmlRelaxNGIncludePtr ret;
848
849 if (ctxt->incNr <= 0)
850 return (0);
851 ctxt->incNr--;
852 if (ctxt->incNr > 0)
853 ctxt->inc = ctxt->incTab[ctxt->incNr - 1];
854 else
855 ctxt->inc = NULL;
856 ret = ctxt->incTab[ctxt->incNr];
857 ctxt->incTab[ctxt->incNr] = 0;
858 return (ret);
859}
860
861/**
862 * xmlRelaxNGLoadInclude:
863 * @ctxt: the parser context
864 * @URL: the normalized URL
865 * @node: the include node.
Daniel Veillard416589a2003-02-17 17:25:42 +0000866 * @ns: the namespace passed from the context.
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000867 *
868 * First lookup if the document is already loaded into the parser context,
869 * check against recursion. If not found the resource is loaded and
870 * the content is preprocessed before being returned back to the caller.
871 *
872 * Returns the xmlRelaxNGIncludePtr or NULL in case of error
873 */
874static xmlRelaxNGIncludePtr
875xmlRelaxNGLoadInclude(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillard416589a2003-02-17 17:25:42 +0000876 xmlNodePtr node, const xmlChar *ns) {
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000877 xmlRelaxNGIncludePtr ret = NULL;
878 xmlDocPtr doc;
879 int i;
880 xmlNodePtr root, tmp, tmp2, cur;
881
882 /*
883 * check against recursion in the stack
884 */
885 for (i = 0;i < ctxt->incNr;i++) {
886 if (xmlStrEqual(ctxt->incTab[i]->href, URL)) {
887 if (ctxt->error != NULL)
888 ctxt->error(ctxt->userData,
889 "Detected an externalRef recursion for %s\n",
890 URL);
891 ctxt->nbErrors++;
892 return(NULL);
893 }
894 }
895
896 /*
897 * Lookup in the hash table
898 */
899 if (ctxt->includes == NULL) {
900 ctxt->includes = xmlHashCreate(10);
901 if (ctxt->includes == NULL) {
902 if (ctxt->error != NULL)
903 ctxt->error(ctxt->userData,
904 "Failed to allocate hash table for document\n");
905 ctxt->nbErrors++;
906 return(NULL);
907 }
908 } else {
Daniel Veillard416589a2003-02-17 17:25:42 +0000909 if (ns == NULL)
910 ret = xmlHashLookup2(ctxt->includes, BAD_CAST "", URL);
911 else
912 ret = xmlHashLookup2(ctxt->includes, ns, URL);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000913 if (ret != NULL)
914 return(ret);
915 }
916
917
918 /*
919 * load the document
920 */
921 doc = xmlParseFile((const char *) URL);
922 if (doc == NULL) {
923 if (ctxt->error != NULL)
924 ctxt->error(ctxt->userData,
925 "xmlRelaxNG: could not load %s\n", URL);
926 ctxt->nbErrors++;
927 return (NULL);
928 }
929
930 /*
931 * Allocate the document structures and register it first.
932 */
933 ret = (xmlRelaxNGIncludePtr) xmlMalloc(sizeof(xmlRelaxNGInclude));
934 if (ret == NULL) {
935 if (ctxt->error != NULL)
936 ctxt->error(ctxt->userData,
937 "xmlRelaxNG: allocate memory for doc %s\n", URL);
938 ctxt->nbErrors++;
939 xmlFreeDoc(doc);
940 return (NULL);
941 }
942 memset(ret, 0, sizeof(xmlRelaxNGInclude));
943 ret->doc = doc;
944 ret->href = xmlStrdup(URL);
945
946 /*
Daniel Veillard416589a2003-02-17 17:25:42 +0000947 * transmit the ns if needed
948 */
949 if (ns != NULL) {
950 root = xmlDocGetRootElement(doc);
951 if (root != NULL) {
952 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
953 xmlSetProp(root, BAD_CAST"ns", ns);
954 }
955 }
956 }
957
958 /*
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000959 * push it on the stack and register it in the hash table
960 */
Daniel Veillard416589a2003-02-17 17:25:42 +0000961 if (ns == NULL)
962 xmlHashAddEntry2(ctxt->includes, BAD_CAST "", URL, ret);
963 else
964 xmlHashAddEntry2(ctxt->includes, ns, URL, ret);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000965 xmlRelaxNGIncludePush(ctxt, ret);
966
967 /*
968 * Some preprocessing of the document content, this include recursing
969 * in the include stack.
970 */
971 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
972 if (doc == NULL) {
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000973 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++;
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000991 return (NULL);
992 }
993 if (!IS_RELAXNG(root, "grammar")) {
994 if (ctxt->error != NULL)
995 ctxt->error(ctxt->userData,
996 "xmlRelaxNG: included document %s root is not a grammar\n",
997 URL);
998 ctxt->nbErrors++;
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000999 return (NULL);
1000 }
1001
1002 /*
1003 * Elimination of redefined rules in the include.
1004 */
1005 cur = node->children;
1006 while (cur != NULL) {
1007 if (IS_RELAXNG(cur, "start")) {
1008 int found = 0;
1009
1010 tmp = root->children;
1011 while (tmp != NULL) {
1012 tmp2 = tmp->next;
1013 if (IS_RELAXNG(tmp, "start")) {
1014 found = 1;
1015 xmlUnlinkNode(tmp);
1016 xmlFreeNode(tmp);
1017 }
1018 tmp = tmp2;
1019 }
1020 if (!found) {
1021 if (ctxt->error != NULL)
1022 ctxt->error(ctxt->userData,
1023 "xmlRelaxNG: include %s has a start but not the included grammar\n",
1024 URL);
1025 ctxt->nbErrors++;
1026 }
1027 } else if (IS_RELAXNG(cur, "define")) {
1028 xmlChar *name, *name2;
1029
1030 name = xmlGetProp(cur, BAD_CAST "name");
1031 if (name == NULL) {
1032 if (ctxt->error != NULL)
1033 ctxt->error(ctxt->userData,
1034 "xmlRelaxNG: include %s has define without name\n",
1035 URL);
1036 ctxt->nbErrors++;
1037 } else {
1038 int found = 0;
1039
Daniel Veillardd2298792003-02-14 16:54:11 +00001040 xmlRelaxNGNormExtSpace(name);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001041 tmp = root->children;
1042 while (tmp != NULL) {
1043 tmp2 = tmp->next;
1044 if (IS_RELAXNG(tmp, "define")) {
1045 name2 = xmlGetProp(tmp, BAD_CAST "name");
Daniel Veillardd2298792003-02-14 16:54:11 +00001046 xmlRelaxNGNormExtSpace(name2);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001047 if (name2 != NULL) {
1048 if (xmlStrEqual(name, name2)) {
1049 found = 1;
1050 xmlUnlinkNode(tmp);
1051 xmlFreeNode(tmp);
1052 }
1053 xmlFree(name2);
1054 }
1055 }
1056 tmp = tmp2;
1057 }
1058 if (!found) {
1059 if (ctxt->error != NULL)
1060 ctxt->error(ctxt->userData,
1061 "xmlRelaxNG: include %s has a define %s but not the included grammar\n",
1062 URL, name);
1063 ctxt->nbErrors++;
1064 }
1065 xmlFree(name);
1066 }
1067 }
1068 cur = cur->next;
1069 }
1070
1071
1072 return(ret);
1073}
1074
1075/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001076 * xmlRelaxNGDocumentPush:
1077 * @ctxt: the parser context
1078 * @value: the element doc
1079 *
1080 * Pushes a new doc on top of the doc stack
1081 *
1082 * Returns 0 in case of error, the index in the stack otherwise
1083 */
1084static int
1085xmlRelaxNGDocumentPush(xmlRelaxNGParserCtxtPtr ctxt,
1086 xmlRelaxNGDocumentPtr value)
1087{
1088 if (ctxt->docTab == NULL) {
1089 ctxt->docMax = 4;
1090 ctxt->docNr = 0;
1091 ctxt->docTab = (xmlRelaxNGDocumentPtr *) xmlMalloc(
1092 ctxt->docMax * sizeof(ctxt->docTab[0]));
1093 if (ctxt->docTab == NULL) {
1094 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
1095 return (0);
1096 }
1097 }
1098 if (ctxt->docNr >= ctxt->docMax) {
1099 ctxt->docMax *= 2;
1100 ctxt->docTab =
1101 (xmlRelaxNGDocumentPtr *) xmlRealloc(ctxt->docTab,
1102 ctxt->docMax *
1103 sizeof(ctxt->docTab[0]));
1104 if (ctxt->docTab == NULL) {
1105 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
1106 return (0);
1107 }
1108 }
1109 ctxt->docTab[ctxt->docNr] = value;
1110 ctxt->doc = value;
1111 return (ctxt->docNr++);
1112}
1113
1114/**
1115 * xmlRelaxNGDocumentPop:
1116 * @ctxt: the parser context
1117 *
1118 * Pops the top doc from the doc stack
1119 *
1120 * Returns the doc just removed
1121 */
1122static xmlRelaxNGDocumentPtr
1123xmlRelaxNGDocumentPop(xmlRelaxNGParserCtxtPtr ctxt)
1124{
1125 xmlRelaxNGDocumentPtr ret;
1126
1127 if (ctxt->docNr <= 0)
1128 return (0);
1129 ctxt->docNr--;
1130 if (ctxt->docNr > 0)
1131 ctxt->doc = ctxt->docTab[ctxt->docNr - 1];
1132 else
1133 ctxt->doc = NULL;
1134 ret = ctxt->docTab[ctxt->docNr];
1135 ctxt->docTab[ctxt->docNr] = 0;
1136 return (ret);
1137}
1138
1139/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001140 * xmlRelaxNGLoadExternalRef:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001141 * @ctxt: the parser context
1142 * @URL: the normalized URL
1143 * @ns: the inherited ns if any
1144 *
1145 * First lookup if the document is already loaded into the parser context,
1146 * check against recursion. If not found the resource is loaded and
1147 * the content is preprocessed before being returned back to the caller.
1148 *
1149 * Returns the xmlRelaxNGDocumentPtr or NULL in case of error
1150 */
1151static xmlRelaxNGDocumentPtr
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001152xmlRelaxNGLoadExternalRef(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001153 const xmlChar *ns) {
1154 xmlRelaxNGDocumentPtr ret = NULL;
1155 xmlDocPtr doc;
1156 xmlNodePtr root;
1157 int i;
1158
1159 /*
1160 * check against recursion in the stack
1161 */
1162 for (i = 0;i < ctxt->docNr;i++) {
1163 if (xmlStrEqual(ctxt->docTab[i]->href, URL)) {
1164 if (ctxt->error != NULL)
1165 ctxt->error(ctxt->userData,
1166 "Detected an externalRef recursion for %s\n",
1167 URL);
1168 ctxt->nbErrors++;
1169 return(NULL);
1170 }
1171 }
1172
1173 /*
1174 * Lookup in the hash table
1175 */
1176 if (ctxt->documents == NULL) {
1177 ctxt->documents = xmlHashCreate(10);
1178 if (ctxt->documents == NULL) {
1179 if (ctxt->error != NULL)
1180 ctxt->error(ctxt->userData,
1181 "Failed to allocate hash table for document\n");
1182 ctxt->nbErrors++;
1183 return(NULL);
1184 }
1185 } else {
1186 if (ns == NULL)
1187 ret = xmlHashLookup2(ctxt->documents, BAD_CAST "", URL);
1188 else
1189 ret = xmlHashLookup2(ctxt->documents, ns, URL);
1190 if (ret != NULL)
1191 return(ret);
1192 }
1193
1194
1195 /*
1196 * load the document
1197 */
1198 doc = xmlParseFile((const char *) URL);
1199 if (doc == NULL) {
1200 if (ctxt->error != NULL)
1201 ctxt->error(ctxt->userData,
1202 "xmlRelaxNG: could not load %s\n", URL);
1203 ctxt->nbErrors++;
1204 return (NULL);
1205 }
1206
1207 /*
1208 * Allocate the document structures and register it first.
1209 */
1210 ret = (xmlRelaxNGDocumentPtr) xmlMalloc(sizeof(xmlRelaxNGDocument));
1211 if (ret == NULL) {
1212 if (ctxt->error != NULL)
1213 ctxt->error(ctxt->userData,
1214 "xmlRelaxNG: allocate memory for doc %s\n", URL);
1215 ctxt->nbErrors++;
1216 xmlFreeDoc(doc);
1217 return (NULL);
1218 }
1219 memset(ret, 0, sizeof(xmlRelaxNGDocument));
1220 ret->doc = doc;
1221 ret->href = xmlStrdup(URL);
1222
1223 /*
1224 * transmit the ns if needed
1225 */
1226 if (ns != NULL) {
1227 root = xmlDocGetRootElement(doc);
1228 if (root != NULL) {
1229 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
1230 xmlSetProp(root, BAD_CAST"ns", ns);
1231 }
1232 }
1233 }
1234
1235 /*
1236 * push it on the stack and register it in the hash table
1237 */
1238 if (ns == NULL)
1239 xmlHashAddEntry2(ctxt->documents, BAD_CAST "", URL, ret);
1240 else
1241 xmlHashAddEntry2(ctxt->documents, ns, URL, ret);
1242 xmlRelaxNGDocumentPush(ctxt, ret);
1243
1244 /*
1245 * Some preprocessing of the document content
1246 */
1247 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
1248 if (doc == NULL) {
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001249 ctxt->doc = NULL;
1250 return(NULL);
1251 }
1252
1253 xmlRelaxNGDocumentPop(ctxt);
1254
1255 return(ret);
1256}
1257
1258/************************************************************************
1259 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +00001260 * Error functions *
1261 * *
1262 ************************************************************************/
1263
1264#define VALID_CTXT() \
Daniel Veillard231d7912003-02-09 14:22:17 +00001265 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1266 xmlGenericError(xmlGenericErrorContext, \
Daniel Veillard6eadf632003-01-23 18:29:16 +00001267 "error detected at %s:%d\n", \
1268 __FILE__, __LINE__);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001269
1270#define VALID_ERROR(a) \
Daniel Veillard231d7912003-02-09 14:22:17 +00001271 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001272 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a)
1273#define VALID_ERROR2(a, b) \
1274 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1275 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a, b)
1276#define VALID_ERROR3(a, b, c) \
1277 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1278 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a, b, c)
Daniel Veillard6eadf632003-01-23 18:29:16 +00001279
Daniel Veillardd2298792003-02-14 16:54:11 +00001280#ifdef DEBUG
Daniel Veillard231d7912003-02-09 14:22:17 +00001281static const char *
1282xmlRelaxNGDefName(xmlRelaxNGDefinePtr def) {
1283 if (def == NULL)
1284 return("none");
1285 switch(def->type) {
1286 case XML_RELAXNG_EMPTY: return("empty");
1287 case XML_RELAXNG_NOT_ALLOWED: return("notAllowed");
1288 case XML_RELAXNG_EXCEPT: return("except");
1289 case XML_RELAXNG_TEXT: return("text");
1290 case XML_RELAXNG_ELEMENT: return("element");
1291 case XML_RELAXNG_DATATYPE: return("datatype");
1292 case XML_RELAXNG_VALUE: return("value");
1293 case XML_RELAXNG_LIST: return("list");
1294 case XML_RELAXNG_ATTRIBUTE: return("attribute");
1295 case XML_RELAXNG_DEF: return("def");
1296 case XML_RELAXNG_REF: return("ref");
1297 case XML_RELAXNG_EXTERNALREF: return("externalRef");
1298 case XML_RELAXNG_PARENTREF: return("parentRef");
1299 case XML_RELAXNG_OPTIONAL: return("optional");
1300 case XML_RELAXNG_ZEROORMORE: return("zeroOrMore");
1301 case XML_RELAXNG_ONEORMORE: return("oneOrMore");
1302 case XML_RELAXNG_CHOICE: return("choice");
1303 case XML_RELAXNG_GROUP: return("group");
1304 case XML_RELAXNG_INTERLEAVE: return("interleave");
1305 case XML_RELAXNG_START: return("start");
1306 }
1307 return("unknown");
1308}
Daniel Veillardd2298792003-02-14 16:54:11 +00001309#endif
1310
Daniel Veillard6eadf632003-01-23 18:29:16 +00001311#if 0
1312/**
1313 * xmlRelaxNGErrorContext:
1314 * @ctxt: the parsing context
1315 * @schema: the schema being built
1316 * @node: the node being processed
1317 * @child: the child being processed
1318 *
1319 * Dump a RelaxNGType structure
1320 */
1321static void
1322xmlRelaxNGErrorContext(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGPtr schema,
1323 xmlNodePtr node, xmlNodePtr child)
1324{
1325 int line = 0;
1326 const xmlChar *file = NULL;
1327 const xmlChar *name = NULL;
1328 const char *type = "error";
1329
1330 if ((ctxt == NULL) || (ctxt->error == NULL))
1331 return;
1332
1333 if (child != NULL)
1334 node = child;
1335
1336 if (node != NULL) {
1337 if ((node->type == XML_DOCUMENT_NODE) ||
1338 (node->type == XML_HTML_DOCUMENT_NODE)) {
1339 xmlDocPtr doc = (xmlDocPtr) node;
1340
1341 file = doc->URL;
1342 } else {
1343 /*
1344 * Try to find contextual informations to report
1345 */
1346 if (node->type == XML_ELEMENT_NODE) {
1347 line = (int) node->content;
1348 } else if ((node->prev != NULL) &&
1349 (node->prev->type == XML_ELEMENT_NODE)) {
1350 line = (int) node->prev->content;
1351 } else if ((node->parent != NULL) &&
1352 (node->parent->type == XML_ELEMENT_NODE)) {
1353 line = (int) node->parent->content;
1354 }
1355 if ((node->doc != NULL) && (node->doc->URL != NULL))
1356 file = node->doc->URL;
1357 if (node->name != NULL)
1358 name = node->name;
1359 }
1360 }
1361
1362 if (ctxt != NULL)
1363 type = "compilation error";
1364 else if (schema != NULL)
1365 type = "runtime error";
1366
1367 if ((file != NULL) && (line != 0) && (name != NULL))
1368 ctxt->error(ctxt->userData, "%s: file %s line %d element %s\n",
1369 type, file, line, name);
1370 else if ((file != NULL) && (name != NULL))
1371 ctxt->error(ctxt->userData, "%s: file %s element %s\n",
1372 type, file, name);
1373 else if ((file != NULL) && (line != 0))
1374 ctxt->error(ctxt->userData, "%s: file %s line %d\n", type, file, line);
1375 else if (file != NULL)
1376 ctxt->error(ctxt->userData, "%s: file %s\n", type, file);
1377 else if (name != NULL)
1378 ctxt->error(ctxt->userData, "%s: element %s\n", type, name);
1379 else
1380 ctxt->error(ctxt->userData, "%s\n", type);
1381}
1382#endif
1383
1384/************************************************************************
1385 * *
1386 * Type library hooks *
1387 * *
1388 ************************************************************************/
Daniel Veillardea3f3982003-01-26 19:45:18 +00001389static xmlChar *xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt,
1390 const xmlChar *str);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001391
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001392/**
1393 * xmlRelaxNGSchemaTypeHave:
1394 * @data: data needed for the library
1395 * @type: the type name
1396 *
1397 * Check if the given type is provided by
1398 * the W3C XMLSchema Datatype library.
1399 *
1400 * Returns 1 if yes, 0 if no and -1 in case of error.
1401 */
1402static int
1403xmlRelaxNGSchemaTypeHave(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001404 const xmlChar *type) {
1405 xmlSchemaTypePtr typ;
1406
1407 if (type == NULL)
1408 return(-1);
1409 typ = xmlSchemaGetPredefinedType(type,
1410 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1411 if (typ == NULL)
1412 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001413 return(1);
1414}
1415
1416/**
1417 * xmlRelaxNGSchemaTypeCheck:
1418 * @data: data needed for the library
1419 * @type: the type name
1420 * @value: the value to check
1421 *
1422 * Check if the given type and value are validated by
1423 * the W3C XMLSchema Datatype library.
1424 *
1425 * Returns 1 if yes, 0 if no and -1 in case of error.
1426 */
1427static int
1428xmlRelaxNGSchemaTypeCheck(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001429 const xmlChar *type,
1430 const xmlChar *value) {
1431 xmlSchemaTypePtr typ;
1432 int ret;
1433
1434 /*
1435 * TODO: the type should be cached ab provided back, interface subject
1436 * to changes.
1437 * TODO: handle facets, may require an additional interface and keep
1438 * the value returned from the validation.
1439 */
1440 if ((type == NULL) || (value == NULL))
1441 return(-1);
1442 typ = xmlSchemaGetPredefinedType(type,
1443 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1444 if (typ == NULL)
1445 return(-1);
1446 ret = xmlSchemaValidatePredefinedType(typ, value, NULL);
1447 if (ret == 0)
1448 return(1);
1449 if (ret > 0)
1450 return(0);
1451 return(-1);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001452}
1453
1454/**
1455 * xmlRelaxNGSchemaTypeCompare:
1456 * @data: data needed for the library
1457 * @type: the type name
1458 * @value1: the first value
1459 * @value2: the second value
1460 *
1461 * Compare two values accordingly a type from the W3C XMLSchema
1462 * Datatype library.
1463 *
1464 * Returns 1 if yes, 0 if no and -1 in case of error.
1465 */
1466static int
1467xmlRelaxNGSchemaTypeCompare(void *data ATTRIBUTE_UNUSED,
1468 const xmlChar *type ATTRIBUTE_UNUSED,
1469 const xmlChar *value1 ATTRIBUTE_UNUSED,
1470 const xmlChar *value2 ATTRIBUTE_UNUSED) {
1471 TODO
1472 return(1);
1473}
1474
1475/**
1476 * xmlRelaxNGDefaultTypeHave:
1477 * @data: data needed for the library
1478 * @type: the type name
1479 *
1480 * Check if the given type is provided by
1481 * the default datatype library.
1482 *
1483 * Returns 1 if yes, 0 if no and -1 in case of error.
1484 */
1485static int
1486xmlRelaxNGDefaultTypeHave(void *data ATTRIBUTE_UNUSED, const xmlChar *type) {
1487 if (type == NULL)
1488 return(-1);
1489 if (xmlStrEqual(type, BAD_CAST "string"))
1490 return(1);
1491 if (xmlStrEqual(type, BAD_CAST "token"))
1492 return(1);
1493 return(0);
1494}
1495
1496/**
1497 * xmlRelaxNGDefaultTypeCheck:
1498 * @data: data needed for the library
1499 * @type: the type name
1500 * @value: the value to check
1501 *
1502 * Check if the given type and value are validated by
1503 * the default datatype library.
1504 *
1505 * Returns 1 if yes, 0 if no and -1 in case of error.
1506 */
1507static int
1508xmlRelaxNGDefaultTypeCheck(void *data ATTRIBUTE_UNUSED,
1509 const xmlChar *type ATTRIBUTE_UNUSED,
1510 const xmlChar *value ATTRIBUTE_UNUSED) {
Daniel Veillardd4310742003-02-18 21:12:46 +00001511 if (value == NULL)
1512 return(-1);
1513 if (xmlStrEqual(type, BAD_CAST "string"))
1514 return(1);
1515 if (xmlStrEqual(type, BAD_CAST "token")) {
1516#if 0
1517 const xmlChar *cur = value;
1518
1519 while (*cur != 0) {
1520 if (!IS_BLANK(*cur))
1521 return(1);
1522 cur++;
1523 }
1524#endif
1525 return(1);
1526 }
1527
1528 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001529}
1530
1531/**
1532 * xmlRelaxNGDefaultTypeCompare:
1533 * @data: data needed for the library
1534 * @type: the type name
1535 * @value1: the first value
1536 * @value2: the second value
1537 *
1538 * Compare two values accordingly a type from the default
1539 * datatype library.
1540 *
1541 * Returns 1 if yes, 0 if no and -1 in case of error.
1542 */
1543static int
1544xmlRelaxNGDefaultTypeCompare(void *data ATTRIBUTE_UNUSED,
1545 const xmlChar *type ATTRIBUTE_UNUSED,
1546 const xmlChar *value1 ATTRIBUTE_UNUSED,
1547 const xmlChar *value2 ATTRIBUTE_UNUSED) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00001548 int ret = -1;
1549
1550 if (xmlStrEqual(type, BAD_CAST "string")) {
1551 ret = xmlStrEqual(value1, value2);
1552 } else if (xmlStrEqual(type, BAD_CAST "token")) {
1553 if (!xmlStrEqual(value1, value2)) {
1554 xmlChar *nval, *nvalue;
1555
1556 /*
1557 * TODO: trivial optimizations are possible by
1558 * computing at compile-time
1559 */
1560 nval = xmlRelaxNGNormalize(NULL, value1);
1561 nvalue = xmlRelaxNGNormalize(NULL, value2);
1562
Daniel Veillardd4310742003-02-18 21:12:46 +00001563 if ((nval == NULL) || (nvalue == NULL))
Daniel Veillardea3f3982003-01-26 19:45:18 +00001564 ret = -1;
Daniel Veillardd4310742003-02-18 21:12:46 +00001565 else if (xmlStrEqual(nval, nvalue))
1566 ret = 1;
1567 else
1568 ret = 0;
Daniel Veillardea3f3982003-01-26 19:45:18 +00001569 if (nval != NULL)
1570 xmlFree(nval);
1571 if (nvalue != NULL)
1572 xmlFree(nvalue);
Daniel Veillardd4310742003-02-18 21:12:46 +00001573 } else
1574 ret = 1;
Daniel Veillardea3f3982003-01-26 19:45:18 +00001575 }
1576 return(ret);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001577}
1578
1579static int xmlRelaxNGTypeInitialized = 0;
1580static xmlHashTablePtr xmlRelaxNGRegisteredTypes = NULL;
1581
1582/**
1583 * xmlRelaxNGFreeTypeLibrary:
1584 * @lib: the type library structure
1585 * @namespace: the URI bound to the library
1586 *
1587 * Free the structure associated to the type library
1588 */
Daniel Veillard6eadf632003-01-23 18:29:16 +00001589static void
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001590xmlRelaxNGFreeTypeLibrary(xmlRelaxNGTypeLibraryPtr lib,
1591 const xmlChar *namespace ATTRIBUTE_UNUSED) {
1592 if (lib == NULL)
1593 return;
1594 if (lib->namespace != NULL)
1595 xmlFree((xmlChar *)lib->namespace);
1596 xmlFree(lib);
1597}
1598
1599/**
1600 * xmlRelaxNGRegisterTypeLibrary:
1601 * @namespace: the URI bound to the library
1602 * @data: data associated to the library
1603 * @have: the provide function
1604 * @check: the checking function
1605 * @comp: the comparison function
1606 *
1607 * Register a new type library
1608 *
1609 * Returns 0 in case of success and -1 in case of error.
1610 */
1611static int
1612xmlRelaxNGRegisterTypeLibrary(const xmlChar *namespace, void *data,
1613 xmlRelaxNGTypeHave have, xmlRelaxNGTypeCheck check,
1614 xmlRelaxNGTypeCompare comp) {
1615 xmlRelaxNGTypeLibraryPtr lib;
1616 int ret;
1617
1618 if ((xmlRelaxNGRegisteredTypes == NULL) || (namespace == NULL) ||
1619 (check == NULL) || (comp == NULL))
1620 return(-1);
1621 if (xmlHashLookup(xmlRelaxNGRegisteredTypes, namespace) != NULL) {
1622 xmlGenericError(xmlGenericErrorContext,
1623 "Relax-NG types library '%s' already registered\n",
1624 namespace);
1625 return(-1);
1626 }
1627 lib = (xmlRelaxNGTypeLibraryPtr) xmlMalloc(sizeof(xmlRelaxNGTypeLibrary));
1628 if (lib == NULL) {
1629 xmlGenericError(xmlGenericErrorContext,
1630 "Relax-NG types library '%s' malloc() failed\n",
1631 namespace);
1632 return (-1);
1633 }
1634 memset(lib, 0, sizeof(xmlRelaxNGTypeLibrary));
1635 lib->namespace = xmlStrdup(namespace);
1636 lib->data = data;
1637 lib->have = have;
1638 lib->comp = comp;
1639 lib->check = check;
1640 ret = xmlHashAddEntry(xmlRelaxNGRegisteredTypes, namespace, lib);
1641 if (ret < 0) {
1642 xmlGenericError(xmlGenericErrorContext,
1643 "Relax-NG types library failed to register '%s'\n",
1644 namespace);
1645 xmlRelaxNGFreeTypeLibrary(lib, namespace);
1646 return(-1);
1647 }
1648 return(0);
1649}
1650
1651/**
1652 * xmlRelaxNGInitTypes:
1653 *
1654 * Initilize the default type libraries.
1655 *
1656 * Returns 0 in case of success and -1 in case of error.
1657 */
1658static int
Daniel Veillard6eadf632003-01-23 18:29:16 +00001659xmlRelaxNGInitTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001660 if (xmlRelaxNGTypeInitialized != 0)
1661 return(0);
1662 xmlRelaxNGRegisteredTypes = xmlHashCreate(10);
1663 if (xmlRelaxNGRegisteredTypes == NULL) {
1664 xmlGenericError(xmlGenericErrorContext,
1665 "Failed to allocate sh table for Relax-NG types\n");
1666 return(-1);
1667 }
1668 xmlRelaxNGRegisterTypeLibrary(
1669 BAD_CAST "http://www.w3.org/2001/XMLSchema-datatypes",
1670 NULL,
1671 xmlRelaxNGSchemaTypeHave,
1672 xmlRelaxNGSchemaTypeCheck,
1673 xmlRelaxNGSchemaTypeCompare);
1674 xmlRelaxNGRegisterTypeLibrary(
1675 xmlRelaxNGNs,
1676 NULL,
1677 xmlRelaxNGDefaultTypeHave,
1678 xmlRelaxNGDefaultTypeCheck,
1679 xmlRelaxNGDefaultTypeCompare);
1680 xmlRelaxNGTypeInitialized = 1;
1681 return(0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001682}
1683
1684/**
1685 * xmlRelaxNGCleanupTypes:
1686 *
1687 * Cleanup the default Schemas type library associated to RelaxNG
1688 */
1689void
1690xmlRelaxNGCleanupTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001691 if (xmlRelaxNGTypeInitialized == 0)
1692 return;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001693 xmlSchemaCleanupTypes();
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001694 xmlHashFree(xmlRelaxNGRegisteredTypes, (xmlHashDeallocator)
1695 xmlRelaxNGFreeTypeLibrary);
1696 xmlRelaxNGTypeInitialized = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001697}
1698
1699/************************************************************************
1700 * *
1701 * Parsing functions *
1702 * *
1703 ************************************************************************/
1704
1705static xmlRelaxNGDefinePtr xmlRelaxNGParseAttribute(
1706 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1707static xmlRelaxNGDefinePtr xmlRelaxNGParseElement(
1708 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1709static xmlRelaxNGDefinePtr xmlRelaxNGParsePatterns(
Daniel Veillard154877e2003-01-30 12:17:05 +00001710 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes, int group);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001711static xmlRelaxNGDefinePtr xmlRelaxNGParsePattern(
1712 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001713static xmlRelaxNGPtr xmlRelaxNGParseDocument(
1714 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00001715static int xmlRelaxNGParseGrammarContent(
1716 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00001717static xmlRelaxNGDefinePtr xmlRelaxNGParseNameClass(
1718 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
1719 xmlRelaxNGDefinePtr def);
Daniel Veillard419a7682003-02-03 23:22:49 +00001720static xmlRelaxNGGrammarPtr xmlRelaxNGParseGrammar(
1721 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001722
1723
1724#define IS_BLANK_NODE(n) \
1725 (((n)->type == XML_TEXT_NODE) && (xmlRelaxNGIsBlank((n)->content)))
1726
1727/**
1728 * xmlRelaxNGIsBlank:
1729 * @str: a string
1730 *
1731 * Check if a string is ignorable c.f. 4.2. Whitespace
1732 *
1733 * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
1734 */
1735static int
1736xmlRelaxNGIsBlank(xmlChar *str) {
1737 if (str == NULL)
1738 return(1);
1739 while (*str != 0) {
1740 if (!(IS_BLANK(*str))) return(0);
1741 str++;
1742 }
1743 return(1);
1744}
1745
Daniel Veillard6eadf632003-01-23 18:29:16 +00001746/**
1747 * xmlRelaxNGGetDataTypeLibrary:
1748 * @ctxt: a Relax-NG parser context
1749 * @node: the current data or value element
1750 *
1751 * Applies algorithm from 4.3. datatypeLibrary attribute
1752 *
1753 * Returns the datatypeLibary value or NULL if not found
1754 */
1755static xmlChar *
1756xmlRelaxNGGetDataTypeLibrary(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1757 xmlNodePtr node) {
1758 xmlChar *ret, *escape;
1759
Daniel Veillard6eadf632003-01-23 18:29:16 +00001760 if ((IS_RELAXNG(node, "data")) || (IS_RELAXNG(node, "value"))) {
1761 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1762 if (ret != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001763 if (ret[0] == 0) {
1764 xmlFree(ret);
1765 return(NULL);
1766 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001767 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +00001768 if (escape == NULL) {
1769 return(ret);
1770 }
1771 xmlFree(ret);
1772 return(escape);
1773 }
1774 }
1775 node = node->parent;
1776 while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) {
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001777 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1778 if (ret != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001779 if (ret[0] == 0) {
1780 xmlFree(ret);
1781 return(NULL);
1782 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001783 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
1784 if (escape == NULL) {
1785 return(ret);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001786 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001787 xmlFree(ret);
1788 return(escape);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001789 }
1790 node = node->parent;
1791 }
1792 return(NULL);
1793}
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001794
1795/**
Daniel Veillardedc91922003-01-26 00:52:04 +00001796 * xmlRelaxNGParseValue:
1797 * @ctxt: a Relax-NG parser context
1798 * @node: the data node.
1799 *
1800 * parse the content of a RelaxNG value node.
1801 *
1802 * Returns the definition pointer or NULL in case of error
1803 */
1804static xmlRelaxNGDefinePtr
1805xmlRelaxNGParseValue(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1806 xmlRelaxNGDefinePtr def = NULL;
1807 xmlRelaxNGTypeLibraryPtr lib;
1808 xmlChar *type;
1809 xmlChar *library;
1810 int tmp;
1811
1812 def = xmlRelaxNGNewDefine(ctxt, node);
1813 if (def == NULL)
1814 return(NULL);
1815 def->type = XML_RELAXNG_VALUE;
1816
1817 type = xmlGetProp(node, BAD_CAST "type");
1818 if (type != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001819 xmlRelaxNGNormExtSpace(type);
1820 if (xmlValidateNCName(type, 0)) {
1821 if (ctxt->error != NULL)
1822 ctxt->error(ctxt->userData,
1823 "value type '%s' is not an NCName\n",
1824 type);
1825 ctxt->nbErrors++;
1826 }
Daniel Veillardedc91922003-01-26 00:52:04 +00001827 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1828 if (library == NULL)
1829 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1830
1831 def->name = type;
1832 def->ns = library;
1833
1834 lib = (xmlRelaxNGTypeLibraryPtr)
1835 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1836 if (lib == NULL) {
1837 if (ctxt->error != NULL)
1838 ctxt->error(ctxt->userData,
1839 "Use of unregistered type library '%s'\n",
1840 library);
1841 ctxt->nbErrors++;
1842 def->data = NULL;
1843 } else {
1844 def->data = lib;
1845 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001846 if (ctxt->error != NULL)
1847 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00001848 "Internal error with type library '%s': no 'have'\n",
1849 library);
1850 ctxt->nbErrors++;
1851 } else {
1852 tmp = lib->have(lib->data, def->name);
1853 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001854 if (ctxt->error != NULL)
1855 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00001856 "Error type '%s' is not exported by type library '%s'\n",
1857 def->name, library);
1858 ctxt->nbErrors++;
1859 }
1860 }
1861 }
1862 }
1863 if (node->children == NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00001864 def->value = xmlStrdup(BAD_CAST "");
Daniel Veillardedc91922003-01-26 00:52:04 +00001865 } else if ((node->children->type != XML_TEXT_NODE) ||
1866 (node->children->next != NULL)) {
1867 if (ctxt->error != NULL)
1868 ctxt->error(ctxt->userData,
1869 "Expecting a single text value for <value>content\n");
1870 ctxt->nbErrors++;
1871 } else {
1872 def->value = xmlNodeGetContent(node);
1873 if (def->value == NULL) {
1874 if (ctxt->error != NULL)
1875 ctxt->error(ctxt->userData,
1876 "Element <value> has no content\n");
1877 ctxt->nbErrors++;
1878 }
1879 }
1880 /* TODO check ahead of time that the value is okay per the type */
1881 return(def);
1882}
1883
1884/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001885 * xmlRelaxNGParseData:
1886 * @ctxt: a Relax-NG parser context
1887 * @node: the data node.
1888 *
1889 * parse the content of a RelaxNG data node.
1890 *
1891 * Returns the definition pointer or NULL in case of error
1892 */
1893static xmlRelaxNGDefinePtr
1894xmlRelaxNGParseData(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
Daniel Veillard416589a2003-02-17 17:25:42 +00001895 xmlRelaxNGDefinePtr def = NULL, except, last = NULL;
Daniel Veillard8fe98712003-02-19 00:19:14 +00001896 xmlRelaxNGDefinePtr param, lastparam = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001897 xmlRelaxNGTypeLibraryPtr lib;
1898 xmlChar *type;
1899 xmlChar *library;
1900 xmlNodePtr content;
1901 int tmp;
1902
1903 type = xmlGetProp(node, BAD_CAST "type");
1904 if (type == NULL) {
1905 if (ctxt->error != NULL)
1906 ctxt->error(ctxt->userData,
1907 "data has no type\n");
1908 ctxt->nbErrors++;
1909 return(NULL);
1910 }
Daniel Veillardd2298792003-02-14 16:54:11 +00001911 xmlRelaxNGNormExtSpace(type);
1912 if (xmlValidateNCName(type, 0)) {
1913 if (ctxt->error != NULL)
1914 ctxt->error(ctxt->userData,
1915 "data type '%s' is not an NCName\n",
1916 type);
1917 ctxt->nbErrors++;
1918 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001919 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1920 if (library == NULL)
1921 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1922
1923 def = xmlRelaxNGNewDefine(ctxt, node);
1924 if (def == NULL) {
1925 xmlFree(type);
1926 return(NULL);
1927 }
1928 def->type = XML_RELAXNG_DATATYPE;
1929 def->name = type;
1930 def->ns = library;
1931
1932 lib = (xmlRelaxNGTypeLibraryPtr)
1933 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1934 if (lib == NULL) {
1935 if (ctxt->error != NULL)
1936 ctxt->error(ctxt->userData,
1937 "Use of unregistered type library '%s'\n",
1938 library);
1939 ctxt->nbErrors++;
1940 def->data = NULL;
1941 } else {
1942 def->data = lib;
1943 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001944 if (ctxt->error != NULL)
1945 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001946 "Internal error with type library '%s': no 'have'\n",
1947 library);
1948 ctxt->nbErrors++;
1949 } else {
1950 tmp = lib->have(lib->data, def->name);
1951 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001952 if (ctxt->error != NULL)
1953 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001954 "Error type '%s' is not exported by type library '%s'\n",
1955 def->name, library);
1956 ctxt->nbErrors++;
1957 }
1958 }
1959 }
1960 content = node->children;
Daniel Veillard416589a2003-02-17 17:25:42 +00001961
1962 /*
1963 * Handle optional params
1964 */
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001965 while (content != NULL) {
Daniel Veillard416589a2003-02-17 17:25:42 +00001966 if (!xmlStrEqual(content->name, BAD_CAST "param"))
1967 break;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00001968 if (xmlStrEqual(library,
1969 BAD_CAST"http://relaxng.org/ns/structure/1.0")) {
1970 if (ctxt->error != NULL)
1971 ctxt->error(ctxt->userData,
1972 "Type library '%s' does not allow type parameters\n",
1973 library);
1974 ctxt->nbErrors++;
1975 content = content->next;
1976 while ((content != NULL) &&
1977 (xmlStrEqual(content->name, BAD_CAST "param")))
1978 content = content->next;
1979 } else {
1980 param = xmlRelaxNGNewDefine(ctxt, node);
1981 if (param != NULL) {
1982 param->type = XML_RELAXNG_PARAM;
1983 param->name = xmlGetProp(content, BAD_CAST "name");
1984 if (param->name == NULL) {
1985 if (ctxt->error != NULL)
1986 ctxt->error(ctxt->userData,
1987 "param has no name\n");
1988 ctxt->nbErrors++;
1989 }
1990 param->value = xmlNodeGetContent(content);
1991 if (lastparam == NULL) {
1992 def->attrs = lastparam = param;
1993 } else {
1994 lastparam->next = param;
1995 lastparam = param;
1996 }
Daniel Veillard8fe98712003-02-19 00:19:14 +00001997 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00001998 content = content->next;
Daniel Veillard8fe98712003-02-19 00:19:14 +00001999 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002000 }
Daniel Veillard416589a2003-02-17 17:25:42 +00002001 /*
2002 * Handle optional except
2003 */
2004 if ((content != NULL) && (xmlStrEqual(content->name, BAD_CAST "except"))) {
2005 xmlNodePtr child;
2006 xmlRelaxNGDefinePtr tmp2, last2 = NULL;
2007
2008 except = xmlRelaxNGNewDefine(ctxt, node);
2009 if (except == NULL) {
2010 return(def);
2011 }
2012 except->type = XML_RELAXNG_EXCEPT;
2013 child = content->children;
2014 if (last == NULL) {
2015 def->content = except;
2016 } else {
2017 last->next = except;
2018 }
2019 if (child == NULL) {
2020 if (ctxt->error != NULL)
2021 ctxt->error(ctxt->userData,
2022 "except has no content\n");
2023 ctxt->nbErrors++;
2024 }
2025 while (child != NULL) {
2026 tmp2 = xmlRelaxNGParsePattern(ctxt, child);
2027 if (tmp2 != NULL) {
2028 if (last2 == NULL) {
2029 except->content = last2 = tmp2;
2030 } else {
2031 last2->next = tmp2;
2032 last2 = tmp2;
2033 }
2034 }
2035 child = child->next;
2036 }
2037 content = content->next;
2038 }
2039 /*
2040 * Check there is no unhandled data
2041 */
2042 if (content != NULL) {
2043 if (ctxt->error != NULL)
2044 ctxt->error(ctxt->userData,
2045 "Element data has unexpected content %s\n", content->name);
2046 ctxt->nbErrors++;
2047 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002048
2049 return(def);
2050}
2051
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002052static const xmlChar *invalidName = BAD_CAST "\1";
2053
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002054/**
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002055 * xmlRelaxNGCompareNameClasses:
2056 * @defs1: the first element/attribute defs
2057 * @defs2: the second element/attribute defs
2058 * @name: the restriction on the name
2059 * @ns: the restriction on the namespace
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002060 *
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
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002068xmlRelaxNGCompareNameClasses(xmlRelaxNGDefinePtr def1,
2069 xmlRelaxNGDefinePtr def2) {
2070 int ret = 1;
2071 xmlNode node;
2072 xmlNs ns;
2073 xmlRelaxNGValidCtxt ctxt;
2074 ctxt.flags = FLAGS_IGNORABLE;
2075
2076 if ((def1->type == XML_RELAXNG_ELEMENT) ||
2077 (def1->type == XML_RELAXNG_ATTRIBUTE)) {
2078 if (def2->type == XML_RELAXNG_TEXT)
2079 return(1);
2080 if (def1->name != NULL) {
2081 node.name = def1->name;
2082 } else {
2083 node.name = invalidName;
2084 }
2085 node.ns = &ns;
2086 if (def1->ns != NULL) {
2087 if (def1->ns[0] == 0) {
2088 node.ns = NULL;
2089 } else {
2090 ns.href = def1->ns;
2091 }
2092 } else {
2093 ns.href = invalidName;
2094 }
2095 if (xmlRelaxNGElementMatch(&ctxt, def2, &node)) {
2096 if (def1->nameClass != NULL) {
2097 ret = xmlRelaxNGCompareNameClasses(def1->nameClass, def2);
2098 } else {
2099 ret = 0;
2100 }
2101 } else {
2102 ret = 1;
2103 }
2104 } else if (def1->type == XML_RELAXNG_TEXT) {
2105 if (def2->type == XML_RELAXNG_TEXT)
2106 return(0);
2107 return(1);
2108 } else if (def1->type == XML_RELAXNG_EXCEPT) {
2109 xmlRelaxNGDefinePtr tmp = def1->content;
2110 TODO
2111 ret = 0;
2112 } else {
2113 TODO
2114 ret = 0;
2115 }
2116 if (ret == 0)
2117 return(ret);
2118 if ((def2->type == XML_RELAXNG_ELEMENT) ||
2119 (def2->type == XML_RELAXNG_ATTRIBUTE)) {
2120 if (def2->name != NULL) {
2121 node.name = def2->name;
2122 } else {
2123 node.name = invalidName;
2124 }
2125 node.ns = &ns;
2126 if (def2->ns != NULL) {
2127 if (def2->ns[0] == 0) {
2128 node.ns = NULL;
2129 } else {
2130 ns.href = def2->ns;
2131 }
2132 } else {
2133 ns.href = invalidName;
2134 }
2135 if (xmlRelaxNGElementMatch(&ctxt, def1, &node)) {
2136 if (def2->nameClass != NULL) {
2137 ret = xmlRelaxNGCompareNameClasses(def2->nameClass, def1);
2138 } else {
2139 ret = 0;
2140 }
2141 } else {
2142 ret = 1;
2143 }
2144 } else {
2145 TODO
2146 ret = 0;
2147 }
2148
2149 return(ret);
2150}
2151
2152/**
2153 * xmlRelaxNGCompareElemDefLists:
2154 * @ctxt: a Relax-NG parser context
2155 * @defs1: the first list of element/attribute defs
2156 * @defs2: the second list of element/attribute defs
2157 *
2158 * Compare the 2 lists of element or attribute definitions. The comparison
2159 * is that if both lists do not accept the same QNames, it returns 1
2160 * If the 2 lists can accept the same QName the comparison returns 0
2161 *
2162 * Returns 1 disttinct, 0 if equal
2163 */
2164static int
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002165xmlRelaxNGCompareElemDefLists(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
2166 xmlRelaxNGDefinePtr *def1,
2167 xmlRelaxNGDefinePtr *def2) {
2168 xmlRelaxNGDefinePtr *basedef2 = def2;
2169
Daniel Veillard154877e2003-01-30 12:17:05 +00002170 if ((def1 == NULL) || (def2 == NULL))
2171 return(1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002172 if ((*def1 == NULL) || (*def2 == NULL))
2173 return(1);
2174 while (*def1 != NULL) {
2175 while ((*def2) != NULL) {
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002176 if (xmlRelaxNGCompareNameClasses(*def1, *def2) == 0)
2177 return(0);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002178 def2++;
2179 }
2180 def2 = basedef2;
2181 def1++;
2182 }
2183 return(1);
2184}
2185
2186/**
2187 * xmlRelaxNGGetElements:
2188 * @ctxt: a Relax-NG parser context
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002189 * @def: the definition definition
2190 * @eora: gather elements (0) or attributes (1)
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002191 *
2192 * Compute the list of top elements a definition can generate
2193 *
2194 * Returns a list of elements or NULL if none was found.
2195 */
2196static xmlRelaxNGDefinePtr *
2197xmlRelaxNGGetElements(xmlRelaxNGParserCtxtPtr ctxt,
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002198 xmlRelaxNGDefinePtr def,
2199 int eora) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002200 xmlRelaxNGDefinePtr *ret = NULL, parent, cur, tmp;
2201 int len = 0;
2202 int max = 0;
2203
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002204 /*
2205 * Don't run that check in case of error. Infinite recursion
2206 * becomes possible.
2207 */
2208 if (ctxt->nbErrors != 0)
2209 return(NULL);
2210
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002211 parent = NULL;
2212 cur = def;
2213 while (cur != NULL) {
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002214 if (((eora == 0) && ((cur->type == XML_RELAXNG_ELEMENT) ||
2215 (cur->type == XML_RELAXNG_TEXT))) ||
2216 ((eora == 1) && (cur->type == XML_RELAXNG_ATTRIBUTE))) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002217 if (ret == NULL) {
2218 max = 10;
2219 ret = (xmlRelaxNGDefinePtr *)
2220 xmlMalloc((max + 1) * sizeof(xmlRelaxNGDefinePtr));
2221 if (ret == NULL) {
2222 if (ctxt->error != NULL)
2223 ctxt->error(ctxt->userData,
2224 "Out of memory in element search\n");
2225 ctxt->nbErrors++;
2226 return(NULL);
2227 }
2228 } else if (max <= len) {
2229 max *= 2;
2230 ret = xmlRealloc(ret, (max + 1) * sizeof(xmlRelaxNGDefinePtr));
2231 if (ret == NULL) {
2232 if (ctxt->error != NULL)
2233 ctxt->error(ctxt->userData,
2234 "Out of memory in element search\n");
2235 ctxt->nbErrors++;
2236 return(NULL);
2237 }
2238 }
Daniel Veillardb08c9812003-01-28 23:09:49 +00002239 ret[len++] = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002240 ret[len] = NULL;
2241 } else if ((cur->type == XML_RELAXNG_CHOICE) ||
2242 (cur->type == XML_RELAXNG_INTERLEAVE) ||
2243 (cur->type == XML_RELAXNG_GROUP) ||
2244 (cur->type == XML_RELAXNG_ONEORMORE) ||
Daniel Veillardb08c9812003-01-28 23:09:49 +00002245 (cur->type == XML_RELAXNG_ZEROORMORE) ||
2246 (cur->type == XML_RELAXNG_OPTIONAL) ||
2247 (cur->type == XML_RELAXNG_REF) ||
2248 (cur->type == XML_RELAXNG_DEF)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002249 /*
2250 * Don't go within elements or attributes or string values.
2251 * Just gather the element top list
2252 */
2253 if (cur->content != NULL) {
2254 parent = cur;
2255 cur = cur->content;
2256 tmp = cur;
2257 while (tmp != NULL) {
2258 tmp->parent = parent;
2259 tmp = tmp->next;
2260 }
2261 continue;
2262 }
2263 }
Daniel Veillard154877e2003-01-30 12:17:05 +00002264 if (cur == def)
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002265 break;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002266 if (cur->next != NULL) {
2267 cur = cur->next;
2268 continue;
2269 }
2270 do {
2271 cur = cur->parent;
2272 if (cur == NULL) break;
2273 if (cur == def) return(ret);
2274 if (cur->next != NULL) {
2275 cur = cur->next;
2276 break;
2277 }
2278 } while (cur != NULL);
2279 }
2280 return(ret);
2281}
2282
2283/**
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002284 * xmlRelaxNGCheckGroupAttrs:
2285 * @ctxt: a Relax-NG parser context
2286 * @def: the group definition
2287 *
2288 * Detects violations of rule 7.3
2289 */
2290static void
2291xmlRelaxNGCheckGroupAttrs(xmlRelaxNGParserCtxtPtr ctxt,
2292 xmlRelaxNGDefinePtr def) {
2293 xmlRelaxNGDefinePtr **list;
2294 xmlRelaxNGDefinePtr cur;
2295 int nbchild = 0, i, j, ret;
2296
2297 if ((def == NULL) ||
2298 ((def->type != XML_RELAXNG_GROUP) &&
2299 (def->type != XML_RELAXNG_ELEMENT)))
2300 return;
2301
2302 /*
2303 * Don't run that check in case of error. Infinite recursion
2304 * becomes possible.
2305 */
2306 if (ctxt->nbErrors != 0)
2307 return;
2308
2309 cur = def->attrs;
2310 while (cur != NULL) {
2311 nbchild++;
2312 cur = cur->next;
2313 }
2314 cur = def->content;
2315 while (cur != NULL) {
2316 nbchild++;
2317 cur = cur->next;
2318 }
2319
2320 list = (xmlRelaxNGDefinePtr **) xmlMalloc(nbchild *
2321 sizeof(xmlRelaxNGDefinePtr *));
2322 if (list == NULL) {
2323 if (ctxt->error != NULL)
2324 ctxt->error(ctxt->userData,
2325 "Out of memory in group computation\n");
2326 ctxt->nbErrors++;
2327 return;
2328 }
2329 i = 0;
2330 cur = def->attrs;
2331 while (cur != NULL) {
2332 list[i] = xmlRelaxNGGetElements(ctxt, cur, 1);
2333 i++;
2334 cur = cur->next;
2335 }
2336 cur = def->content;
2337 while (cur != NULL) {
2338 list[i] = xmlRelaxNGGetElements(ctxt, cur, 1);
2339 i++;
2340 cur = cur->next;
2341 }
2342
2343 for (i = 0;i < nbchild;i++) {
2344 if (list[i] == NULL)
2345 continue;
2346 for (j = 0;j < i;j++) {
2347 if (list[j] == NULL)
2348 continue;
2349 ret = xmlRelaxNGCompareElemDefLists(ctxt, list[i], list[j]);
2350 if (ret == 0) {
2351 if (ctxt->error != NULL)
2352 ctxt->error(ctxt->userData,
2353 "Attributes conflicts in group\n");
2354 ctxt->nbErrors++;
2355 }
2356 }
2357 }
2358 for (i = 0;i < nbchild;i++) {
2359 if (list[i] != NULL)
2360 xmlFree(list[i]);
2361 }
2362 xmlFree(list);
2363}
2364
2365/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002366 * xmlRelaxNGComputeInterleaves:
2367 * @def: the interleave definition
2368 * @ctxt: a Relax-NG parser context
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002369 * @name: the definition name
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002370 *
2371 * A lot of work for preprocessing interleave definitions
2372 * is potentially needed to get a decent execution speed at runtime
2373 * - trying to get a total order on the element nodes generated
2374 * by the interleaves, order the list of interleave definitions
2375 * following that order.
2376 * - if <text/> is used to handle mixed content, it is better to
2377 * flag this in the define and simplify the runtime checking
2378 * algorithm
2379 */
2380static void
2381xmlRelaxNGComputeInterleaves(xmlRelaxNGDefinePtr def,
2382 xmlRelaxNGParserCtxtPtr ctxt,
2383 xmlChar *name ATTRIBUTE_UNUSED) {
2384 xmlRelaxNGDefinePtr cur;
2385
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002386 xmlRelaxNGPartitionPtr partitions = NULL;
2387 xmlRelaxNGInterleaveGroupPtr *groups = NULL;
2388 xmlRelaxNGInterleaveGroupPtr group;
2389 int i,j,ret;
2390 int nbgroups = 0;
2391 int nbchild = 0;
2392
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002393 /*
2394 * Don't run that check in case of error. Infinite recursion
2395 * becomes possible.
2396 */
2397 if (ctxt->nbErrors != 0)
2398 return;
2399
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002400#ifdef DEBUG_INTERLEAVE
2401 xmlGenericError(xmlGenericErrorContext,
2402 "xmlRelaxNGComputeInterleaves(%s)\n",
2403 name);
2404#endif
2405 cur = def->content;
2406 while (cur != NULL) {
2407 nbchild++;
2408 cur = cur->next;
2409 }
2410
2411#ifdef DEBUG_INTERLEAVE
2412 xmlGenericError(xmlGenericErrorContext, " %d child\n", nbchild);
2413#endif
2414 groups = (xmlRelaxNGInterleaveGroupPtr *)
2415 xmlMalloc(nbchild * sizeof(xmlRelaxNGInterleaveGroupPtr));
2416 if (groups == NULL)
2417 goto error;
2418 cur = def->content;
2419 while (cur != NULL) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002420 groups[nbgroups] = (xmlRelaxNGInterleaveGroupPtr)
2421 xmlMalloc(sizeof(xmlRelaxNGInterleaveGroup));
2422 if (groups[nbgroups] == NULL)
2423 goto error;
2424 groups[nbgroups]->rule = cur;
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002425 groups[nbgroups]->defs = xmlRelaxNGGetElements(ctxt, cur, 0);
2426 groups[nbgroups]->attrs = xmlRelaxNGGetElements(ctxt, cur, 1);
Daniel Veillard154877e2003-01-30 12:17:05 +00002427 nbgroups++;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002428 cur = cur->next;
2429 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002430#ifdef DEBUG_INTERLEAVE
2431 xmlGenericError(xmlGenericErrorContext, " %d groups\n", nbgroups);
2432#endif
2433
2434 /*
2435 * Let's check that all rules makes a partitions according to 7.4
2436 */
2437 partitions = (xmlRelaxNGPartitionPtr)
2438 xmlMalloc(sizeof(xmlRelaxNGPartition));
2439 if (partitions == NULL)
2440 goto error;
2441 partitions->nbgroups = nbgroups;
2442 for (i = 0;i < nbgroups;i++) {
2443 group = groups[i];
2444 for (j = i+1;j < nbgroups;j++) {
2445 if (groups[j] == NULL)
2446 continue;
2447 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->defs,
2448 groups[j]->defs);
2449 if (ret == 0) {
2450 if (ctxt->error != NULL)
2451 ctxt->error(ctxt->userData,
2452 "Element or text conflicts in interleave\n");
2453 ctxt->nbErrors++;
2454 }
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002455 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->attrs,
2456 groups[j]->attrs);
2457 if (ret == 0) {
2458 if (ctxt->error != NULL)
2459 ctxt->error(ctxt->userData,
2460 "Attributes conflicts in interleave\n");
2461 ctxt->nbErrors++;
2462 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002463 }
2464 }
2465 partitions->groups = groups;
2466
2467 /*
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002468 * and save the partition list back in the def
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002469 */
2470 def->data = partitions;
2471 return;
2472
2473error:
2474 if (ctxt->error != NULL)
2475 ctxt->error(ctxt->userData,
2476 "Out of memory in interleave computation\n");
2477 ctxt->nbErrors++;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002478 if (groups != NULL) {
2479 for (i = 0;i < nbgroups;i++)
2480 if (groups[i] != NULL) {
2481 if (groups[i]->defs != NULL)
2482 xmlFree(groups[i]->defs);
2483 xmlFree(groups[i]);
2484 }
2485 xmlFree(groups);
2486 }
2487 xmlRelaxNGFreePartition(partitions);
2488}
2489
2490/**
2491 * xmlRelaxNGParseInterleave:
2492 * @ctxt: a Relax-NG parser context
2493 * @node: the data node.
2494 *
2495 * parse the content of a RelaxNG interleave node.
2496 *
2497 * Returns the definition pointer or NULL in case of error
2498 */
2499static xmlRelaxNGDefinePtr
2500xmlRelaxNGParseInterleave(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2501 xmlRelaxNGDefinePtr def = NULL;
2502 xmlRelaxNGDefinePtr last = NULL, cur;
2503 xmlNodePtr child;
2504
2505 def = xmlRelaxNGNewDefine(ctxt, node);
2506 if (def == NULL) {
2507 return(NULL);
2508 }
2509 def->type = XML_RELAXNG_INTERLEAVE;
2510
2511 if (ctxt->interleaves == NULL)
2512 ctxt->interleaves = xmlHashCreate(10);
2513 if (ctxt->interleaves == NULL) {
2514 if (ctxt->error != NULL)
2515 ctxt->error(ctxt->userData,
2516 "Failed to create interleaves hash table\n");
2517 ctxt->nbErrors++;
2518 } else {
2519 char name[32];
2520
2521 snprintf(name, 32, "interleave%d", ctxt->nbInterleaves++);
2522 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST name, def) < 0) {
2523 if (ctxt->error != NULL)
2524 ctxt->error(ctxt->userData,
2525 "Failed to add %s to hash table\n", name);
2526 ctxt->nbErrors++;
2527 }
2528 }
2529 child = node->children;
Daniel Veillardd2298792003-02-14 16:54:11 +00002530 if (child == NULL) {
2531 if (ctxt->error != NULL)
2532 ctxt->error(ctxt->userData, "Element interleave is empty\n");
2533 ctxt->nbErrors++;
2534 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002535 while (child != NULL) {
2536 if (IS_RELAXNG(child, "element")) {
2537 cur = xmlRelaxNGParseElement(ctxt, child);
2538 } else {
2539 cur = xmlRelaxNGParsePattern(ctxt, child);
2540 }
2541 if (cur != NULL) {
2542 cur->parent = def;
2543 if (last == NULL) {
2544 def->content = last = cur;
2545 } else {
2546 last->next = cur;
2547 last = cur;
2548 }
2549 }
2550 child = child->next;
2551 }
2552
2553 return(def);
2554}
Daniel Veillard6eadf632003-01-23 18:29:16 +00002555
2556/**
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002557 * xmlRelaxNGParseInclude:
2558 * @ctxt: a Relax-NG parser context
2559 * @node: the include node
2560 *
2561 * Integrate the content of an include node in the current grammar
2562 *
2563 * Returns 0 in case of success or -1 in case of error
2564 */
2565static int
2566xmlRelaxNGParseInclude(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2567 xmlRelaxNGIncludePtr incl;
2568 xmlNodePtr root;
2569 int ret = 0, tmp;
2570
2571 incl = node->_private;
2572 if (incl == NULL) {
2573 if (ctxt->error != NULL)
2574 ctxt->error(ctxt->userData,
2575 "Include node has no data\n");
2576 ctxt->nbErrors++;
2577 return(-1);
2578 }
2579 root = xmlDocGetRootElement(incl->doc);
2580 if (root == NULL) {
2581 if (ctxt->error != NULL)
2582 ctxt->error(ctxt->userData,
2583 "Include document is empty\n");
2584 ctxt->nbErrors++;
2585 return(-1);
2586 }
2587 if (!xmlStrEqual(root->name, BAD_CAST "grammar")) {
2588 if (ctxt->error != NULL)
2589 ctxt->error(ctxt->userData,
2590 "Include document root is not a grammar\n");
2591 ctxt->nbErrors++;
2592 return(-1);
2593 }
2594
2595 /*
2596 * Merge the definition from both the include and the internal list
2597 */
2598 if (root->children != NULL) {
2599 tmp = xmlRelaxNGParseGrammarContent(ctxt, root->children);
2600 if (tmp != 0)
2601 ret = -1;
2602 }
2603 if (node->children != NULL) {
2604 tmp = xmlRelaxNGParseGrammarContent(ctxt, node->children);
2605 if (tmp != 0)
2606 ret = -1;
2607 }
2608 return(ret);
2609}
2610
2611/**
Daniel Veillard276be4a2003-01-24 01:03:34 +00002612 * xmlRelaxNGParseDefine:
2613 * @ctxt: a Relax-NG parser context
2614 * @node: the define node
2615 *
2616 * parse the content of a RelaxNG define element node.
2617 *
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002618 * Returns 0 in case of success or -1 in case of error
Daniel Veillard276be4a2003-01-24 01:03:34 +00002619 */
2620static int
2621xmlRelaxNGParseDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2622 xmlChar *name;
2623 int ret = 0, tmp;
2624 xmlRelaxNGDefinePtr def;
2625 const xmlChar *olddefine;
2626
2627 name = xmlGetProp(node, BAD_CAST "name");
2628 if (name == NULL) {
2629 if (ctxt->error != NULL)
2630 ctxt->error(ctxt->userData,
2631 "define has no name\n");
2632 ctxt->nbErrors++;
2633 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00002634 xmlRelaxNGNormExtSpace(name);
2635 if (xmlValidateNCName(name, 0)) {
2636 if (ctxt->error != NULL)
2637 ctxt->error(ctxt->userData,
2638 "define name '%s' is not an NCName\n",
2639 name);
2640 ctxt->nbErrors++;
2641 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002642 def = xmlRelaxNGNewDefine(ctxt, node);
2643 if (def == NULL) {
2644 xmlFree(name);
2645 return(-1);
2646 }
2647 def->type = XML_RELAXNG_DEF;
2648 def->name = name;
2649 if (node->children == NULL) {
2650 if (ctxt->error != NULL)
2651 ctxt->error(ctxt->userData,
2652 "define has no children\n");
2653 ctxt->nbErrors++;
2654 } else {
2655 olddefine = ctxt->define;
2656 ctxt->define = name;
Daniel Veillard154877e2003-01-30 12:17:05 +00002657 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard276be4a2003-01-24 01:03:34 +00002658 ctxt->define = olddefine;
2659 }
2660 if (ctxt->grammar->defs == NULL)
2661 ctxt->grammar->defs = xmlHashCreate(10);
2662 if (ctxt->grammar->defs == NULL) {
2663 if (ctxt->error != NULL)
2664 ctxt->error(ctxt->userData,
2665 "Could not create definition hash\n");
2666 ctxt->nbErrors++;
2667 ret = -1;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002668 } else {
2669 tmp = xmlHashAddEntry(ctxt->grammar->defs, name, def);
2670 if (tmp < 0) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002671 xmlRelaxNGDefinePtr prev;
2672
2673 prev = xmlHashLookup(ctxt->grammar->defs, name);
2674 if (prev == NULL) {
2675 if (ctxt->error != NULL)
2676 ctxt->error(ctxt->userData,
2677 "Internal error on define aggregation of %s\n",
2678 name);
2679 ctxt->nbErrors++;
2680 ret = -1;
Daniel Veillard154877e2003-01-30 12:17:05 +00002681 } else {
2682 while (prev->nextHash != NULL)
2683 prev = prev->nextHash;
2684 prev->nextHash = def;
2685 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002686 }
2687 }
2688 }
2689 return(ret);
2690}
2691
2692/**
Daniel Veillardfebcca42003-02-16 15:44:18 +00002693 * xmlRelaxNGProcessExternalRef:
2694 * @ctxt: the parser context
2695 * @node: the externlRef node
2696 *
2697 * Process and compile an externlRef node
2698 *
2699 * Returns the xmlRelaxNGDefinePtr or NULL in case of error
2700 */
2701static xmlRelaxNGDefinePtr
2702xmlRelaxNGProcessExternalRef(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2703 xmlRelaxNGDocumentPtr docu;
2704 xmlNodePtr root, tmp;
2705 xmlChar *ns;
Daniel Veillard77648bb2003-02-20 15:03:22 +00002706 int newNs = 0, oldflags;
Daniel Veillardfebcca42003-02-16 15:44:18 +00002707 xmlRelaxNGDefinePtr def;
2708
2709 docu = node->_private;
2710 if (docu != NULL) {
2711 def = xmlRelaxNGNewDefine(ctxt, node);
2712 if (def == NULL)
2713 return(NULL);
2714 def->type = XML_RELAXNG_EXTERNALREF;
2715
2716 if (docu->content == NULL) {
2717 /*
2718 * Then do the parsing for good
2719 */
2720 root = xmlDocGetRootElement(docu->doc);
2721 if (root == NULL) {
2722 if (ctxt->error != NULL)
2723 ctxt->error(ctxt->userData,
2724 "xmlRelaxNGParse: %s is empty\n",
2725 ctxt->URL);
2726 ctxt->nbErrors++;
2727 return (NULL);
2728 }
2729 /*
2730 * ns transmission rules
2731 */
2732 ns = xmlGetProp(root, BAD_CAST "ns");
2733 if (ns == NULL) {
2734 tmp = node;
2735 while ((tmp != NULL) &&
2736 (tmp->type == XML_ELEMENT_NODE)) {
2737 ns = xmlGetProp(tmp, BAD_CAST "ns");
2738 if (ns != NULL) {
2739 break;
2740 }
2741 tmp = tmp->parent;
2742 }
2743 if (ns != NULL) {
2744 xmlSetProp(root, BAD_CAST "ns", ns);
2745 newNs = 1;
2746 xmlFree(ns);
2747 }
2748 } else {
2749 xmlFree(ns);
2750 }
2751
2752 /*
2753 * Parsing to get a precompiled schemas.
2754 */
Daniel Veillard77648bb2003-02-20 15:03:22 +00002755 oldflags = ctxt->flags;
2756 ctxt->flags |= XML_RELAXNG_IN_EXTERNALREF;
Daniel Veillardfebcca42003-02-16 15:44:18 +00002757 docu->schema = xmlRelaxNGParseDocument(ctxt, root);
Daniel Veillard77648bb2003-02-20 15:03:22 +00002758 ctxt->flags = oldflags;
Daniel Veillardfebcca42003-02-16 15:44:18 +00002759 if ((docu->schema != NULL) &&
2760 (docu->schema->topgrammar != NULL)) {
2761 docu->content = docu->schema->topgrammar->start;
2762 }
2763
2764 /*
2765 * the externalRef may be reused in a different ns context
2766 */
2767 if (newNs == 1) {
2768 xmlUnsetProp(root, BAD_CAST "ns");
2769 }
2770 }
2771 def->content = docu->content;
2772 } else {
2773 def = NULL;
2774 }
2775 return(def);
2776}
2777
2778/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00002779 * xmlRelaxNGParsePattern:
2780 * @ctxt: a Relax-NG parser context
2781 * @node: the pattern node.
2782 *
2783 * parse the content of a RelaxNG pattern node.
2784 *
Daniel Veillard276be4a2003-01-24 01:03:34 +00002785 * Returns the definition pointer or NULL in case of error or if no
2786 * pattern is generated.
Daniel Veillard6eadf632003-01-23 18:29:16 +00002787 */
2788static xmlRelaxNGDefinePtr
2789xmlRelaxNGParsePattern(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2790 xmlRelaxNGDefinePtr def = NULL;
2791
Daniel Veillardd2298792003-02-14 16:54:11 +00002792 if (node == NULL) {
2793 return(NULL);
2794 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002795 if (IS_RELAXNG(node, "element")) {
2796 def = xmlRelaxNGParseElement(ctxt, node);
2797 } else if (IS_RELAXNG(node, "attribute")) {
2798 def = xmlRelaxNGParseAttribute(ctxt, node);
2799 } else if (IS_RELAXNG(node, "empty")) {
2800 def = xmlRelaxNGNewDefine(ctxt, node);
2801 if (def == NULL)
2802 return(NULL);
2803 def->type = XML_RELAXNG_EMPTY;
Daniel Veillardd2298792003-02-14 16:54:11 +00002804 if (node->children != NULL) {
2805 if (ctxt->error != NULL)
2806 ctxt->error(ctxt->userData, "empty: had a child node\n");
2807 ctxt->nbErrors++;
2808 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002809 } else if (IS_RELAXNG(node, "text")) {
2810 def = xmlRelaxNGNewDefine(ctxt, node);
2811 if (def == NULL)
2812 return(NULL);
2813 def->type = XML_RELAXNG_TEXT;
2814 if (node->children != NULL) {
2815 if (ctxt->error != NULL)
2816 ctxt->error(ctxt->userData, "text: had a child node\n");
2817 ctxt->nbErrors++;
2818 }
2819 } else if (IS_RELAXNG(node, "zeroOrMore")) {
2820 def = xmlRelaxNGNewDefine(ctxt, node);
2821 if (def == NULL)
2822 return(NULL);
2823 def->type = XML_RELAXNG_ZEROORMORE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002824 if (node->children == NULL) {
2825 if (ctxt->error != NULL)
2826 ctxt->error(ctxt->userData,
2827 "Element %s is empty\n", node->name);
2828 ctxt->nbErrors++;
2829 } else {
2830 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2831 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002832 } else if (IS_RELAXNG(node, "oneOrMore")) {
2833 def = xmlRelaxNGNewDefine(ctxt, node);
2834 if (def == NULL)
2835 return(NULL);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002836 def->type = XML_RELAXNG_ONEORMORE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002837 if (node->children == NULL) {
2838 if (ctxt->error != NULL)
2839 ctxt->error(ctxt->userData,
2840 "Element %s is empty\n", node->name);
2841 ctxt->nbErrors++;
2842 } else {
2843 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2844 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002845 } else if (IS_RELAXNG(node, "optional")) {
2846 def = xmlRelaxNGNewDefine(ctxt, node);
2847 if (def == NULL)
2848 return(NULL);
2849 def->type = XML_RELAXNG_OPTIONAL;
Daniel Veillardd2298792003-02-14 16:54:11 +00002850 if (node->children == NULL) {
2851 if (ctxt->error != NULL)
2852 ctxt->error(ctxt->userData,
2853 "Element %s is empty\n", node->name);
2854 ctxt->nbErrors++;
2855 } else {
2856 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2857 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002858 } else if (IS_RELAXNG(node, "choice")) {
2859 def = xmlRelaxNGNewDefine(ctxt, node);
2860 if (def == NULL)
2861 return(NULL);
2862 def->type = XML_RELAXNG_CHOICE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002863 if (node->children == NULL) {
2864 if (ctxt->error != NULL)
2865 ctxt->error(ctxt->userData,
2866 "Element %s is empty\n", node->name);
2867 ctxt->nbErrors++;
2868 } else {
2869 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2870 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002871 } else if (IS_RELAXNG(node, "group")) {
2872 def = xmlRelaxNGNewDefine(ctxt, node);
2873 if (def == NULL)
2874 return(NULL);
2875 def->type = XML_RELAXNG_GROUP;
Daniel Veillardd2298792003-02-14 16:54:11 +00002876 if (node->children == NULL) {
2877 if (ctxt->error != NULL)
2878 ctxt->error(ctxt->userData,
2879 "Element %s is empty\n", node->name);
2880 ctxt->nbErrors++;
2881 } else {
2882 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2883 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002884 } else if (IS_RELAXNG(node, "ref")) {
2885 def = xmlRelaxNGNewDefine(ctxt, node);
2886 if (def == NULL)
2887 return(NULL);
2888 def->type = XML_RELAXNG_REF;
2889 def->name = xmlGetProp(node, BAD_CAST "name");
2890 if (def->name == NULL) {
2891 if (ctxt->error != NULL)
2892 ctxt->error(ctxt->userData,
2893 "ref has no name\n");
2894 ctxt->nbErrors++;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002895 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00002896 xmlRelaxNGNormExtSpace(def->name);
2897 if (xmlValidateNCName(def->name, 0)) {
2898 if (ctxt->error != NULL)
2899 ctxt->error(ctxt->userData,
2900 "ref name '%s' is not an NCName\n",
2901 def->name);
2902 ctxt->nbErrors++;
2903 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002904 }
2905 if (node->children != NULL) {
2906 if (ctxt->error != NULL)
2907 ctxt->error(ctxt->userData,
2908 "ref is not empty\n");
2909 ctxt->nbErrors++;
2910 }
2911 if (ctxt->grammar->refs == NULL)
2912 ctxt->grammar->refs = xmlHashCreate(10);
2913 if (ctxt->grammar->refs == NULL) {
2914 if (ctxt->error != NULL)
2915 ctxt->error(ctxt->userData,
2916 "Could not create references hash\n");
2917 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002918 def = NULL;
2919 } else {
2920 int tmp;
2921
2922 tmp = xmlHashAddEntry(ctxt->grammar->refs, def->name, def);
2923 if (tmp < 0) {
2924 xmlRelaxNGDefinePtr prev;
2925
2926 prev = (xmlRelaxNGDefinePtr)
2927 xmlHashLookup(ctxt->grammar->refs, def->name);
2928 if (prev == NULL) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00002929 if (def->name != NULL) {
2930 if (ctxt->error != NULL)
2931 ctxt->error(ctxt->userData,
2932 "Error refs definitions '%s'\n",
2933 def->name);
2934 } else {
2935 if (ctxt->error != NULL)
2936 ctxt->error(ctxt->userData,
2937 "Error refs definitions\n");
2938 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002939 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002940 def = NULL;
2941 } else {
2942 def->nextHash = prev->nextHash;
2943 prev->nextHash = def;
2944 }
2945 }
2946 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002947 } else if (IS_RELAXNG(node, "data")) {
2948 def = xmlRelaxNGParseData(ctxt, node);
Daniel Veillardd2298792003-02-14 16:54:11 +00002949#if 0
Daniel Veillard276be4a2003-01-24 01:03:34 +00002950 } else if (IS_RELAXNG(node, "define")) {
2951 xmlRelaxNGParseDefine(ctxt, node);
2952 def = NULL;
Daniel Veillardd2298792003-02-14 16:54:11 +00002953#endif
Daniel Veillardedc91922003-01-26 00:52:04 +00002954 } else if (IS_RELAXNG(node, "value")) {
2955 def = xmlRelaxNGParseValue(ctxt, node);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002956 } else if (IS_RELAXNG(node, "list")) {
2957 def = xmlRelaxNGNewDefine(ctxt, node);
2958 if (def == NULL)
2959 return(NULL);
2960 def->type = XML_RELAXNG_LIST;
Daniel Veillardd2298792003-02-14 16:54:11 +00002961 if (node->children == NULL) {
2962 if (ctxt->error != NULL)
2963 ctxt->error(ctxt->userData,
2964 "Element %s is empty\n", node->name);
2965 ctxt->nbErrors++;
2966 } else {
2967 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2968 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002969 } else if (IS_RELAXNG(node, "interleave")) {
2970 def = xmlRelaxNGParseInterleave(ctxt, node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002971 } else if (IS_RELAXNG(node, "externalRef")) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00002972 def = xmlRelaxNGProcessExternalRef(ctxt, node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002973 } else if (IS_RELAXNG(node, "notAllowed")) {
2974 def = xmlRelaxNGNewDefine(ctxt, node);
2975 if (def == NULL)
2976 return(NULL);
2977 def->type = XML_RELAXNG_NOT_ALLOWED;
2978 if (node->children != NULL) {
2979 if (ctxt->error != NULL)
2980 ctxt->error(ctxt->userData,
2981 "xmlRelaxNGParse: notAllowed element is not empty\n");
2982 ctxt->nbErrors++;
2983 }
Daniel Veillard419a7682003-02-03 23:22:49 +00002984 } else if (IS_RELAXNG(node, "grammar")) {
2985 xmlRelaxNGGrammarPtr grammar, old;
2986 xmlRelaxNGGrammarPtr oldparent;
2987
2988 oldparent = ctxt->parentgrammar;
2989 old = ctxt->grammar;
2990 ctxt->parentgrammar = old;
2991 grammar = xmlRelaxNGParseGrammar(ctxt, node->children);
2992 if (old != NULL) {
2993 ctxt->grammar = old;
2994 ctxt->parentgrammar = oldparent;
2995 if (grammar != NULL) {
2996 grammar->next = old->next;
2997 old->next = grammar;
2998 }
2999 }
3000 if (grammar != NULL)
3001 def = grammar->start;
3002 else
3003 def = NULL;
3004 } else if (IS_RELAXNG(node, "parentRef")) {
3005 if (ctxt->parentgrammar == NULL) {
3006 if (ctxt->error != NULL)
3007 ctxt->error(ctxt->userData,
3008 "Use of parentRef without a parent grammar\n");
3009 ctxt->nbErrors++;
3010 return(NULL);
3011 }
3012 def = xmlRelaxNGNewDefine(ctxt, node);
3013 if (def == NULL)
3014 return(NULL);
3015 def->type = XML_RELAXNG_PARENTREF;
3016 def->name = xmlGetProp(node, BAD_CAST "name");
3017 if (def->name == NULL) {
3018 if (ctxt->error != NULL)
3019 ctxt->error(ctxt->userData,
3020 "parentRef has no name\n");
3021 ctxt->nbErrors++;
Daniel Veillardd2298792003-02-14 16:54:11 +00003022 } else {
3023 xmlRelaxNGNormExtSpace(def->name);
3024 if (xmlValidateNCName(def->name, 0)) {
3025 if (ctxt->error != NULL)
3026 ctxt->error(ctxt->userData,
3027 "parentRef name '%s' is not an NCName\n",
3028 def->name);
3029 ctxt->nbErrors++;
3030 }
Daniel Veillard419a7682003-02-03 23:22:49 +00003031 }
3032 if (node->children != NULL) {
3033 if (ctxt->error != NULL)
3034 ctxt->error(ctxt->userData,
3035 "parentRef is not empty\n");
3036 ctxt->nbErrors++;
3037 }
3038 if (ctxt->parentgrammar->refs == NULL)
3039 ctxt->parentgrammar->refs = xmlHashCreate(10);
3040 if (ctxt->parentgrammar->refs == NULL) {
3041 if (ctxt->error != NULL)
3042 ctxt->error(ctxt->userData,
3043 "Could not create references hash\n");
3044 ctxt->nbErrors++;
3045 def = NULL;
Daniel Veillardd2298792003-02-14 16:54:11 +00003046 } else if (def->name != NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +00003047 int tmp;
3048
3049 tmp = xmlHashAddEntry(ctxt->parentgrammar->refs, def->name, def);
3050 if (tmp < 0) {
3051 xmlRelaxNGDefinePtr prev;
3052
3053 prev = (xmlRelaxNGDefinePtr)
3054 xmlHashLookup(ctxt->parentgrammar->refs, def->name);
3055 if (prev == NULL) {
3056 if (ctxt->error != NULL)
3057 ctxt->error(ctxt->userData,
3058 "Internal error parentRef definitions '%s'\n",
3059 def->name);
3060 ctxt->nbErrors++;
3061 def = NULL;
3062 } else {
3063 def->nextHash = prev->nextHash;
3064 prev->nextHash = def;
3065 }
3066 }
3067 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003068 } else if (IS_RELAXNG(node, "mixed")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00003069 if (node->children == NULL) {
3070 if (ctxt->error != NULL)
3071 ctxt->error(ctxt->userData,
3072 "Mixed is empty\n");
3073 ctxt->nbErrors++;
3074 def = NULL;
Daniel Veillard416589a2003-02-17 17:25:42 +00003075 } else {
3076 def = xmlRelaxNGParseInterleave(ctxt, node);
3077 if (def != NULL) {
3078 xmlRelaxNGDefinePtr tmp;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003079
3080 if ((def->content != NULL) && (def->content->next != NULL)) {
3081 tmp = xmlRelaxNGNewDefine(ctxt, node);
3082 if (tmp != NULL) {
3083 tmp->type = XML_RELAXNG_GROUP;
3084 tmp->content = def->content;
3085 def->content = tmp;
3086 }
3087 }
3088
Daniel Veillard416589a2003-02-17 17:25:42 +00003089 tmp = xmlRelaxNGNewDefine(ctxt, node);
3090 if (tmp == NULL)
3091 return(def);
3092 tmp->type = XML_RELAXNG_TEXT;
3093 tmp->next = def->content;
3094 def->content = tmp;
3095 }
3096 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003097 } else {
3098 if (ctxt->error != NULL)
3099 ctxt->error(ctxt->userData,
3100 "Unexpected node %s is not a pattern\n",
3101 node->name);
3102 ctxt->nbErrors++;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003103 def = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003104 }
3105 return(def);
3106}
3107
3108/**
3109 * xmlRelaxNGParseAttribute:
3110 * @ctxt: a Relax-NG parser context
3111 * @node: the element node
3112 *
3113 * parse the content of a RelaxNG attribute node.
3114 *
3115 * Returns the definition pointer or NULL in case of error.
3116 */
3117static xmlRelaxNGDefinePtr
3118xmlRelaxNGParseAttribute(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
Daniel Veillardd2298792003-02-14 16:54:11 +00003119 xmlRelaxNGDefinePtr ret, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003120 xmlNodePtr child;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003121 int old_flags;
3122
3123 ret = xmlRelaxNGNewDefine(ctxt, node);
3124 if (ret == NULL)
3125 return(NULL);
3126 ret->type = XML_RELAXNG_ATTRIBUTE;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003127 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003128 child = node->children;
3129 if (child == NULL) {
3130 if (ctxt->error != NULL)
3131 ctxt->error(ctxt->userData,
3132 "xmlRelaxNGParseattribute: attribute has no children\n");
3133 ctxt->nbErrors++;
3134 return(ret);
3135 }
3136 old_flags = ctxt->flags;
3137 ctxt->flags |= XML_RELAXNG_IN_ATTRIBUTE;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003138 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
3139 if (cur != NULL)
3140 child = child->next;
3141
Daniel Veillardd2298792003-02-14 16:54:11 +00003142 if (child != NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003143 cur = xmlRelaxNGParsePattern(ctxt, child);
3144 if (cur != NULL) {
3145 switch (cur->type) {
3146 case XML_RELAXNG_EMPTY:
3147 case XML_RELAXNG_NOT_ALLOWED:
3148 case XML_RELAXNG_TEXT:
3149 case XML_RELAXNG_ELEMENT:
3150 case XML_RELAXNG_DATATYPE:
3151 case XML_RELAXNG_VALUE:
3152 case XML_RELAXNG_LIST:
3153 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00003154 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003155 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00003156 case XML_RELAXNG_DEF:
3157 case XML_RELAXNG_ONEORMORE:
3158 case XML_RELAXNG_ZEROORMORE:
3159 case XML_RELAXNG_OPTIONAL:
3160 case XML_RELAXNG_CHOICE:
3161 case XML_RELAXNG_GROUP:
3162 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard77648bb2003-02-20 15:03:22 +00003163 case XML_RELAXNG_ATTRIBUTE:
Daniel Veillardd2298792003-02-14 16:54:11 +00003164 ret->content = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003165 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003166 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003167 case XML_RELAXNG_START:
Daniel Veillard8fe98712003-02-19 00:19:14 +00003168 case XML_RELAXNG_PARAM:
Daniel Veillard144fae12003-02-03 13:17:57 +00003169 case XML_RELAXNG_EXCEPT:
Daniel Veillardd2298792003-02-14 16:54:11 +00003170 if (ctxt->error != NULL)
3171 ctxt->error(ctxt->userData,
3172 "attribute has invalid content\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003173 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003174 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00003175 case XML_RELAXNG_NOOP:
3176 TODO
3177 if (ctxt->error != NULL)
3178 ctxt->error(ctxt->userData,
3179 "Internal error, noop found\n");
3180 ctxt->nbErrors++;
3181 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003182 }
3183 }
3184 child = child->next;
3185 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003186 if (child != NULL) {
3187 if (ctxt->error != NULL)
3188 ctxt->error(ctxt->userData, "attribute has multiple children\n");
3189 ctxt->nbErrors++;
3190 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003191 ctxt->flags = old_flags;
3192 return(ret);
3193}
3194
3195/**
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003196 * xmlRelaxNGParseExceptNameClass:
3197 * @ctxt: a Relax-NG parser context
3198 * @node: the except node
Daniel Veillard144fae12003-02-03 13:17:57 +00003199 * @attr: 1 if within an attribute, 0 if within an element
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003200 *
3201 * parse the content of a RelaxNG nameClass node.
3202 *
3203 * Returns the definition pointer or NULL in case of error.
3204 */
3205static xmlRelaxNGDefinePtr
Daniel Veillard144fae12003-02-03 13:17:57 +00003206xmlRelaxNGParseExceptNameClass(xmlRelaxNGParserCtxtPtr ctxt,
3207 xmlNodePtr node, int attr) {
3208 xmlRelaxNGDefinePtr ret, cur, last = NULL;
3209 xmlNodePtr child;
3210
Daniel Veillardd2298792003-02-14 16:54:11 +00003211 if (!IS_RELAXNG(node, "except")) {
3212 if (ctxt->error != NULL)
3213 ctxt->error(ctxt->userData,
3214 "Expecting an except node\n");
3215 ctxt->nbErrors++;
Daniel Veillard144fae12003-02-03 13:17:57 +00003216 return(NULL);
Daniel Veillardd2298792003-02-14 16:54:11 +00003217 }
3218 if (node->next != NULL) {
3219 if (ctxt->error != NULL)
3220 ctxt->error(ctxt->userData,
3221 "exceptNameClass allows only a single except node\n");
3222 ctxt->nbErrors++;
3223 }
Daniel Veillard144fae12003-02-03 13:17:57 +00003224 if (node->children == NULL) {
3225 if (ctxt->error != NULL)
3226 ctxt->error(ctxt->userData,
3227 "except has no content\n");
3228 ctxt->nbErrors++;
3229 return(NULL);
3230 }
3231
3232 ret = xmlRelaxNGNewDefine(ctxt, node);
3233 if (ret == NULL)
3234 return(NULL);
3235 ret->type = XML_RELAXNG_EXCEPT;
3236 child = node->children;
3237 while (child != NULL) {
3238 cur = xmlRelaxNGNewDefine(ctxt, child);
3239 if (cur == NULL)
3240 break;
3241 if (attr)
3242 cur->type = XML_RELAXNG_ATTRIBUTE;
3243 else
3244 cur->type = XML_RELAXNG_ELEMENT;
3245
Daniel Veillard419a7682003-02-03 23:22:49 +00003246 if (xmlRelaxNGParseNameClass(ctxt, child, cur) != NULL) {
Daniel Veillard144fae12003-02-03 13:17:57 +00003247 if (last == NULL) {
3248 ret->content = cur;
3249 } else {
3250 last->next = cur;
3251 }
3252 last = cur;
3253 }
3254 child = child->next;
3255 }
3256
3257 return(ret);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003258}
3259
3260/**
3261 * xmlRelaxNGParseNameClass:
3262 * @ctxt: a Relax-NG parser context
3263 * @node: the nameClass node
3264 * @def: the current definition
3265 *
3266 * parse the content of a RelaxNG nameClass node.
3267 *
3268 * Returns the definition pointer or NULL in case of error.
3269 */
3270static xmlRelaxNGDefinePtr
3271xmlRelaxNGParseNameClass(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
3272 xmlRelaxNGDefinePtr def) {
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003273 xmlRelaxNGDefinePtr ret, tmp;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003274 xmlChar *val;
3275
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003276 ret = def;
3277 if ((IS_RELAXNG(node, "name")) || (IS_RELAXNG(node, "anyName")) ||
3278 (IS_RELAXNG(node, "nsName"))) {
3279 if ((def->type != XML_RELAXNG_ELEMENT) &&
3280 (def->type != XML_RELAXNG_ATTRIBUTE)) {
3281 ret = xmlRelaxNGNewDefine(ctxt, node);
3282 if (ret == NULL)
3283 return(NULL);
3284 ret->parent = def;
3285 if (ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE)
3286 ret->type = XML_RELAXNG_ATTRIBUTE;
3287 else
3288 ret->type = XML_RELAXNG_ELEMENT;
3289 }
3290 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003291 if (IS_RELAXNG(node, "name")) {
3292 val = xmlNodeGetContent(node);
Daniel Veillardd2298792003-02-14 16:54:11 +00003293 xmlRelaxNGNormExtSpace(val);
3294 if (xmlValidateNCName(val, 0)) {
3295 if (ctxt->error != NULL) {
3296 if (node->parent != NULL)
3297 ctxt->error(ctxt->userData,
3298 "Element %s name '%s' is not an NCName\n",
3299 node->parent->name, val);
3300 else
3301 ctxt->error(ctxt->userData,
3302 "name '%s' is not an NCName\n",
3303 val);
3304 }
3305 ctxt->nbErrors++;
3306 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003307 ret->name = val;
3308 val = xmlGetProp(node, BAD_CAST "ns");
3309 ret->ns = val;
Daniel Veillard416589a2003-02-17 17:25:42 +00003310 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3311 (val != NULL) &&
3312 (xmlStrEqual(val, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
3313 ctxt->error(ctxt->userData,
3314 "Attribute with namespace '%s' is not allowed\n",
3315 val);
3316 ctxt->nbErrors++;
3317 }
3318 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3319 (val != NULL) &&
3320 (val[0] == 0) &&
3321 (xmlStrEqual(ret->name, BAD_CAST "xmlns"))) {
3322 ctxt->error(ctxt->userData,
3323 "Attribute with QName 'xmlns' is not allowed\n",
3324 val);
3325 ctxt->nbErrors++;
3326 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003327 } else if (IS_RELAXNG(node, "anyName")) {
3328 ret->name = NULL;
3329 ret->ns = NULL;
3330 if (node->children != NULL) {
3331 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00003332 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
3333 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003334 }
3335 } else if (IS_RELAXNG(node, "nsName")) {
3336 ret->name = NULL;
3337 ret->ns = xmlGetProp(node, BAD_CAST "ns");
3338 if (ret->ns == NULL) {
3339 if (ctxt->error != NULL)
3340 ctxt->error(ctxt->userData,
3341 "nsName has no ns attribute\n");
3342 ctxt->nbErrors++;
3343 }
Daniel Veillard416589a2003-02-17 17:25:42 +00003344 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3345 (ret->ns != NULL) &&
3346 (xmlStrEqual(ret->ns, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
3347 ctxt->error(ctxt->userData,
3348 "Attribute with namespace '%s' is not allowed\n",
3349 ret->ns);
3350 ctxt->nbErrors++;
3351 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003352 if (node->children != NULL) {
3353 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00003354 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
3355 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003356 }
3357 } else if (IS_RELAXNG(node, "choice")) {
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003358 xmlNodePtr child;
3359 xmlRelaxNGDefinePtr last = NULL;
3360
Daniel Veillardd4310742003-02-18 21:12:46 +00003361 ret = xmlRelaxNGNewDefine(ctxt, node);
3362 if (ret == NULL)
3363 return(NULL);
3364 ret->parent = def;
3365 ret->type = XML_RELAXNG_CHOICE;
3366
Daniel Veillardd2298792003-02-14 16:54:11 +00003367 if (node->children == NULL) {
3368 if (ctxt->error != NULL)
3369 ctxt->error(ctxt->userData,
3370 "Element choice is empty\n");
3371 ctxt->nbErrors++;
3372 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00003373
3374 child = node->children;
3375 while (child != NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00003376 tmp = xmlRelaxNGParseNameClass(ctxt, child, ret);
Daniel Veillardd2298792003-02-14 16:54:11 +00003377 if (tmp != NULL) {
3378 if (last == NULL) {
3379 last = ret->nameClass = tmp;
3380 } else {
3381 last->next = tmp;
3382 last = tmp;
3383 }
3384 }
3385 child = child->next;
3386 }
3387 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003388 } else {
3389 if (ctxt->error != NULL)
3390 ctxt->error(ctxt->userData,
3391 "expecting name, anyName, nsName or choice : got %s\n",
3392 node->name);
3393 ctxt->nbErrors++;
3394 return(NULL);
3395 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003396 if (ret != def) {
3397 if (def->nameClass == NULL) {
3398 def->nameClass = ret;
3399 } else {
3400 tmp = def->nameClass;
3401 while (tmp->next != NULL) {
3402 tmp = tmp->next;
3403 }
3404 tmp->next = ret;
3405 }
3406 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003407 return(ret);
3408}
3409
3410/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00003411 * xmlRelaxNGParseElement:
3412 * @ctxt: a Relax-NG parser context
3413 * @node: the element node
3414 *
3415 * parse the content of a RelaxNG element node.
3416 *
3417 * Returns the definition pointer or NULL in case of error.
3418 */
3419static xmlRelaxNGDefinePtr
3420xmlRelaxNGParseElement(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3421 xmlRelaxNGDefinePtr ret, cur, last;
3422 xmlNodePtr child;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003423 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003424
3425 ret = xmlRelaxNGNewDefine(ctxt, node);
3426 if (ret == NULL)
3427 return(NULL);
3428 ret->type = XML_RELAXNG_ELEMENT;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003429 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003430 child = node->children;
3431 if (child == NULL) {
3432 if (ctxt->error != NULL)
3433 ctxt->error(ctxt->userData,
3434 "xmlRelaxNGParseElement: element has no children\n");
3435 ctxt->nbErrors++;
3436 return(ret);
3437 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003438 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
3439 if (cur != NULL)
3440 child = child->next;
3441
Daniel Veillard6eadf632003-01-23 18:29:16 +00003442 if (child == NULL) {
3443 if (ctxt->error != NULL)
3444 ctxt->error(ctxt->userData,
3445 "xmlRelaxNGParseElement: element has no content\n");
3446 ctxt->nbErrors++;
3447 return(ret);
3448 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003449 olddefine = ctxt->define;
3450 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003451 last = NULL;
3452 while (child != NULL) {
3453 cur = xmlRelaxNGParsePattern(ctxt, child);
3454 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003455 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003456 switch (cur->type) {
3457 case XML_RELAXNG_EMPTY:
3458 case XML_RELAXNG_NOT_ALLOWED:
3459 case XML_RELAXNG_TEXT:
3460 case XML_RELAXNG_ELEMENT:
3461 case XML_RELAXNG_DATATYPE:
3462 case XML_RELAXNG_VALUE:
3463 case XML_RELAXNG_LIST:
3464 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00003465 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003466 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00003467 case XML_RELAXNG_DEF:
3468 case XML_RELAXNG_ZEROORMORE:
3469 case XML_RELAXNG_ONEORMORE:
3470 case XML_RELAXNG_OPTIONAL:
3471 case XML_RELAXNG_CHOICE:
3472 case XML_RELAXNG_GROUP:
3473 case XML_RELAXNG_INTERLEAVE:
3474 if (last == NULL) {
3475 ret->content = last = cur;
3476 } else {
3477 if ((last->type == XML_RELAXNG_ELEMENT) &&
3478 (ret->content == last)) {
3479 ret->content = xmlRelaxNGNewDefine(ctxt, node);
3480 if (ret->content != NULL) {
3481 ret->content->type = XML_RELAXNG_GROUP;
3482 ret->content->content = last;
3483 } else {
3484 ret->content = last;
3485 }
3486 }
3487 last->next = cur;
3488 last = cur;
3489 }
3490 break;
3491 case XML_RELAXNG_ATTRIBUTE:
3492 cur->next = ret->attrs;
3493 ret->attrs = cur;
3494 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003495 case XML_RELAXNG_START:
Daniel Veillard8fe98712003-02-19 00:19:14 +00003496 case XML_RELAXNG_PARAM:
Daniel Veillard144fae12003-02-03 13:17:57 +00003497 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003498 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003499 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003500 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00003501 case XML_RELAXNG_NOOP:
3502 TODO
3503 if (ctxt->error != NULL)
3504 ctxt->error(ctxt->userData,
3505 "Internal error, noop found\n");
3506 ctxt->nbErrors++;
3507 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003508 }
3509 }
3510 child = child->next;
3511 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003512 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003513 return(ret);
3514}
3515
3516/**
3517 * xmlRelaxNGParsePatterns:
3518 * @ctxt: a Relax-NG parser context
3519 * @nodes: list of nodes
Daniel Veillard154877e2003-01-30 12:17:05 +00003520 * @group: use an implicit <group> for elements
Daniel Veillard6eadf632003-01-23 18:29:16 +00003521 *
3522 * parse the content of a RelaxNG start node.
3523 *
3524 * Returns the definition pointer or NULL in case of error.
3525 */
3526static xmlRelaxNGDefinePtr
Daniel Veillard154877e2003-01-30 12:17:05 +00003527xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes,
3528 int group) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003529 xmlRelaxNGDefinePtr def = NULL, last = NULL, cur, parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003530
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003531 parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003532 while (nodes != NULL) {
3533 if (IS_RELAXNG(nodes, "element")) {
3534 cur = xmlRelaxNGParseElement(ctxt, nodes);
3535 if (def == NULL) {
3536 def = last = cur;
3537 } else {
Daniel Veillard154877e2003-01-30 12:17:05 +00003538 if ((group == 1) && (def->type == XML_RELAXNG_ELEMENT) &&
3539 (def == last)) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003540 def = xmlRelaxNGNewDefine(ctxt, nodes);
3541 def->type = XML_RELAXNG_GROUP;
3542 def->content = last;
3543 }
3544 last->next = cur;
3545 last = cur;
3546 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003547 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003548 } else {
3549 cur = xmlRelaxNGParsePattern(ctxt, nodes);
Daniel Veillard419a7682003-02-03 23:22:49 +00003550 if (cur != NULL) {
3551 if (def == NULL) {
3552 def = last = cur;
3553 } else {
3554 last->next = cur;
3555 last = cur;
3556 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003557 }
3558 }
3559 nodes = nodes->next;
3560 }
3561 return(def);
3562}
3563
3564/**
3565 * xmlRelaxNGParseStart:
3566 * @ctxt: a Relax-NG parser context
3567 * @nodes: start children nodes
3568 *
3569 * parse the content of a RelaxNG start node.
3570 *
3571 * Returns 0 in case of success, -1 in case of error
3572 */
3573static int
3574xmlRelaxNGParseStart(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
3575 int ret = 0;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003576 xmlRelaxNGDefinePtr def = NULL, last;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003577
Daniel Veillardd2298792003-02-14 16:54:11 +00003578 if (nodes == NULL) {
3579 if (ctxt->error != NULL)
3580 ctxt->error(ctxt->userData,
3581 "start has no children\n");
3582 ctxt->nbErrors++;
3583 return(-1);
3584 }
3585 if (IS_RELAXNG(nodes, "empty")) {
3586 def = xmlRelaxNGNewDefine(ctxt, nodes);
3587 if (def == NULL)
3588 return(-1);
3589 def->type = XML_RELAXNG_EMPTY;
3590 if (nodes->children != NULL) {
3591 if (ctxt->error != NULL)
3592 ctxt->error(ctxt->userData, "element empty is not empty\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003593 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003594 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003595 } else if (IS_RELAXNG(nodes, "notAllowed")) {
3596 def = xmlRelaxNGNewDefine(ctxt, nodes);
3597 if (def == NULL)
3598 return(-1);
3599 def->type = XML_RELAXNG_NOT_ALLOWED;
3600 if (nodes->children != NULL) {
3601 if (ctxt->error != NULL)
3602 ctxt->error(ctxt->userData,
3603 "element notAllowed is not empty\n");
3604 ctxt->nbErrors++;
3605 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003606 } else {
3607 def = xmlRelaxNGParsePatterns(ctxt, nodes, 1);
Daniel Veillard2df2de22003-02-17 23:34:33 +00003608 }
3609 if (ctxt->grammar->start != NULL) {
3610 last = ctxt->grammar->start;
3611 while (last->next != NULL)
3612 last = last->next;
3613 last->next = def;
3614 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00003615 ctxt->grammar->start = def;
3616 }
3617 nodes = nodes->next;
3618 if (nodes != NULL) {
3619 if (ctxt->error != NULL)
3620 ctxt->error(ctxt->userData,
3621 "start more than one children\n");
3622 ctxt->nbErrors++;
3623 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003624 }
3625 return(ret);
3626}
3627
3628/**
3629 * xmlRelaxNGParseGrammarContent:
3630 * @ctxt: a Relax-NG parser context
3631 * @nodes: grammar children nodes
3632 *
3633 * parse the content of a RelaxNG grammar node.
3634 *
3635 * Returns 0 in case of success, -1 in case of error
3636 */
3637static int
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003638xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003639{
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003640 int ret = 0, tmp;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003641
3642 if (nodes == NULL) {
3643 if (ctxt->error != NULL)
3644 ctxt->error(ctxt->userData,
3645 "grammar has no children\n");
3646 ctxt->nbErrors++;
3647 return(-1);
3648 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003649 while (nodes != NULL) {
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003650 if (IS_RELAXNG(nodes, "start")) {
3651 if (nodes->children == NULL) {
3652 if (ctxt->error != NULL)
3653 ctxt->error(ctxt->userData,
Daniel Veillardd2298792003-02-14 16:54:11 +00003654 "start has no children\n");
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003655 ctxt->nbErrors++;
3656 } else {
3657 tmp = xmlRelaxNGParseStart(ctxt, nodes->children);
3658 if (tmp != 0)
3659 ret = -1;
3660 }
3661 } else if (IS_RELAXNG(nodes, "define")) {
3662 tmp = xmlRelaxNGParseDefine(ctxt, nodes);
3663 if (tmp != 0)
3664 ret = -1;
3665 } else if (IS_RELAXNG(nodes, "include")) {
3666 tmp = xmlRelaxNGParseInclude(ctxt, nodes);
3667 if (tmp != 0)
3668 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003669 } else {
3670 if (ctxt->error != NULL)
3671 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003672 "grammar has unexpected child %s\n", nodes->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003673 ctxt->nbErrors++;
3674 ret = -1;
3675 }
3676 nodes = nodes->next;
3677 }
3678 return (ret);
3679}
3680
3681/**
3682 * xmlRelaxNGCheckReference:
3683 * @ref: the ref
3684 * @ctxt: a Relax-NG parser context
3685 * @name: the name associated to the defines
3686 *
3687 * Applies the 4.17. combine attribute rule for all the define
3688 * element of a given grammar using the same name.
3689 */
3690static void
3691xmlRelaxNGCheckReference(xmlRelaxNGDefinePtr ref,
3692 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
3693 xmlRelaxNGGrammarPtr grammar;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003694 xmlRelaxNGDefinePtr def, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003695
3696 grammar = ctxt->grammar;
3697 if (grammar == NULL) {
3698 if (ctxt->error != NULL)
3699 ctxt->error(ctxt->userData,
3700 "Internal error: no grammar in CheckReference %s\n",
3701 name);
3702 ctxt->nbErrors++;
3703 return;
3704 }
3705 if (ref->content != NULL) {
3706 if (ctxt->error != NULL)
3707 ctxt->error(ctxt->userData,
3708 "Internal error: reference has content in CheckReference %s\n",
3709 name);
3710 ctxt->nbErrors++;
3711 return;
3712 }
3713 if (grammar->defs != NULL) {
3714 def = xmlHashLookup(grammar->defs, name);
3715 if (def != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00003716 cur = ref;
3717 while (cur != NULL) {
3718 cur->content = def;
3719 cur = cur->nextHash;
3720 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003721 } else {
Daniel Veillardd4310742003-02-18 21:12:46 +00003722 if (ctxt->error != NULL)
3723 ctxt->error(ctxt->userData,
3724 "Reference %s has no matching definition\n",
3725 name);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003726 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003727 }
Daniel Veillardd4310742003-02-18 21:12:46 +00003728 } else {
3729 if (ctxt->error != NULL)
3730 ctxt->error(ctxt->userData,
3731 "Reference %s has no matching definition\n",
3732 name);
3733 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003734 }
3735 /*
3736 * TODO: make a closure and verify there is no loop !
3737 */
3738}
3739
3740/**
3741 * xmlRelaxNGCheckCombine:
3742 * @define: the define(s) list
3743 * @ctxt: a Relax-NG parser context
3744 * @name: the name associated to the defines
3745 *
3746 * Applies the 4.17. combine attribute rule for all the define
3747 * element of a given grammar using the same name.
3748 */
3749static void
3750xmlRelaxNGCheckCombine(xmlRelaxNGDefinePtr define,
3751 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
3752 xmlChar *combine;
3753 int choiceOrInterleave = -1;
3754 int missing = 0;
3755 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
3756
3757 if (define->nextHash == NULL)
3758 return;
3759 cur = define;
3760 while (cur != NULL) {
3761 combine = xmlGetProp(cur->node, BAD_CAST "combine");
3762 if (combine != NULL) {
3763 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3764 if (choiceOrInterleave == -1)
3765 choiceOrInterleave = 1;
3766 else if (choiceOrInterleave == 0) {
3767 if (ctxt->error != NULL)
3768 ctxt->error(ctxt->userData,
3769 "Defines for %s use both 'choice' and 'interleave'\n",
3770 name);
3771 ctxt->nbErrors++;
3772 }
Daniel Veillard154877e2003-01-30 12:17:05 +00003773 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003774 if (choiceOrInterleave == -1)
3775 choiceOrInterleave = 0;
3776 else if (choiceOrInterleave == 1) {
3777 if (ctxt->error != NULL)
3778 ctxt->error(ctxt->userData,
3779 "Defines for %s use both 'choice' and 'interleave'\n",
3780 name);
3781 ctxt->nbErrors++;
3782 }
3783 } else {
3784 if (ctxt->error != NULL)
3785 ctxt->error(ctxt->userData,
3786 "Defines for %s use unknown combine value '%s''\n",
3787 name, combine);
3788 ctxt->nbErrors++;
3789 }
3790 xmlFree(combine);
3791 } else {
3792 if (missing == 0)
3793 missing = 1;
3794 else {
3795 if (ctxt->error != NULL)
3796 ctxt->error(ctxt->userData,
3797 "Some defines for %s lacks the combine attribute\n",
3798 name);
3799 ctxt->nbErrors++;
3800 }
3801 }
3802
3803 cur = cur->nextHash;
3804 }
3805#ifdef DEBUG
3806 xmlGenericError(xmlGenericErrorContext,
3807 "xmlRelaxNGCheckCombine(): merging %s defines: %d\n",
3808 name, choiceOrInterleave);
3809#endif
3810 if (choiceOrInterleave == -1)
3811 choiceOrInterleave = 0;
3812 cur = xmlRelaxNGNewDefine(ctxt, define->node);
3813 if (cur == NULL)
3814 return;
3815 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003816 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard154877e2003-01-30 12:17:05 +00003817 else
3818 cur->type = XML_RELAXNG_CHOICE;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003819 tmp = define;
3820 last = NULL;
3821 while (tmp != NULL) {
3822 if (tmp->content != NULL) {
3823 if (tmp->content->next != NULL) {
3824 /*
3825 * we need first to create a wrapper.
3826 */
3827 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
3828 if (tmp2 == NULL)
3829 break;
3830 tmp2->type = XML_RELAXNG_GROUP;
3831 tmp2->content = tmp->content;
3832 } else {
3833 tmp2 = tmp->content;
3834 }
3835 if (last == NULL) {
3836 cur->content = tmp2;
3837 } else {
3838 last->next = tmp2;
3839 }
3840 last = tmp2;
3841 tmp->content = NULL;
3842 }
3843 tmp = tmp->nextHash;
3844 }
3845 define->content = cur;
Daniel Veillard154877e2003-01-30 12:17:05 +00003846 if (choiceOrInterleave == 0) {
3847 if (ctxt->interleaves == NULL)
3848 ctxt->interleaves = xmlHashCreate(10);
3849 if (ctxt->interleaves == NULL) {
3850 if (ctxt->error != NULL)
3851 ctxt->error(ctxt->userData,
3852 "Failed to create interleaves hash table\n");
3853 ctxt->nbErrors++;
3854 } else {
3855 char tmpname[32];
3856
3857 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
3858 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
3859 if (ctxt->error != NULL)
3860 ctxt->error(ctxt->userData,
3861 "Failed to add %s to hash table\n", tmpname);
3862 ctxt->nbErrors++;
3863 }
3864 }
3865 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003866}
3867
3868/**
3869 * xmlRelaxNGCombineStart:
3870 * @ctxt: a Relax-NG parser context
3871 * @grammar: the grammar
3872 *
3873 * Applies the 4.17. combine rule for all the start
3874 * element of a given grammar.
3875 */
3876static void
3877xmlRelaxNGCombineStart(xmlRelaxNGParserCtxtPtr ctxt,
3878 xmlRelaxNGGrammarPtr grammar) {
3879 xmlRelaxNGDefinePtr starts;
3880 xmlChar *combine;
3881 int choiceOrInterleave = -1;
3882 int missing = 0;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003883 xmlRelaxNGDefinePtr cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003884
Daniel Veillard2df2de22003-02-17 23:34:33 +00003885 starts = grammar->start;
3886 if ((starts == NULL) || (starts->next == NULL))
Daniel Veillard6eadf632003-01-23 18:29:16 +00003887 return;
3888 cur = starts;
3889 while (cur != NULL) {
Daniel Veillard2df2de22003-02-17 23:34:33 +00003890 if ((cur->node == NULL) || (cur->node->parent == NULL) ||
3891 (!xmlStrEqual(cur->node->parent->name, BAD_CAST "start"))) {
3892 combine = NULL;
3893 if (ctxt->error != NULL)
3894 ctxt->error(ctxt->userData,
3895 "Internal error: start element not found\n");
3896 ctxt->nbErrors++;
3897 } else {
3898 combine = xmlGetProp(cur->node->parent, BAD_CAST "combine");
3899 }
3900
Daniel Veillard6eadf632003-01-23 18:29:16 +00003901 if (combine != NULL) {
3902 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3903 if (choiceOrInterleave == -1)
3904 choiceOrInterleave = 1;
3905 else if (choiceOrInterleave == 0) {
3906 if (ctxt->error != NULL)
3907 ctxt->error(ctxt->userData,
3908 "<start> use both 'choice' and 'interleave'\n");
3909 ctxt->nbErrors++;
3910 }
Daniel Veillard2df2de22003-02-17 23:34:33 +00003911 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003912 if (choiceOrInterleave == -1)
3913 choiceOrInterleave = 0;
3914 else if (choiceOrInterleave == 1) {
3915 if (ctxt->error != NULL)
3916 ctxt->error(ctxt->userData,
3917 "<start> use both 'choice' and 'interleave'\n");
3918 ctxt->nbErrors++;
3919 }
3920 } else {
3921 if (ctxt->error != NULL)
3922 ctxt->error(ctxt->userData,
3923 "<start> uses unknown combine value '%s''\n", combine);
3924 ctxt->nbErrors++;
3925 }
3926 xmlFree(combine);
3927 } else {
3928 if (missing == 0)
3929 missing = 1;
3930 else {
3931 if (ctxt->error != NULL)
3932 ctxt->error(ctxt->userData,
3933 "Some <start> elements lacks the combine attribute\n");
3934 ctxt->nbErrors++;
3935 }
3936 }
3937
Daniel Veillard2df2de22003-02-17 23:34:33 +00003938 cur = cur->next;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003939 }
3940#ifdef DEBUG
3941 xmlGenericError(xmlGenericErrorContext,
3942 "xmlRelaxNGCombineStart(): merging <start>: %d\n",
3943 choiceOrInterleave);
3944#endif
3945 if (choiceOrInterleave == -1)
3946 choiceOrInterleave = 0;
3947 cur = xmlRelaxNGNewDefine(ctxt, starts->node);
3948 if (cur == NULL)
3949 return;
3950 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003951 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003952 else
3953 cur->type = XML_RELAXNG_CHOICE;
3954 cur->content = grammar->start;
3955 grammar->start = cur;
3956 if (choiceOrInterleave == 0) {
3957 if (ctxt->interleaves == NULL)
3958 ctxt->interleaves = xmlHashCreate(10);
3959 if (ctxt->interleaves == NULL) {
3960 if (ctxt->error != NULL)
3961 ctxt->error(ctxt->userData,
3962 "Failed to create interleaves hash table\n");
3963 ctxt->nbErrors++;
3964 } else {
3965 char tmpname[32];
3966
3967 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
3968 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
3969 if (ctxt->error != NULL)
3970 ctxt->error(ctxt->userData,
3971 "Failed to add %s to hash table\n", tmpname);
3972 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003973 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003974 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003975 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003976}
3977
3978/**
Daniel Veillardd4310742003-02-18 21:12:46 +00003979 * xmlRelaxNGCheckCycles:
3980 * @ctxt: a Relax-NG parser context
3981 * @nodes: grammar children nodes
3982 * @depth: the counter
3983 *
3984 * Check for cycles.
3985 *
3986 * Returns 0 if check passed, and -1 in case of error
3987 */
3988static int
3989xmlRelaxNGCheckCycles(xmlRelaxNGParserCtxtPtr ctxt,
3990 xmlRelaxNGDefinePtr cur, int depth) {
3991 int ret = 0;
3992
3993 while ((ret == 0) && (cur != NULL)) {
3994 if ((cur->type == XML_RELAXNG_REF) ||
3995 (cur->type == XML_RELAXNG_PARENTREF)) {
3996 if (cur->depth == -1) {
3997 cur->depth = depth;
3998 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth);
3999 cur->depth = -2;
4000 } else if (depth == cur->depth) {
4001 if (ctxt->error != NULL)
4002 ctxt->error(ctxt->userData,
4003 "Detected a cycle in %s references\n", cur->name);
4004 ctxt->nbErrors++;
4005 return(-1);
4006 }
4007 } else if (cur->type == XML_RELAXNG_ELEMENT) {
4008 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth + 1);
4009 } else {
4010 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth);
4011 }
4012 cur = cur->next;
4013 }
4014 return(ret);
4015}
4016
4017/**
Daniel Veillard77648bb2003-02-20 15:03:22 +00004018 * xmlRelaxNGTryUnlink:
4019 * @ctxt: a Relax-NG parser context
4020 * @cur: the definition to unlink
4021 * @parent: the parent definition
4022 * @prev: the previous sibling definition
4023 *
4024 * Try to unlink a definition. If not possble make it a NOOP
4025 *
4026 * Returns the new prev definition
4027 */
4028static xmlRelaxNGDefinePtr
4029xmlRelaxNGTryUnlink(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
4030 xmlRelaxNGDefinePtr cur,
4031 xmlRelaxNGDefinePtr parent,
4032 xmlRelaxNGDefinePtr prev) {
4033 if (prev != NULL) {
4034 prev->next = cur->next;
4035 } else {
4036 if (parent != NULL) {
4037 if (parent->content == cur)
4038 parent->content = cur->next;
4039 else if (parent->attrs == cur)
4040 parent->attrs = cur->next;
4041 else if (parent->nameClass == cur)
4042 parent->nameClass = cur->next;
4043 } else {
4044 cur->type = XML_RELAXNG_NOOP;
4045 prev = cur;
4046 }
4047 }
4048 return(prev);
4049}
4050
4051/**
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004052 * xmlRelaxNGSimplify:
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004053 * @ctxt: a Relax-NG parser context
4054 * @nodes: grammar children nodes
4055 *
4056 * Check for simplification of empty and notAllowed
4057 */
4058static void
4059xmlRelaxNGSimplify(xmlRelaxNGParserCtxtPtr ctxt,
4060 xmlRelaxNGDefinePtr cur,
4061 xmlRelaxNGDefinePtr parent) {
4062 xmlRelaxNGDefinePtr prev = NULL;
4063
4064 while (cur != NULL) {
4065 if ((cur->type == XML_RELAXNG_REF) ||
4066 (cur->type == XML_RELAXNG_PARENTREF)) {
4067 if (cur->depth != -3) {
4068 cur->depth = -3;
4069 xmlRelaxNGSimplify(ctxt, cur->content, cur);
4070 }
4071 } else if (cur->type == XML_RELAXNG_NOT_ALLOWED) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004072 cur->parent = parent;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004073 if ((parent != NULL) &&
4074 ((parent->type == XML_RELAXNG_ATTRIBUTE) ||
4075 (parent->type == XML_RELAXNG_LIST) ||
4076 (parent->type == XML_RELAXNG_GROUP) ||
4077 (parent->type == XML_RELAXNG_INTERLEAVE) ||
4078 (parent->type == XML_RELAXNG_ONEORMORE) ||
4079 (parent->type == XML_RELAXNG_ZEROORMORE))) {
4080 parent->type = XML_RELAXNG_NOT_ALLOWED;
4081 break;
4082 }
4083 if ((parent != NULL) &&
4084 (parent->type == XML_RELAXNG_CHOICE)) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004085 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004086 } else
4087 prev = cur;
4088 } else if (cur->type == XML_RELAXNG_EMPTY){
Daniel Veillard77648bb2003-02-20 15:03:22 +00004089 cur->parent = parent;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004090 if ((parent != NULL) &&
4091 ((parent->type == XML_RELAXNG_ONEORMORE) ||
4092 (parent->type == XML_RELAXNG_ZEROORMORE))) {
4093 parent->type = XML_RELAXNG_EMPTY;
4094 break;
4095 }
4096 if ((parent != NULL) &&
4097 ((parent->type == XML_RELAXNG_GROUP) ||
4098 (parent->type == XML_RELAXNG_INTERLEAVE))) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004099 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004100 } else
4101 prev = cur;
4102 } else {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004103 cur->parent = parent;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004104 if (cur->content != NULL)
4105 xmlRelaxNGSimplify(ctxt, cur->content, cur);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004106 if (cur->attrs != NULL)
4107 xmlRelaxNGSimplify(ctxt, cur->attrs, cur);
4108 if (cur->nameClass != NULL)
4109 xmlRelaxNGSimplify(ctxt, cur->nameClass, cur);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004110 /*
4111 * This may result in a simplification
4112 */
4113 if ((cur->type == XML_RELAXNG_GROUP) ||
4114 (cur->type == XML_RELAXNG_INTERLEAVE)) {
4115 if (cur->content == NULL)
4116 cur->type = XML_RELAXNG_EMPTY;
4117 else if (cur->content->next == NULL) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004118 if ((parent == NULL) && (prev == NULL)) {
4119 cur->type = XML_RELAXNG_NOOP;
4120 } else if (prev == NULL) {
4121 parent->content = cur->content;
4122 cur->content->next = cur->next;
4123 cur = cur->content;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004124 } else {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004125 cur->content->next = cur->next;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004126 prev->next = cur->content;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004127 cur = cur->content;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004128 }
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004129 }
4130 }
4131 /*
4132 * the current node may have been transformed back
4133 */
4134 if ((cur->type == XML_RELAXNG_EXCEPT) &&
4135 (cur->content != NULL) &&
4136 (cur->content->type == XML_RELAXNG_NOT_ALLOWED)) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004137 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004138 } else if (cur->type == XML_RELAXNG_NOT_ALLOWED) {
4139 if ((parent != NULL) &&
4140 ((parent->type == XML_RELAXNG_ATTRIBUTE) ||
4141 (parent->type == XML_RELAXNG_LIST) ||
4142 (parent->type == XML_RELAXNG_GROUP) ||
4143 (parent->type == XML_RELAXNG_INTERLEAVE) ||
4144 (parent->type == XML_RELAXNG_ONEORMORE) ||
4145 (parent->type == XML_RELAXNG_ZEROORMORE))) {
4146 parent->type = XML_RELAXNG_NOT_ALLOWED;
4147 break;
4148 }
4149 if ((parent != NULL) &&
4150 (parent->type == XML_RELAXNG_CHOICE)) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004151 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004152 } else
4153 prev = cur;
4154 } else if (cur->type == XML_RELAXNG_EMPTY){
4155 if ((parent != NULL) &&
4156 ((parent->type == XML_RELAXNG_ONEORMORE) ||
4157 (parent->type == XML_RELAXNG_ZEROORMORE))) {
4158 parent->type = XML_RELAXNG_EMPTY;
4159 break;
4160 }
4161 if ((parent != NULL) &&
4162 ((parent->type == XML_RELAXNG_GROUP) ||
4163 (parent->type == XML_RELAXNG_INTERLEAVE) ||
4164 (parent->type == XML_RELAXNG_CHOICE))) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004165 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004166 } else
4167 prev = cur;
4168 } else {
4169 prev = cur;
4170 }
4171 }
4172 cur = cur->next;
4173 }
4174}
4175
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004176/**
4177 * xmlRelaxNGGroupContentType:
4178 * @ct1: the first content type
4179 * @ct2: the second content type
4180 *
4181 * Try to group 2 content types
4182 *
4183 * Returns the content type
4184 */
4185static xmlRelaxNGContentType
4186xmlRelaxNGGroupContentType(xmlRelaxNGContentType ct1,
4187 xmlRelaxNGContentType ct2) {
4188 if ((ct1 == XML_RELAXNG_CONTENT_ERROR) ||
4189 (ct2 == XML_RELAXNG_CONTENT_ERROR))
4190 return(XML_RELAXNG_CONTENT_ERROR);
4191 if (ct1 == XML_RELAXNG_CONTENT_EMPTY)
4192 return(ct2);
4193 if (ct2 == XML_RELAXNG_CONTENT_EMPTY)
4194 return(ct1);
4195 if ((ct1 == XML_RELAXNG_CONTENT_COMPLEX) &&
4196 (ct2 == XML_RELAXNG_CONTENT_COMPLEX))
4197 return(XML_RELAXNG_CONTENT_COMPLEX);
4198 return(XML_RELAXNG_CONTENT_ERROR);
4199}
4200
4201/**
4202 * xmlRelaxNGMaxContentType:
4203 * @ct1: the first content type
4204 * @ct2: the second content type
4205 *
4206 * Compute the max content-type
4207 *
4208 * Returns the content type
4209 */
4210static xmlRelaxNGContentType
4211xmlRelaxNGMaxContentType(xmlRelaxNGContentType ct1,
4212 xmlRelaxNGContentType ct2) {
4213 if ((ct1 == XML_RELAXNG_CONTENT_ERROR) ||
4214 (ct2 == XML_RELAXNG_CONTENT_ERROR))
4215 return(XML_RELAXNG_CONTENT_ERROR);
4216 if ((ct1 == XML_RELAXNG_CONTENT_SIMPLE) ||
4217 (ct2 == XML_RELAXNG_CONTENT_SIMPLE))
4218 return(XML_RELAXNG_CONTENT_SIMPLE);
4219 if ((ct1 == XML_RELAXNG_CONTENT_COMPLEX) ||
4220 (ct2 == XML_RELAXNG_CONTENT_COMPLEX))
4221 return(XML_RELAXNG_CONTENT_COMPLEX);
4222 return(XML_RELAXNG_CONTENT_EMPTY);
4223}
Daniel Veillard77648bb2003-02-20 15:03:22 +00004224
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004225/**
4226 * xmlRelaxNGCheckRules:
4227 * @ctxt: a Relax-NG parser context
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004228 * @cur: the current definition
Daniel Veillard77648bb2003-02-20 15:03:22 +00004229 * @flags: some accumulated flags
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004230 * @ptype: the parent type
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004231 *
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004232 * Check for rules in section 7.1 and 7.2
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004233 *
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004234 * Returns the content type of @cur
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004235 */
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004236static xmlRelaxNGContentType
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004237xmlRelaxNGCheckRules(xmlRelaxNGParserCtxtPtr ctxt,
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004238 xmlRelaxNGDefinePtr cur, int flags,
4239 xmlRelaxNGType ptype) {
4240 int nflags = flags;
4241 xmlRelaxNGContentType ret, tmp, val = XML_RELAXNG_CONTENT_EMPTY;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004242
Daniel Veillard77648bb2003-02-20 15:03:22 +00004243 while (cur != NULL) {
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004244 ret = XML_RELAXNG_CONTENT_EMPTY;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004245 if ((cur->type == XML_RELAXNG_REF) ||
4246 (cur->type == XML_RELAXNG_PARENTREF)) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004247 if (flags & XML_RELAXNG_IN_LIST) {
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004248 if (ctxt->error != NULL)
4249 ctxt->error(ctxt->userData,
Daniel Veillard77648bb2003-02-20 15:03:22 +00004250 "Found forbidden pattern list//ref\n");
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004251 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004252 }
4253 if (flags & XML_RELAXNG_IN_ATTRIBUTE) {
4254 if (ctxt->error != NULL)
4255 ctxt->error(ctxt->userData,
4256 "Found forbidden pattern attribute//ref\n");
4257 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004258 }
4259 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4260 if (ctxt->error != NULL)
4261 ctxt->error(ctxt->userData,
4262 "Found forbidden pattern data/except//ref\n");
4263 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004264 }
Daniel Veillardc5312d72003-02-21 17:14:10 +00004265 if (cur->depth > -4) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004266 cur->depth = -4;
Daniel Veillardc5312d72003-02-21 17:14:10 +00004267 ret = xmlRelaxNGCheckRules(ctxt, cur->content,
4268 flags, cur->type);
4269 cur->depth = ret - 15 ;
4270 } else if (cur->depth == -4) {
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004271 ret = XML_RELAXNG_CONTENT_COMPLEX;
Daniel Veillardc5312d72003-02-21 17:14:10 +00004272 } else {
4273 ret = (xmlRelaxNGContentType) cur->depth + 15;
4274 }
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004275 } else if (cur->type == XML_RELAXNG_ELEMENT) {
Daniel Veillard44e1dd02003-02-21 23:23:28 +00004276 /*
4277 * The 7.3 Attribute derivation rule for groups is plugged there
4278 */
4279 xmlRelaxNGCheckGroupAttrs(ctxt, cur);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004280 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4281 if (ctxt->error != NULL)
4282 ctxt->error(ctxt->userData,
4283 "Found forbidden pattern data/except//element(ref)\n");
4284 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004285 }
4286 if (flags & XML_RELAXNG_IN_LIST) {
4287 if (ctxt->error != NULL)
4288 ctxt->error(ctxt->userData,
4289 "Found forbidden pattern list//element(ref)\n");
4290 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004291 }
4292 if (flags & XML_RELAXNG_IN_ATTRIBUTE) {
4293 if (ctxt->error != NULL)
4294 ctxt->error(ctxt->userData,
4295 "Found forbidden pattern attribute//element(ref)\n");
4296 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004297 }
4298 /*
4299 * reset since in the simple form elements are only child
4300 * of grammar/define
4301 */
4302 nflags = 0;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004303 ret = xmlRelaxNGCheckRules(ctxt, cur->attrs, nflags, cur->type);
4304 if (ret != XML_RELAXNG_CONTENT_EMPTY) {
4305 if (ctxt->error != NULL)
4306 ctxt->error(ctxt->userData,
4307 "Element %s attributes have a content type error\n",
4308 cur->name);
4309 ctxt->nbErrors++;
4310 }
4311 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
4312 if (ret == XML_RELAXNG_CONTENT_ERROR) {
4313 if (ctxt->error != NULL)
4314 ctxt->error(ctxt->userData,
4315 "Element %s has a content type error\n",
4316 cur->name);
4317 ctxt->nbErrors++;
4318 } else {
4319 ret = XML_RELAXNG_CONTENT_COMPLEX;
4320 }
Daniel Veillard77648bb2003-02-20 15:03:22 +00004321 } else if (cur->type == XML_RELAXNG_ATTRIBUTE) {
4322 if (flags & XML_RELAXNG_IN_ATTRIBUTE) {
4323 if (ctxt->error != NULL)
4324 ctxt->error(ctxt->userData,
4325 "Found forbidden pattern attribute//attribute\n");
4326 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004327 }
4328 if (flags & XML_RELAXNG_IN_LIST) {
4329 if (ctxt->error != NULL)
4330 ctxt->error(ctxt->userData,
4331 "Found forbidden pattern list//attribute\n");
4332 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004333 }
4334 if (flags & XML_RELAXNG_IN_OOMGROUP) {
4335 if (ctxt->error != NULL)
4336 ctxt->error(ctxt->userData,
4337 "Found forbidden pattern oneOrMore//group//attribute\n");
4338 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004339 }
4340 if (flags & XML_RELAXNG_IN_OOMINTERLEAVE) {
4341 if (ctxt->error != NULL)
4342 ctxt->error(ctxt->userData,
4343 "Found forbidden pattern oneOrMore//interleave//attribute\n");
4344 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004345 }
4346 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4347 if (ctxt->error != NULL)
4348 ctxt->error(ctxt->userData,
4349 "Found forbidden pattern data/except//attribute\n");
4350 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004351 }
4352 if (flags & XML_RELAXNG_IN_START) {
4353 if (ctxt->error != NULL)
4354 ctxt->error(ctxt->userData,
4355 "Found forbidden pattern start//attribute\n");
4356 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004357 }
4358 nflags = flags | XML_RELAXNG_IN_ATTRIBUTE;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004359 xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
4360 ret = XML_RELAXNG_CONTENT_EMPTY;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004361 } else if ((cur->type == XML_RELAXNG_ONEORMORE) ||
4362 (cur->type == XML_RELAXNG_ZEROORMORE)) {
4363 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4364 if (ctxt->error != NULL)
4365 ctxt->error(ctxt->userData,
4366 "Found forbidden pattern data/except//oneOrMore\n");
4367 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004368 }
4369 if (flags & XML_RELAXNG_IN_START) {
4370 if (ctxt->error != NULL)
4371 ctxt->error(ctxt->userData,
4372 "Found forbidden pattern start//oneOrMore\n");
4373 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004374 }
4375 nflags = flags | XML_RELAXNG_IN_ONEORMORE;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004376 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
4377 ret = xmlRelaxNGGroupContentType(ret, ret);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004378 } else if (cur->type == XML_RELAXNG_LIST) {
4379 if (flags & XML_RELAXNG_IN_LIST) {
4380 if (ctxt->error != NULL)
4381 ctxt->error(ctxt->userData,
4382 "Found forbidden pattern list//list\n");
4383 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004384 }
4385 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4386 if (ctxt->error != NULL)
4387 ctxt->error(ctxt->userData,
4388 "Found forbidden pattern data/except//list\n");
4389 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004390 }
4391 if (flags & XML_RELAXNG_IN_START) {
4392 if (ctxt->error != NULL)
4393 ctxt->error(ctxt->userData,
4394 "Found forbidden pattern start//list\n");
4395 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004396 }
4397 nflags = flags | XML_RELAXNG_IN_LIST;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004398 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004399 } else if (cur->type == XML_RELAXNG_GROUP) {
4400 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4401 if (ctxt->error != NULL)
4402 ctxt->error(ctxt->userData,
4403 "Found forbidden pattern data/except//group\n");
4404 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004405 }
4406 if (flags & XML_RELAXNG_IN_START) {
4407 if (ctxt->error != NULL)
4408 ctxt->error(ctxt->userData,
4409 "Found forbidden pattern start//group\n");
4410 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004411 }
4412 if (flags & XML_RELAXNG_IN_ONEORMORE)
4413 nflags = flags | XML_RELAXNG_IN_OOMGROUP;
4414 else
4415 nflags = flags;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004416 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
Daniel Veillard44e1dd02003-02-21 23:23:28 +00004417 /*
4418 * The 7.3 Attribute derivation rule for groups is plugged there
4419 */
4420 xmlRelaxNGCheckGroupAttrs(ctxt, cur);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004421 } else if (cur->type == XML_RELAXNG_INTERLEAVE) {
4422 if (flags & XML_RELAXNG_IN_LIST) {
4423 if (ctxt->error != NULL)
4424 ctxt->error(ctxt->userData,
4425 "Found forbidden pattern list//interleave\n");
4426 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004427 }
4428 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4429 if (ctxt->error != NULL)
4430 ctxt->error(ctxt->userData,
4431 "Found forbidden pattern data/except//interleave\n");
4432 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004433 }
4434 if (flags & XML_RELAXNG_IN_START) {
4435 if (ctxt->error != NULL)
4436 ctxt->error(ctxt->userData,
4437 "Found forbidden pattern start//interleave\n");
4438 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004439 }
4440 if (flags & XML_RELAXNG_IN_ONEORMORE)
4441 nflags = flags | XML_RELAXNG_IN_OOMINTERLEAVE;
4442 else
4443 nflags = flags;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004444 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004445 } else if (cur->type == XML_RELAXNG_EXCEPT) {
4446 if ((cur->parent != NULL) &&
4447 (cur->parent->type == XML_RELAXNG_DATATYPE))
4448 nflags = flags | XML_RELAXNG_IN_DATAEXCEPT;
4449 else
4450 nflags = flags;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004451 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004452 } else if (cur->type == XML_RELAXNG_DATATYPE) {
4453 if (flags & XML_RELAXNG_IN_START) {
4454 if (ctxt->error != NULL)
4455 ctxt->error(ctxt->userData,
4456 "Found forbidden pattern start//data\n");
4457 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004458 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004459 xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type);
4460 ret = XML_RELAXNG_CONTENT_SIMPLE;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004461 } else if (cur->type == XML_RELAXNG_VALUE) {
4462 if (flags & XML_RELAXNG_IN_START) {
4463 if (ctxt->error != NULL)
4464 ctxt->error(ctxt->userData,
4465 "Found forbidden pattern start//value\n");
4466 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004467 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004468 xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type);
4469 ret = XML_RELAXNG_CONTENT_SIMPLE;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004470 } else if (cur->type == XML_RELAXNG_TEXT) {
4471 if (flags & XML_RELAXNG_IN_LIST) {
4472 if (ctxt->error != NULL)
4473 ctxt->error(ctxt->userData,
4474 "Found forbidden pattern list//text\n");
4475 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004476 }
4477 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4478 if (ctxt->error != NULL)
4479 ctxt->error(ctxt->userData,
4480 "Found forbidden pattern data/except//text\n");
4481 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004482 }
4483 if (flags & XML_RELAXNG_IN_START) {
4484 if (ctxt->error != NULL)
4485 ctxt->error(ctxt->userData,
4486 "Found forbidden pattern start//text\n");
4487 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004488 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004489 ret = XML_RELAXNG_CONTENT_COMPLEX;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004490 } else if (cur->type == XML_RELAXNG_EMPTY) {
4491 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4492 if (ctxt->error != NULL)
4493 ctxt->error(ctxt->userData,
4494 "Found forbidden pattern data/except//empty\n");
4495 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004496 }
4497 if (flags & XML_RELAXNG_IN_START) {
4498 if (ctxt->error != NULL)
4499 ctxt->error(ctxt->userData,
4500 "Found forbidden pattern start//empty\n");
4501 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004502 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004503 ret = XML_RELAXNG_CONTENT_EMPTY;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004504 } else {
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004505 ret = xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004506 }
4507 cur = cur->next;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004508 if (ptype == XML_RELAXNG_GROUP) {
4509 val = xmlRelaxNGGroupContentType(val, ret);
4510 } else if (ptype == XML_RELAXNG_INTERLEAVE) {
4511 tmp = xmlRelaxNGGroupContentType(val, ret);
4512 if (tmp != XML_RELAXNG_CONTENT_ERROR)
4513 tmp = xmlRelaxNGMaxContentType(val, ret);
4514 } else if (ptype == XML_RELAXNG_CHOICE) {
4515 val = xmlRelaxNGMaxContentType(val, ret);
4516 } else if (ptype == XML_RELAXNG_LIST) {
4517 val = XML_RELAXNG_CONTENT_SIMPLE;
4518 } else if (ptype == XML_RELAXNG_EXCEPT) {
4519 if (ret == XML_RELAXNG_CONTENT_ERROR)
4520 val = XML_RELAXNG_CONTENT_ERROR;
4521 else
4522 val = XML_RELAXNG_CONTENT_SIMPLE;
4523 } else {
4524 val = xmlRelaxNGGroupContentType(val, ret);
4525 }
4526
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004527 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004528 return(val);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004529}
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004530
4531/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004532 * xmlRelaxNGParseGrammar:
4533 * @ctxt: a Relax-NG parser context
4534 * @nodes: grammar children nodes
4535 *
4536 * parse a Relax-NG <grammar> node
4537 *
4538 * Returns the internal xmlRelaxNGGrammarPtr built or
4539 * NULL in case of error
4540 */
4541static xmlRelaxNGGrammarPtr
4542xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
4543 xmlRelaxNGGrammarPtr ret, tmp, old;
4544
Daniel Veillard6eadf632003-01-23 18:29:16 +00004545 ret = xmlRelaxNGNewGrammar(ctxt);
4546 if (ret == NULL)
4547 return(NULL);
4548
4549 /*
4550 * Link the new grammar in the tree
4551 */
4552 ret->parent = ctxt->grammar;
4553 if (ctxt->grammar != NULL) {
4554 tmp = ctxt->grammar->children;
4555 if (tmp == NULL) {
4556 ctxt->grammar->children = ret;
4557 } else {
4558 while (tmp->next != NULL)
4559 tmp = tmp->next;
4560 tmp->next = ret;
4561 }
4562 }
4563
4564 old = ctxt->grammar;
4565 ctxt->grammar = ret;
4566 xmlRelaxNGParseGrammarContent(ctxt, nodes);
4567 ctxt->grammar = ret;
Daniel Veillard2df2de22003-02-17 23:34:33 +00004568 if (ctxt->grammar == NULL) {
4569 if (ctxt->error != NULL)
4570 ctxt->error(ctxt->userData,
4571 "Failed to parse <grammar> content\n");
4572 ctxt->nbErrors++;
4573 } else if (ctxt->grammar->start == NULL) {
4574 if (ctxt->error != NULL)
4575 ctxt->error(ctxt->userData,
4576 "Element <grammar> has no <start>\n");
4577 ctxt->nbErrors++;
4578 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004579
4580 /*
4581 * Apply 4.17 mergingd rules to defines and starts
4582 */
4583 xmlRelaxNGCombineStart(ctxt, ret);
4584 if (ret->defs != NULL) {
4585 xmlHashScan(ret->defs, (xmlHashScanner) xmlRelaxNGCheckCombine,
4586 ctxt);
4587 }
4588
4589 /*
4590 * link together defines and refs in this grammar
4591 */
4592 if (ret->refs != NULL) {
4593 xmlHashScan(ret->refs, (xmlHashScanner) xmlRelaxNGCheckReference,
4594 ctxt);
4595 }
4596 ctxt->grammar = old;
4597 return(ret);
4598}
4599
4600/**
4601 * xmlRelaxNGParseDocument:
4602 * @ctxt: a Relax-NG parser context
4603 * @node: the root node of the RelaxNG schema
4604 *
4605 * parse a Relax-NG definition resource and build an internal
4606 * xmlRelaxNG struture which can be used to validate instances.
4607 *
4608 * Returns the internal XML RelaxNG structure built or
4609 * NULL in case of error
4610 */
4611static xmlRelaxNGPtr
4612xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
4613 xmlRelaxNGPtr schema = NULL;
Daniel Veillard276be4a2003-01-24 01:03:34 +00004614 const xmlChar *olddefine;
Daniel Veillarde431a272003-01-29 23:02:33 +00004615 xmlRelaxNGGrammarPtr old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004616
4617 if ((ctxt == NULL) || (node == NULL))
4618 return (NULL);
4619
4620 schema = xmlRelaxNGNewRelaxNG(ctxt);
4621 if (schema == NULL)
4622 return(NULL);
4623
Daniel Veillard276be4a2003-01-24 01:03:34 +00004624 olddefine = ctxt->define;
4625 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004626 if (IS_RELAXNG(node, "grammar")) {
4627 schema->topgrammar = xmlRelaxNGParseGrammar(ctxt, node->children);
4628 } else {
4629 schema->topgrammar = xmlRelaxNGNewGrammar(ctxt);
4630 if (schema->topgrammar == NULL) {
4631 return(schema);
4632 }
4633 schema->topgrammar->parent = NULL;
Daniel Veillarde431a272003-01-29 23:02:33 +00004634 old = ctxt->grammar;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004635 ctxt->grammar = schema->topgrammar;
4636 xmlRelaxNGParseStart(ctxt, node);
Daniel Veillarde431a272003-01-29 23:02:33 +00004637 if (old != NULL)
4638 ctxt->grammar = old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004639 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00004640 ctxt->define = olddefine;
Daniel Veillardd4310742003-02-18 21:12:46 +00004641 if (schema->topgrammar->start != NULL) {
4642 xmlRelaxNGCheckCycles(ctxt, schema->topgrammar->start, 0);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004643 if ((ctxt->flags & XML_RELAXNG_IN_EXTERNALREF) == 0) {
4644 xmlRelaxNGSimplify(ctxt, schema->topgrammar->start, NULL);
4645 while ((schema->topgrammar->start != NULL) &&
4646 (schema->topgrammar->start->type == XML_RELAXNG_NOOP) &&
4647 (schema->topgrammar->start->next != NULL))
4648 schema->topgrammar->start = schema->topgrammar->start->content;
4649 xmlRelaxNGCheckRules(ctxt, schema->topgrammar->start,
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004650 XML_RELAXNG_IN_START, XML_RELAXNG_NOOP);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004651 }
Daniel Veillardd4310742003-02-18 21:12:46 +00004652 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004653
4654#ifdef DEBUG
4655 if (schema == NULL)
4656 xmlGenericError(xmlGenericErrorContext,
4657 "xmlRelaxNGParseDocument() failed\n");
4658#endif
4659
4660 return (schema);
4661}
4662
4663/************************************************************************
4664 * *
4665 * Reading RelaxNGs *
4666 * *
4667 ************************************************************************/
4668
4669/**
4670 * xmlRelaxNGNewParserCtxt:
4671 * @URL: the location of the schema
4672 *
4673 * Create an XML RelaxNGs parse context for that file/resource expected
4674 * to contain an XML RelaxNGs file.
4675 *
4676 * Returns the parser context or NULL in case of error
4677 */
4678xmlRelaxNGParserCtxtPtr
4679xmlRelaxNGNewParserCtxt(const char *URL) {
4680 xmlRelaxNGParserCtxtPtr ret;
4681
4682 if (URL == NULL)
4683 return(NULL);
4684
4685 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
4686 if (ret == NULL) {
4687 xmlGenericError(xmlGenericErrorContext,
4688 "Failed to allocate new schama parser context for %s\n", URL);
4689 return (NULL);
4690 }
4691 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
4692 ret->URL = xmlStrdup((const xmlChar *)URL);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004693 ret->error = xmlGenericError;
4694 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004695 return (ret);
4696}
4697
4698/**
4699 * xmlRelaxNGNewMemParserCtxt:
4700 * @buffer: a pointer to a char array containing the schemas
4701 * @size: the size of the array
4702 *
4703 * Create an XML RelaxNGs parse context for that memory buffer expected
4704 * to contain an XML RelaxNGs file.
4705 *
4706 * Returns the parser context or NULL in case of error
4707 */
4708xmlRelaxNGParserCtxtPtr
4709xmlRelaxNGNewMemParserCtxt(const char *buffer, int size) {
4710 xmlRelaxNGParserCtxtPtr ret;
4711
4712 if ((buffer == NULL) || (size <= 0))
4713 return(NULL);
4714
4715 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
4716 if (ret == NULL) {
4717 xmlGenericError(xmlGenericErrorContext,
4718 "Failed to allocate new schama parser context\n");
4719 return (NULL);
4720 }
4721 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
4722 ret->buffer = buffer;
4723 ret->size = size;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004724 ret->error = xmlGenericError;
4725 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004726 return (ret);
4727}
4728
4729/**
4730 * xmlRelaxNGFreeParserCtxt:
4731 * @ctxt: the schema parser context
4732 *
4733 * Free the resources associated to the schema parser context
4734 */
4735void
4736xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxtPtr ctxt) {
4737 if (ctxt == NULL)
4738 return;
4739 if (ctxt->URL != NULL)
4740 xmlFree(ctxt->URL);
4741 if (ctxt->doc != NULL)
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004742 xmlFreeDoc(ctxt->document);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004743 if (ctxt->interleaves != NULL)
4744 xmlHashFree(ctxt->interleaves, NULL);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004745 if (ctxt->documents != NULL)
4746 xmlHashFree(ctxt->documents, (xmlHashDeallocator)
4747 xmlRelaxNGFreeDocument);
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00004748 if (ctxt->includes != NULL)
4749 xmlHashFree(ctxt->includes, (xmlHashDeallocator)
4750 xmlRelaxNGFreeInclude);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004751 if (ctxt->docTab != NULL)
4752 xmlFree(ctxt->docTab);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004753 if (ctxt->incTab != NULL)
4754 xmlFree(ctxt->incTab);
Daniel Veillard419a7682003-02-03 23:22:49 +00004755 if (ctxt->defTab != NULL) {
4756 int i;
4757
4758 for (i = 0;i < ctxt->defNr;i++)
4759 xmlRelaxNGFreeDefine(ctxt->defTab[i]);
4760 xmlFree(ctxt->defTab);
4761 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004762 xmlFree(ctxt);
4763}
4764
Daniel Veillard6eadf632003-01-23 18:29:16 +00004765/**
Daniel Veillardd2298792003-02-14 16:54:11 +00004766 * xmlRelaxNGNormExtSpace:
4767 * @value: a value
4768 *
4769 * Removes the leading and ending spaces of the value
4770 * The string is modified "in situ"
4771 */
4772static void
4773xmlRelaxNGNormExtSpace(xmlChar *value) {
4774 xmlChar *start = value;
4775 xmlChar *cur = value;
4776 if (value == NULL)
4777 return;
4778
4779 while (IS_BLANK(*cur)) cur++;
4780 if (cur == start) {
4781 do {
4782 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
4783 if (*cur == 0)
4784 return;
4785 start = cur;
4786 while (IS_BLANK(*cur)) cur++;
4787 if (*cur == 0) {
4788 *start = 0;
4789 return;
4790 }
4791 } while (1);
4792 } else {
4793 do {
4794 while ((*cur != 0) && (!IS_BLANK(*cur)))
4795 *start++ = *cur++;
4796 if (*cur == 0) {
4797 *start = 0;
4798 return;
4799 }
4800 /* don't try to normalize the inner spaces */
4801 while (IS_BLANK(*cur)) cur++;
4802 *start++ = *cur++;
4803 if (*cur == 0) {
4804 *start = 0;
4805 return;
4806 }
4807 } while (1);
4808 }
4809}
4810
4811/**
4812 * xmlRelaxNGCheckAttributes:
4813 * @ctxt: a Relax-NG parser context
4814 * @node: a Relax-NG node
4815 *
4816 * Check all the attributes on the given node
4817 */
4818static void
4819xmlRelaxNGCleanupAttributes(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
4820 xmlAttrPtr cur, next;
4821
4822 cur = node->properties;
4823 while (cur != NULL) {
4824 next = cur->next;
4825 if ((cur->ns == NULL) ||
4826 (xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
4827 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
4828 if ((!xmlStrEqual(node->name, BAD_CAST "element")) &&
4829 (!xmlStrEqual(node->name, BAD_CAST "attribute")) &&
4830 (!xmlStrEqual(node->name, BAD_CAST "ref")) &&
4831 (!xmlStrEqual(node->name, BAD_CAST "parentRef")) &&
Daniel Veillard2df2de22003-02-17 23:34:33 +00004832 (!xmlStrEqual(node->name, BAD_CAST "param")) &&
Daniel Veillardd2298792003-02-14 16:54:11 +00004833 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
4834 if (ctxt->error != NULL)
4835 ctxt->error(ctxt->userData,
4836 "Attribute %s is not allowed on %s\n",
4837 cur->name, node->name);
4838 ctxt->nbErrors++;
4839 }
4840 } else if (xmlStrEqual(cur->name, BAD_CAST "type")) {
4841 if ((!xmlStrEqual(node->name, BAD_CAST "value")) &&
4842 (!xmlStrEqual(node->name, BAD_CAST "data"))) {
4843 if (ctxt->error != NULL)
4844 ctxt->error(ctxt->userData,
4845 "Attribute %s is not allowed on %s\n",
4846 cur->name, node->name);
4847 ctxt->nbErrors++;
4848 }
4849 } else if (xmlStrEqual(cur->name, BAD_CAST "href")) {
4850 if ((!xmlStrEqual(node->name, BAD_CAST "externalRef")) &&
4851 (!xmlStrEqual(node->name, BAD_CAST "include"))) {
4852 if (ctxt->error != NULL)
4853 ctxt->error(ctxt->userData,
4854 "Attribute %s is not allowed on %s\n",
4855 cur->name, node->name);
4856 ctxt->nbErrors++;
4857 }
4858 } else if (xmlStrEqual(cur->name, BAD_CAST "combine")) {
4859 if ((!xmlStrEqual(node->name, BAD_CAST "start")) &&
4860 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
4861 if (ctxt->error != NULL)
4862 ctxt->error(ctxt->userData,
4863 "Attribute %s is not allowed on %s\n",
4864 cur->name, node->name);
4865 ctxt->nbErrors++;
4866 }
4867 } else if (xmlStrEqual(cur->name, BAD_CAST "datatypeLibrary")) {
4868 xmlChar *val;
4869 xmlURIPtr uri;
4870
4871 val = xmlNodeListGetString(node->doc, cur->children, 1);
4872 if (val != NULL) {
4873 if (val[0] != 0) {
4874 uri = xmlParseURI((const char *) val);
4875 if (uri == NULL) {
4876 if (ctxt->error != NULL)
4877 ctxt->error(ctxt->userData,
4878 "Attribute %s contains invalid URI %s\n",
4879 cur->name, val);
4880 ctxt->nbErrors++;
4881 } else {
4882 if (uri->scheme == NULL) {
4883 if (ctxt->error != NULL)
4884 ctxt->error(ctxt->userData,
4885 "Attribute %s URI %s is not absolute\n",
4886 cur->name, val);
4887 ctxt->nbErrors++;
4888 }
4889 if (uri->fragment != NULL) {
4890 if (ctxt->error != NULL)
4891 ctxt->error(ctxt->userData,
4892 "Attribute %s URI %s has a fragment ID\n",
4893 cur->name, val);
4894 ctxt->nbErrors++;
4895 }
4896 xmlFreeURI(uri);
4897 }
4898 }
4899 xmlFree(val);
4900 }
4901 } else if (!xmlStrEqual(cur->name, BAD_CAST "ns")) {
4902 if (ctxt->error != NULL)
4903 ctxt->error(ctxt->userData,
4904 "Unknown attribute %s on %s\n",
4905 cur->name, node->name);
4906 ctxt->nbErrors++;
4907 }
4908 }
4909 cur = next;
4910 }
4911}
4912
4913/**
Daniel Veillardc5312d72003-02-21 17:14:10 +00004914 * xmlRelaxNGCleanupTree:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004915 * @ctxt: a Relax-NG parser context
Daniel Veillardc5312d72003-02-21 17:14:10 +00004916 * @root: an xmlNodePtr subtree
Daniel Veillard6eadf632003-01-23 18:29:16 +00004917 *
Daniel Veillardc5312d72003-02-21 17:14:10 +00004918 * Cleanup the subtree from unwanted nodes for parsing, resolve
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004919 * Include and externalRef lookups.
Daniel Veillard6eadf632003-01-23 18:29:16 +00004920 */
Daniel Veillardc5312d72003-02-21 17:14:10 +00004921static void
4922xmlRelaxNGCleanupTree(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr root) {
4923 xmlNodePtr cur, delete;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004924
Daniel Veillard6eadf632003-01-23 18:29:16 +00004925 delete = NULL;
4926 cur = root;
4927 while (cur != NULL) {
4928 if (delete != NULL) {
4929 xmlUnlinkNode(delete);
4930 xmlFreeNode(delete);
4931 delete = NULL;
4932 }
4933 if (cur->type == XML_ELEMENT_NODE) {
4934 /*
4935 * Simplification 4.1. Annotations
4936 */
4937 if ((cur->ns == NULL) ||
4938 (!xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
Daniel Veillardd2298792003-02-14 16:54:11 +00004939 if ((cur->parent != NULL) &&
4940 (cur->parent->type == XML_ELEMENT_NODE) &&
4941 ((xmlStrEqual(cur->parent->name, BAD_CAST "name")) ||
4942 (xmlStrEqual(cur->parent->name, BAD_CAST "value")) ||
4943 (xmlStrEqual(cur->parent->name, BAD_CAST "param")))) {
4944 if (ctxt->error != NULL)
4945 ctxt->error(ctxt->userData,
4946 "element %s doesn't allow foreign elements\n",
4947 cur->parent->name);
4948 ctxt->nbErrors++;
4949 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004950 delete = cur;
4951 goto skip_children;
4952 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00004953 xmlRelaxNGCleanupAttributes(ctxt, cur);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004954 if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004955 xmlChar *href, *ns, *base, *URL;
4956 xmlRelaxNGDocumentPtr docu;
Daniel Veillard416589a2003-02-17 17:25:42 +00004957 xmlNodePtr tmp;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004958
4959 ns = xmlGetProp(cur, BAD_CAST "ns");
Daniel Veillard416589a2003-02-17 17:25:42 +00004960 if (ns == NULL) {
4961 tmp = cur->parent;
4962 while ((tmp != NULL) &&
4963 (tmp->type == XML_ELEMENT_NODE)) {
4964 ns = xmlGetProp(tmp, BAD_CAST "ns");
4965 if (ns != NULL)
4966 break;
4967 tmp = tmp->parent;
4968 }
4969 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004970 href = xmlGetProp(cur, BAD_CAST "href");
4971 if (href == NULL) {
4972 if (ctxt->error != NULL)
4973 ctxt->error(ctxt->userData,
4974 "xmlRelaxNGParse: externalRef has no href attribute\n");
4975 ctxt->nbErrors++;
4976 delete = cur;
4977 goto skip_children;
4978 }
4979 base = xmlNodeGetBase(cur->doc, cur);
4980 URL = xmlBuildURI(href, base);
4981 if (URL == NULL) {
4982 if (ctxt->error != NULL)
4983 ctxt->error(ctxt->userData,
4984 "Failed to compute URL for externalRef %s\n", href);
4985 ctxt->nbErrors++;
4986 if (href != NULL)
4987 xmlFree(href);
4988 if (base != NULL)
4989 xmlFree(base);
4990 delete = cur;
4991 goto skip_children;
4992 }
4993 if (href != NULL)
4994 xmlFree(href);
4995 if (base != NULL)
4996 xmlFree(base);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004997 docu = xmlRelaxNGLoadExternalRef(ctxt, URL, ns);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004998 if (docu == NULL) {
4999 if (ctxt->error != NULL)
5000 ctxt->error(ctxt->userData,
5001 "Failed to load externalRef %s\n", URL);
5002 ctxt->nbErrors++;
5003 xmlFree(URL);
5004 delete = cur;
5005 goto skip_children;
5006 }
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00005007 if (ns != NULL)
5008 xmlFree(ns);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005009 xmlFree(URL);
5010 cur->_private = docu;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005011 } else if (xmlStrEqual(cur->name, BAD_CAST "include")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005012 xmlChar *href, *ns, *base, *URL;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005013 xmlRelaxNGIncludePtr incl;
Daniel Veillard416589a2003-02-17 17:25:42 +00005014 xmlNodePtr tmp;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005015
5016 href = xmlGetProp(cur, BAD_CAST "href");
5017 if (href == NULL) {
5018 if (ctxt->error != NULL)
5019 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005020 "xmlRelaxNGParse: include has no href attribute\n");
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005021 ctxt->nbErrors++;
5022 delete = cur;
5023 goto skip_children;
5024 }
5025 base = xmlNodeGetBase(cur->doc, cur);
5026 URL = xmlBuildURI(href, base);
5027 if (URL == NULL) {
5028 if (ctxt->error != NULL)
5029 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005030 "Failed to compute URL for include %s\n", href);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005031 ctxt->nbErrors++;
5032 if (href != NULL)
5033 xmlFree(href);
5034 if (base != NULL)
5035 xmlFree(base);
5036 delete = cur;
5037 goto skip_children;
5038 }
5039 if (href != NULL)
5040 xmlFree(href);
5041 if (base != NULL)
5042 xmlFree(base);
Daniel Veillard416589a2003-02-17 17:25:42 +00005043 ns = xmlGetProp(cur, BAD_CAST "ns");
5044 if (ns == NULL) {
5045 tmp = cur->parent;
5046 while ((tmp != NULL) &&
5047 (tmp->type == XML_ELEMENT_NODE)) {
5048 ns = xmlGetProp(tmp, BAD_CAST "ns");
5049 if (ns != NULL)
5050 break;
5051 tmp = tmp->parent;
5052 }
5053 }
5054 incl = xmlRelaxNGLoadInclude(ctxt, URL, cur, ns);
5055 if (ns != NULL)
5056 xmlFree(ns);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005057 if (incl == NULL) {
5058 if (ctxt->error != NULL)
5059 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005060 "Failed to load include %s\n", URL);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005061 ctxt->nbErrors++;
5062 xmlFree(URL);
5063 delete = cur;
5064 goto skip_children;
5065 }
5066 xmlFree(URL);
5067 cur->_private = incl;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005068 } else if ((xmlStrEqual(cur->name, BAD_CAST "element")) ||
5069 (xmlStrEqual(cur->name, BAD_CAST "attribute"))) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00005070 xmlChar *name, *ns;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005071 xmlNodePtr text = NULL;
5072
5073 /*
5074 * Simplification 4.8. name attribute of element
5075 * and attribute elements
5076 */
5077 name = xmlGetProp(cur, BAD_CAST "name");
5078 if (name != NULL) {
5079 if (cur->children == NULL) {
5080 text = xmlNewChild(cur, cur->ns, BAD_CAST "name",
5081 name);
5082 } else {
5083 xmlNodePtr node;
5084 node = xmlNewNode(cur->ns, BAD_CAST "name");
5085 if (node != NULL) {
5086 xmlAddPrevSibling(cur->children, node);
5087 text = xmlNewText(name);
5088 xmlAddChild(node, text);
5089 text = node;
5090 }
5091 }
Daniel Veillardfebcca42003-02-16 15:44:18 +00005092 if (text == NULL) {
5093 if (ctxt->error != NULL)
5094 ctxt->error(ctxt->userData,
5095 "Failed to create a name %s element\n", name);
5096 ctxt->nbErrors++;
5097 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005098 xmlUnsetProp(cur, BAD_CAST "name");
5099 xmlFree(name);
Daniel Veillardfebcca42003-02-16 15:44:18 +00005100 ns = xmlGetProp(cur, BAD_CAST "ns");
5101 if (ns != NULL) {
5102 if (text != NULL) {
5103 xmlSetProp(text, BAD_CAST "ns", ns);
5104 /* xmlUnsetProp(cur, BAD_CAST "ns"); */
Daniel Veillard6eadf632003-01-23 18:29:16 +00005105 }
Daniel Veillardfebcca42003-02-16 15:44:18 +00005106 xmlFree(ns);
5107 } else if (xmlStrEqual(cur->name,
5108 BAD_CAST "attribute")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005109 xmlSetProp(text, BAD_CAST "ns", BAD_CAST "");
5110 }
5111 }
5112 } else if ((xmlStrEqual(cur->name, BAD_CAST "name")) ||
5113 (xmlStrEqual(cur->name, BAD_CAST "nsName")) ||
5114 (xmlStrEqual(cur->name, BAD_CAST "value"))) {
5115 /*
5116 * Simplification 4.8. name attribute of element
5117 * and attribute elements
5118 */
5119 if (xmlHasProp(cur, BAD_CAST "ns") == NULL) {
5120 xmlNodePtr node;
5121 xmlChar *ns = NULL;
5122
5123 node = cur->parent;
5124 while ((node != NULL) &&
5125 (node->type == XML_ELEMENT_NODE)) {
5126 ns = xmlGetProp(node, BAD_CAST "ns");
5127 if (ns != NULL) {
5128 break;
5129 }
5130 node = node->parent;
5131 }
5132 if (ns == NULL) {
5133 xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
5134 } else {
5135 xmlSetProp(cur, BAD_CAST "ns", ns);
5136 xmlFree(ns);
5137 }
5138 }
5139 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
5140 xmlChar *name, *local, *prefix;
5141
5142 /*
5143 * Simplification: 4.10. QNames
5144 */
5145 name = xmlNodeGetContent(cur);
5146 if (name != NULL) {
5147 local = xmlSplitQName2(name, &prefix);
5148 if (local != NULL) {
5149 xmlNsPtr ns;
5150
5151 ns = xmlSearchNs(cur->doc, cur, prefix);
5152 if (ns == NULL) {
5153 if (ctxt->error != NULL)
5154 ctxt->error(ctxt->userData,
5155 "xmlRelaxNGParse: no namespace for prefix %s\n", prefix);
5156 ctxt->nbErrors++;
5157 } else {
5158 xmlSetProp(cur, BAD_CAST "ns", ns->href);
5159 xmlNodeSetContent(cur, local);
5160 }
5161 xmlFree(local);
5162 xmlFree(prefix);
5163 }
5164 xmlFree(name);
5165 }
5166 }
Daniel Veillard44e1dd02003-02-21 23:23:28 +00005167 /*
5168 * 4.16
5169 */
Daniel Veillardc5312d72003-02-21 17:14:10 +00005170 if (xmlStrEqual(cur->name, BAD_CAST "nsName")) {
5171 if (ctxt->flags & XML_RELAXNG_IN_NSEXCEPT) {
5172 if (ctxt->error != NULL)
5173 ctxt->error(ctxt->userData,
5174 "Found nsName/except//nsName forbidden construct\n");
5175 ctxt->nbErrors++;
5176 }
5177 }
5178 } else if ((xmlStrEqual(cur->name, BAD_CAST "except")) &&
5179 (cur != root)) {
5180 int oldflags = ctxt->flags;
5181
Daniel Veillard44e1dd02003-02-21 23:23:28 +00005182 /*
5183 * 4.16
5184 */
Daniel Veillardc5312d72003-02-21 17:14:10 +00005185 if ((cur->parent != NULL) &&
5186 (xmlStrEqual(cur->parent->name, BAD_CAST "anyName"))) {
5187 ctxt->flags |= XML_RELAXNG_IN_ANYEXCEPT;
5188 xmlRelaxNGCleanupTree(ctxt, cur);
5189 ctxt->flags = oldflags;
5190 goto skip_children;
5191 } else if ((cur->parent != NULL) &&
5192 (xmlStrEqual(cur->parent->name, BAD_CAST "nsName"))) {
5193 ctxt->flags |= XML_RELAXNG_IN_NSEXCEPT;
5194 xmlRelaxNGCleanupTree(ctxt, cur);
5195 ctxt->flags = oldflags;
5196 goto skip_children;
5197 }
5198 } else if (xmlStrEqual(cur->name, BAD_CAST "anyName")) {
Daniel Veillard44e1dd02003-02-21 23:23:28 +00005199 /*
5200 * 4.16
5201 */
Daniel Veillardc5312d72003-02-21 17:14:10 +00005202 if (ctxt->flags & XML_RELAXNG_IN_ANYEXCEPT) {
5203 if (ctxt->error != NULL)
5204 ctxt->error(ctxt->userData,
5205 "Found anyName/except//anyName forbidden construct\n");
5206 ctxt->nbErrors++;
5207 } else if (ctxt->flags & XML_RELAXNG_IN_NSEXCEPT) {
5208 if (ctxt->error != NULL)
5209 ctxt->error(ctxt->userData,
5210 "Found nsName/except//anyName forbidden construct\n");
5211 ctxt->nbErrors++;
5212 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005213 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005214 /*
5215 * Thisd is not an else since "include" is transformed
5216 * into a div
5217 */
5218 if (xmlStrEqual(cur->name, BAD_CAST "div")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005219 xmlChar *ns;
5220 xmlNodePtr child, ins, tmp;
5221
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005222 /*
5223 * implements rule 4.11
5224 */
Daniel Veillard416589a2003-02-17 17:25:42 +00005225
5226 ns = xmlGetProp(cur, BAD_CAST "ns");
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005227
5228 child = cur->children;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005229 ins = cur;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005230 while (child != NULL) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005231 if (ns != NULL) {
5232 if (!xmlHasProp(child, BAD_CAST "ns")) {
5233 xmlSetProp(child, BAD_CAST "ns", ns);
5234 }
5235 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005236 tmp = child->next;
5237 xmlUnlinkNode(child);
5238 ins = xmlAddNextSibling(ins, child);
5239 child = tmp;
5240 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005241 if (ns != NULL)
5242 xmlFree(ns);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005243 delete = cur;
5244 goto skip_children;
5245 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005246 }
5247 }
5248 /*
5249 * Simplification 4.2 whitespaces
5250 */
5251 else if (cur->type == XML_TEXT_NODE) {
5252 if (IS_BLANK_NODE(cur)) {
5253 if (cur->parent->type == XML_ELEMENT_NODE) {
5254 if ((!xmlStrEqual(cur->parent->name, BAD_CAST "value")) &&
5255 (!xmlStrEqual(cur->parent->name, BAD_CAST "param")))
5256 delete = cur;
5257 } else {
5258 delete = cur;
5259 goto skip_children;
5260 }
5261 }
5262 } else if (cur->type != XML_CDATA_SECTION_NODE) {
5263 delete = cur;
5264 goto skip_children;
5265 }
5266
5267 /*
5268 * Skip to next node
5269 */
5270 if (cur->children != NULL) {
5271 if ((cur->children->type != XML_ENTITY_DECL) &&
5272 (cur->children->type != XML_ENTITY_REF_NODE) &&
5273 (cur->children->type != XML_ENTITY_NODE)) {
5274 cur = cur->children;
5275 continue;
5276 }
5277 }
5278skip_children:
5279 if (cur->next != NULL) {
5280 cur = cur->next;
5281 continue;
5282 }
5283
5284 do {
5285 cur = cur->parent;
5286 if (cur == NULL)
5287 break;
5288 if (cur == root) {
5289 cur = NULL;
5290 break;
5291 }
5292 if (cur->next != NULL) {
5293 cur = cur->next;
5294 break;
5295 }
5296 } while (cur != NULL);
5297 }
5298 if (delete != NULL) {
5299 xmlUnlinkNode(delete);
5300 xmlFreeNode(delete);
5301 delete = NULL;
5302 }
Daniel Veillardc5312d72003-02-21 17:14:10 +00005303}
Daniel Veillard6eadf632003-01-23 18:29:16 +00005304
Daniel Veillardc5312d72003-02-21 17:14:10 +00005305/**
5306 * xmlRelaxNGCleanupDoc:
5307 * @ctxt: a Relax-NG parser context
5308 * @doc: an xmldocPtr document pointer
5309 *
5310 * Cleanup the document from unwanted nodes for parsing, resolve
5311 * Include and externalRef lookups.
5312 *
5313 * Returns the cleaned up document or NULL in case of error
5314 */
5315static xmlDocPtr
5316xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt, xmlDocPtr doc) {
5317 xmlNodePtr root;
5318
5319 /*
5320 * Extract the root
5321 */
5322 root = xmlDocGetRootElement(doc);
5323 if (root == NULL) {
5324 if (ctxt->error != NULL)
5325 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
5326 ctxt->URL);
5327 ctxt->nbErrors++;
5328 return (NULL);
5329 }
5330 xmlRelaxNGCleanupTree(ctxt, root);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005331 return(doc);
5332}
5333
5334/**
5335 * xmlRelaxNGParse:
5336 * @ctxt: a Relax-NG parser context
5337 *
5338 * parse a schema definition resource and build an internal
5339 * XML Shema struture which can be used to validate instances.
5340 * *WARNING* this interface is highly subject to change
5341 *
5342 * Returns the internal XML RelaxNG structure built from the resource or
5343 * NULL in case of error
5344 */
5345xmlRelaxNGPtr
5346xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt)
5347{
5348 xmlRelaxNGPtr ret = NULL;
5349 xmlDocPtr doc;
5350 xmlNodePtr root;
5351
5352 xmlRelaxNGInitTypes();
5353
5354 if (ctxt == NULL)
5355 return (NULL);
5356
5357 /*
5358 * First step is to parse the input document into an DOM/Infoset
5359 */
5360 if (ctxt->URL != NULL) {
5361 doc = xmlParseFile((const char *) ctxt->URL);
5362 if (doc == NULL) {
5363 if (ctxt->error != NULL)
5364 ctxt->error(ctxt->userData,
5365 "xmlRelaxNGParse: could not load %s\n", ctxt->URL);
5366 ctxt->nbErrors++;
5367 return (NULL);
5368 }
5369 } else if (ctxt->buffer != NULL) {
5370 doc = xmlParseMemory(ctxt->buffer, ctxt->size);
5371 if (doc == NULL) {
5372 if (ctxt->error != NULL)
5373 ctxt->error(ctxt->userData,
5374 "xmlRelaxNGParse: could not parse schemas\n");
5375 ctxt->nbErrors++;
5376 return (NULL);
5377 }
5378 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
5379 ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
5380 } else {
5381 if (ctxt->error != NULL)
5382 ctxt->error(ctxt->userData,
5383 "xmlRelaxNGParse: nothing to parse\n");
5384 ctxt->nbErrors++;
5385 return (NULL);
5386 }
5387 ctxt->document = doc;
5388
5389 /*
5390 * Some preprocessing of the document content
5391 */
5392 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
5393 if (doc == NULL) {
5394 xmlFreeDoc(ctxt->document);
5395 ctxt->document = NULL;
5396 return(NULL);
5397 }
5398
Daniel Veillard6eadf632003-01-23 18:29:16 +00005399 /*
5400 * Then do the parsing for good
5401 */
5402 root = xmlDocGetRootElement(doc);
5403 if (root == NULL) {
5404 if (ctxt->error != NULL)
5405 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
5406 ctxt->URL);
5407 ctxt->nbErrors++;
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00005408 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005409 return (NULL);
5410 }
5411 ret = xmlRelaxNGParseDocument(ctxt, root);
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00005412 if (ret == NULL) {
5413 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005414 return(NULL);
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00005415 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005416
5417 /*
5418 * Check the ref/defines links
5419 */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005420 /*
5421 * try to preprocess interleaves
5422 */
5423 if (ctxt->interleaves != NULL) {
5424 xmlHashScan(ctxt->interleaves,
5425 (xmlHashScanner)xmlRelaxNGComputeInterleaves, ctxt);
5426 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005427
5428 /*
5429 * if there was a parsing error return NULL
5430 */
5431 if (ctxt->nbErrors > 0) {
5432 xmlRelaxNGFree(ret);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005433 ctxt->document = NULL;
5434 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005435 return(NULL);
5436 }
5437
5438 /*
5439 * Transfer the pointer for cleanup at the schema level.
5440 */
5441 ret->doc = doc;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005442 ctxt->document = NULL;
5443 ret->documents = ctxt->documents;
5444 ctxt->documents = NULL;
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00005445
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005446 ret->includes = ctxt->includes;
5447 ctxt->includes = NULL;
Daniel Veillard419a7682003-02-03 23:22:49 +00005448 ret->defNr = ctxt->defNr;
5449 ret->defTab = ctxt->defTab;
5450 ctxt->defTab = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005451
5452 return (ret);
5453}
5454
5455/**
5456 * xmlRelaxNGSetParserErrors:
5457 * @ctxt: a Relax-NG validation context
5458 * @err: the error callback
5459 * @warn: the warning callback
5460 * @ctx: contextual data for the callbacks
5461 *
5462 * Set the callback functions used to handle errors for a validation context
5463 */
5464void
5465xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
5466 xmlRelaxNGValidityErrorFunc err,
5467 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
5468 if (ctxt == NULL)
5469 return;
5470 ctxt->error = err;
5471 ctxt->warning = warn;
5472 ctxt->userData = ctx;
5473}
5474/************************************************************************
5475 * *
5476 * Dump back a compiled form *
5477 * *
5478 ************************************************************************/
5479static void xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define);
5480
5481/**
5482 * xmlRelaxNGDumpDefines:
5483 * @output: the file output
5484 * @defines: a list of define structures
5485 *
5486 * Dump a RelaxNG structure back
5487 */
5488static void
5489xmlRelaxNGDumpDefines(FILE * output, xmlRelaxNGDefinePtr defines) {
5490 while (defines != NULL) {
5491 xmlRelaxNGDumpDefine(output, defines);
5492 defines = defines->next;
5493 }
5494}
5495
5496/**
5497 * xmlRelaxNGDumpDefine:
5498 * @output: the file output
5499 * @define: a define structure
5500 *
5501 * Dump a RelaxNG structure back
5502 */
5503static void
5504xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define) {
5505 if (define == NULL)
5506 return;
5507 switch(define->type) {
5508 case XML_RELAXNG_EMPTY:
5509 fprintf(output, "<empty/>\n");
5510 break;
5511 case XML_RELAXNG_NOT_ALLOWED:
5512 fprintf(output, "<notAllowed/>\n");
5513 break;
5514 case XML_RELAXNG_TEXT:
5515 fprintf(output, "<text/>\n");
5516 break;
5517 case XML_RELAXNG_ELEMENT:
5518 fprintf(output, "<element>\n");
5519 if (define->name != NULL) {
5520 fprintf(output, "<name");
5521 if (define->ns != NULL)
5522 fprintf(output, " ns=\"%s\"", define->ns);
5523 fprintf(output, ">%s</name>\n", define->name);
5524 }
5525 xmlRelaxNGDumpDefines(output, define->attrs);
5526 xmlRelaxNGDumpDefines(output, define->content);
5527 fprintf(output, "</element>\n");
5528 break;
5529 case XML_RELAXNG_LIST:
5530 fprintf(output, "<list>\n");
5531 xmlRelaxNGDumpDefines(output, define->content);
5532 fprintf(output, "</list>\n");
5533 break;
5534 case XML_RELAXNG_ONEORMORE:
5535 fprintf(output, "<oneOrMore>\n");
5536 xmlRelaxNGDumpDefines(output, define->content);
5537 fprintf(output, "</oneOrMore>\n");
5538 break;
5539 case XML_RELAXNG_ZEROORMORE:
5540 fprintf(output, "<zeroOrMore>\n");
5541 xmlRelaxNGDumpDefines(output, define->content);
5542 fprintf(output, "</zeroOrMore>\n");
5543 break;
5544 case XML_RELAXNG_CHOICE:
5545 fprintf(output, "<choice>\n");
5546 xmlRelaxNGDumpDefines(output, define->content);
5547 fprintf(output, "</choice>\n");
5548 break;
5549 case XML_RELAXNG_GROUP:
5550 fprintf(output, "<group>\n");
5551 xmlRelaxNGDumpDefines(output, define->content);
5552 fprintf(output, "</group>\n");
5553 break;
5554 case XML_RELAXNG_INTERLEAVE:
5555 fprintf(output, "<interleave>\n");
5556 xmlRelaxNGDumpDefines(output, define->content);
5557 fprintf(output, "</interleave>\n");
5558 break;
5559 case XML_RELAXNG_OPTIONAL:
5560 fprintf(output, "<optional>\n");
5561 xmlRelaxNGDumpDefines(output, define->content);
5562 fprintf(output, "</optional>\n");
5563 break;
5564 case XML_RELAXNG_ATTRIBUTE:
5565 fprintf(output, "<attribute>\n");
5566 xmlRelaxNGDumpDefines(output, define->content);
5567 fprintf(output, "</attribute>\n");
5568 break;
5569 case XML_RELAXNG_DEF:
5570 fprintf(output, "<define");
5571 if (define->name != NULL)
5572 fprintf(output, " name=\"%s\"", define->name);
5573 fprintf(output, ">\n");
5574 xmlRelaxNGDumpDefines(output, define->content);
5575 fprintf(output, "</define>\n");
5576 break;
5577 case XML_RELAXNG_REF:
5578 fprintf(output, "<ref");
5579 if (define->name != NULL)
5580 fprintf(output, " name=\"%s\"", define->name);
5581 fprintf(output, ">\n");
5582 xmlRelaxNGDumpDefines(output, define->content);
5583 fprintf(output, "</ref>\n");
5584 break;
Daniel Veillard419a7682003-02-03 23:22:49 +00005585 case XML_RELAXNG_PARENTREF:
5586 fprintf(output, "<parentRef");
5587 if (define->name != NULL)
5588 fprintf(output, " name=\"%s\"", define->name);
5589 fprintf(output, ">\n");
5590 xmlRelaxNGDumpDefines(output, define->content);
5591 fprintf(output, "</parentRef>\n");
5592 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005593 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard416589a2003-02-17 17:25:42 +00005594 fprintf(output, "<externalRef>");
Daniel Veillarde431a272003-01-29 23:02:33 +00005595 xmlRelaxNGDumpDefines(output, define->content);
5596 fprintf(output, "</externalRef>\n");
5597 break;
5598 case XML_RELAXNG_DATATYPE:
Daniel Veillard6eadf632003-01-23 18:29:16 +00005599 case XML_RELAXNG_VALUE:
5600 TODO
5601 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005602 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00005603 case XML_RELAXNG_EXCEPT:
Daniel Veillard8fe98712003-02-19 00:19:14 +00005604 case XML_RELAXNG_PARAM:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005605 TODO
5606 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00005607 case XML_RELAXNG_NOOP:
5608 xmlRelaxNGDumpDefines(output, define->content);
5609 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005610 }
5611}
5612
5613/**
5614 * xmlRelaxNGDumpGrammar:
5615 * @output: the file output
5616 * @grammar: a grammar structure
5617 * @top: is this a top grammar
5618 *
5619 * Dump a RelaxNG structure back
5620 */
5621static void
5622xmlRelaxNGDumpGrammar(FILE * output, xmlRelaxNGGrammarPtr grammar, int top)
5623{
5624 if (grammar == NULL)
5625 return;
5626
5627 fprintf(output, "<grammar");
5628 if (top)
5629 fprintf(output,
5630 " xmlns=\"http://relaxng.org/ns/structure/1.0\"");
5631 switch(grammar->combine) {
5632 case XML_RELAXNG_COMBINE_UNDEFINED:
5633 break;
5634 case XML_RELAXNG_COMBINE_CHOICE:
5635 fprintf(output, " combine=\"choice\"");
5636 break;
5637 case XML_RELAXNG_COMBINE_INTERLEAVE:
5638 fprintf(output, " combine=\"interleave\"");
5639 break;
5640 default:
5641 fprintf(output, " <!-- invalid combine value -->");
5642 }
5643 fprintf(output, ">\n");
5644 if (grammar->start == NULL) {
5645 fprintf(output, " <!-- grammar had no start -->");
5646 } else {
5647 fprintf(output, "<start>\n");
5648 xmlRelaxNGDumpDefine(output, grammar->start);
5649 fprintf(output, "</start>\n");
5650 }
5651 /* TODO ? Dump the defines ? */
5652 fprintf(output, "</grammar>\n");
5653}
5654
5655/**
5656 * xmlRelaxNGDump:
5657 * @output: the file output
5658 * @schema: a schema structure
5659 *
5660 * Dump a RelaxNG structure back
5661 */
5662void
5663xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema)
5664{
5665 if (schema == NULL) {
5666 fprintf(output, "RelaxNG empty or failed to compile\n");
5667 return;
5668 }
5669 fprintf(output, "RelaxNG: ");
5670 if (schema->doc == NULL) {
5671 fprintf(output, "no document\n");
5672 } else if (schema->doc->URL != NULL) {
5673 fprintf(output, "%s\n", schema->doc->URL);
5674 } else {
5675 fprintf(output, "\n");
5676 }
5677 if (schema->topgrammar == NULL) {
5678 fprintf(output, "RelaxNG has no top grammar\n");
5679 return;
5680 }
5681 xmlRelaxNGDumpGrammar(output, schema->topgrammar, 1);
5682}
5683
Daniel Veillardfebcca42003-02-16 15:44:18 +00005684/**
5685 * xmlRelaxNGDumpTree:
5686 * @output: the file output
5687 * @schema: a schema structure
5688 *
5689 * Dump the transformed RelaxNG tree.
5690 */
5691void
5692xmlRelaxNGDumpTree(FILE * output, xmlRelaxNGPtr schema)
5693{
5694 if (schema == NULL) {
5695 fprintf(output, "RelaxNG empty or failed to compile\n");
5696 return;
5697 }
5698 if (schema->doc == NULL) {
5699 fprintf(output, "no document\n");
5700 } else {
5701 xmlDocDump(output, schema->doc);
5702 }
5703}
5704
Daniel Veillard6eadf632003-01-23 18:29:16 +00005705/************************************************************************
5706 * *
5707 * Validation implementation *
5708 * *
5709 ************************************************************************/
5710static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
5711 xmlRelaxNGDefinePtr define);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005712static int xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
5713 xmlRelaxNGDefinePtr define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005714
5715/**
5716 * xmlRelaxNGSkipIgnored:
5717 * @ctxt: a schema validation context
5718 * @node: the top node.
5719 *
5720 * Skip ignorable nodes in that context
5721 *
5722 * Returns the new sibling or NULL in case of error.
5723 */
5724static xmlNodePtr
5725xmlRelaxNGSkipIgnored(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
5726 xmlNodePtr node) {
5727 /*
5728 * TODO complete and handle entities
5729 */
5730 while ((node != NULL) &&
5731 ((node->type == XML_COMMENT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005732 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00005733 ((node->type == XML_TEXT_NODE) &&
5734 (IS_BLANK_NODE(node))))) {
5735 node = node->next;
5736 }
5737 return(node);
5738}
5739
5740/**
Daniel Veillardedc91922003-01-26 00:52:04 +00005741 * xmlRelaxNGNormalize:
5742 * @ctxt: a schema validation context
5743 * @str: the string to normalize
5744 *
5745 * Implements the normalizeWhiteSpace( s ) function from
5746 * section 6.2.9 of the spec
5747 *
5748 * Returns the new string or NULL in case of error.
5749 */
5750static xmlChar *
5751xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *str) {
5752 xmlChar *ret, *p;
5753 const xmlChar *tmp;
5754 int len;
5755
5756 if (str == NULL)
5757 return(NULL);
5758 tmp = str;
5759 while (*tmp != 0) tmp++;
5760 len = tmp - str;
5761
5762 ret = (xmlChar *) xmlMalloc((len + 1) * sizeof(xmlChar));
5763 if (ret == NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00005764 if (ctxt != NULL) {
5765 VALID_CTXT();
5766 VALID_ERROR("xmlRelaxNGNormalize: out of memory\n");
5767 } else {
5768 xmlGenericError(xmlGenericErrorContext,
5769 "xmlRelaxNGNormalize: out of memory\n");
5770 }
Daniel Veillardedc91922003-01-26 00:52:04 +00005771 return(NULL);
5772 }
5773 p = ret;
5774 while (IS_BLANK(*str)) str++;
5775 while (*str != 0) {
5776 if (IS_BLANK(*str)) {
5777 while (IS_BLANK(*str)) str++;
5778 if (*str == 0)
5779 break;
5780 *p++ = ' ';
5781 } else
5782 *p++ = *str++;
5783 }
5784 *p = 0;
5785 return(ret);
5786}
5787
5788/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005789 * xmlRelaxNGValidateDatatype:
5790 * @ctxt: a Relax-NG validation context
5791 * @value: the string value
5792 * @type: the datatype definition
5793 *
5794 * Validate the given value against the dataype
5795 *
5796 * Returns 0 if the validation succeeded or an error code.
5797 */
5798static int
5799xmlRelaxNGValidateDatatype(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *value,
5800 xmlRelaxNGDefinePtr define) {
5801 int ret;
5802 xmlRelaxNGTypeLibraryPtr lib;
5803
5804 if ((define == NULL) || (define->data == NULL)) {
5805 return(-1);
5806 }
5807 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
5808 if (lib->check != NULL)
5809 ret = lib->check(lib->data, define->name, value);
5810 else
5811 ret = -1;
5812 if (ret < 0) {
5813 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005814 VALID_ERROR2("Internal: failed to validate type %s\n", define->name);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005815 return(-1);
5816 } else if (ret == 1) {
5817 ret = 0;
5818 } else {
5819 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005820 VALID_ERROR3("Type %s doesn't allow value %s\n", define->name, value);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005821 return(-1);
5822 ret = -1;
5823 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005824 if ((ret == 0) && (define->content != NULL)) {
5825 const xmlChar *oldvalue, *oldendvalue;
5826
5827 oldvalue = ctxt->state->value;
5828 oldendvalue = ctxt->state->endvalue;
5829 ctxt->state->value = (xmlChar *) value;
5830 ctxt->state->endvalue = NULL;
5831 ret = xmlRelaxNGValidateValue(ctxt, define->content);
5832 ctxt->state->value = (xmlChar *) oldvalue;
5833 ctxt->state->endvalue = (xmlChar *) oldendvalue;
5834 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005835 return(ret);
5836}
5837
5838/**
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005839 * xmlRelaxNGNextValue:
5840 * @ctxt: a Relax-NG validation context
5841 *
5842 * Skip to the next value when validating within a list
5843 *
5844 * Returns 0 if the operation succeeded or an error code.
5845 */
5846static int
5847xmlRelaxNGNextValue(xmlRelaxNGValidCtxtPtr ctxt) {
5848 xmlChar *cur;
5849
5850 cur = ctxt->state->value;
5851 if ((cur == NULL) || (ctxt->state->endvalue == NULL)) {
5852 ctxt->state->value = NULL;
Daniel Veillarde5b110b2003-02-04 14:43:39 +00005853 ctxt->state->endvalue = NULL;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005854 return(0);
5855 }
5856 while (*cur != 0) cur++;
5857 while ((cur != ctxt->state->endvalue) && (*cur == 0)) cur++;
5858 if (cur == ctxt->state->endvalue)
5859 ctxt->state->value = NULL;
5860 else
5861 ctxt->state->value = cur;
5862 return(0);
5863}
5864
5865/**
5866 * xmlRelaxNGValidateValueList:
5867 * @ctxt: a Relax-NG validation context
5868 * @defines: the list of definitions to verify
5869 *
5870 * Validate the given set of definitions for the current value
5871 *
5872 * Returns 0 if the validation succeeded or an error code.
5873 */
5874static int
5875xmlRelaxNGValidateValueList(xmlRelaxNGValidCtxtPtr ctxt,
5876 xmlRelaxNGDefinePtr defines) {
5877 int ret = 0;
5878
5879 while (defines != NULL) {
5880 ret = xmlRelaxNGValidateValue(ctxt, defines);
5881 if (ret != 0)
5882 break;
5883 defines = defines->next;
5884 }
5885 return(ret);
5886}
5887
5888/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00005889 * xmlRelaxNGValidateValue:
5890 * @ctxt: a Relax-NG validation context
5891 * @define: the definition to verify
5892 *
5893 * Validate the given definition for the current value
5894 *
5895 * Returns 0 if the validation succeeded or an error code.
5896 */
5897static int
5898xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
5899 xmlRelaxNGDefinePtr define) {
Daniel Veillardedc91922003-01-26 00:52:04 +00005900 int ret = 0, oldflags;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005901 xmlChar *value;
5902
5903 value = ctxt->state->value;
5904 switch (define->type) {
Daniel Veillardd4310742003-02-18 21:12:46 +00005905 case XML_RELAXNG_EMPTY: {
5906 if ((value != NULL) && (value[0] != 0)) {
5907 int idx = 0;
5908
5909 while (IS_BLANK(value[idx]))
5910 idx++;
5911 if (value[idx] != 0)
5912 ret = -1;
5913 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005914 break;
Daniel Veillardd4310742003-02-18 21:12:46 +00005915 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005916 case XML_RELAXNG_TEXT:
5917 break;
Daniel Veillardedc91922003-01-26 00:52:04 +00005918 case XML_RELAXNG_VALUE: {
5919 if (!xmlStrEqual(value, define->value)) {
5920 if (define->name != NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00005921 xmlRelaxNGTypeLibraryPtr lib;
5922
5923 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
5924 if ((lib != NULL) && (lib->comp != NULL))
5925 ret = lib->comp(lib->data, define->name, value,
5926 define->value);
5927 else
5928 ret = -1;
5929 if (ret < 0) {
5930 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005931 VALID_ERROR2("Internal: failed to compare type %s\n",
Daniel Veillardea3f3982003-01-26 19:45:18 +00005932 define->name);
5933 return(-1);
5934 } else if (ret == 1) {
5935 ret = 0;
5936 } else {
5937 ret = -1;
5938 }
Daniel Veillardedc91922003-01-26 00:52:04 +00005939 } else {
5940 xmlChar *nval, *nvalue;
5941
5942 /*
5943 * TODO: trivial optimizations are possible by
5944 * computing at compile-time
5945 */
5946 nval = xmlRelaxNGNormalize(ctxt, define->value);
5947 nvalue = xmlRelaxNGNormalize(ctxt, value);
5948
Daniel Veillardea3f3982003-01-26 19:45:18 +00005949 if ((nval == NULL) || (nvalue == NULL) ||
5950 (!xmlStrEqual(nval, nvalue)))
Daniel Veillardedc91922003-01-26 00:52:04 +00005951 ret = -1;
5952 if (nval != NULL)
5953 xmlFree(nval);
5954 if (nvalue != NULL)
5955 xmlFree(nvalue);
5956 }
5957 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005958 if (ret == 0)
5959 xmlRelaxNGNextValue(ctxt);
Daniel Veillardedc91922003-01-26 00:52:04 +00005960 break;
5961 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005962 case XML_RELAXNG_DATATYPE: {
5963 ret = xmlRelaxNGValidateDatatype(ctxt, value, define);
5964 if (ret == 0)
5965 xmlRelaxNGNextValue(ctxt);
5966
5967 break;
5968 }
5969 case XML_RELAXNG_CHOICE: {
5970 xmlRelaxNGDefinePtr list = define->content;
5971 xmlChar *oldvalue;
5972
5973 oldflags = ctxt->flags;
5974 ctxt->flags |= FLAGS_IGNORABLE;
5975
5976 oldvalue = ctxt->state->value;
5977 while (list != NULL) {
5978 ret = xmlRelaxNGValidateValue(ctxt, list);
5979 if (ret == 0) {
5980 break;
5981 }
5982 ctxt->state->value = oldvalue;
5983 list = list->next;
5984 }
5985 ctxt->flags = oldflags;
Daniel Veillard416589a2003-02-17 17:25:42 +00005986 if (ret == 0)
5987 xmlRelaxNGNextValue(ctxt);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005988 break;
5989 }
5990 case XML_RELAXNG_LIST: {
5991 xmlRelaxNGDefinePtr list = define->content;
5992 xmlChar *oldvalue, *oldend, *val, *cur;
Daniel Veillard416589a2003-02-17 17:25:42 +00005993#ifdef DEBUG_LIST
5994 int nb_values = 0;
5995#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005996
5997 oldvalue = ctxt->state->value;
5998 oldend = ctxt->state->endvalue;
5999
6000 val = xmlStrdup(oldvalue);
6001 if (val == NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00006002 val = xmlStrdup(BAD_CAST "");
6003 }
6004 if (val == NULL) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006005 VALID_CTXT();
6006 VALID_ERROR("Internal: no state\n");
6007 return(-1);
6008 }
6009 cur = val;
6010 while (*cur != 0) {
Daniel Veillard416589a2003-02-17 17:25:42 +00006011 if (IS_BLANK(*cur)) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006012 *cur = 0;
Daniel Veillard416589a2003-02-17 17:25:42 +00006013 cur++;
6014#ifdef DEBUG_LIST
6015 nb_values++;
6016#endif
6017 while (IS_BLANK(*cur))
6018 *cur++ = 0;
6019 } else
6020 cur++;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006021 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006022#ifdef DEBUG_LIST
6023 xmlGenericError(xmlGenericErrorContext,
6024 "list value: '%s' found %d items\n", oldvalue, nb_values);
6025 nb_values = 0;
6026#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006027 ctxt->state->endvalue = cur;
6028 cur = val;
6029 while ((*cur == 0) && (cur != ctxt->state->endvalue)) cur++;
6030
6031 ctxt->state->value = cur;
6032
6033 while (list != NULL) {
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006034 if (ctxt->state->value == ctxt->state->endvalue)
6035 ctxt->state->value = NULL;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006036 ret = xmlRelaxNGValidateValue(ctxt, list);
6037 if (ret != 0) {
Daniel Veillard416589a2003-02-17 17:25:42 +00006038#ifdef DEBUG_LIST
6039 xmlGenericError(xmlGenericErrorContext,
6040 "Failed to validate value: '%s' with %d rule\n",
6041 ctxt->state->value, nb_values);
6042#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006043 break;
6044 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006045#ifdef DEBUG_LIST
6046 nb_values++;
6047#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006048 list = list->next;
6049 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006050
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006051 if ((ret == 0) && (ctxt->state->value != NULL) &&
6052 (ctxt->state->value != ctxt->state->endvalue)) {
6053 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006054 VALID_ERROR2("Extra data in list: %s\n", ctxt->state->value);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006055 ret = -1;
6056 }
6057 xmlFree(val);
6058 ctxt->state->value = oldvalue;
6059 ctxt->state->endvalue = oldend;
6060 break;
6061 }
6062 case XML_RELAXNG_ONEORMORE:
6063 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
6064 if (ret != 0) {
6065 break;
6066 }
6067 /* no break on purpose */
6068 case XML_RELAXNG_ZEROORMORE: {
6069 xmlChar *cur, *temp;
6070
6071 oldflags = ctxt->flags;
6072 ctxt->flags |= FLAGS_IGNORABLE;
6073 cur = ctxt->state->value;
6074 temp = NULL;
6075 while ((cur != NULL) && (cur != ctxt->state->endvalue) &&
6076 (temp != cur)) {
6077 temp = cur;
6078 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
6079 if (ret != 0) {
6080 ctxt->state->value = temp;
6081 ret = 0;
6082 break;
6083 }
6084 cur = ctxt->state->value;
6085 }
6086 ctxt->flags = oldflags;
6087 break;
6088 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006089 case XML_RELAXNG_EXCEPT: {
6090 xmlRelaxNGDefinePtr list;
6091
6092 list = define->content;
6093 while (list != NULL) {
6094 ret = xmlRelaxNGValidateValue(ctxt, list);
6095 if (ret == 0) {
6096 ret = -1;
6097 break;
6098 } else
6099 ret = 0;
6100 list = list->next;
6101 }
6102 break;
6103 }
Daniel Veillardd4310742003-02-18 21:12:46 +00006104 case XML_RELAXNG_GROUP: {
6105 xmlRelaxNGDefinePtr list;
6106
6107 list = define->content;
6108 while (list != NULL) {
6109 ret = xmlRelaxNGValidateValue(ctxt, list);
6110 if (ret != 0) {
6111 ret = -1;
6112 break;
6113 } else
6114 ret = 0;
6115 list = list->next;
6116 }
6117 break;
6118 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006119 default:
6120 TODO
6121 ret = -1;
6122 }
6123 return(ret);
6124}
6125
6126/**
6127 * xmlRelaxNGValidateValueContent:
6128 * @ctxt: a Relax-NG validation context
6129 * @defines: the list of definitions to verify
6130 *
6131 * Validate the given definitions for the current value
6132 *
6133 * Returns 0 if the validation succeeded or an error code.
6134 */
6135static int
6136xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt,
6137 xmlRelaxNGDefinePtr defines) {
6138 int ret = 0;
6139
6140 while (defines != NULL) {
6141 ret = xmlRelaxNGValidateValue(ctxt, defines);
6142 if (ret != 0)
6143 break;
6144 defines = defines->next;
6145 }
6146 return(ret);
6147}
6148
6149/**
Daniel Veillard144fae12003-02-03 13:17:57 +00006150 * xmlRelaxNGAttributeMatch:
6151 * @ctxt: a Relax-NG validation context
6152 * @define: the definition to check
6153 * @prop: the attribute
6154 *
6155 * Check if the attribute matches the definition nameClass
6156 *
6157 * Returns 1 if the attribute matches, 0 if no, or -1 in case of error
6158 */
6159static int
6160xmlRelaxNGAttributeMatch(xmlRelaxNGValidCtxtPtr ctxt,
6161 xmlRelaxNGDefinePtr define,
6162 xmlAttrPtr prop) {
6163 int ret;
6164
6165 if (define->name != NULL) {
6166 if (!xmlStrEqual(define->name, prop->name))
6167 return(0);
6168 }
6169 if (define->ns != NULL) {
6170 if (define->ns[0] == 0) {
6171 if (prop->ns != NULL)
6172 return(0);
6173 } else {
6174 if ((prop->ns == NULL) ||
6175 (!xmlStrEqual(define->ns, prop->ns->href)))
6176 return(0);
6177 }
6178 }
6179 if (define->nameClass == NULL)
6180 return(1);
6181 define = define->nameClass;
6182 if (define->type == XML_RELAXNG_EXCEPT) {
6183 xmlRelaxNGDefinePtr list;
6184
6185 list = define->content;
6186 while (list != NULL) {
6187 ret = xmlRelaxNGAttributeMatch(ctxt, list, prop);
6188 if (ret == 1)
6189 return(0);
6190 if (ret < 0)
6191 return(ret);
6192 list = list->next;
6193 }
6194 } else {
6195 TODO
6196 }
6197 return(1);
6198}
6199
6200/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00006201 * xmlRelaxNGValidateAttribute:
6202 * @ctxt: a Relax-NG validation context
6203 * @define: the definition to verify
6204 *
6205 * Validate the given attribute definition for that node
6206 *
6207 * Returns 0 if the validation succeeded or an error code.
6208 */
6209static int
6210xmlRelaxNGValidateAttribute(xmlRelaxNGValidCtxtPtr ctxt,
6211 xmlRelaxNGDefinePtr define) {
6212 int ret = 0, i;
6213 xmlChar *value, *oldvalue;
6214 xmlAttrPtr prop = NULL, tmp;
6215
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006216 if (ctxt->state->nbAttrLeft <= 0)
6217 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006218 if (define->name != NULL) {
6219 for (i = 0;i < ctxt->state->nbAttrs;i++) {
6220 tmp = ctxt->state->attrs[i];
6221 if ((tmp != NULL) && (xmlStrEqual(define->name, tmp->name))) {
6222 if ((((define->ns == NULL) || (define->ns[0] == 0)) &&
6223 (tmp->ns == NULL)) ||
6224 ((tmp->ns != NULL) &&
6225 (xmlStrEqual(define->ns, tmp->ns->href)))) {
6226 prop = tmp;
6227 break;
6228 }
6229 }
6230 }
6231 if (prop != NULL) {
6232 value = xmlNodeListGetString(prop->doc, prop->children, 1);
6233 oldvalue = ctxt->state->value;
6234 ctxt->state->value = value;
Daniel Veillard231d7912003-02-09 14:22:17 +00006235 ctxt->state->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006236 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00006237 if (ctxt->state->value != NULL)
6238 value = ctxt->state->value;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006239 if (value != NULL)
6240 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00006241 ctxt->state->value = oldvalue;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006242 if (ret == 0) {
6243 /*
6244 * flag the attribute as processed
6245 */
6246 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006247 ctxt->state->nbAttrLeft--;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006248 }
6249 } else {
6250 ret = -1;
6251 }
6252#ifdef DEBUG
6253 xmlGenericError(xmlGenericErrorContext,
6254 "xmlRelaxNGValidateAttribute(%s): %d\n", define->name, ret);
6255#endif
6256 } else {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006257 for (i = 0;i < ctxt->state->nbAttrs;i++) {
6258 tmp = ctxt->state->attrs[i];
Daniel Veillard144fae12003-02-03 13:17:57 +00006259 if ((tmp != NULL) &&
6260 (xmlRelaxNGAttributeMatch(ctxt, define, tmp) == 1)) {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006261 prop = tmp;
6262 break;
6263 }
6264 }
6265 if (prop != NULL) {
6266 value = xmlNodeListGetString(prop->doc, prop->children, 1);
6267 oldvalue = ctxt->state->value;
6268 ctxt->state->value = value;
6269 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00006270 if (ctxt->state->value != NULL)
6271 value = ctxt->state->value;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006272 if (value != NULL)
6273 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00006274 ctxt->state->value = oldvalue;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006275 if (ret == 0) {
6276 /*
6277 * flag the attribute as processed
6278 */
6279 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006280 ctxt->state->nbAttrLeft--;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006281 }
6282 } else {
6283 ret = -1;
6284 }
6285#ifdef DEBUG
Daniel Veillard144fae12003-02-03 13:17:57 +00006286 if (define->ns != NULL) {
6287 xmlGenericError(xmlGenericErrorContext,
6288 "xmlRelaxNGValidateAttribute(nsName ns = %s): %d\n",
6289 define->ns, ret);
6290 } else {
6291 xmlGenericError(xmlGenericErrorContext,
6292 "xmlRelaxNGValidateAttribute(anyName): %d\n",
6293 ret);
6294 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006295#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00006296 }
6297
6298 return(ret);
6299}
6300
6301/**
6302 * xmlRelaxNGValidateAttributeList:
6303 * @ctxt: a Relax-NG validation context
6304 * @define: the list of definition to verify
6305 *
6306 * Validate the given node against the list of attribute definitions
6307 *
6308 * Returns 0 if the validation succeeded or an error code.
6309 */
6310static int
6311xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt,
6312 xmlRelaxNGDefinePtr defines) {
6313 int ret = 0;
6314 while (defines != NULL) {
6315 if (xmlRelaxNGValidateAttribute(ctxt, defines) != 0)
6316 ret = -1;
6317 defines = defines->next;
6318 }
6319 return(ret);
6320}
6321
6322/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006323 * xmlRelaxNGNodeMatchesList:
6324 * @node: the node
6325 * @list: a NULL terminated array of definitions
6326 *
6327 * Check if a node can be matched by one of the definitions
6328 *
6329 * Returns 1 if matches 0 otherwise
6330 */
6331static int
6332xmlRelaxNGNodeMatchesList(xmlNodePtr node, xmlRelaxNGDefinePtr *list) {
6333 xmlRelaxNGDefinePtr cur;
6334 int i = 0;
6335
6336 if ((node == NULL) || (list == NULL))
6337 return(0);
6338
6339 cur = list[i++];
6340 while (cur != NULL) {
6341 if ((node->type == XML_ELEMENT_NODE) &&
6342 (cur->type == XML_RELAXNG_ELEMENT)) {
6343 if (cur->name == NULL) {
6344 if ((node->ns != NULL) &&
6345 (xmlStrEqual(node->ns->href, cur->ns)))
6346 return(1);
6347 } else if (xmlStrEqual(cur->name, node->name)) {
6348 if ((cur->ns == NULL) || (cur->ns[0] == 0)) {
6349 if (node->ns == NULL)
6350 return(1);
6351 } else {
6352 if ((node->ns != NULL) &&
6353 (xmlStrEqual(node->ns->href, cur->ns)))
6354 return(1);
6355 }
6356 }
6357 } else if ((node->type == XML_TEXT_NODE) &&
6358 (cur->type == XML_RELAXNG_TEXT)) {
6359 return(1);
6360 }
6361 cur = list[i++];
6362 }
6363 return(0);
6364}
6365
6366/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006367 * xmlRelaxNGValidateInterleave:
6368 * @ctxt: a Relax-NG validation context
6369 * @define: the definition to verify
6370 *
6371 * Validate an interleave definition for a node.
6372 *
6373 * Returns 0 if the validation succeeded or an error code.
6374 */
6375static int
6376xmlRelaxNGValidateInterleave(xmlRelaxNGValidCtxtPtr ctxt,
6377 xmlRelaxNGDefinePtr define) {
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006378 int ret = 0, i, nbgroups, left;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006379 xmlRelaxNGPartitionPtr partitions;
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006380 xmlRelaxNGInterleaveGroupPtr group = NULL;
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00006381 xmlNodePtr cur, start, last = NULL, lastchg = NULL, lastelem;
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006382 xmlNodePtr *list = NULL, *lasts = NULL;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006383
6384 if (define->data != NULL) {
6385 partitions = (xmlRelaxNGPartitionPtr) define->data;
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006386 nbgroups = partitions->nbgroups;
6387 left = nbgroups;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006388 } else {
6389 VALID_CTXT();
6390 VALID_ERROR("Internal: interleave block has no data\n");
6391 return(-1);
6392 }
6393
6394 /*
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006395 * Build arrays to store the first and last node of the chain
6396 * pertaining to each group
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006397 */
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006398 list = (xmlNodePtr *) xmlMalloc(nbgroups * sizeof(xmlNodePtr));
6399 if (list == NULL) {
6400 VALID_CTXT();
6401 VALID_ERROR("Internal: out of memory in interleave check\n");
6402 return(-1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006403 }
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006404 memset(list, 0, nbgroups * sizeof(xmlNodePtr));
6405 lasts = (xmlNodePtr *) xmlMalloc(nbgroups * sizeof(xmlNodePtr));
6406 if (lasts == NULL) {
6407 VALID_CTXT();
6408 VALID_ERROR("Internal: out of memory in interleave check\n");
6409 return(-1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006410 }
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006411 memset(lasts, 0, nbgroups * sizeof(xmlNodePtr));
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006412
6413 /*
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006414 * Walk the sequence of children finding the right group and
6415 * sorting them in sequences.
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006416 */
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006417 cur = ctxt->state->seq;
6418 cur = xmlRelaxNGSkipIgnored(ctxt, cur);
6419 start = cur;
6420 while (cur != NULL) {
6421 ctxt->state->seq = cur;
6422 for (i = 0;i < nbgroups;i++) {
6423 group = partitions->groups[i];
6424 if (group == NULL)
6425 continue;
6426 if (xmlRelaxNGNodeMatchesList(cur, group->defs))
6427 break;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006428 }
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006429 /*
6430 * We break as soon as an element not matched is found
6431 */
6432 if (i >= nbgroups) {
6433 break;
6434 }
6435 if (lasts[i] != NULL) {
6436 lasts[i]->next = cur;
6437 lasts[i] = cur;
6438 } else {
6439 list[i] = cur;
6440 lasts[i] = cur;
6441 }
6442 if (cur->next != NULL)
6443 lastchg = cur->next;
6444 else
6445 lastchg = cur;
6446 cur = xmlRelaxNGSkipIgnored(ctxt, cur->next);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006447 }
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006448 if (ret != 0) {
6449 VALID_CTXT();
6450 VALID_ERROR("Invalid sequence in interleave\n");
6451 ret = -1;
6452 goto done;
6453 }
6454 lastelem = cur;
6455 for (i = 0;i < nbgroups;i++) {
6456 group = partitions->groups[i];
6457 if (lasts[i] != NULL) {
6458 last = lasts[i]->next;
6459 lasts[i]->next = NULL;
6460 }
6461 ctxt->state->seq = list[i];
6462 ret = xmlRelaxNGValidateDefinition(ctxt, group->rule);
6463 if (ret != 0)
6464 break;
6465 cur = ctxt->state->seq;
6466 cur = xmlRelaxNGSkipIgnored(ctxt, cur);
6467 if (cur != NULL) {
6468 VALID_CTXT();
6469 VALID_ERROR2("Extra element %s in interleave\n", cur->name);
6470 ret = -1;
6471 goto done;
6472 }
6473 if (lasts[i] != NULL) {
6474 lasts[i]->next = last;
6475 }
6476 }
6477 ctxt->state->seq = lastelem;
6478 if (ret != 0) {
6479 VALID_CTXT();
6480 VALID_ERROR("Invalid sequence in interleave\n");
6481 ret = -1;
6482 goto done;
6483 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006484
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006485done:
6486 /*
6487 * builds the next links chain from the prev one
6488 */
6489 cur = lastchg;
6490 while (cur != NULL) {
6491 if ((cur == start) || (cur->prev == NULL))
6492 break;
6493 cur->prev->next = cur;
6494 cur = cur->prev;
6495 }
6496
6497 xmlFree(list);
6498 xmlFree(lasts);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006499 return(ret);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006500}
6501
6502/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00006503 * xmlRelaxNGValidateElementContent:
6504 * @ctxt: a Relax-NG validation context
6505 * @define: the list of definition to verify
6506 *
6507 * Validate the given node content against the (list) of definitions
6508 *
6509 * Returns 0 if the validation succeeded or an error code.
6510 */
6511static int
6512xmlRelaxNGValidateElementContent(xmlRelaxNGValidCtxtPtr ctxt,
6513 xmlRelaxNGDefinePtr defines) {
6514 int ret = 0, res;
6515
6516 if (ctxt->state == NULL) {
6517 VALID_CTXT();
6518 VALID_ERROR("Internal: no state\n");
6519 return(-1);
6520 }
6521 while (defines != NULL) {
6522 res = xmlRelaxNGValidateDefinition(ctxt, defines);
6523 if (res < 0)
6524 ret = -1;
6525 defines = defines->next;
6526 }
6527
6528 return(ret);
6529}
6530
6531/**
Daniel Veillard416589a2003-02-17 17:25:42 +00006532 * xmlRelaxNGElementMatch:
6533 * @ctxt: a Relax-NG validation context
6534 * @define: the definition to check
6535 * @elem: the element
6536 *
6537 * Check if the element matches the definition nameClass
6538 *
6539 * Returns 1 if the element matches, 0 if no, or -1 in case of error
6540 */
6541static int
6542xmlRelaxNGElementMatch(xmlRelaxNGValidCtxtPtr ctxt,
6543 xmlRelaxNGDefinePtr define,
6544 xmlNodePtr elem) {
6545 int ret, oldflags;
6546
6547 if (define->name != NULL) {
6548 if (!xmlStrEqual(elem->name, define->name)) {
6549 VALID_CTXT();
6550 VALID_ERROR3("Expecting element %s, got %s\n",
6551 define->name, elem->name);
6552 return(0);
6553 }
6554 }
6555 if ((define->ns != NULL) && (define->ns[0] != 0)) {
6556 if (elem->ns == NULL) {
6557 VALID_CTXT();
6558 VALID_ERROR2("Expecting a namespace for element %s\n",
6559 elem->name);
6560 return(0);
6561 } else if (!xmlStrEqual(elem->ns->href, define->ns)) {
6562 VALID_CTXT();
6563 VALID_ERROR3("Expecting element %s has wrong namespace: expecting %s\n",
6564 elem->name, define->ns);
6565 return(0);
6566 }
Daniel Veillard8fe98712003-02-19 00:19:14 +00006567 } else if ((elem->ns != NULL) && (define->ns != NULL) &&
6568 (define->name == NULL)) {
6569 VALID_CTXT();
6570 VALID_ERROR2("Expecting no namespace for element %s\n",
6571 define->name);
6572 return(0);
6573 } else if ((elem->ns != NULL) && (define->name != NULL)) {
6574 VALID_CTXT();
6575 VALID_ERROR2("Expecting no namespace for element %s\n",
6576 define->name);
6577 return(0);
Daniel Veillard416589a2003-02-17 17:25:42 +00006578 }
6579
6580 if (define->nameClass == NULL)
6581 return(1);
6582
6583 define = define->nameClass;
6584 if (define->type == XML_RELAXNG_EXCEPT) {
6585 xmlRelaxNGDefinePtr list;
6586 oldflags = ctxt->flags;
6587 ctxt->flags |= FLAGS_IGNORABLE;
6588
6589 list = define->content;
6590 while (list != NULL) {
6591 ret = xmlRelaxNGElementMatch(ctxt, list, elem);
6592 if (ret == 1) {
6593 ctxt->flags = oldflags;
6594 return(0);
6595 }
6596 if (ret < 0) {
6597 ctxt->flags = oldflags;
6598 return(ret);
6599 }
6600 list = list->next;
6601 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006602 ret = 1;
6603 ctxt->flags = oldflags;
6604 } else if (define->type == XML_RELAXNG_CHOICE) {
6605 xmlRelaxNGDefinePtr list;
6606 oldflags = ctxt->flags;
6607 ctxt->flags |= FLAGS_IGNORABLE;
6608
6609 list = define->nameClass;
6610 while (list != NULL) {
6611 ret = xmlRelaxNGElementMatch(ctxt, list, elem);
6612 if (ret == 1) {
6613 ctxt->flags = oldflags;
6614 return(1);
6615 }
6616 if (ret < 0) {
6617 ctxt->flags = oldflags;
6618 return(ret);
6619 }
6620 list = list->next;
6621 }
6622 ret = 0;
Daniel Veillard416589a2003-02-17 17:25:42 +00006623 ctxt->flags = oldflags;
6624 } else {
6625 TODO
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006626 ret = -1;
Daniel Veillard416589a2003-02-17 17:25:42 +00006627 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006628 return(ret);
Daniel Veillard416589a2003-02-17 17:25:42 +00006629}
6630
6631/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00006632 * xmlRelaxNGValidateDefinition:
6633 * @ctxt: a Relax-NG validation context
6634 * @define: the definition to verify
6635 *
6636 * Validate the current node against the definition
6637 *
6638 * Returns 0 if the validation succeeded or an error code.
6639 */
6640static int
6641xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
6642 xmlRelaxNGDefinePtr define) {
6643 xmlNodePtr node;
6644 int ret = 0, i, tmp, oldflags;
6645 xmlRelaxNGValidStatePtr oldstate, state;
6646
6647 if (define == NULL) {
6648 VALID_CTXT();
6649 VALID_ERROR("internal error: define == NULL\n");
6650 return(-1);
6651 }
6652 if (ctxt->state != NULL) {
6653 node = ctxt->state->seq;
6654 } else {
6655 node = NULL;
6656 }
Daniel Veillard231d7912003-02-09 14:22:17 +00006657#ifdef DEBUG
6658 for (i = 0;i < ctxt->depth;i++)
6659 xmlGenericError(xmlGenericErrorContext, " ");
6660 xmlGenericError(xmlGenericErrorContext,
6661 "Start validating %s ", xmlRelaxNGDefName(define));
6662 if (define->name != NULL)
6663 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
6664 if ((node != NULL) && (node->name != NULL))
6665 xmlGenericError(xmlGenericErrorContext, "on %s\n", node->name);
6666 else
6667 xmlGenericError(xmlGenericErrorContext, "\n");
6668#endif
6669 ctxt->depth++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006670 switch (define->type) {
6671 case XML_RELAXNG_EMPTY:
Daniel Veillardd4310742003-02-18 21:12:46 +00006672 node = xmlRelaxNGSkipIgnored(ctxt, node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006673 if (node != NULL) {
6674 VALID_CTXT();
6675 VALID_ERROR("Expecting an empty element\n");
Daniel Veillard231d7912003-02-09 14:22:17 +00006676 ret = -1;
6677 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006678 }
Daniel Veillard231d7912003-02-09 14:22:17 +00006679 ret = 0;
6680 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006681 case XML_RELAXNG_NOT_ALLOWED:
Daniel Veillard231d7912003-02-09 14:22:17 +00006682 ret = -1;
6683 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006684 case XML_RELAXNG_TEXT:
Daniel Veillardce14fa52003-02-19 17:32:48 +00006685#if 0
Daniel Veillard231d7912003-02-09 14:22:17 +00006686 if (node == NULL) {
6687 ret = 0;
6688 break;
6689 }
Daniel Veillardce14fa52003-02-19 17:32:48 +00006690#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00006691 while ((node != NULL) &&
6692 ((node->type == XML_TEXT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006693 (node->type == XML_COMMENT_NODE) ||
6694 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00006695 (node->type == XML_CDATA_SECTION_NODE)))
6696 node = node->next;
Daniel Veillardce14fa52003-02-19 17:32:48 +00006697#if 0
Daniel Veillard276be4a2003-01-24 01:03:34 +00006698 if (node == ctxt->state->seq) {
6699 VALID_CTXT();
6700 VALID_ERROR("Expecting text content\n");
6701 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006702 }
Daniel Veillardce14fa52003-02-19 17:32:48 +00006703#endif
Daniel Veillard276be4a2003-01-24 01:03:34 +00006704 ctxt->state->seq = node;
6705 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006706 case XML_RELAXNG_ELEMENT:
6707 node = xmlRelaxNGSkipIgnored(ctxt, node);
Daniel Veillard231d7912003-02-09 14:22:17 +00006708 if (node == NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006709 VALID_CTXT();
Daniel Veillard231d7912003-02-09 14:22:17 +00006710 VALID_ERROR("Expecting an element, got empty\n");
6711 ret = -1;
6712 break;
6713 }
6714 if (node->type != XML_ELEMENT_NODE) {
6715 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006716 VALID_ERROR2("Expecting an element got %d type\n", node->type);
Daniel Veillard231d7912003-02-09 14:22:17 +00006717 ret = -1;
6718 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006719 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00006720 /*
6721 * This node was already validated successfully against
6722 * this definition.
6723 */
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006724 if (node->_private == define) {
6725 ctxt->state->seq = xmlRelaxNGSkipIgnored(ctxt, node->next);
Daniel Veillarde5b110b2003-02-04 14:43:39 +00006726 break;
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006727 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006728
6729 ret = xmlRelaxNGElementMatch(ctxt, define, node);
6730 if (ret <= 0) {
6731 ret = -1;
6732 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006733 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006734 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006735
6736 state = xmlRelaxNGNewValidState(ctxt, node);
6737 if (state == NULL) {
Daniel Veillard231d7912003-02-09 14:22:17 +00006738 ret = -1;
6739 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006740 }
6741
6742 oldstate = ctxt->state;
6743 ctxt->state = state;
6744 if (define->attrs != NULL) {
6745 tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
Daniel Veillard231d7912003-02-09 14:22:17 +00006746 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006747 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00006748#ifdef DEBUG
6749 xmlGenericError(xmlGenericErrorContext,
6750 "E: Element %s failed to validate attributes\n",
6751 node->name);
6752#endif
6753 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006754 }
6755 if (define->content != NULL) {
6756 tmp = xmlRelaxNGValidateElementContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00006757 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006758 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00006759#ifdef DEBUG
6760 xmlGenericError(xmlGenericErrorContext,
6761 "E: Element %s failed to validate element content\n",
6762 node->name);
6763#endif
6764 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006765 }
6766 state = ctxt->state;
6767 if (state->seq != NULL) {
6768 state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq);
6769 if (state->seq != NULL) {
6770 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006771 VALID_ERROR3("Extra content for element %s: %s\n",
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006772 node->name, state->seq->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006773 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00006774#ifdef DEBUG
6775 xmlGenericError(xmlGenericErrorContext,
6776 "E: Element %s has extra content: %s\n",
6777 node->name, state->seq->name);
6778#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00006779 }
6780 }
6781 for (i = 0;i < state->nbAttrs;i++) {
6782 if (state->attrs[i] != NULL) {
6783 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006784 VALID_ERROR3("Invalid attribute %s for element %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00006785 state->attrs[i]->name, node->name);
6786 ret = -1;
6787 }
6788 }
6789 ctxt->state = oldstate;
6790 xmlRelaxNGFreeValidState(state);
6791 if (oldstate != NULL)
Daniel Veillarde5b110b2003-02-04 14:43:39 +00006792 oldstate->seq = xmlRelaxNGSkipIgnored(ctxt, node->next);
6793 if (ret == 0)
6794 node->_private = define;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006795
6796
6797#ifdef DEBUG
6798 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard231d7912003-02-09 14:22:17 +00006799 "xmlRelaxNGValidateDefinition(): validated %s : %d",
Daniel Veillard6eadf632003-01-23 18:29:16 +00006800 node->name, ret);
Daniel Veillard231d7912003-02-09 14:22:17 +00006801 if (oldstate == NULL)
6802 xmlGenericError(xmlGenericErrorContext, ": no state\n");
6803 else if (oldstate->seq == NULL)
6804 xmlGenericError(xmlGenericErrorContext, ": done\n");
6805 else if (oldstate->seq->type == XML_ELEMENT_NODE)
6806 xmlGenericError(xmlGenericErrorContext, ": next elem %s\n",
6807 oldstate->seq->name);
6808 else
6809 xmlGenericError(xmlGenericErrorContext, ": next %s %d\n",
6810 oldstate->seq->name, oldstate->seq->type);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006811#endif
6812 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006813 case XML_RELAXNG_OPTIONAL:
6814 oldflags = ctxt->flags;
6815 ctxt->flags |= FLAGS_IGNORABLE;
6816 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6817 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6818 if (ret != 0) {
6819 xmlRelaxNGFreeValidState(ctxt->state);
6820 ctxt->state = oldstate;
6821 ret = 0;
6822 break;
6823 }
6824 xmlRelaxNGFreeValidState(oldstate);
6825 ctxt->flags = oldflags;
6826 ret = 0;
6827 break;
6828 case XML_RELAXNG_ONEORMORE:
6829 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6830 if (ret != 0) {
6831 break;
6832 }
6833 /* no break on purpose */
Daniel Veillard276be4a2003-01-24 01:03:34 +00006834 case XML_RELAXNG_ZEROORMORE: {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006835 oldflags = ctxt->flags;
6836 ctxt->flags |= FLAGS_IGNORABLE;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006837 while (ctxt->state->nbAttrLeft != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006838 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6839 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6840 if (ret != 0) {
6841 xmlRelaxNGFreeValidState(ctxt->state);
6842 ctxt->state = oldstate;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006843 break;
6844 }
6845 xmlRelaxNGFreeValidState(oldstate);
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006846 }
6847 if (ret == 0) {
6848 /*
6849 * There is no attribute left to be consumed,
6850 * we can check the closure by looking at ctxt->state->seq
6851 */
6852 xmlNodePtr cur, temp;
6853
Daniel Veillard276be4a2003-01-24 01:03:34 +00006854 cur = ctxt->state->seq;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006855 temp = NULL;
6856 while ((cur != NULL) && (temp != cur)) {
6857 temp = cur;
6858 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6859 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6860 if (ret != 0) {
6861 xmlRelaxNGFreeValidState(ctxt->state);
6862 ctxt->state = oldstate;
6863 break;
6864 }
6865 xmlRelaxNGFreeValidState(oldstate);
6866 cur = ctxt->state->seq;
6867 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006868 }
6869 ctxt->flags = oldflags;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006870 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006871 break;
Daniel Veillard276be4a2003-01-24 01:03:34 +00006872 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006873 case XML_RELAXNG_CHOICE: {
6874 xmlRelaxNGDefinePtr list = define->content;
Daniel Veillardce14fa52003-02-19 17:32:48 +00006875 int success = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006876
6877 oldflags = ctxt->flags;
6878 ctxt->flags |= FLAGS_IGNORABLE;
6879
6880 while (list != NULL) {
6881 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6882 ret = xmlRelaxNGValidateDefinition(ctxt, list);
6883 if (ret == 0) {
Daniel Veillardce14fa52003-02-19 17:32:48 +00006884 if (xmlRelaxNGEqualValidState(ctxt, ctxt->state, oldstate)){
6885 /*
6886 * if that pattern was nullable flag it but try
6887 * to make more progresses
6888 */
6889 success = 1;
6890 } else {
6891 xmlRelaxNGFreeValidState(oldstate);
6892 break;
6893 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006894 }
6895 xmlRelaxNGFreeValidState(ctxt->state);
6896 ctxt->state = oldstate;
6897 list = list->next;
6898 }
6899 ctxt->flags = oldflags;
Daniel Veillardce14fa52003-02-19 17:32:48 +00006900 if (success == 1)
6901 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006902 break;
6903 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00006904 case XML_RELAXNG_DEF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00006905 case XML_RELAXNG_GROUP: {
6906 xmlRelaxNGDefinePtr list = define->content;
6907
6908 while (list != NULL) {
6909 ret = xmlRelaxNGValidateDefinition(ctxt, list);
6910 if (ret != 0)
6911 break;
6912 list = list->next;
6913 }
6914 break;
6915 }
6916 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006917 ret = xmlRelaxNGValidateInterleave(ctxt, define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006918 break;
6919 case XML_RELAXNG_ATTRIBUTE:
6920 ret = xmlRelaxNGValidateAttribute(ctxt, define);
6921 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00006922 case XML_RELAXNG_NOOP:
Daniel Veillard6eadf632003-01-23 18:29:16 +00006923 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00006924 case XML_RELAXNG_PARENTREF:
6925 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00006926 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6927 break;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006928 case XML_RELAXNG_DATATYPE: {
Daniel Veillardd4310742003-02-18 21:12:46 +00006929 xmlNodePtr child;
6930 xmlChar *content = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006931
Daniel Veillardd4310742003-02-18 21:12:46 +00006932 child = node;
6933 while (child != NULL) {
6934 if (child->type == XML_ELEMENT_NODE) {
6935 VALID_CTXT();
6936 VALID_ERROR2("Element %s has child elements\n",
6937 node->parent->name);
6938 ret = -1;
6939 break;
6940 } else if ((child->type == XML_TEXT_NODE) ||
6941 (child->type == XML_CDATA_SECTION_NODE)) {
6942 content = xmlStrcat(content, child->content);
6943 }
6944 /* TODO: handle entities ... */
6945 child = child->next;
6946 }
6947 if (ret == -1) {
6948 if (content != NULL)
6949 xmlFree(content);
6950 break;
6951 }
6952 if (content == NULL) {
6953 content = xmlStrdup(BAD_CAST "");
6954 if (content == NULL) {
6955 VALID_CTXT();
6956 VALID_ERROR("Allocation failure\n");
6957 ret = -1;
6958 break;
6959 }
6960 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006961 ret = xmlRelaxNGValidateDatatype(ctxt, content, define);
6962 if (ret == -1) {
6963 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006964 VALID_ERROR2("internal error validating %s\n", define->name);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006965 } else if (ret == 0) {
Daniel Veillardd4310742003-02-18 21:12:46 +00006966 ctxt->state->seq = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006967 }
6968 if (content != NULL)
6969 xmlFree(content);
6970 break;
6971 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00006972 case XML_RELAXNG_VALUE: {
Daniel Veillardd4310742003-02-18 21:12:46 +00006973 xmlChar *content = NULL;
Daniel Veillardea3f3982003-01-26 19:45:18 +00006974 xmlChar *oldvalue;
Daniel Veillardd4310742003-02-18 21:12:46 +00006975 xmlNodePtr child;
Daniel Veillardea3f3982003-01-26 19:45:18 +00006976
Daniel Veillardd4310742003-02-18 21:12:46 +00006977 child = node;
6978 while (child != NULL) {
6979 if (child->type == XML_ELEMENT_NODE) {
6980 VALID_CTXT();
6981 VALID_ERROR2("Element %s has child elements\n",
6982 node->parent->name);
6983 ret = -1;
6984 break;
6985 } else if ((child->type == XML_TEXT_NODE) ||
6986 (child->type == XML_CDATA_SECTION_NODE)) {
6987 content = xmlStrcat(content, child->content);
6988 }
6989 /* TODO: handle entities ... */
6990 child = child->next;
6991 }
6992 if (ret == -1) {
6993 if (content != NULL)
6994 xmlFree(content);
6995 break;
6996 }
6997 if (content == NULL) {
6998 content = xmlStrdup(BAD_CAST "");
6999 if (content == NULL) {
7000 VALID_CTXT();
7001 VALID_ERROR("Allocation failure\n");
7002 ret = -1;
7003 break;
7004 }
7005 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00007006 oldvalue = ctxt->state->value;
7007 ctxt->state->value = content;
7008 ret = xmlRelaxNGValidateValue(ctxt, define);
7009 ctxt->state->value = oldvalue;
7010 if (ret == -1) {
7011 VALID_CTXT();
Daniel Veillardd2298792003-02-14 16:54:11 +00007012 if (define->name != NULL) {
7013 VALID_ERROR2("error validating value %s\n", define->name);
7014 } else {
7015 VALID_ERROR("error validating value\n");
7016 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00007017 } else if (ret == 0) {
Daniel Veillardd4310742003-02-18 21:12:46 +00007018 ctxt->state->seq = NULL;
Daniel Veillardea3f3982003-01-26 19:45:18 +00007019 }
7020 if (content != NULL)
7021 xmlFree(content);
7022 break;
7023 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007024 case XML_RELAXNG_LIST: {
7025 xmlChar *content;
Daniel Veillardd4310742003-02-18 21:12:46 +00007026 xmlNodePtr child;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007027 xmlChar *oldvalue, *oldendvalue;
7028 int len;
Daniel Veillardea3f3982003-01-26 19:45:18 +00007029
Daniel Veillardd4310742003-02-18 21:12:46 +00007030 /*
7031 * Make sure it's only text nodes
7032 */
7033
7034 content = NULL;
7035 child = node;
7036 while (child != NULL) {
7037 if (child->type == XML_ELEMENT_NODE) {
7038 VALID_CTXT();
7039 VALID_ERROR2("Element %s has child elements\n",
7040 node->parent->name);
7041 ret = -1;
7042 break;
7043 } else if ((child->type == XML_TEXT_NODE) ||
7044 (child->type == XML_CDATA_SECTION_NODE)) {
7045 content = xmlStrcat(content, child->content);
7046 }
7047 /* TODO: handle entities ... */
7048 child = child->next;
7049 }
7050 if (ret == -1) {
7051 if (content != NULL)
7052 xmlFree(content);
7053 break;
7054 }
7055 if (content == NULL) {
7056 content = xmlStrdup(BAD_CAST "");
7057 if (content == NULL) {
7058 VALID_CTXT();
7059 VALID_ERROR("Allocation failure\n");
7060 ret = -1;
7061 break;
7062 }
7063 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007064 len = xmlStrlen(content);
7065 oldvalue = ctxt->state->value;
7066 oldendvalue = ctxt->state->endvalue;
7067 ctxt->state->value = content;
7068 ctxt->state->endvalue = content + len;
7069 ret = xmlRelaxNGValidateValue(ctxt, define);
7070 ctxt->state->value = oldvalue;
7071 ctxt->state->endvalue = oldendvalue;
7072 if (ret == -1) {
7073 VALID_CTXT();
Daniel Veillard2e9b1652003-02-19 13:29:45 +00007074 VALID_ERROR("error validating list\n");
Daniel Veillardd4310742003-02-18 21:12:46 +00007075 } else if ((ret == 0) && (node != NULL)) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007076 ctxt->state->seq = node->next;
7077 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007078 if (content != NULL)
7079 xmlFree(content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00007080 break;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007081 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00007082 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00007083 case XML_RELAXNG_EXCEPT:
Daniel Veillard8fe98712003-02-19 00:19:14 +00007084 case XML_RELAXNG_PARAM:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00007085 TODO
Daniel Veillard416589a2003-02-17 17:25:42 +00007086 ret = -1;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00007087 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007088 }
Daniel Veillard231d7912003-02-09 14:22:17 +00007089 ctxt->depth--;
7090#ifdef DEBUG
7091 for (i = 0;i < ctxt->depth;i++)
7092 xmlGenericError(xmlGenericErrorContext, " ");
7093 xmlGenericError(xmlGenericErrorContext,
7094 "Validating %s ", xmlRelaxNGDefName(define));
7095 if (define->name != NULL)
7096 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
7097 if (ret == 0)
7098 xmlGenericError(xmlGenericErrorContext, "suceeded\n");
7099 else
7100 xmlGenericError(xmlGenericErrorContext, "failed\n");
7101#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00007102 return(ret);
7103}
7104
7105/**
7106 * xmlRelaxNGValidateDocument:
7107 * @ctxt: a Relax-NG validation context
7108 * @doc: the document
7109 *
7110 * Validate the given document
7111 *
7112 * Returns 0 if the validation succeeded or an error code.
7113 */
7114static int
7115xmlRelaxNGValidateDocument(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
7116 int ret;
7117 xmlRelaxNGPtr schema;
7118 xmlRelaxNGGrammarPtr grammar;
7119 xmlRelaxNGValidStatePtr state;
7120
7121 if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL))
7122 return(-1);
7123
7124 schema = ctxt->schema;
7125 grammar = schema->topgrammar;
7126 if (grammar == NULL) {
7127 VALID_CTXT();
7128 VALID_ERROR("No top grammar defined\n");
7129 return(-1);
7130 }
7131 state = xmlRelaxNGNewValidState(ctxt, NULL);
7132 ctxt->state = state;
7133 ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start);
7134 state = ctxt->state;
7135 if ((state != NULL) && (state->seq != NULL)) {
7136 xmlNodePtr node;
7137
7138 node = state->seq;
7139 node = xmlRelaxNGSkipIgnored(ctxt, node);
7140 if (node != NULL) {
7141 VALID_CTXT();
7142 VALID_ERROR("extra data on the document\n");
7143 ret = -1;
7144 }
7145 }
7146 xmlRelaxNGFreeValidState(state);
7147
7148 return(ret);
7149}
7150
7151/************************************************************************
7152 * *
7153 * Validation interfaces *
7154 * *
7155 ************************************************************************/
7156/**
7157 * xmlRelaxNGNewValidCtxt:
7158 * @schema: a precompiled XML RelaxNGs
7159 *
7160 * Create an XML RelaxNGs validation context based on the given schema
7161 *
7162 * Returns the validation context or NULL in case of error
7163 */
7164xmlRelaxNGValidCtxtPtr
7165xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema) {
7166 xmlRelaxNGValidCtxtPtr ret;
7167
7168 ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt));
7169 if (ret == NULL) {
7170 xmlGenericError(xmlGenericErrorContext,
7171 "Failed to allocate new schama validation context\n");
7172 return (NULL);
7173 }
7174 memset(ret, 0, sizeof(xmlRelaxNGValidCtxt));
7175 ret->schema = schema;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00007176 ret->error = xmlGenericError;
7177 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007178 return (ret);
7179}
7180
7181/**
7182 * xmlRelaxNGFreeValidCtxt:
7183 * @ctxt: the schema validation context
7184 *
7185 * Free the resources associated to the schema validation context
7186 */
7187void
7188xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt) {
7189 if (ctxt == NULL)
7190 return;
7191 xmlFree(ctxt);
7192}
7193
7194/**
7195 * xmlRelaxNGSetValidErrors:
7196 * @ctxt: a Relax-NG validation context
7197 * @err: the error function
7198 * @warn: the warning function
7199 * @ctx: the functions context
7200 *
7201 * Set the error and warning callback informations
7202 */
7203void
7204xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
7205 xmlRelaxNGValidityErrorFunc err,
7206 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
7207 if (ctxt == NULL)
7208 return;
7209 ctxt->error = err;
7210 ctxt->warning = warn;
7211 ctxt->userData = ctx;
7212}
7213
7214/**
7215 * xmlRelaxNGValidateDoc:
7216 * @ctxt: a Relax-NG validation context
7217 * @doc: a parsed document tree
7218 *
7219 * Validate a document tree in memory.
7220 *
7221 * Returns 0 if the document is valid, a positive error code
7222 * number otherwise and -1 in case of internal or API error.
7223 */
7224int
7225xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
7226 int ret;
7227
7228 if ((ctxt == NULL) || (doc == NULL))
7229 return(-1);
7230
7231 ctxt->doc = doc;
7232
7233 ret = xmlRelaxNGValidateDocument(ctxt, doc);
Daniel Veillard71531f32003-02-05 13:19:53 +00007234 /*
7235 * TODO: build error codes
7236 */
7237 if (ret == -1)
7238 return(1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00007239 return(ret);
7240}
7241
7242#endif /* LIBXML_SCHEMAS_ENABLED */
7243