blob: ca7d19401ace2f7f2909309f8f7fd2515167f4e9 [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 Veillard17bed982003-02-24 20:11:43 +00001722static int xmlRelaxNGElementMatch(xmlRelaxNGValidCtxtPtr ctxt,
1723 xmlRelaxNGDefinePtr define, xmlNodePtr elem);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001724
1725
1726#define IS_BLANK_NODE(n) \
1727 (((n)->type == XML_TEXT_NODE) && (xmlRelaxNGIsBlank((n)->content)))
1728
1729/**
1730 * xmlRelaxNGIsBlank:
1731 * @str: a string
1732 *
1733 * Check if a string is ignorable c.f. 4.2. Whitespace
1734 *
1735 * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
1736 */
1737static int
1738xmlRelaxNGIsBlank(xmlChar *str) {
1739 if (str == NULL)
1740 return(1);
1741 while (*str != 0) {
1742 if (!(IS_BLANK(*str))) return(0);
1743 str++;
1744 }
1745 return(1);
1746}
1747
Daniel Veillard6eadf632003-01-23 18:29:16 +00001748/**
1749 * xmlRelaxNGGetDataTypeLibrary:
1750 * @ctxt: a Relax-NG parser context
1751 * @node: the current data or value element
1752 *
1753 * Applies algorithm from 4.3. datatypeLibrary attribute
1754 *
1755 * Returns the datatypeLibary value or NULL if not found
1756 */
1757static xmlChar *
1758xmlRelaxNGGetDataTypeLibrary(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1759 xmlNodePtr node) {
1760 xmlChar *ret, *escape;
1761
Daniel Veillard6eadf632003-01-23 18:29:16 +00001762 if ((IS_RELAXNG(node, "data")) || (IS_RELAXNG(node, "value"))) {
1763 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1764 if (ret != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001765 if (ret[0] == 0) {
1766 xmlFree(ret);
1767 return(NULL);
1768 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001769 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +00001770 if (escape == NULL) {
1771 return(ret);
1772 }
1773 xmlFree(ret);
1774 return(escape);
1775 }
1776 }
1777 node = node->parent;
1778 while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) {
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001779 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1780 if (ret != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001781 if (ret[0] == 0) {
1782 xmlFree(ret);
1783 return(NULL);
1784 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001785 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
1786 if (escape == NULL) {
1787 return(ret);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001788 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001789 xmlFree(ret);
1790 return(escape);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001791 }
1792 node = node->parent;
1793 }
1794 return(NULL);
1795}
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001796
1797/**
Daniel Veillardedc91922003-01-26 00:52:04 +00001798 * xmlRelaxNGParseValue:
1799 * @ctxt: a Relax-NG parser context
1800 * @node: the data node.
1801 *
1802 * parse the content of a RelaxNG value node.
1803 *
1804 * Returns the definition pointer or NULL in case of error
1805 */
1806static xmlRelaxNGDefinePtr
1807xmlRelaxNGParseValue(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1808 xmlRelaxNGDefinePtr def = NULL;
1809 xmlRelaxNGTypeLibraryPtr lib;
1810 xmlChar *type;
1811 xmlChar *library;
1812 int tmp;
1813
1814 def = xmlRelaxNGNewDefine(ctxt, node);
1815 if (def == NULL)
1816 return(NULL);
1817 def->type = XML_RELAXNG_VALUE;
1818
1819 type = xmlGetProp(node, BAD_CAST "type");
1820 if (type != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001821 xmlRelaxNGNormExtSpace(type);
1822 if (xmlValidateNCName(type, 0)) {
1823 if (ctxt->error != NULL)
1824 ctxt->error(ctxt->userData,
1825 "value type '%s' is not an NCName\n",
1826 type);
1827 ctxt->nbErrors++;
1828 }
Daniel Veillardedc91922003-01-26 00:52:04 +00001829 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1830 if (library == NULL)
1831 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1832
1833 def->name = type;
1834 def->ns = library;
1835
1836 lib = (xmlRelaxNGTypeLibraryPtr)
1837 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1838 if (lib == NULL) {
1839 if (ctxt->error != NULL)
1840 ctxt->error(ctxt->userData,
1841 "Use of unregistered type library '%s'\n",
1842 library);
1843 ctxt->nbErrors++;
1844 def->data = NULL;
1845 } else {
1846 def->data = lib;
1847 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001848 if (ctxt->error != NULL)
1849 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00001850 "Internal error with type library '%s': no 'have'\n",
1851 library);
1852 ctxt->nbErrors++;
1853 } else {
1854 tmp = lib->have(lib->data, def->name);
1855 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001856 if (ctxt->error != NULL)
1857 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00001858 "Error type '%s' is not exported by type library '%s'\n",
1859 def->name, library);
1860 ctxt->nbErrors++;
1861 }
1862 }
1863 }
1864 }
1865 if (node->children == NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00001866 def->value = xmlStrdup(BAD_CAST "");
Daniel Veillardedc91922003-01-26 00:52:04 +00001867 } else if ((node->children->type != XML_TEXT_NODE) ||
1868 (node->children->next != NULL)) {
1869 if (ctxt->error != NULL)
1870 ctxt->error(ctxt->userData,
1871 "Expecting a single text value for <value>content\n");
1872 ctxt->nbErrors++;
1873 } else {
1874 def->value = xmlNodeGetContent(node);
1875 if (def->value == NULL) {
1876 if (ctxt->error != NULL)
1877 ctxt->error(ctxt->userData,
1878 "Element <value> has no content\n");
1879 ctxt->nbErrors++;
1880 }
1881 }
1882 /* TODO check ahead of time that the value is okay per the type */
1883 return(def);
1884}
1885
1886/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001887 * xmlRelaxNGParseData:
1888 * @ctxt: a Relax-NG parser context
1889 * @node: the data node.
1890 *
1891 * parse the content of a RelaxNG data node.
1892 *
1893 * Returns the definition pointer or NULL in case of error
1894 */
1895static xmlRelaxNGDefinePtr
1896xmlRelaxNGParseData(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
Daniel Veillard416589a2003-02-17 17:25:42 +00001897 xmlRelaxNGDefinePtr def = NULL, except, last = NULL;
Daniel Veillard8fe98712003-02-19 00:19:14 +00001898 xmlRelaxNGDefinePtr param, lastparam = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001899 xmlRelaxNGTypeLibraryPtr lib;
1900 xmlChar *type;
1901 xmlChar *library;
1902 xmlNodePtr content;
1903 int tmp;
1904
1905 type = xmlGetProp(node, BAD_CAST "type");
1906 if (type == NULL) {
1907 if (ctxt->error != NULL)
1908 ctxt->error(ctxt->userData,
1909 "data has no type\n");
1910 ctxt->nbErrors++;
1911 return(NULL);
1912 }
Daniel Veillardd2298792003-02-14 16:54:11 +00001913 xmlRelaxNGNormExtSpace(type);
1914 if (xmlValidateNCName(type, 0)) {
1915 if (ctxt->error != NULL)
1916 ctxt->error(ctxt->userData,
1917 "data type '%s' is not an NCName\n",
1918 type);
1919 ctxt->nbErrors++;
1920 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001921 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1922 if (library == NULL)
1923 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1924
1925 def = xmlRelaxNGNewDefine(ctxt, node);
1926 if (def == NULL) {
1927 xmlFree(type);
1928 return(NULL);
1929 }
1930 def->type = XML_RELAXNG_DATATYPE;
1931 def->name = type;
1932 def->ns = library;
1933
1934 lib = (xmlRelaxNGTypeLibraryPtr)
1935 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1936 if (lib == NULL) {
1937 if (ctxt->error != NULL)
1938 ctxt->error(ctxt->userData,
1939 "Use of unregistered type library '%s'\n",
1940 library);
1941 ctxt->nbErrors++;
1942 def->data = NULL;
1943 } else {
1944 def->data = lib;
1945 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001946 if (ctxt->error != NULL)
1947 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001948 "Internal error with type library '%s': no 'have'\n",
1949 library);
1950 ctxt->nbErrors++;
1951 } else {
1952 tmp = lib->have(lib->data, def->name);
1953 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001954 if (ctxt->error != NULL)
1955 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001956 "Error type '%s' is not exported by type library '%s'\n",
1957 def->name, library);
1958 ctxt->nbErrors++;
1959 }
1960 }
1961 }
1962 content = node->children;
Daniel Veillard416589a2003-02-17 17:25:42 +00001963
1964 /*
1965 * Handle optional params
1966 */
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001967 while (content != NULL) {
Daniel Veillard416589a2003-02-17 17:25:42 +00001968 if (!xmlStrEqual(content->name, BAD_CAST "param"))
1969 break;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00001970 if (xmlStrEqual(library,
1971 BAD_CAST"http://relaxng.org/ns/structure/1.0")) {
1972 if (ctxt->error != NULL)
1973 ctxt->error(ctxt->userData,
1974 "Type library '%s' does not allow type parameters\n",
1975 library);
1976 ctxt->nbErrors++;
1977 content = content->next;
1978 while ((content != NULL) &&
1979 (xmlStrEqual(content->name, BAD_CAST "param")))
1980 content = content->next;
1981 } else {
1982 param = xmlRelaxNGNewDefine(ctxt, node);
1983 if (param != NULL) {
1984 param->type = XML_RELAXNG_PARAM;
1985 param->name = xmlGetProp(content, BAD_CAST "name");
1986 if (param->name == NULL) {
1987 if (ctxt->error != NULL)
1988 ctxt->error(ctxt->userData,
1989 "param has no name\n");
1990 ctxt->nbErrors++;
1991 }
1992 param->value = xmlNodeGetContent(content);
1993 if (lastparam == NULL) {
1994 def->attrs = lastparam = param;
1995 } else {
1996 lastparam->next = param;
1997 lastparam = param;
1998 }
Daniel Veillard8fe98712003-02-19 00:19:14 +00001999 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00002000 content = content->next;
Daniel Veillard8fe98712003-02-19 00:19:14 +00002001 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002002 }
Daniel Veillard416589a2003-02-17 17:25:42 +00002003 /*
2004 * Handle optional except
2005 */
2006 if ((content != NULL) && (xmlStrEqual(content->name, BAD_CAST "except"))) {
2007 xmlNodePtr child;
2008 xmlRelaxNGDefinePtr tmp2, last2 = NULL;
2009
2010 except = xmlRelaxNGNewDefine(ctxt, node);
2011 if (except == NULL) {
2012 return(def);
2013 }
2014 except->type = XML_RELAXNG_EXCEPT;
2015 child = content->children;
2016 if (last == NULL) {
2017 def->content = except;
2018 } else {
2019 last->next = except;
2020 }
2021 if (child == NULL) {
2022 if (ctxt->error != NULL)
2023 ctxt->error(ctxt->userData,
2024 "except has no content\n");
2025 ctxt->nbErrors++;
2026 }
2027 while (child != NULL) {
2028 tmp2 = xmlRelaxNGParsePattern(ctxt, child);
2029 if (tmp2 != NULL) {
2030 if (last2 == NULL) {
2031 except->content = last2 = tmp2;
2032 } else {
2033 last2->next = tmp2;
2034 last2 = tmp2;
2035 }
2036 }
2037 child = child->next;
2038 }
2039 content = content->next;
2040 }
2041 /*
2042 * Check there is no unhandled data
2043 */
2044 if (content != NULL) {
2045 if (ctxt->error != NULL)
2046 ctxt->error(ctxt->userData,
2047 "Element data has unexpected content %s\n", content->name);
2048 ctxt->nbErrors++;
2049 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002050
2051 return(def);
2052}
2053
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002054static const xmlChar *invalidName = BAD_CAST "\1";
2055
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002056/**
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002057 * xmlRelaxNGCompareNameClasses:
2058 * @defs1: the first element/attribute defs
2059 * @defs2: the second element/attribute defs
2060 * @name: the restriction on the name
2061 * @ns: the restriction on the namespace
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002062 *
2063 * Compare the 2 lists of element definitions. The comparison is
2064 * that if both lists do not accept the same QNames, it returns 1
2065 * If the 2 lists can accept the same QName the comparison returns 0
2066 *
2067 * Returns 1 disttinct, 0 if equal
2068 */
2069static int
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002070xmlRelaxNGCompareNameClasses(xmlRelaxNGDefinePtr def1,
2071 xmlRelaxNGDefinePtr def2) {
2072 int ret = 1;
2073 xmlNode node;
2074 xmlNs ns;
2075 xmlRelaxNGValidCtxt ctxt;
2076 ctxt.flags = FLAGS_IGNORABLE;
2077
2078 if ((def1->type == XML_RELAXNG_ELEMENT) ||
2079 (def1->type == XML_RELAXNG_ATTRIBUTE)) {
2080 if (def2->type == XML_RELAXNG_TEXT)
2081 return(1);
2082 if (def1->name != NULL) {
2083 node.name = def1->name;
2084 } else {
2085 node.name = invalidName;
2086 }
2087 node.ns = &ns;
2088 if (def1->ns != NULL) {
2089 if (def1->ns[0] == 0) {
2090 node.ns = NULL;
2091 } else {
2092 ns.href = def1->ns;
2093 }
2094 } else {
2095 ns.href = invalidName;
2096 }
2097 if (xmlRelaxNGElementMatch(&ctxt, def2, &node)) {
2098 if (def1->nameClass != NULL) {
2099 ret = xmlRelaxNGCompareNameClasses(def1->nameClass, def2);
2100 } else {
2101 ret = 0;
2102 }
2103 } else {
2104 ret = 1;
2105 }
2106 } else if (def1->type == XML_RELAXNG_TEXT) {
2107 if (def2->type == XML_RELAXNG_TEXT)
2108 return(0);
2109 return(1);
2110 } else if (def1->type == XML_RELAXNG_EXCEPT) {
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002111 TODO
2112 ret = 0;
2113 } else {
2114 TODO
2115 ret = 0;
2116 }
2117 if (ret == 0)
2118 return(ret);
2119 if ((def2->type == XML_RELAXNG_ELEMENT) ||
2120 (def2->type == XML_RELAXNG_ATTRIBUTE)) {
2121 if (def2->name != NULL) {
2122 node.name = def2->name;
2123 } else {
2124 node.name = invalidName;
2125 }
2126 node.ns = &ns;
2127 if (def2->ns != NULL) {
2128 if (def2->ns[0] == 0) {
2129 node.ns = NULL;
2130 } else {
2131 ns.href = def2->ns;
2132 }
2133 } else {
2134 ns.href = invalidName;
2135 }
2136 if (xmlRelaxNGElementMatch(&ctxt, def1, &node)) {
2137 if (def2->nameClass != NULL) {
2138 ret = xmlRelaxNGCompareNameClasses(def2->nameClass, def1);
2139 } else {
2140 ret = 0;
2141 }
2142 } else {
2143 ret = 1;
2144 }
2145 } else {
2146 TODO
2147 ret = 0;
2148 }
2149
2150 return(ret);
2151}
2152
2153/**
2154 * xmlRelaxNGCompareElemDefLists:
2155 * @ctxt: a Relax-NG parser context
2156 * @defs1: the first list of element/attribute defs
2157 * @defs2: the second list of element/attribute defs
2158 *
2159 * Compare the 2 lists of element or attribute definitions. The comparison
2160 * is that if both lists do not accept the same QNames, it returns 1
2161 * If the 2 lists can accept the same QName the comparison returns 0
2162 *
2163 * Returns 1 disttinct, 0 if equal
2164 */
2165static int
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002166xmlRelaxNGCompareElemDefLists(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
2167 xmlRelaxNGDefinePtr *def1,
2168 xmlRelaxNGDefinePtr *def2) {
2169 xmlRelaxNGDefinePtr *basedef2 = def2;
2170
Daniel Veillard154877e2003-01-30 12:17:05 +00002171 if ((def1 == NULL) || (def2 == NULL))
2172 return(1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002173 if ((*def1 == NULL) || (*def2 == NULL))
2174 return(1);
2175 while (*def1 != NULL) {
2176 while ((*def2) != NULL) {
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002177 if (xmlRelaxNGCompareNameClasses(*def1, *def2) == 0)
2178 return(0);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002179 def2++;
2180 }
2181 def2 = basedef2;
2182 def1++;
2183 }
2184 return(1);
2185}
2186
2187/**
2188 * xmlRelaxNGGetElements:
2189 * @ctxt: a Relax-NG parser context
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002190 * @def: the definition definition
2191 * @eora: gather elements (0) or attributes (1)
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002192 *
2193 * Compute the list of top elements a definition can generate
2194 *
2195 * Returns a list of elements or NULL if none was found.
2196 */
2197static xmlRelaxNGDefinePtr *
2198xmlRelaxNGGetElements(xmlRelaxNGParserCtxtPtr ctxt,
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002199 xmlRelaxNGDefinePtr def,
2200 int eora) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002201 xmlRelaxNGDefinePtr *ret = NULL, parent, cur, tmp;
2202 int len = 0;
2203 int max = 0;
2204
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002205 /*
2206 * Don't run that check in case of error. Infinite recursion
2207 * becomes possible.
2208 */
2209 if (ctxt->nbErrors != 0)
2210 return(NULL);
2211
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002212 parent = NULL;
2213 cur = def;
2214 while (cur != NULL) {
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002215 if (((eora == 0) && ((cur->type == XML_RELAXNG_ELEMENT) ||
2216 (cur->type == XML_RELAXNG_TEXT))) ||
2217 ((eora == 1) && (cur->type == XML_RELAXNG_ATTRIBUTE))) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002218 if (ret == NULL) {
2219 max = 10;
2220 ret = (xmlRelaxNGDefinePtr *)
2221 xmlMalloc((max + 1) * sizeof(xmlRelaxNGDefinePtr));
2222 if (ret == NULL) {
2223 if (ctxt->error != NULL)
2224 ctxt->error(ctxt->userData,
2225 "Out of memory in element search\n");
2226 ctxt->nbErrors++;
2227 return(NULL);
2228 }
2229 } else if (max <= len) {
2230 max *= 2;
2231 ret = xmlRealloc(ret, (max + 1) * sizeof(xmlRelaxNGDefinePtr));
2232 if (ret == NULL) {
2233 if (ctxt->error != NULL)
2234 ctxt->error(ctxt->userData,
2235 "Out of memory in element search\n");
2236 ctxt->nbErrors++;
2237 return(NULL);
2238 }
2239 }
Daniel Veillardb08c9812003-01-28 23:09:49 +00002240 ret[len++] = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002241 ret[len] = NULL;
2242 } else if ((cur->type == XML_RELAXNG_CHOICE) ||
2243 (cur->type == XML_RELAXNG_INTERLEAVE) ||
2244 (cur->type == XML_RELAXNG_GROUP) ||
2245 (cur->type == XML_RELAXNG_ONEORMORE) ||
Daniel Veillardb08c9812003-01-28 23:09:49 +00002246 (cur->type == XML_RELAXNG_ZEROORMORE) ||
2247 (cur->type == XML_RELAXNG_OPTIONAL) ||
2248 (cur->type == XML_RELAXNG_REF) ||
2249 (cur->type == XML_RELAXNG_DEF)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002250 /*
2251 * Don't go within elements or attributes or string values.
2252 * Just gather the element top list
2253 */
2254 if (cur->content != NULL) {
2255 parent = cur;
2256 cur = cur->content;
2257 tmp = cur;
2258 while (tmp != NULL) {
2259 tmp->parent = parent;
2260 tmp = tmp->next;
2261 }
2262 continue;
2263 }
2264 }
Daniel Veillard154877e2003-01-30 12:17:05 +00002265 if (cur == def)
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002266 break;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002267 if (cur->next != NULL) {
2268 cur = cur->next;
2269 continue;
2270 }
2271 do {
2272 cur = cur->parent;
2273 if (cur == NULL) break;
2274 if (cur == def) return(ret);
2275 if (cur->next != NULL) {
2276 cur = cur->next;
2277 break;
2278 }
2279 } while (cur != NULL);
2280 }
2281 return(ret);
2282}
2283
2284/**
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002285 * xmlRelaxNGCheckGroupAttrs:
2286 * @ctxt: a Relax-NG parser context
2287 * @def: the group definition
2288 *
2289 * Detects violations of rule 7.3
2290 */
2291static void
2292xmlRelaxNGCheckGroupAttrs(xmlRelaxNGParserCtxtPtr ctxt,
2293 xmlRelaxNGDefinePtr def) {
2294 xmlRelaxNGDefinePtr **list;
2295 xmlRelaxNGDefinePtr cur;
2296 int nbchild = 0, i, j, ret;
2297
2298 if ((def == NULL) ||
2299 ((def->type != XML_RELAXNG_GROUP) &&
2300 (def->type != XML_RELAXNG_ELEMENT)))
2301 return;
2302
2303 /*
2304 * Don't run that check in case of error. Infinite recursion
2305 * becomes possible.
2306 */
2307 if (ctxt->nbErrors != 0)
2308 return;
2309
2310 cur = def->attrs;
2311 while (cur != NULL) {
2312 nbchild++;
2313 cur = cur->next;
2314 }
2315 cur = def->content;
2316 while (cur != NULL) {
2317 nbchild++;
2318 cur = cur->next;
2319 }
2320
2321 list = (xmlRelaxNGDefinePtr **) xmlMalloc(nbchild *
2322 sizeof(xmlRelaxNGDefinePtr *));
2323 if (list == NULL) {
2324 if (ctxt->error != NULL)
2325 ctxt->error(ctxt->userData,
2326 "Out of memory in group computation\n");
2327 ctxt->nbErrors++;
2328 return;
2329 }
2330 i = 0;
2331 cur = def->attrs;
2332 while (cur != NULL) {
2333 list[i] = xmlRelaxNGGetElements(ctxt, cur, 1);
2334 i++;
2335 cur = cur->next;
2336 }
2337 cur = def->content;
2338 while (cur != NULL) {
2339 list[i] = xmlRelaxNGGetElements(ctxt, cur, 1);
2340 i++;
2341 cur = cur->next;
2342 }
2343
2344 for (i = 0;i < nbchild;i++) {
2345 if (list[i] == NULL)
2346 continue;
2347 for (j = 0;j < i;j++) {
2348 if (list[j] == NULL)
2349 continue;
2350 ret = xmlRelaxNGCompareElemDefLists(ctxt, list[i], list[j]);
2351 if (ret == 0) {
2352 if (ctxt->error != NULL)
2353 ctxt->error(ctxt->userData,
2354 "Attributes conflicts in group\n");
2355 ctxt->nbErrors++;
2356 }
2357 }
2358 }
2359 for (i = 0;i < nbchild;i++) {
2360 if (list[i] != NULL)
2361 xmlFree(list[i]);
2362 }
2363 xmlFree(list);
2364}
2365
2366/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002367 * xmlRelaxNGComputeInterleaves:
2368 * @def: the interleave definition
2369 * @ctxt: a Relax-NG parser context
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002370 * @name: the definition name
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002371 *
2372 * A lot of work for preprocessing interleave definitions
2373 * is potentially needed to get a decent execution speed at runtime
2374 * - trying to get a total order on the element nodes generated
2375 * by the interleaves, order the list of interleave definitions
2376 * following that order.
2377 * - if <text/> is used to handle mixed content, it is better to
2378 * flag this in the define and simplify the runtime checking
2379 * algorithm
2380 */
2381static void
2382xmlRelaxNGComputeInterleaves(xmlRelaxNGDefinePtr def,
2383 xmlRelaxNGParserCtxtPtr ctxt,
2384 xmlChar *name ATTRIBUTE_UNUSED) {
2385 xmlRelaxNGDefinePtr cur;
2386
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002387 xmlRelaxNGPartitionPtr partitions = NULL;
2388 xmlRelaxNGInterleaveGroupPtr *groups = NULL;
2389 xmlRelaxNGInterleaveGroupPtr group;
2390 int i,j,ret;
2391 int nbgroups = 0;
2392 int nbchild = 0;
2393
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002394 /*
2395 * Don't run that check in case of error. Infinite recursion
2396 * becomes possible.
2397 */
2398 if (ctxt->nbErrors != 0)
2399 return;
2400
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002401#ifdef DEBUG_INTERLEAVE
2402 xmlGenericError(xmlGenericErrorContext,
2403 "xmlRelaxNGComputeInterleaves(%s)\n",
2404 name);
2405#endif
2406 cur = def->content;
2407 while (cur != NULL) {
2408 nbchild++;
2409 cur = cur->next;
2410 }
2411
2412#ifdef DEBUG_INTERLEAVE
2413 xmlGenericError(xmlGenericErrorContext, " %d child\n", nbchild);
2414#endif
2415 groups = (xmlRelaxNGInterleaveGroupPtr *)
2416 xmlMalloc(nbchild * sizeof(xmlRelaxNGInterleaveGroupPtr));
2417 if (groups == NULL)
2418 goto error;
2419 cur = def->content;
2420 while (cur != NULL) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002421 groups[nbgroups] = (xmlRelaxNGInterleaveGroupPtr)
2422 xmlMalloc(sizeof(xmlRelaxNGInterleaveGroup));
2423 if (groups[nbgroups] == NULL)
2424 goto error;
2425 groups[nbgroups]->rule = cur;
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002426 groups[nbgroups]->defs = xmlRelaxNGGetElements(ctxt, cur, 0);
2427 groups[nbgroups]->attrs = xmlRelaxNGGetElements(ctxt, cur, 1);
Daniel Veillard154877e2003-01-30 12:17:05 +00002428 nbgroups++;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002429 cur = cur->next;
2430 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002431#ifdef DEBUG_INTERLEAVE
2432 xmlGenericError(xmlGenericErrorContext, " %d groups\n", nbgroups);
2433#endif
2434
2435 /*
2436 * Let's check that all rules makes a partitions according to 7.4
2437 */
2438 partitions = (xmlRelaxNGPartitionPtr)
2439 xmlMalloc(sizeof(xmlRelaxNGPartition));
2440 if (partitions == NULL)
2441 goto error;
2442 partitions->nbgroups = nbgroups;
2443 for (i = 0;i < nbgroups;i++) {
2444 group = groups[i];
2445 for (j = i+1;j < nbgroups;j++) {
2446 if (groups[j] == NULL)
2447 continue;
2448 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->defs,
2449 groups[j]->defs);
2450 if (ret == 0) {
2451 if (ctxt->error != NULL)
2452 ctxt->error(ctxt->userData,
2453 "Element or text conflicts in interleave\n");
2454 ctxt->nbErrors++;
2455 }
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002456 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->attrs,
2457 groups[j]->attrs);
2458 if (ret == 0) {
2459 if (ctxt->error != NULL)
2460 ctxt->error(ctxt->userData,
2461 "Attributes conflicts in interleave\n");
2462 ctxt->nbErrors++;
2463 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002464 }
2465 }
2466 partitions->groups = groups;
2467
2468 /*
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002469 * and save the partition list back in the def
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002470 */
2471 def->data = partitions;
2472 return;
2473
2474error:
2475 if (ctxt->error != NULL)
2476 ctxt->error(ctxt->userData,
2477 "Out of memory in interleave computation\n");
2478 ctxt->nbErrors++;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002479 if (groups != NULL) {
2480 for (i = 0;i < nbgroups;i++)
2481 if (groups[i] != NULL) {
2482 if (groups[i]->defs != NULL)
2483 xmlFree(groups[i]->defs);
2484 xmlFree(groups[i]);
2485 }
2486 xmlFree(groups);
2487 }
2488 xmlRelaxNGFreePartition(partitions);
2489}
2490
2491/**
2492 * xmlRelaxNGParseInterleave:
2493 * @ctxt: a Relax-NG parser context
2494 * @node: the data node.
2495 *
2496 * parse the content of a RelaxNG interleave node.
2497 *
2498 * Returns the definition pointer or NULL in case of error
2499 */
2500static xmlRelaxNGDefinePtr
2501xmlRelaxNGParseInterleave(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2502 xmlRelaxNGDefinePtr def = NULL;
2503 xmlRelaxNGDefinePtr last = NULL, cur;
2504 xmlNodePtr child;
2505
2506 def = xmlRelaxNGNewDefine(ctxt, node);
2507 if (def == NULL) {
2508 return(NULL);
2509 }
2510 def->type = XML_RELAXNG_INTERLEAVE;
2511
2512 if (ctxt->interleaves == NULL)
2513 ctxt->interleaves = xmlHashCreate(10);
2514 if (ctxt->interleaves == NULL) {
2515 if (ctxt->error != NULL)
2516 ctxt->error(ctxt->userData,
2517 "Failed to create interleaves hash table\n");
2518 ctxt->nbErrors++;
2519 } else {
2520 char name[32];
2521
2522 snprintf(name, 32, "interleave%d", ctxt->nbInterleaves++);
2523 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST name, def) < 0) {
2524 if (ctxt->error != NULL)
2525 ctxt->error(ctxt->userData,
2526 "Failed to add %s to hash table\n", name);
2527 ctxt->nbErrors++;
2528 }
2529 }
2530 child = node->children;
Daniel Veillardd2298792003-02-14 16:54:11 +00002531 if (child == NULL) {
2532 if (ctxt->error != NULL)
2533 ctxt->error(ctxt->userData, "Element interleave is empty\n");
2534 ctxt->nbErrors++;
2535 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002536 while (child != NULL) {
2537 if (IS_RELAXNG(child, "element")) {
2538 cur = xmlRelaxNGParseElement(ctxt, child);
2539 } else {
2540 cur = xmlRelaxNGParsePattern(ctxt, child);
2541 }
2542 if (cur != NULL) {
2543 cur->parent = def;
2544 if (last == NULL) {
2545 def->content = last = cur;
2546 } else {
2547 last->next = cur;
2548 last = cur;
2549 }
2550 }
2551 child = child->next;
2552 }
2553
2554 return(def);
2555}
Daniel Veillard6eadf632003-01-23 18:29:16 +00002556
2557/**
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002558 * xmlRelaxNGParseInclude:
2559 * @ctxt: a Relax-NG parser context
2560 * @node: the include node
2561 *
2562 * Integrate the content of an include node in the current grammar
2563 *
2564 * Returns 0 in case of success or -1 in case of error
2565 */
2566static int
2567xmlRelaxNGParseInclude(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2568 xmlRelaxNGIncludePtr incl;
2569 xmlNodePtr root;
2570 int ret = 0, tmp;
2571
2572 incl = node->_private;
2573 if (incl == NULL) {
2574 if (ctxt->error != NULL)
2575 ctxt->error(ctxt->userData,
2576 "Include node has no data\n");
2577 ctxt->nbErrors++;
2578 return(-1);
2579 }
2580 root = xmlDocGetRootElement(incl->doc);
2581 if (root == NULL) {
2582 if (ctxt->error != NULL)
2583 ctxt->error(ctxt->userData,
2584 "Include document is empty\n");
2585 ctxt->nbErrors++;
2586 return(-1);
2587 }
2588 if (!xmlStrEqual(root->name, BAD_CAST "grammar")) {
2589 if (ctxt->error != NULL)
2590 ctxt->error(ctxt->userData,
2591 "Include document root is not a grammar\n");
2592 ctxt->nbErrors++;
2593 return(-1);
2594 }
2595
2596 /*
2597 * Merge the definition from both the include and the internal list
2598 */
2599 if (root->children != NULL) {
2600 tmp = xmlRelaxNGParseGrammarContent(ctxt, root->children);
2601 if (tmp != 0)
2602 ret = -1;
2603 }
2604 if (node->children != NULL) {
2605 tmp = xmlRelaxNGParseGrammarContent(ctxt, node->children);
2606 if (tmp != 0)
2607 ret = -1;
2608 }
2609 return(ret);
2610}
2611
2612/**
Daniel Veillard276be4a2003-01-24 01:03:34 +00002613 * xmlRelaxNGParseDefine:
2614 * @ctxt: a Relax-NG parser context
2615 * @node: the define node
2616 *
2617 * parse the content of a RelaxNG define element node.
2618 *
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002619 * Returns 0 in case of success or -1 in case of error
Daniel Veillard276be4a2003-01-24 01:03:34 +00002620 */
2621static int
2622xmlRelaxNGParseDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2623 xmlChar *name;
2624 int ret = 0, tmp;
2625 xmlRelaxNGDefinePtr def;
2626 const xmlChar *olddefine;
2627
2628 name = xmlGetProp(node, BAD_CAST "name");
2629 if (name == NULL) {
2630 if (ctxt->error != NULL)
2631 ctxt->error(ctxt->userData,
2632 "define has no name\n");
2633 ctxt->nbErrors++;
2634 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00002635 xmlRelaxNGNormExtSpace(name);
2636 if (xmlValidateNCName(name, 0)) {
2637 if (ctxt->error != NULL)
2638 ctxt->error(ctxt->userData,
2639 "define name '%s' is not an NCName\n",
2640 name);
2641 ctxt->nbErrors++;
2642 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002643 def = xmlRelaxNGNewDefine(ctxt, node);
2644 if (def == NULL) {
2645 xmlFree(name);
2646 return(-1);
2647 }
2648 def->type = XML_RELAXNG_DEF;
2649 def->name = name;
2650 if (node->children == NULL) {
2651 if (ctxt->error != NULL)
2652 ctxt->error(ctxt->userData,
2653 "define has no children\n");
2654 ctxt->nbErrors++;
2655 } else {
2656 olddefine = ctxt->define;
2657 ctxt->define = name;
Daniel Veillard154877e2003-01-30 12:17:05 +00002658 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard276be4a2003-01-24 01:03:34 +00002659 ctxt->define = olddefine;
2660 }
2661 if (ctxt->grammar->defs == NULL)
2662 ctxt->grammar->defs = xmlHashCreate(10);
2663 if (ctxt->grammar->defs == NULL) {
2664 if (ctxt->error != NULL)
2665 ctxt->error(ctxt->userData,
2666 "Could not create definition hash\n");
2667 ctxt->nbErrors++;
2668 ret = -1;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002669 } else {
2670 tmp = xmlHashAddEntry(ctxt->grammar->defs, name, def);
2671 if (tmp < 0) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002672 xmlRelaxNGDefinePtr prev;
2673
2674 prev = xmlHashLookup(ctxt->grammar->defs, name);
2675 if (prev == NULL) {
2676 if (ctxt->error != NULL)
2677 ctxt->error(ctxt->userData,
2678 "Internal error on define aggregation of %s\n",
2679 name);
2680 ctxt->nbErrors++;
2681 ret = -1;
Daniel Veillard154877e2003-01-30 12:17:05 +00002682 } else {
2683 while (prev->nextHash != NULL)
2684 prev = prev->nextHash;
2685 prev->nextHash = def;
2686 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002687 }
2688 }
2689 }
2690 return(ret);
2691}
2692
2693/**
Daniel Veillardfebcca42003-02-16 15:44:18 +00002694 * xmlRelaxNGProcessExternalRef:
2695 * @ctxt: the parser context
2696 * @node: the externlRef node
2697 *
2698 * Process and compile an externlRef node
2699 *
2700 * Returns the xmlRelaxNGDefinePtr or NULL in case of error
2701 */
2702static xmlRelaxNGDefinePtr
2703xmlRelaxNGProcessExternalRef(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2704 xmlRelaxNGDocumentPtr docu;
2705 xmlNodePtr root, tmp;
2706 xmlChar *ns;
Daniel Veillard77648bb2003-02-20 15:03:22 +00002707 int newNs = 0, oldflags;
Daniel Veillardfebcca42003-02-16 15:44:18 +00002708 xmlRelaxNGDefinePtr def;
2709
2710 docu = node->_private;
2711 if (docu != NULL) {
2712 def = xmlRelaxNGNewDefine(ctxt, node);
2713 if (def == NULL)
2714 return(NULL);
2715 def->type = XML_RELAXNG_EXTERNALREF;
2716
2717 if (docu->content == NULL) {
2718 /*
2719 * Then do the parsing for good
2720 */
2721 root = xmlDocGetRootElement(docu->doc);
2722 if (root == NULL) {
2723 if (ctxt->error != NULL)
2724 ctxt->error(ctxt->userData,
2725 "xmlRelaxNGParse: %s is empty\n",
2726 ctxt->URL);
2727 ctxt->nbErrors++;
2728 return (NULL);
2729 }
2730 /*
2731 * ns transmission rules
2732 */
2733 ns = xmlGetProp(root, BAD_CAST "ns");
2734 if (ns == NULL) {
2735 tmp = node;
2736 while ((tmp != NULL) &&
2737 (tmp->type == XML_ELEMENT_NODE)) {
2738 ns = xmlGetProp(tmp, BAD_CAST "ns");
2739 if (ns != NULL) {
2740 break;
2741 }
2742 tmp = tmp->parent;
2743 }
2744 if (ns != NULL) {
2745 xmlSetProp(root, BAD_CAST "ns", ns);
2746 newNs = 1;
2747 xmlFree(ns);
2748 }
2749 } else {
2750 xmlFree(ns);
2751 }
2752
2753 /*
2754 * Parsing to get a precompiled schemas.
2755 */
Daniel Veillard77648bb2003-02-20 15:03:22 +00002756 oldflags = ctxt->flags;
2757 ctxt->flags |= XML_RELAXNG_IN_EXTERNALREF;
Daniel Veillardfebcca42003-02-16 15:44:18 +00002758 docu->schema = xmlRelaxNGParseDocument(ctxt, root);
Daniel Veillard77648bb2003-02-20 15:03:22 +00002759 ctxt->flags = oldflags;
Daniel Veillardfebcca42003-02-16 15:44:18 +00002760 if ((docu->schema != NULL) &&
2761 (docu->schema->topgrammar != NULL)) {
2762 docu->content = docu->schema->topgrammar->start;
2763 }
2764
2765 /*
2766 * the externalRef may be reused in a different ns context
2767 */
2768 if (newNs == 1) {
2769 xmlUnsetProp(root, BAD_CAST "ns");
2770 }
2771 }
2772 def->content = docu->content;
2773 } else {
2774 def = NULL;
2775 }
2776 return(def);
2777}
2778
2779/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00002780 * xmlRelaxNGParsePattern:
2781 * @ctxt: a Relax-NG parser context
2782 * @node: the pattern node.
2783 *
2784 * parse the content of a RelaxNG pattern node.
2785 *
Daniel Veillard276be4a2003-01-24 01:03:34 +00002786 * Returns the definition pointer or NULL in case of error or if no
2787 * pattern is generated.
Daniel Veillard6eadf632003-01-23 18:29:16 +00002788 */
2789static xmlRelaxNGDefinePtr
2790xmlRelaxNGParsePattern(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2791 xmlRelaxNGDefinePtr def = NULL;
2792
Daniel Veillardd2298792003-02-14 16:54:11 +00002793 if (node == NULL) {
2794 return(NULL);
2795 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002796 if (IS_RELAXNG(node, "element")) {
2797 def = xmlRelaxNGParseElement(ctxt, node);
2798 } else if (IS_RELAXNG(node, "attribute")) {
2799 def = xmlRelaxNGParseAttribute(ctxt, node);
2800 } else if (IS_RELAXNG(node, "empty")) {
2801 def = xmlRelaxNGNewDefine(ctxt, node);
2802 if (def == NULL)
2803 return(NULL);
2804 def->type = XML_RELAXNG_EMPTY;
Daniel Veillardd2298792003-02-14 16:54:11 +00002805 if (node->children != NULL) {
2806 if (ctxt->error != NULL)
2807 ctxt->error(ctxt->userData, "empty: had a child node\n");
2808 ctxt->nbErrors++;
2809 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002810 } else if (IS_RELAXNG(node, "text")) {
2811 def = xmlRelaxNGNewDefine(ctxt, node);
2812 if (def == NULL)
2813 return(NULL);
2814 def->type = XML_RELAXNG_TEXT;
2815 if (node->children != NULL) {
2816 if (ctxt->error != NULL)
2817 ctxt->error(ctxt->userData, "text: had a child node\n");
2818 ctxt->nbErrors++;
2819 }
2820 } else if (IS_RELAXNG(node, "zeroOrMore")) {
2821 def = xmlRelaxNGNewDefine(ctxt, node);
2822 if (def == NULL)
2823 return(NULL);
2824 def->type = XML_RELAXNG_ZEROORMORE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002825 if (node->children == NULL) {
2826 if (ctxt->error != NULL)
2827 ctxt->error(ctxt->userData,
2828 "Element %s is empty\n", node->name);
2829 ctxt->nbErrors++;
2830 } else {
2831 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2832 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002833 } else if (IS_RELAXNG(node, "oneOrMore")) {
2834 def = xmlRelaxNGNewDefine(ctxt, node);
2835 if (def == NULL)
2836 return(NULL);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002837 def->type = XML_RELAXNG_ONEORMORE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002838 if (node->children == NULL) {
2839 if (ctxt->error != NULL)
2840 ctxt->error(ctxt->userData,
2841 "Element %s is empty\n", node->name);
2842 ctxt->nbErrors++;
2843 } else {
2844 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2845 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002846 } else if (IS_RELAXNG(node, "optional")) {
2847 def = xmlRelaxNGNewDefine(ctxt, node);
2848 if (def == NULL)
2849 return(NULL);
2850 def->type = XML_RELAXNG_OPTIONAL;
Daniel Veillardd2298792003-02-14 16:54:11 +00002851 if (node->children == NULL) {
2852 if (ctxt->error != NULL)
2853 ctxt->error(ctxt->userData,
2854 "Element %s is empty\n", node->name);
2855 ctxt->nbErrors++;
2856 } else {
2857 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2858 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002859 } else if (IS_RELAXNG(node, "choice")) {
2860 def = xmlRelaxNGNewDefine(ctxt, node);
2861 if (def == NULL)
2862 return(NULL);
2863 def->type = XML_RELAXNG_CHOICE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002864 if (node->children == NULL) {
2865 if (ctxt->error != NULL)
2866 ctxt->error(ctxt->userData,
2867 "Element %s is empty\n", node->name);
2868 ctxt->nbErrors++;
2869 } else {
2870 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2871 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002872 } else if (IS_RELAXNG(node, "group")) {
2873 def = xmlRelaxNGNewDefine(ctxt, node);
2874 if (def == NULL)
2875 return(NULL);
2876 def->type = XML_RELAXNG_GROUP;
Daniel Veillardd2298792003-02-14 16:54:11 +00002877 if (node->children == NULL) {
2878 if (ctxt->error != NULL)
2879 ctxt->error(ctxt->userData,
2880 "Element %s is empty\n", node->name);
2881 ctxt->nbErrors++;
2882 } else {
2883 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2884 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002885 } else if (IS_RELAXNG(node, "ref")) {
2886 def = xmlRelaxNGNewDefine(ctxt, node);
2887 if (def == NULL)
2888 return(NULL);
2889 def->type = XML_RELAXNG_REF;
2890 def->name = xmlGetProp(node, BAD_CAST "name");
2891 if (def->name == NULL) {
2892 if (ctxt->error != NULL)
2893 ctxt->error(ctxt->userData,
2894 "ref has no name\n");
2895 ctxt->nbErrors++;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002896 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00002897 xmlRelaxNGNormExtSpace(def->name);
2898 if (xmlValidateNCName(def->name, 0)) {
2899 if (ctxt->error != NULL)
2900 ctxt->error(ctxt->userData,
2901 "ref name '%s' is not an NCName\n",
2902 def->name);
2903 ctxt->nbErrors++;
2904 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002905 }
2906 if (node->children != NULL) {
2907 if (ctxt->error != NULL)
2908 ctxt->error(ctxt->userData,
2909 "ref is not empty\n");
2910 ctxt->nbErrors++;
2911 }
2912 if (ctxt->grammar->refs == NULL)
2913 ctxt->grammar->refs = xmlHashCreate(10);
2914 if (ctxt->grammar->refs == NULL) {
2915 if (ctxt->error != NULL)
2916 ctxt->error(ctxt->userData,
2917 "Could not create references hash\n");
2918 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002919 def = NULL;
2920 } else {
2921 int tmp;
2922
2923 tmp = xmlHashAddEntry(ctxt->grammar->refs, def->name, def);
2924 if (tmp < 0) {
2925 xmlRelaxNGDefinePtr prev;
2926
2927 prev = (xmlRelaxNGDefinePtr)
2928 xmlHashLookup(ctxt->grammar->refs, def->name);
2929 if (prev == NULL) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00002930 if (def->name != NULL) {
2931 if (ctxt->error != NULL)
2932 ctxt->error(ctxt->userData,
2933 "Error refs definitions '%s'\n",
2934 def->name);
2935 } else {
2936 if (ctxt->error != NULL)
2937 ctxt->error(ctxt->userData,
2938 "Error refs definitions\n");
2939 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002940 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002941 def = NULL;
2942 } else {
2943 def->nextHash = prev->nextHash;
2944 prev->nextHash = def;
2945 }
2946 }
2947 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002948 } else if (IS_RELAXNG(node, "data")) {
2949 def = xmlRelaxNGParseData(ctxt, node);
Daniel Veillardd2298792003-02-14 16:54:11 +00002950#if 0
Daniel Veillard276be4a2003-01-24 01:03:34 +00002951 } else if (IS_RELAXNG(node, "define")) {
2952 xmlRelaxNGParseDefine(ctxt, node);
2953 def = NULL;
Daniel Veillardd2298792003-02-14 16:54:11 +00002954#endif
Daniel Veillardedc91922003-01-26 00:52:04 +00002955 } else if (IS_RELAXNG(node, "value")) {
2956 def = xmlRelaxNGParseValue(ctxt, node);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002957 } else if (IS_RELAXNG(node, "list")) {
2958 def = xmlRelaxNGNewDefine(ctxt, node);
2959 if (def == NULL)
2960 return(NULL);
2961 def->type = XML_RELAXNG_LIST;
Daniel Veillardd2298792003-02-14 16:54:11 +00002962 if (node->children == NULL) {
2963 if (ctxt->error != NULL)
2964 ctxt->error(ctxt->userData,
2965 "Element %s is empty\n", node->name);
2966 ctxt->nbErrors++;
2967 } else {
2968 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2969 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002970 } else if (IS_RELAXNG(node, "interleave")) {
2971 def = xmlRelaxNGParseInterleave(ctxt, node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002972 } else if (IS_RELAXNG(node, "externalRef")) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00002973 def = xmlRelaxNGProcessExternalRef(ctxt, node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002974 } else if (IS_RELAXNG(node, "notAllowed")) {
2975 def = xmlRelaxNGNewDefine(ctxt, node);
2976 if (def == NULL)
2977 return(NULL);
2978 def->type = XML_RELAXNG_NOT_ALLOWED;
2979 if (node->children != NULL) {
2980 if (ctxt->error != NULL)
2981 ctxt->error(ctxt->userData,
2982 "xmlRelaxNGParse: notAllowed element is not empty\n");
2983 ctxt->nbErrors++;
2984 }
Daniel Veillard419a7682003-02-03 23:22:49 +00002985 } else if (IS_RELAXNG(node, "grammar")) {
2986 xmlRelaxNGGrammarPtr grammar, old;
2987 xmlRelaxNGGrammarPtr oldparent;
2988
2989 oldparent = ctxt->parentgrammar;
2990 old = ctxt->grammar;
2991 ctxt->parentgrammar = old;
2992 grammar = xmlRelaxNGParseGrammar(ctxt, node->children);
2993 if (old != NULL) {
2994 ctxt->grammar = old;
2995 ctxt->parentgrammar = oldparent;
2996 if (grammar != NULL) {
2997 grammar->next = old->next;
2998 old->next = grammar;
2999 }
3000 }
3001 if (grammar != NULL)
3002 def = grammar->start;
3003 else
3004 def = NULL;
3005 } else if (IS_RELAXNG(node, "parentRef")) {
3006 if (ctxt->parentgrammar == NULL) {
3007 if (ctxt->error != NULL)
3008 ctxt->error(ctxt->userData,
3009 "Use of parentRef without a parent grammar\n");
3010 ctxt->nbErrors++;
3011 return(NULL);
3012 }
3013 def = xmlRelaxNGNewDefine(ctxt, node);
3014 if (def == NULL)
3015 return(NULL);
3016 def->type = XML_RELAXNG_PARENTREF;
3017 def->name = xmlGetProp(node, BAD_CAST "name");
3018 if (def->name == NULL) {
3019 if (ctxt->error != NULL)
3020 ctxt->error(ctxt->userData,
3021 "parentRef has no name\n");
3022 ctxt->nbErrors++;
Daniel Veillardd2298792003-02-14 16:54:11 +00003023 } else {
3024 xmlRelaxNGNormExtSpace(def->name);
3025 if (xmlValidateNCName(def->name, 0)) {
3026 if (ctxt->error != NULL)
3027 ctxt->error(ctxt->userData,
3028 "parentRef name '%s' is not an NCName\n",
3029 def->name);
3030 ctxt->nbErrors++;
3031 }
Daniel Veillard419a7682003-02-03 23:22:49 +00003032 }
3033 if (node->children != NULL) {
3034 if (ctxt->error != NULL)
3035 ctxt->error(ctxt->userData,
3036 "parentRef is not empty\n");
3037 ctxt->nbErrors++;
3038 }
3039 if (ctxt->parentgrammar->refs == NULL)
3040 ctxt->parentgrammar->refs = xmlHashCreate(10);
3041 if (ctxt->parentgrammar->refs == NULL) {
3042 if (ctxt->error != NULL)
3043 ctxt->error(ctxt->userData,
3044 "Could not create references hash\n");
3045 ctxt->nbErrors++;
3046 def = NULL;
Daniel Veillardd2298792003-02-14 16:54:11 +00003047 } else if (def->name != NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +00003048 int tmp;
3049
3050 tmp = xmlHashAddEntry(ctxt->parentgrammar->refs, def->name, def);
3051 if (tmp < 0) {
3052 xmlRelaxNGDefinePtr prev;
3053
3054 prev = (xmlRelaxNGDefinePtr)
3055 xmlHashLookup(ctxt->parentgrammar->refs, def->name);
3056 if (prev == NULL) {
3057 if (ctxt->error != NULL)
3058 ctxt->error(ctxt->userData,
3059 "Internal error parentRef definitions '%s'\n",
3060 def->name);
3061 ctxt->nbErrors++;
3062 def = NULL;
3063 } else {
3064 def->nextHash = prev->nextHash;
3065 prev->nextHash = def;
3066 }
3067 }
3068 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003069 } else if (IS_RELAXNG(node, "mixed")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00003070 if (node->children == NULL) {
3071 if (ctxt->error != NULL)
3072 ctxt->error(ctxt->userData,
3073 "Mixed is empty\n");
3074 ctxt->nbErrors++;
3075 def = NULL;
Daniel Veillard416589a2003-02-17 17:25:42 +00003076 } else {
3077 def = xmlRelaxNGParseInterleave(ctxt, node);
3078 if (def != NULL) {
3079 xmlRelaxNGDefinePtr tmp;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003080
3081 if ((def->content != NULL) && (def->content->next != NULL)) {
3082 tmp = xmlRelaxNGNewDefine(ctxt, node);
3083 if (tmp != NULL) {
3084 tmp->type = XML_RELAXNG_GROUP;
3085 tmp->content = def->content;
3086 def->content = tmp;
3087 }
3088 }
3089
Daniel Veillard416589a2003-02-17 17:25:42 +00003090 tmp = xmlRelaxNGNewDefine(ctxt, node);
3091 if (tmp == NULL)
3092 return(def);
3093 tmp->type = XML_RELAXNG_TEXT;
3094 tmp->next = def->content;
3095 def->content = tmp;
3096 }
3097 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003098 } else {
3099 if (ctxt->error != NULL)
3100 ctxt->error(ctxt->userData,
3101 "Unexpected node %s is not a pattern\n",
3102 node->name);
3103 ctxt->nbErrors++;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003104 def = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003105 }
3106 return(def);
3107}
3108
3109/**
3110 * xmlRelaxNGParseAttribute:
3111 * @ctxt: a Relax-NG parser context
3112 * @node: the element node
3113 *
3114 * parse the content of a RelaxNG attribute node.
3115 *
3116 * Returns the definition pointer or NULL in case of error.
3117 */
3118static xmlRelaxNGDefinePtr
3119xmlRelaxNGParseAttribute(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
Daniel Veillardd2298792003-02-14 16:54:11 +00003120 xmlRelaxNGDefinePtr ret, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003121 xmlNodePtr child;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003122 int old_flags;
3123
3124 ret = xmlRelaxNGNewDefine(ctxt, node);
3125 if (ret == NULL)
3126 return(NULL);
3127 ret->type = XML_RELAXNG_ATTRIBUTE;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003128 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003129 child = node->children;
3130 if (child == NULL) {
3131 if (ctxt->error != NULL)
3132 ctxt->error(ctxt->userData,
3133 "xmlRelaxNGParseattribute: attribute has no children\n");
3134 ctxt->nbErrors++;
3135 return(ret);
3136 }
3137 old_flags = ctxt->flags;
3138 ctxt->flags |= XML_RELAXNG_IN_ATTRIBUTE;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003139 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
3140 if (cur != NULL)
3141 child = child->next;
3142
Daniel Veillardd2298792003-02-14 16:54:11 +00003143 if (child != NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003144 cur = xmlRelaxNGParsePattern(ctxt, child);
3145 if (cur != NULL) {
3146 switch (cur->type) {
3147 case XML_RELAXNG_EMPTY:
3148 case XML_RELAXNG_NOT_ALLOWED:
3149 case XML_RELAXNG_TEXT:
3150 case XML_RELAXNG_ELEMENT:
3151 case XML_RELAXNG_DATATYPE:
3152 case XML_RELAXNG_VALUE:
3153 case XML_RELAXNG_LIST:
3154 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00003155 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003156 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00003157 case XML_RELAXNG_DEF:
3158 case XML_RELAXNG_ONEORMORE:
3159 case XML_RELAXNG_ZEROORMORE:
3160 case XML_RELAXNG_OPTIONAL:
3161 case XML_RELAXNG_CHOICE:
3162 case XML_RELAXNG_GROUP:
3163 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard77648bb2003-02-20 15:03:22 +00003164 case XML_RELAXNG_ATTRIBUTE:
Daniel Veillardd2298792003-02-14 16:54:11 +00003165 ret->content = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003166 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003167 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003168 case XML_RELAXNG_START:
Daniel Veillard8fe98712003-02-19 00:19:14 +00003169 case XML_RELAXNG_PARAM:
Daniel Veillard144fae12003-02-03 13:17:57 +00003170 case XML_RELAXNG_EXCEPT:
Daniel Veillardd2298792003-02-14 16:54:11 +00003171 if (ctxt->error != NULL)
3172 ctxt->error(ctxt->userData,
3173 "attribute has invalid content\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003174 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003175 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00003176 case XML_RELAXNG_NOOP:
3177 TODO
3178 if (ctxt->error != NULL)
3179 ctxt->error(ctxt->userData,
3180 "Internal error, noop found\n");
3181 ctxt->nbErrors++;
3182 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003183 }
3184 }
3185 child = child->next;
3186 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003187 if (child != NULL) {
3188 if (ctxt->error != NULL)
3189 ctxt->error(ctxt->userData, "attribute has multiple children\n");
3190 ctxt->nbErrors++;
3191 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003192 ctxt->flags = old_flags;
3193 return(ret);
3194}
3195
3196/**
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003197 * xmlRelaxNGParseExceptNameClass:
3198 * @ctxt: a Relax-NG parser context
3199 * @node: the except node
Daniel Veillard144fae12003-02-03 13:17:57 +00003200 * @attr: 1 if within an attribute, 0 if within an element
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003201 *
3202 * parse the content of a RelaxNG nameClass node.
3203 *
3204 * Returns the definition pointer or NULL in case of error.
3205 */
3206static xmlRelaxNGDefinePtr
Daniel Veillard144fae12003-02-03 13:17:57 +00003207xmlRelaxNGParseExceptNameClass(xmlRelaxNGParserCtxtPtr ctxt,
3208 xmlNodePtr node, int attr) {
3209 xmlRelaxNGDefinePtr ret, cur, last = NULL;
3210 xmlNodePtr child;
3211
Daniel Veillardd2298792003-02-14 16:54:11 +00003212 if (!IS_RELAXNG(node, "except")) {
3213 if (ctxt->error != NULL)
3214 ctxt->error(ctxt->userData,
3215 "Expecting an except node\n");
3216 ctxt->nbErrors++;
Daniel Veillard144fae12003-02-03 13:17:57 +00003217 return(NULL);
Daniel Veillardd2298792003-02-14 16:54:11 +00003218 }
3219 if (node->next != NULL) {
3220 if (ctxt->error != NULL)
3221 ctxt->error(ctxt->userData,
3222 "exceptNameClass allows only a single except node\n");
3223 ctxt->nbErrors++;
3224 }
Daniel Veillard144fae12003-02-03 13:17:57 +00003225 if (node->children == NULL) {
3226 if (ctxt->error != NULL)
3227 ctxt->error(ctxt->userData,
3228 "except has no content\n");
3229 ctxt->nbErrors++;
3230 return(NULL);
3231 }
3232
3233 ret = xmlRelaxNGNewDefine(ctxt, node);
3234 if (ret == NULL)
3235 return(NULL);
3236 ret->type = XML_RELAXNG_EXCEPT;
3237 child = node->children;
3238 while (child != NULL) {
3239 cur = xmlRelaxNGNewDefine(ctxt, child);
3240 if (cur == NULL)
3241 break;
3242 if (attr)
3243 cur->type = XML_RELAXNG_ATTRIBUTE;
3244 else
3245 cur->type = XML_RELAXNG_ELEMENT;
3246
Daniel Veillard419a7682003-02-03 23:22:49 +00003247 if (xmlRelaxNGParseNameClass(ctxt, child, cur) != NULL) {
Daniel Veillard144fae12003-02-03 13:17:57 +00003248 if (last == NULL) {
3249 ret->content = cur;
3250 } else {
3251 last->next = cur;
3252 }
3253 last = cur;
3254 }
3255 child = child->next;
3256 }
3257
3258 return(ret);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003259}
3260
3261/**
3262 * xmlRelaxNGParseNameClass:
3263 * @ctxt: a Relax-NG parser context
3264 * @node: the nameClass node
3265 * @def: the current definition
3266 *
3267 * parse the content of a RelaxNG nameClass node.
3268 *
3269 * Returns the definition pointer or NULL in case of error.
3270 */
3271static xmlRelaxNGDefinePtr
3272xmlRelaxNGParseNameClass(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
3273 xmlRelaxNGDefinePtr def) {
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003274 xmlRelaxNGDefinePtr ret, tmp;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003275 xmlChar *val;
3276
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003277 ret = def;
3278 if ((IS_RELAXNG(node, "name")) || (IS_RELAXNG(node, "anyName")) ||
3279 (IS_RELAXNG(node, "nsName"))) {
3280 if ((def->type != XML_RELAXNG_ELEMENT) &&
3281 (def->type != XML_RELAXNG_ATTRIBUTE)) {
3282 ret = xmlRelaxNGNewDefine(ctxt, node);
3283 if (ret == NULL)
3284 return(NULL);
3285 ret->parent = def;
3286 if (ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE)
3287 ret->type = XML_RELAXNG_ATTRIBUTE;
3288 else
3289 ret->type = XML_RELAXNG_ELEMENT;
3290 }
3291 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003292 if (IS_RELAXNG(node, "name")) {
3293 val = xmlNodeGetContent(node);
Daniel Veillardd2298792003-02-14 16:54:11 +00003294 xmlRelaxNGNormExtSpace(val);
3295 if (xmlValidateNCName(val, 0)) {
3296 if (ctxt->error != NULL) {
3297 if (node->parent != NULL)
3298 ctxt->error(ctxt->userData,
3299 "Element %s name '%s' is not an NCName\n",
3300 node->parent->name, val);
3301 else
3302 ctxt->error(ctxt->userData,
3303 "name '%s' is not an NCName\n",
3304 val);
3305 }
3306 ctxt->nbErrors++;
3307 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003308 ret->name = val;
3309 val = xmlGetProp(node, BAD_CAST "ns");
3310 ret->ns = val;
Daniel Veillard416589a2003-02-17 17:25:42 +00003311 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3312 (val != NULL) &&
3313 (xmlStrEqual(val, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
3314 ctxt->error(ctxt->userData,
3315 "Attribute with namespace '%s' is not allowed\n",
3316 val);
3317 ctxt->nbErrors++;
3318 }
3319 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3320 (val != NULL) &&
3321 (val[0] == 0) &&
3322 (xmlStrEqual(ret->name, BAD_CAST "xmlns"))) {
3323 ctxt->error(ctxt->userData,
3324 "Attribute with QName 'xmlns' is not allowed\n",
3325 val);
3326 ctxt->nbErrors++;
3327 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003328 } else if (IS_RELAXNG(node, "anyName")) {
3329 ret->name = NULL;
3330 ret->ns = NULL;
3331 if (node->children != NULL) {
3332 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00003333 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
3334 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003335 }
3336 } else if (IS_RELAXNG(node, "nsName")) {
3337 ret->name = NULL;
3338 ret->ns = xmlGetProp(node, BAD_CAST "ns");
3339 if (ret->ns == NULL) {
3340 if (ctxt->error != NULL)
3341 ctxt->error(ctxt->userData,
3342 "nsName has no ns attribute\n");
3343 ctxt->nbErrors++;
3344 }
Daniel Veillard416589a2003-02-17 17:25:42 +00003345 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3346 (ret->ns != NULL) &&
3347 (xmlStrEqual(ret->ns, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
3348 ctxt->error(ctxt->userData,
3349 "Attribute with namespace '%s' is not allowed\n",
3350 ret->ns);
3351 ctxt->nbErrors++;
3352 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003353 if (node->children != NULL) {
3354 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00003355 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
3356 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003357 }
3358 } else if (IS_RELAXNG(node, "choice")) {
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003359 xmlNodePtr child;
3360 xmlRelaxNGDefinePtr last = NULL;
3361
Daniel Veillardd4310742003-02-18 21:12:46 +00003362 ret = xmlRelaxNGNewDefine(ctxt, node);
3363 if (ret == NULL)
3364 return(NULL);
3365 ret->parent = def;
3366 ret->type = XML_RELAXNG_CHOICE;
3367
Daniel Veillardd2298792003-02-14 16:54:11 +00003368 if (node->children == NULL) {
3369 if (ctxt->error != NULL)
3370 ctxt->error(ctxt->userData,
3371 "Element choice is empty\n");
3372 ctxt->nbErrors++;
3373 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00003374
3375 child = node->children;
3376 while (child != NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00003377 tmp = xmlRelaxNGParseNameClass(ctxt, child, ret);
Daniel Veillardd2298792003-02-14 16:54:11 +00003378 if (tmp != NULL) {
3379 if (last == NULL) {
3380 last = ret->nameClass = tmp;
3381 } else {
3382 last->next = tmp;
3383 last = tmp;
3384 }
3385 }
3386 child = child->next;
3387 }
3388 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003389 } else {
3390 if (ctxt->error != NULL)
3391 ctxt->error(ctxt->userData,
3392 "expecting name, anyName, nsName or choice : got %s\n",
3393 node->name);
3394 ctxt->nbErrors++;
3395 return(NULL);
3396 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003397 if (ret != def) {
3398 if (def->nameClass == NULL) {
3399 def->nameClass = ret;
3400 } else {
3401 tmp = def->nameClass;
3402 while (tmp->next != NULL) {
3403 tmp = tmp->next;
3404 }
3405 tmp->next = ret;
3406 }
3407 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003408 return(ret);
3409}
3410
3411/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00003412 * xmlRelaxNGParseElement:
3413 * @ctxt: a Relax-NG parser context
3414 * @node: the element node
3415 *
3416 * parse the content of a RelaxNG element node.
3417 *
3418 * Returns the definition pointer or NULL in case of error.
3419 */
3420static xmlRelaxNGDefinePtr
3421xmlRelaxNGParseElement(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3422 xmlRelaxNGDefinePtr ret, cur, last;
3423 xmlNodePtr child;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003424 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003425
3426 ret = xmlRelaxNGNewDefine(ctxt, node);
3427 if (ret == NULL)
3428 return(NULL);
3429 ret->type = XML_RELAXNG_ELEMENT;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003430 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003431 child = node->children;
3432 if (child == NULL) {
3433 if (ctxt->error != NULL)
3434 ctxt->error(ctxt->userData,
3435 "xmlRelaxNGParseElement: element has no children\n");
3436 ctxt->nbErrors++;
3437 return(ret);
3438 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003439 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
3440 if (cur != NULL)
3441 child = child->next;
3442
Daniel Veillard6eadf632003-01-23 18:29:16 +00003443 if (child == NULL) {
3444 if (ctxt->error != NULL)
3445 ctxt->error(ctxt->userData,
3446 "xmlRelaxNGParseElement: element has no content\n");
3447 ctxt->nbErrors++;
3448 return(ret);
3449 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003450 olddefine = ctxt->define;
3451 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003452 last = NULL;
3453 while (child != NULL) {
3454 cur = xmlRelaxNGParsePattern(ctxt, child);
3455 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003456 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003457 switch (cur->type) {
3458 case XML_RELAXNG_EMPTY:
3459 case XML_RELAXNG_NOT_ALLOWED:
3460 case XML_RELAXNG_TEXT:
3461 case XML_RELAXNG_ELEMENT:
3462 case XML_RELAXNG_DATATYPE:
3463 case XML_RELAXNG_VALUE:
3464 case XML_RELAXNG_LIST:
3465 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00003466 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003467 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00003468 case XML_RELAXNG_DEF:
3469 case XML_RELAXNG_ZEROORMORE:
3470 case XML_RELAXNG_ONEORMORE:
3471 case XML_RELAXNG_OPTIONAL:
3472 case XML_RELAXNG_CHOICE:
3473 case XML_RELAXNG_GROUP:
3474 case XML_RELAXNG_INTERLEAVE:
3475 if (last == NULL) {
3476 ret->content = last = cur;
3477 } else {
3478 if ((last->type == XML_RELAXNG_ELEMENT) &&
3479 (ret->content == last)) {
3480 ret->content = xmlRelaxNGNewDefine(ctxt, node);
3481 if (ret->content != NULL) {
3482 ret->content->type = XML_RELAXNG_GROUP;
3483 ret->content->content = last;
3484 } else {
3485 ret->content = last;
3486 }
3487 }
3488 last->next = cur;
3489 last = cur;
3490 }
3491 break;
3492 case XML_RELAXNG_ATTRIBUTE:
3493 cur->next = ret->attrs;
3494 ret->attrs = cur;
3495 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003496 case XML_RELAXNG_START:
Daniel Veillard8fe98712003-02-19 00:19:14 +00003497 case XML_RELAXNG_PARAM:
Daniel Veillard144fae12003-02-03 13:17:57 +00003498 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003499 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003500 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003501 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00003502 case XML_RELAXNG_NOOP:
3503 TODO
3504 if (ctxt->error != NULL)
3505 ctxt->error(ctxt->userData,
3506 "Internal error, noop found\n");
3507 ctxt->nbErrors++;
3508 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003509 }
3510 }
3511 child = child->next;
3512 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003513 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003514 return(ret);
3515}
3516
3517/**
3518 * xmlRelaxNGParsePatterns:
3519 * @ctxt: a Relax-NG parser context
3520 * @nodes: list of nodes
Daniel Veillard154877e2003-01-30 12:17:05 +00003521 * @group: use an implicit <group> for elements
Daniel Veillard6eadf632003-01-23 18:29:16 +00003522 *
3523 * parse the content of a RelaxNG start node.
3524 *
3525 * Returns the definition pointer or NULL in case of error.
3526 */
3527static xmlRelaxNGDefinePtr
Daniel Veillard154877e2003-01-30 12:17:05 +00003528xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes,
3529 int group) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003530 xmlRelaxNGDefinePtr def = NULL, last = NULL, cur, parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003531
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003532 parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003533 while (nodes != NULL) {
3534 if (IS_RELAXNG(nodes, "element")) {
3535 cur = xmlRelaxNGParseElement(ctxt, nodes);
3536 if (def == NULL) {
3537 def = last = cur;
3538 } else {
Daniel Veillard154877e2003-01-30 12:17:05 +00003539 if ((group == 1) && (def->type == XML_RELAXNG_ELEMENT) &&
3540 (def == last)) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003541 def = xmlRelaxNGNewDefine(ctxt, nodes);
3542 def->type = XML_RELAXNG_GROUP;
3543 def->content = last;
3544 }
3545 last->next = cur;
3546 last = cur;
3547 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003548 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003549 } else {
3550 cur = xmlRelaxNGParsePattern(ctxt, nodes);
Daniel Veillard419a7682003-02-03 23:22:49 +00003551 if (cur != NULL) {
3552 if (def == NULL) {
3553 def = last = cur;
3554 } else {
3555 last->next = cur;
3556 last = cur;
3557 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003558 }
3559 }
3560 nodes = nodes->next;
3561 }
3562 return(def);
3563}
3564
3565/**
3566 * xmlRelaxNGParseStart:
3567 * @ctxt: a Relax-NG parser context
3568 * @nodes: start children nodes
3569 *
3570 * parse the content of a RelaxNG start node.
3571 *
3572 * Returns 0 in case of success, -1 in case of error
3573 */
3574static int
3575xmlRelaxNGParseStart(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
3576 int ret = 0;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003577 xmlRelaxNGDefinePtr def = NULL, last;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003578
Daniel Veillardd2298792003-02-14 16:54:11 +00003579 if (nodes == NULL) {
3580 if (ctxt->error != NULL)
3581 ctxt->error(ctxt->userData,
3582 "start has no children\n");
3583 ctxt->nbErrors++;
3584 return(-1);
3585 }
3586 if (IS_RELAXNG(nodes, "empty")) {
3587 def = xmlRelaxNGNewDefine(ctxt, nodes);
3588 if (def == NULL)
3589 return(-1);
3590 def->type = XML_RELAXNG_EMPTY;
3591 if (nodes->children != NULL) {
3592 if (ctxt->error != NULL)
3593 ctxt->error(ctxt->userData, "element empty is not empty\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003594 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003595 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003596 } else if (IS_RELAXNG(nodes, "notAllowed")) {
3597 def = xmlRelaxNGNewDefine(ctxt, nodes);
3598 if (def == NULL)
3599 return(-1);
3600 def->type = XML_RELAXNG_NOT_ALLOWED;
3601 if (nodes->children != NULL) {
3602 if (ctxt->error != NULL)
3603 ctxt->error(ctxt->userData,
3604 "element notAllowed is not empty\n");
3605 ctxt->nbErrors++;
3606 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003607 } else {
3608 def = xmlRelaxNGParsePatterns(ctxt, nodes, 1);
Daniel Veillard2df2de22003-02-17 23:34:33 +00003609 }
3610 if (ctxt->grammar->start != NULL) {
3611 last = ctxt->grammar->start;
3612 while (last->next != NULL)
3613 last = last->next;
3614 last->next = def;
3615 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00003616 ctxt->grammar->start = def;
3617 }
3618 nodes = nodes->next;
3619 if (nodes != NULL) {
3620 if (ctxt->error != NULL)
3621 ctxt->error(ctxt->userData,
3622 "start more than one children\n");
3623 ctxt->nbErrors++;
3624 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003625 }
3626 return(ret);
3627}
3628
3629/**
3630 * xmlRelaxNGParseGrammarContent:
3631 * @ctxt: a Relax-NG parser context
3632 * @nodes: grammar children nodes
3633 *
3634 * parse the content of a RelaxNG grammar node.
3635 *
3636 * Returns 0 in case of success, -1 in case of error
3637 */
3638static int
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003639xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003640{
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003641 int ret = 0, tmp;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003642
3643 if (nodes == NULL) {
3644 if (ctxt->error != NULL)
3645 ctxt->error(ctxt->userData,
3646 "grammar has no children\n");
3647 ctxt->nbErrors++;
3648 return(-1);
3649 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003650 while (nodes != NULL) {
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003651 if (IS_RELAXNG(nodes, "start")) {
3652 if (nodes->children == NULL) {
3653 if (ctxt->error != NULL)
3654 ctxt->error(ctxt->userData,
Daniel Veillardd2298792003-02-14 16:54:11 +00003655 "start has no children\n");
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003656 ctxt->nbErrors++;
3657 } else {
3658 tmp = xmlRelaxNGParseStart(ctxt, nodes->children);
3659 if (tmp != 0)
3660 ret = -1;
3661 }
3662 } else if (IS_RELAXNG(nodes, "define")) {
3663 tmp = xmlRelaxNGParseDefine(ctxt, nodes);
3664 if (tmp != 0)
3665 ret = -1;
3666 } else if (IS_RELAXNG(nodes, "include")) {
3667 tmp = xmlRelaxNGParseInclude(ctxt, nodes);
3668 if (tmp != 0)
3669 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003670 } else {
3671 if (ctxt->error != NULL)
3672 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003673 "grammar has unexpected child %s\n", nodes->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003674 ctxt->nbErrors++;
3675 ret = -1;
3676 }
3677 nodes = nodes->next;
3678 }
3679 return (ret);
3680}
3681
3682/**
3683 * xmlRelaxNGCheckReference:
3684 * @ref: the ref
3685 * @ctxt: a Relax-NG parser context
3686 * @name: the name associated to the defines
3687 *
3688 * Applies the 4.17. combine attribute rule for all the define
3689 * element of a given grammar using the same name.
3690 */
3691static void
3692xmlRelaxNGCheckReference(xmlRelaxNGDefinePtr ref,
3693 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
3694 xmlRelaxNGGrammarPtr grammar;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003695 xmlRelaxNGDefinePtr def, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003696
3697 grammar = ctxt->grammar;
3698 if (grammar == NULL) {
3699 if (ctxt->error != NULL)
3700 ctxt->error(ctxt->userData,
3701 "Internal error: no grammar in CheckReference %s\n",
3702 name);
3703 ctxt->nbErrors++;
3704 return;
3705 }
3706 if (ref->content != NULL) {
3707 if (ctxt->error != NULL)
3708 ctxt->error(ctxt->userData,
3709 "Internal error: reference has content in CheckReference %s\n",
3710 name);
3711 ctxt->nbErrors++;
3712 return;
3713 }
3714 if (grammar->defs != NULL) {
3715 def = xmlHashLookup(grammar->defs, name);
3716 if (def != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00003717 cur = ref;
3718 while (cur != NULL) {
3719 cur->content = def;
3720 cur = cur->nextHash;
3721 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003722 } else {
Daniel Veillardd4310742003-02-18 21:12:46 +00003723 if (ctxt->error != NULL)
3724 ctxt->error(ctxt->userData,
3725 "Reference %s has no matching definition\n",
3726 name);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003727 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003728 }
Daniel Veillardd4310742003-02-18 21:12:46 +00003729 } else {
3730 if (ctxt->error != NULL)
3731 ctxt->error(ctxt->userData,
3732 "Reference %s has no matching definition\n",
3733 name);
3734 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003735 }
3736 /*
3737 * TODO: make a closure and verify there is no loop !
3738 */
3739}
3740
3741/**
3742 * xmlRelaxNGCheckCombine:
3743 * @define: the define(s) list
3744 * @ctxt: a Relax-NG parser context
3745 * @name: the name associated to the defines
3746 *
3747 * Applies the 4.17. combine attribute rule for all the define
3748 * element of a given grammar using the same name.
3749 */
3750static void
3751xmlRelaxNGCheckCombine(xmlRelaxNGDefinePtr define,
3752 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
3753 xmlChar *combine;
3754 int choiceOrInterleave = -1;
3755 int missing = 0;
3756 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
3757
3758 if (define->nextHash == NULL)
3759 return;
3760 cur = define;
3761 while (cur != NULL) {
3762 combine = xmlGetProp(cur->node, BAD_CAST "combine");
3763 if (combine != NULL) {
3764 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3765 if (choiceOrInterleave == -1)
3766 choiceOrInterleave = 1;
3767 else if (choiceOrInterleave == 0) {
3768 if (ctxt->error != NULL)
3769 ctxt->error(ctxt->userData,
3770 "Defines for %s use both 'choice' and 'interleave'\n",
3771 name);
3772 ctxt->nbErrors++;
3773 }
Daniel Veillard154877e2003-01-30 12:17:05 +00003774 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003775 if (choiceOrInterleave == -1)
3776 choiceOrInterleave = 0;
3777 else if (choiceOrInterleave == 1) {
3778 if (ctxt->error != NULL)
3779 ctxt->error(ctxt->userData,
3780 "Defines for %s use both 'choice' and 'interleave'\n",
3781 name);
3782 ctxt->nbErrors++;
3783 }
3784 } else {
3785 if (ctxt->error != NULL)
3786 ctxt->error(ctxt->userData,
3787 "Defines for %s use unknown combine value '%s''\n",
3788 name, combine);
3789 ctxt->nbErrors++;
3790 }
3791 xmlFree(combine);
3792 } else {
3793 if (missing == 0)
3794 missing = 1;
3795 else {
3796 if (ctxt->error != NULL)
3797 ctxt->error(ctxt->userData,
3798 "Some defines for %s lacks the combine attribute\n",
3799 name);
3800 ctxt->nbErrors++;
3801 }
3802 }
3803
3804 cur = cur->nextHash;
3805 }
3806#ifdef DEBUG
3807 xmlGenericError(xmlGenericErrorContext,
3808 "xmlRelaxNGCheckCombine(): merging %s defines: %d\n",
3809 name, choiceOrInterleave);
3810#endif
3811 if (choiceOrInterleave == -1)
3812 choiceOrInterleave = 0;
3813 cur = xmlRelaxNGNewDefine(ctxt, define->node);
3814 if (cur == NULL)
3815 return;
3816 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003817 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard154877e2003-01-30 12:17:05 +00003818 else
3819 cur->type = XML_RELAXNG_CHOICE;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003820 tmp = define;
3821 last = NULL;
3822 while (tmp != NULL) {
3823 if (tmp->content != NULL) {
3824 if (tmp->content->next != NULL) {
3825 /*
3826 * we need first to create a wrapper.
3827 */
3828 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
3829 if (tmp2 == NULL)
3830 break;
3831 tmp2->type = XML_RELAXNG_GROUP;
3832 tmp2->content = tmp->content;
3833 } else {
3834 tmp2 = tmp->content;
3835 }
3836 if (last == NULL) {
3837 cur->content = tmp2;
3838 } else {
3839 last->next = tmp2;
3840 }
3841 last = tmp2;
3842 tmp->content = NULL;
3843 }
3844 tmp = tmp->nextHash;
3845 }
3846 define->content = cur;
Daniel Veillard154877e2003-01-30 12:17:05 +00003847 if (choiceOrInterleave == 0) {
3848 if (ctxt->interleaves == NULL)
3849 ctxt->interleaves = xmlHashCreate(10);
3850 if (ctxt->interleaves == NULL) {
3851 if (ctxt->error != NULL)
3852 ctxt->error(ctxt->userData,
3853 "Failed to create interleaves hash table\n");
3854 ctxt->nbErrors++;
3855 } else {
3856 char tmpname[32];
3857
3858 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
3859 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
3860 if (ctxt->error != NULL)
3861 ctxt->error(ctxt->userData,
3862 "Failed to add %s to hash table\n", tmpname);
3863 ctxt->nbErrors++;
3864 }
3865 }
3866 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003867}
3868
3869/**
3870 * xmlRelaxNGCombineStart:
3871 * @ctxt: a Relax-NG parser context
3872 * @grammar: the grammar
3873 *
3874 * Applies the 4.17. combine rule for all the start
3875 * element of a given grammar.
3876 */
3877static void
3878xmlRelaxNGCombineStart(xmlRelaxNGParserCtxtPtr ctxt,
3879 xmlRelaxNGGrammarPtr grammar) {
3880 xmlRelaxNGDefinePtr starts;
3881 xmlChar *combine;
3882 int choiceOrInterleave = -1;
3883 int missing = 0;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003884 xmlRelaxNGDefinePtr cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003885
Daniel Veillard2df2de22003-02-17 23:34:33 +00003886 starts = grammar->start;
3887 if ((starts == NULL) || (starts->next == NULL))
Daniel Veillard6eadf632003-01-23 18:29:16 +00003888 return;
3889 cur = starts;
3890 while (cur != NULL) {
Daniel Veillard2df2de22003-02-17 23:34:33 +00003891 if ((cur->node == NULL) || (cur->node->parent == NULL) ||
3892 (!xmlStrEqual(cur->node->parent->name, BAD_CAST "start"))) {
3893 combine = NULL;
3894 if (ctxt->error != NULL)
3895 ctxt->error(ctxt->userData,
3896 "Internal error: start element not found\n");
3897 ctxt->nbErrors++;
3898 } else {
3899 combine = xmlGetProp(cur->node->parent, BAD_CAST "combine");
3900 }
3901
Daniel Veillard6eadf632003-01-23 18:29:16 +00003902 if (combine != NULL) {
3903 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3904 if (choiceOrInterleave == -1)
3905 choiceOrInterleave = 1;
3906 else if (choiceOrInterleave == 0) {
3907 if (ctxt->error != NULL)
3908 ctxt->error(ctxt->userData,
3909 "<start> use both 'choice' and 'interleave'\n");
3910 ctxt->nbErrors++;
3911 }
Daniel Veillard2df2de22003-02-17 23:34:33 +00003912 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003913 if (choiceOrInterleave == -1)
3914 choiceOrInterleave = 0;
3915 else if (choiceOrInterleave == 1) {
3916 if (ctxt->error != NULL)
3917 ctxt->error(ctxt->userData,
3918 "<start> use both 'choice' and 'interleave'\n");
3919 ctxt->nbErrors++;
3920 }
3921 } else {
3922 if (ctxt->error != NULL)
3923 ctxt->error(ctxt->userData,
3924 "<start> uses unknown combine value '%s''\n", combine);
3925 ctxt->nbErrors++;
3926 }
3927 xmlFree(combine);
3928 } else {
3929 if (missing == 0)
3930 missing = 1;
3931 else {
3932 if (ctxt->error != NULL)
3933 ctxt->error(ctxt->userData,
3934 "Some <start> elements lacks the combine attribute\n");
3935 ctxt->nbErrors++;
3936 }
3937 }
3938
Daniel Veillard2df2de22003-02-17 23:34:33 +00003939 cur = cur->next;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003940 }
3941#ifdef DEBUG
3942 xmlGenericError(xmlGenericErrorContext,
3943 "xmlRelaxNGCombineStart(): merging <start>: %d\n",
3944 choiceOrInterleave);
3945#endif
3946 if (choiceOrInterleave == -1)
3947 choiceOrInterleave = 0;
3948 cur = xmlRelaxNGNewDefine(ctxt, starts->node);
3949 if (cur == NULL)
3950 return;
3951 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003952 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003953 else
3954 cur->type = XML_RELAXNG_CHOICE;
3955 cur->content = grammar->start;
3956 grammar->start = cur;
3957 if (choiceOrInterleave == 0) {
3958 if (ctxt->interleaves == NULL)
3959 ctxt->interleaves = xmlHashCreate(10);
3960 if (ctxt->interleaves == NULL) {
3961 if (ctxt->error != NULL)
3962 ctxt->error(ctxt->userData,
3963 "Failed to create interleaves hash table\n");
3964 ctxt->nbErrors++;
3965 } else {
3966 char tmpname[32];
3967
3968 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
3969 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
3970 if (ctxt->error != NULL)
3971 ctxt->error(ctxt->userData,
3972 "Failed to add %s to hash table\n", tmpname);
3973 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003974 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003975 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003976 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003977}
3978
3979/**
Daniel Veillardd4310742003-02-18 21:12:46 +00003980 * xmlRelaxNGCheckCycles:
3981 * @ctxt: a Relax-NG parser context
3982 * @nodes: grammar children nodes
3983 * @depth: the counter
3984 *
3985 * Check for cycles.
3986 *
3987 * Returns 0 if check passed, and -1 in case of error
3988 */
3989static int
3990xmlRelaxNGCheckCycles(xmlRelaxNGParserCtxtPtr ctxt,
3991 xmlRelaxNGDefinePtr cur, int depth) {
3992 int ret = 0;
3993
3994 while ((ret == 0) && (cur != NULL)) {
3995 if ((cur->type == XML_RELAXNG_REF) ||
3996 (cur->type == XML_RELAXNG_PARENTREF)) {
3997 if (cur->depth == -1) {
3998 cur->depth = depth;
3999 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth);
4000 cur->depth = -2;
4001 } else if (depth == cur->depth) {
4002 if (ctxt->error != NULL)
4003 ctxt->error(ctxt->userData,
4004 "Detected a cycle in %s references\n", cur->name);
4005 ctxt->nbErrors++;
4006 return(-1);
4007 }
4008 } else if (cur->type == XML_RELAXNG_ELEMENT) {
4009 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth + 1);
4010 } else {
4011 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth);
4012 }
4013 cur = cur->next;
4014 }
4015 return(ret);
4016}
4017
4018/**
Daniel Veillard77648bb2003-02-20 15:03:22 +00004019 * xmlRelaxNGTryUnlink:
4020 * @ctxt: a Relax-NG parser context
4021 * @cur: the definition to unlink
4022 * @parent: the parent definition
4023 * @prev: the previous sibling definition
4024 *
4025 * Try to unlink a definition. If not possble make it a NOOP
4026 *
4027 * Returns the new prev definition
4028 */
4029static xmlRelaxNGDefinePtr
4030xmlRelaxNGTryUnlink(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
4031 xmlRelaxNGDefinePtr cur,
4032 xmlRelaxNGDefinePtr parent,
4033 xmlRelaxNGDefinePtr prev) {
4034 if (prev != NULL) {
4035 prev->next = cur->next;
4036 } else {
4037 if (parent != NULL) {
4038 if (parent->content == cur)
4039 parent->content = cur->next;
4040 else if (parent->attrs == cur)
4041 parent->attrs = cur->next;
4042 else if (parent->nameClass == cur)
4043 parent->nameClass = cur->next;
4044 } else {
4045 cur->type = XML_RELAXNG_NOOP;
4046 prev = cur;
4047 }
4048 }
4049 return(prev);
4050}
4051
4052/**
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004053 * xmlRelaxNGSimplify:
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004054 * @ctxt: a Relax-NG parser context
4055 * @nodes: grammar children nodes
4056 *
4057 * Check for simplification of empty and notAllowed
4058 */
4059static void
4060xmlRelaxNGSimplify(xmlRelaxNGParserCtxtPtr ctxt,
4061 xmlRelaxNGDefinePtr cur,
4062 xmlRelaxNGDefinePtr parent) {
4063 xmlRelaxNGDefinePtr prev = NULL;
4064
4065 while (cur != NULL) {
4066 if ((cur->type == XML_RELAXNG_REF) ||
4067 (cur->type == XML_RELAXNG_PARENTREF)) {
4068 if (cur->depth != -3) {
4069 cur->depth = -3;
4070 xmlRelaxNGSimplify(ctxt, cur->content, cur);
4071 }
4072 } else if (cur->type == XML_RELAXNG_NOT_ALLOWED) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004073 cur->parent = parent;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004074 if ((parent != NULL) &&
4075 ((parent->type == XML_RELAXNG_ATTRIBUTE) ||
4076 (parent->type == XML_RELAXNG_LIST) ||
4077 (parent->type == XML_RELAXNG_GROUP) ||
4078 (parent->type == XML_RELAXNG_INTERLEAVE) ||
4079 (parent->type == XML_RELAXNG_ONEORMORE) ||
4080 (parent->type == XML_RELAXNG_ZEROORMORE))) {
4081 parent->type = XML_RELAXNG_NOT_ALLOWED;
4082 break;
4083 }
4084 if ((parent != NULL) &&
4085 (parent->type == XML_RELAXNG_CHOICE)) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004086 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004087 } else
4088 prev = cur;
4089 } else if (cur->type == XML_RELAXNG_EMPTY){
Daniel Veillard77648bb2003-02-20 15:03:22 +00004090 cur->parent = parent;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004091 if ((parent != NULL) &&
4092 ((parent->type == XML_RELAXNG_ONEORMORE) ||
4093 (parent->type == XML_RELAXNG_ZEROORMORE))) {
4094 parent->type = XML_RELAXNG_EMPTY;
4095 break;
4096 }
4097 if ((parent != NULL) &&
4098 ((parent->type == XML_RELAXNG_GROUP) ||
4099 (parent->type == XML_RELAXNG_INTERLEAVE))) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004100 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004101 } else
4102 prev = cur;
4103 } else {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004104 cur->parent = parent;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004105 if (cur->content != NULL)
4106 xmlRelaxNGSimplify(ctxt, cur->content, cur);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004107 if (cur->attrs != NULL)
4108 xmlRelaxNGSimplify(ctxt, cur->attrs, cur);
4109 if (cur->nameClass != NULL)
4110 xmlRelaxNGSimplify(ctxt, cur->nameClass, cur);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004111 /*
4112 * This may result in a simplification
4113 */
4114 if ((cur->type == XML_RELAXNG_GROUP) ||
4115 (cur->type == XML_RELAXNG_INTERLEAVE)) {
4116 if (cur->content == NULL)
4117 cur->type = XML_RELAXNG_EMPTY;
4118 else if (cur->content->next == NULL) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004119 if ((parent == NULL) && (prev == NULL)) {
4120 cur->type = XML_RELAXNG_NOOP;
4121 } else if (prev == NULL) {
4122 parent->content = cur->content;
4123 cur->content->next = cur->next;
4124 cur = cur->content;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004125 } else {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004126 cur->content->next = cur->next;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004127 prev->next = cur->content;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004128 cur = cur->content;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004129 }
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004130 }
4131 }
4132 /*
4133 * the current node may have been transformed back
4134 */
4135 if ((cur->type == XML_RELAXNG_EXCEPT) &&
4136 (cur->content != NULL) &&
4137 (cur->content->type == XML_RELAXNG_NOT_ALLOWED)) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004138 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004139 } else if (cur->type == XML_RELAXNG_NOT_ALLOWED) {
4140 if ((parent != NULL) &&
4141 ((parent->type == XML_RELAXNG_ATTRIBUTE) ||
4142 (parent->type == XML_RELAXNG_LIST) ||
4143 (parent->type == XML_RELAXNG_GROUP) ||
4144 (parent->type == XML_RELAXNG_INTERLEAVE) ||
4145 (parent->type == XML_RELAXNG_ONEORMORE) ||
4146 (parent->type == XML_RELAXNG_ZEROORMORE))) {
4147 parent->type = XML_RELAXNG_NOT_ALLOWED;
4148 break;
4149 }
4150 if ((parent != NULL) &&
4151 (parent->type == XML_RELAXNG_CHOICE)) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004152 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004153 } else
4154 prev = cur;
4155 } else if (cur->type == XML_RELAXNG_EMPTY){
4156 if ((parent != NULL) &&
4157 ((parent->type == XML_RELAXNG_ONEORMORE) ||
4158 (parent->type == XML_RELAXNG_ZEROORMORE))) {
4159 parent->type = XML_RELAXNG_EMPTY;
4160 break;
4161 }
4162 if ((parent != NULL) &&
4163 ((parent->type == XML_RELAXNG_GROUP) ||
4164 (parent->type == XML_RELAXNG_INTERLEAVE) ||
4165 (parent->type == XML_RELAXNG_CHOICE))) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004166 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004167 } else
4168 prev = cur;
4169 } else {
4170 prev = cur;
4171 }
4172 }
4173 cur = cur->next;
4174 }
4175}
4176
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004177/**
4178 * xmlRelaxNGGroupContentType:
4179 * @ct1: the first content type
4180 * @ct2: the second content type
4181 *
4182 * Try to group 2 content types
4183 *
4184 * Returns the content type
4185 */
4186static xmlRelaxNGContentType
4187xmlRelaxNGGroupContentType(xmlRelaxNGContentType ct1,
4188 xmlRelaxNGContentType ct2) {
4189 if ((ct1 == XML_RELAXNG_CONTENT_ERROR) ||
4190 (ct2 == XML_RELAXNG_CONTENT_ERROR))
4191 return(XML_RELAXNG_CONTENT_ERROR);
4192 if (ct1 == XML_RELAXNG_CONTENT_EMPTY)
4193 return(ct2);
4194 if (ct2 == XML_RELAXNG_CONTENT_EMPTY)
4195 return(ct1);
4196 if ((ct1 == XML_RELAXNG_CONTENT_COMPLEX) &&
4197 (ct2 == XML_RELAXNG_CONTENT_COMPLEX))
4198 return(XML_RELAXNG_CONTENT_COMPLEX);
4199 return(XML_RELAXNG_CONTENT_ERROR);
4200}
4201
4202/**
4203 * xmlRelaxNGMaxContentType:
4204 * @ct1: the first content type
4205 * @ct2: the second content type
4206 *
4207 * Compute the max content-type
4208 *
4209 * Returns the content type
4210 */
4211static xmlRelaxNGContentType
4212xmlRelaxNGMaxContentType(xmlRelaxNGContentType ct1,
4213 xmlRelaxNGContentType ct2) {
4214 if ((ct1 == XML_RELAXNG_CONTENT_ERROR) ||
4215 (ct2 == XML_RELAXNG_CONTENT_ERROR))
4216 return(XML_RELAXNG_CONTENT_ERROR);
4217 if ((ct1 == XML_RELAXNG_CONTENT_SIMPLE) ||
4218 (ct2 == XML_RELAXNG_CONTENT_SIMPLE))
4219 return(XML_RELAXNG_CONTENT_SIMPLE);
4220 if ((ct1 == XML_RELAXNG_CONTENT_COMPLEX) ||
4221 (ct2 == XML_RELAXNG_CONTENT_COMPLEX))
4222 return(XML_RELAXNG_CONTENT_COMPLEX);
4223 return(XML_RELAXNG_CONTENT_EMPTY);
4224}
Daniel Veillard77648bb2003-02-20 15:03:22 +00004225
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004226/**
4227 * xmlRelaxNGCheckRules:
4228 * @ctxt: a Relax-NG parser context
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004229 * @cur: the current definition
Daniel Veillard77648bb2003-02-20 15:03:22 +00004230 * @flags: some accumulated flags
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004231 * @ptype: the parent type
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004232 *
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004233 * Check for rules in section 7.1 and 7.2
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004234 *
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004235 * Returns the content type of @cur
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004236 */
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004237static xmlRelaxNGContentType
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004238xmlRelaxNGCheckRules(xmlRelaxNGParserCtxtPtr ctxt,
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004239 xmlRelaxNGDefinePtr cur, int flags,
4240 xmlRelaxNGType ptype) {
4241 int nflags = flags;
4242 xmlRelaxNGContentType ret, tmp, val = XML_RELAXNG_CONTENT_EMPTY;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004243
Daniel Veillard77648bb2003-02-20 15:03:22 +00004244 while (cur != NULL) {
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004245 ret = XML_RELAXNG_CONTENT_EMPTY;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004246 if ((cur->type == XML_RELAXNG_REF) ||
4247 (cur->type == XML_RELAXNG_PARENTREF)) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004248 if (flags & XML_RELAXNG_IN_LIST) {
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004249 if (ctxt->error != NULL)
4250 ctxt->error(ctxt->userData,
Daniel Veillard77648bb2003-02-20 15:03:22 +00004251 "Found forbidden pattern list//ref\n");
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004252 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004253 }
4254 if (flags & XML_RELAXNG_IN_ATTRIBUTE) {
4255 if (ctxt->error != NULL)
4256 ctxt->error(ctxt->userData,
4257 "Found forbidden pattern attribute//ref\n");
4258 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004259 }
4260 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4261 if (ctxt->error != NULL)
4262 ctxt->error(ctxt->userData,
4263 "Found forbidden pattern data/except//ref\n");
4264 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004265 }
Daniel Veillardc5312d72003-02-21 17:14:10 +00004266 if (cur->depth > -4) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004267 cur->depth = -4;
Daniel Veillardc5312d72003-02-21 17:14:10 +00004268 ret = xmlRelaxNGCheckRules(ctxt, cur->content,
4269 flags, cur->type);
4270 cur->depth = ret - 15 ;
4271 } else if (cur->depth == -4) {
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004272 ret = XML_RELAXNG_CONTENT_COMPLEX;
Daniel Veillardc5312d72003-02-21 17:14:10 +00004273 } else {
4274 ret = (xmlRelaxNGContentType) cur->depth + 15;
4275 }
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004276 } else if (cur->type == XML_RELAXNG_ELEMENT) {
Daniel Veillard44e1dd02003-02-21 23:23:28 +00004277 /*
4278 * The 7.3 Attribute derivation rule for groups is plugged there
4279 */
4280 xmlRelaxNGCheckGroupAttrs(ctxt, cur);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004281 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4282 if (ctxt->error != NULL)
4283 ctxt->error(ctxt->userData,
4284 "Found forbidden pattern data/except//element(ref)\n");
4285 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004286 }
4287 if (flags & XML_RELAXNG_IN_LIST) {
4288 if (ctxt->error != NULL)
4289 ctxt->error(ctxt->userData,
4290 "Found forbidden pattern list//element(ref)\n");
4291 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004292 }
4293 if (flags & XML_RELAXNG_IN_ATTRIBUTE) {
4294 if (ctxt->error != NULL)
4295 ctxt->error(ctxt->userData,
4296 "Found forbidden pattern attribute//element(ref)\n");
4297 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004298 }
4299 /*
4300 * reset since in the simple form elements are only child
4301 * of grammar/define
4302 */
4303 nflags = 0;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004304 ret = xmlRelaxNGCheckRules(ctxt, cur->attrs, nflags, cur->type);
4305 if (ret != XML_RELAXNG_CONTENT_EMPTY) {
4306 if (ctxt->error != NULL)
4307 ctxt->error(ctxt->userData,
4308 "Element %s attributes have a content type error\n",
4309 cur->name);
4310 ctxt->nbErrors++;
4311 }
4312 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
4313 if (ret == XML_RELAXNG_CONTENT_ERROR) {
4314 if (ctxt->error != NULL)
4315 ctxt->error(ctxt->userData,
4316 "Element %s has a content type error\n",
4317 cur->name);
4318 ctxt->nbErrors++;
4319 } else {
4320 ret = XML_RELAXNG_CONTENT_COMPLEX;
4321 }
Daniel Veillard77648bb2003-02-20 15:03:22 +00004322 } else if (cur->type == XML_RELAXNG_ATTRIBUTE) {
4323 if (flags & XML_RELAXNG_IN_ATTRIBUTE) {
4324 if (ctxt->error != NULL)
4325 ctxt->error(ctxt->userData,
4326 "Found forbidden pattern attribute//attribute\n");
4327 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004328 }
4329 if (flags & XML_RELAXNG_IN_LIST) {
4330 if (ctxt->error != NULL)
4331 ctxt->error(ctxt->userData,
4332 "Found forbidden pattern list//attribute\n");
4333 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004334 }
4335 if (flags & XML_RELAXNG_IN_OOMGROUP) {
4336 if (ctxt->error != NULL)
4337 ctxt->error(ctxt->userData,
4338 "Found forbidden pattern oneOrMore//group//attribute\n");
4339 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004340 }
4341 if (flags & XML_RELAXNG_IN_OOMINTERLEAVE) {
4342 if (ctxt->error != NULL)
4343 ctxt->error(ctxt->userData,
4344 "Found forbidden pattern oneOrMore//interleave//attribute\n");
4345 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004346 }
4347 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4348 if (ctxt->error != NULL)
4349 ctxt->error(ctxt->userData,
4350 "Found forbidden pattern data/except//attribute\n");
4351 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004352 }
4353 if (flags & XML_RELAXNG_IN_START) {
4354 if (ctxt->error != NULL)
4355 ctxt->error(ctxt->userData,
4356 "Found forbidden pattern start//attribute\n");
4357 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004358 }
4359 nflags = flags | XML_RELAXNG_IN_ATTRIBUTE;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004360 xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
4361 ret = XML_RELAXNG_CONTENT_EMPTY;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004362 } else if ((cur->type == XML_RELAXNG_ONEORMORE) ||
4363 (cur->type == XML_RELAXNG_ZEROORMORE)) {
4364 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4365 if (ctxt->error != NULL)
4366 ctxt->error(ctxt->userData,
4367 "Found forbidden pattern data/except//oneOrMore\n");
4368 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004369 }
4370 if (flags & XML_RELAXNG_IN_START) {
4371 if (ctxt->error != NULL)
4372 ctxt->error(ctxt->userData,
4373 "Found forbidden pattern start//oneOrMore\n");
4374 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004375 }
4376 nflags = flags | XML_RELAXNG_IN_ONEORMORE;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004377 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
4378 ret = xmlRelaxNGGroupContentType(ret, ret);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004379 } else if (cur->type == XML_RELAXNG_LIST) {
4380 if (flags & XML_RELAXNG_IN_LIST) {
4381 if (ctxt->error != NULL)
4382 ctxt->error(ctxt->userData,
4383 "Found forbidden pattern list//list\n");
4384 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004385 }
4386 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4387 if (ctxt->error != NULL)
4388 ctxt->error(ctxt->userData,
4389 "Found forbidden pattern data/except//list\n");
4390 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004391 }
4392 if (flags & XML_RELAXNG_IN_START) {
4393 if (ctxt->error != NULL)
4394 ctxt->error(ctxt->userData,
4395 "Found forbidden pattern start//list\n");
4396 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004397 }
4398 nflags = flags | XML_RELAXNG_IN_LIST;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004399 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004400 } else if (cur->type == XML_RELAXNG_GROUP) {
4401 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4402 if (ctxt->error != NULL)
4403 ctxt->error(ctxt->userData,
4404 "Found forbidden pattern data/except//group\n");
4405 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004406 }
4407 if (flags & XML_RELAXNG_IN_START) {
4408 if (ctxt->error != NULL)
4409 ctxt->error(ctxt->userData,
4410 "Found forbidden pattern start//group\n");
4411 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004412 }
4413 if (flags & XML_RELAXNG_IN_ONEORMORE)
4414 nflags = flags | XML_RELAXNG_IN_OOMGROUP;
4415 else
4416 nflags = flags;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004417 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
Daniel Veillard44e1dd02003-02-21 23:23:28 +00004418 /*
4419 * The 7.3 Attribute derivation rule for groups is plugged there
4420 */
4421 xmlRelaxNGCheckGroupAttrs(ctxt, cur);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004422 } else if (cur->type == XML_RELAXNG_INTERLEAVE) {
4423 if (flags & XML_RELAXNG_IN_LIST) {
4424 if (ctxt->error != NULL)
4425 ctxt->error(ctxt->userData,
4426 "Found forbidden pattern list//interleave\n");
4427 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004428 }
4429 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4430 if (ctxt->error != NULL)
4431 ctxt->error(ctxt->userData,
4432 "Found forbidden pattern data/except//interleave\n");
4433 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004434 }
4435 if (flags & XML_RELAXNG_IN_START) {
4436 if (ctxt->error != NULL)
4437 ctxt->error(ctxt->userData,
4438 "Found forbidden pattern start//interleave\n");
4439 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004440 }
4441 if (flags & XML_RELAXNG_IN_ONEORMORE)
4442 nflags = flags | XML_RELAXNG_IN_OOMINTERLEAVE;
4443 else
4444 nflags = flags;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004445 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004446 } else if (cur->type == XML_RELAXNG_EXCEPT) {
4447 if ((cur->parent != NULL) &&
4448 (cur->parent->type == XML_RELAXNG_DATATYPE))
4449 nflags = flags | XML_RELAXNG_IN_DATAEXCEPT;
4450 else
4451 nflags = flags;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004452 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004453 } else if (cur->type == XML_RELAXNG_DATATYPE) {
4454 if (flags & XML_RELAXNG_IN_START) {
4455 if (ctxt->error != NULL)
4456 ctxt->error(ctxt->userData,
4457 "Found forbidden pattern start//data\n");
4458 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004459 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004460 xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type);
4461 ret = XML_RELAXNG_CONTENT_SIMPLE;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004462 } else if (cur->type == XML_RELAXNG_VALUE) {
4463 if (flags & XML_RELAXNG_IN_START) {
4464 if (ctxt->error != NULL)
4465 ctxt->error(ctxt->userData,
4466 "Found forbidden pattern start//value\n");
4467 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004468 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004469 xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type);
4470 ret = XML_RELAXNG_CONTENT_SIMPLE;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004471 } else if (cur->type == XML_RELAXNG_TEXT) {
4472 if (flags & XML_RELAXNG_IN_LIST) {
4473 if (ctxt->error != NULL)
4474 ctxt->error(ctxt->userData,
4475 "Found forbidden pattern list//text\n");
4476 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004477 }
4478 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4479 if (ctxt->error != NULL)
4480 ctxt->error(ctxt->userData,
4481 "Found forbidden pattern data/except//text\n");
4482 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004483 }
4484 if (flags & XML_RELAXNG_IN_START) {
4485 if (ctxt->error != NULL)
4486 ctxt->error(ctxt->userData,
4487 "Found forbidden pattern start//text\n");
4488 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004489 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004490 ret = XML_RELAXNG_CONTENT_COMPLEX;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004491 } else if (cur->type == XML_RELAXNG_EMPTY) {
4492 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4493 if (ctxt->error != NULL)
4494 ctxt->error(ctxt->userData,
4495 "Found forbidden pattern data/except//empty\n");
4496 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004497 }
4498 if (flags & XML_RELAXNG_IN_START) {
4499 if (ctxt->error != NULL)
4500 ctxt->error(ctxt->userData,
4501 "Found forbidden pattern start//empty\n");
4502 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004503 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004504 ret = XML_RELAXNG_CONTENT_EMPTY;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004505 } else {
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004506 ret = xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004507 }
4508 cur = cur->next;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004509 if (ptype == XML_RELAXNG_GROUP) {
4510 val = xmlRelaxNGGroupContentType(val, ret);
4511 } else if (ptype == XML_RELAXNG_INTERLEAVE) {
4512 tmp = xmlRelaxNGGroupContentType(val, ret);
4513 if (tmp != XML_RELAXNG_CONTENT_ERROR)
4514 tmp = xmlRelaxNGMaxContentType(val, ret);
4515 } else if (ptype == XML_RELAXNG_CHOICE) {
4516 val = xmlRelaxNGMaxContentType(val, ret);
4517 } else if (ptype == XML_RELAXNG_LIST) {
4518 val = XML_RELAXNG_CONTENT_SIMPLE;
4519 } else if (ptype == XML_RELAXNG_EXCEPT) {
4520 if (ret == XML_RELAXNG_CONTENT_ERROR)
4521 val = XML_RELAXNG_CONTENT_ERROR;
4522 else
4523 val = XML_RELAXNG_CONTENT_SIMPLE;
4524 } else {
4525 val = xmlRelaxNGGroupContentType(val, ret);
4526 }
4527
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004528 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004529 return(val);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004530}
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004531
4532/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004533 * xmlRelaxNGParseGrammar:
4534 * @ctxt: a Relax-NG parser context
4535 * @nodes: grammar children nodes
4536 *
4537 * parse a Relax-NG <grammar> node
4538 *
4539 * Returns the internal xmlRelaxNGGrammarPtr built or
4540 * NULL in case of error
4541 */
4542static xmlRelaxNGGrammarPtr
4543xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
4544 xmlRelaxNGGrammarPtr ret, tmp, old;
4545
Daniel Veillard6eadf632003-01-23 18:29:16 +00004546 ret = xmlRelaxNGNewGrammar(ctxt);
4547 if (ret == NULL)
4548 return(NULL);
4549
4550 /*
4551 * Link the new grammar in the tree
4552 */
4553 ret->parent = ctxt->grammar;
4554 if (ctxt->grammar != NULL) {
4555 tmp = ctxt->grammar->children;
4556 if (tmp == NULL) {
4557 ctxt->grammar->children = ret;
4558 } else {
4559 while (tmp->next != NULL)
4560 tmp = tmp->next;
4561 tmp->next = ret;
4562 }
4563 }
4564
4565 old = ctxt->grammar;
4566 ctxt->grammar = ret;
4567 xmlRelaxNGParseGrammarContent(ctxt, nodes);
4568 ctxt->grammar = ret;
Daniel Veillard2df2de22003-02-17 23:34:33 +00004569 if (ctxt->grammar == NULL) {
4570 if (ctxt->error != NULL)
4571 ctxt->error(ctxt->userData,
4572 "Failed to parse <grammar> content\n");
4573 ctxt->nbErrors++;
4574 } else if (ctxt->grammar->start == NULL) {
4575 if (ctxt->error != NULL)
4576 ctxt->error(ctxt->userData,
4577 "Element <grammar> has no <start>\n");
4578 ctxt->nbErrors++;
4579 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004580
4581 /*
4582 * Apply 4.17 mergingd rules to defines and starts
4583 */
4584 xmlRelaxNGCombineStart(ctxt, ret);
4585 if (ret->defs != NULL) {
4586 xmlHashScan(ret->defs, (xmlHashScanner) xmlRelaxNGCheckCombine,
4587 ctxt);
4588 }
4589
4590 /*
4591 * link together defines and refs in this grammar
4592 */
4593 if (ret->refs != NULL) {
4594 xmlHashScan(ret->refs, (xmlHashScanner) xmlRelaxNGCheckReference,
4595 ctxt);
4596 }
4597 ctxt->grammar = old;
4598 return(ret);
4599}
4600
4601/**
4602 * xmlRelaxNGParseDocument:
4603 * @ctxt: a Relax-NG parser context
4604 * @node: the root node of the RelaxNG schema
4605 *
4606 * parse a Relax-NG definition resource and build an internal
4607 * xmlRelaxNG struture which can be used to validate instances.
4608 *
4609 * Returns the internal XML RelaxNG structure built or
4610 * NULL in case of error
4611 */
4612static xmlRelaxNGPtr
4613xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
4614 xmlRelaxNGPtr schema = NULL;
Daniel Veillard276be4a2003-01-24 01:03:34 +00004615 const xmlChar *olddefine;
Daniel Veillarde431a272003-01-29 23:02:33 +00004616 xmlRelaxNGGrammarPtr old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004617
4618 if ((ctxt == NULL) || (node == NULL))
4619 return (NULL);
4620
4621 schema = xmlRelaxNGNewRelaxNG(ctxt);
4622 if (schema == NULL)
4623 return(NULL);
4624
Daniel Veillard276be4a2003-01-24 01:03:34 +00004625 olddefine = ctxt->define;
4626 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004627 if (IS_RELAXNG(node, "grammar")) {
4628 schema->topgrammar = xmlRelaxNGParseGrammar(ctxt, node->children);
4629 } else {
4630 schema->topgrammar = xmlRelaxNGNewGrammar(ctxt);
4631 if (schema->topgrammar == NULL) {
4632 return(schema);
4633 }
4634 schema->topgrammar->parent = NULL;
Daniel Veillarde431a272003-01-29 23:02:33 +00004635 old = ctxt->grammar;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004636 ctxt->grammar = schema->topgrammar;
4637 xmlRelaxNGParseStart(ctxt, node);
Daniel Veillarde431a272003-01-29 23:02:33 +00004638 if (old != NULL)
4639 ctxt->grammar = old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004640 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00004641 ctxt->define = olddefine;
Daniel Veillardd4310742003-02-18 21:12:46 +00004642 if (schema->topgrammar->start != NULL) {
4643 xmlRelaxNGCheckCycles(ctxt, schema->topgrammar->start, 0);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004644 if ((ctxt->flags & XML_RELAXNG_IN_EXTERNALREF) == 0) {
4645 xmlRelaxNGSimplify(ctxt, schema->topgrammar->start, NULL);
4646 while ((schema->topgrammar->start != NULL) &&
4647 (schema->topgrammar->start->type == XML_RELAXNG_NOOP) &&
4648 (schema->topgrammar->start->next != NULL))
4649 schema->topgrammar->start = schema->topgrammar->start->content;
4650 xmlRelaxNGCheckRules(ctxt, schema->topgrammar->start,
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004651 XML_RELAXNG_IN_START, XML_RELAXNG_NOOP);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004652 }
Daniel Veillardd4310742003-02-18 21:12:46 +00004653 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004654
4655#ifdef DEBUG
4656 if (schema == NULL)
4657 xmlGenericError(xmlGenericErrorContext,
4658 "xmlRelaxNGParseDocument() failed\n");
4659#endif
4660
4661 return (schema);
4662}
4663
4664/************************************************************************
4665 * *
4666 * Reading RelaxNGs *
4667 * *
4668 ************************************************************************/
4669
4670/**
4671 * xmlRelaxNGNewParserCtxt:
4672 * @URL: the location of the schema
4673 *
4674 * Create an XML RelaxNGs parse context for that file/resource expected
4675 * to contain an XML RelaxNGs file.
4676 *
4677 * Returns the parser context or NULL in case of error
4678 */
4679xmlRelaxNGParserCtxtPtr
4680xmlRelaxNGNewParserCtxt(const char *URL) {
4681 xmlRelaxNGParserCtxtPtr ret;
4682
4683 if (URL == NULL)
4684 return(NULL);
4685
4686 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
4687 if (ret == NULL) {
4688 xmlGenericError(xmlGenericErrorContext,
4689 "Failed to allocate new schama parser context for %s\n", URL);
4690 return (NULL);
4691 }
4692 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
4693 ret->URL = xmlStrdup((const xmlChar *)URL);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004694 ret->error = xmlGenericError;
4695 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004696 return (ret);
4697}
4698
4699/**
4700 * xmlRelaxNGNewMemParserCtxt:
4701 * @buffer: a pointer to a char array containing the schemas
4702 * @size: the size of the array
4703 *
4704 * Create an XML RelaxNGs parse context for that memory buffer expected
4705 * to contain an XML RelaxNGs file.
4706 *
4707 * Returns the parser context or NULL in case of error
4708 */
4709xmlRelaxNGParserCtxtPtr
4710xmlRelaxNGNewMemParserCtxt(const char *buffer, int size) {
4711 xmlRelaxNGParserCtxtPtr ret;
4712
4713 if ((buffer == NULL) || (size <= 0))
4714 return(NULL);
4715
4716 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
4717 if (ret == NULL) {
4718 xmlGenericError(xmlGenericErrorContext,
4719 "Failed to allocate new schama parser context\n");
4720 return (NULL);
4721 }
4722 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
4723 ret->buffer = buffer;
4724 ret->size = size;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004725 ret->error = xmlGenericError;
4726 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004727 return (ret);
4728}
4729
4730/**
4731 * xmlRelaxNGFreeParserCtxt:
4732 * @ctxt: the schema parser context
4733 *
4734 * Free the resources associated to the schema parser context
4735 */
4736void
4737xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxtPtr ctxt) {
4738 if (ctxt == NULL)
4739 return;
4740 if (ctxt->URL != NULL)
4741 xmlFree(ctxt->URL);
4742 if (ctxt->doc != NULL)
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004743 xmlFreeDoc(ctxt->document);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004744 if (ctxt->interleaves != NULL)
4745 xmlHashFree(ctxt->interleaves, NULL);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004746 if (ctxt->documents != NULL)
4747 xmlHashFree(ctxt->documents, (xmlHashDeallocator)
4748 xmlRelaxNGFreeDocument);
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00004749 if (ctxt->includes != NULL)
4750 xmlHashFree(ctxt->includes, (xmlHashDeallocator)
4751 xmlRelaxNGFreeInclude);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004752 if (ctxt->docTab != NULL)
4753 xmlFree(ctxt->docTab);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004754 if (ctxt->incTab != NULL)
4755 xmlFree(ctxt->incTab);
Daniel Veillard419a7682003-02-03 23:22:49 +00004756 if (ctxt->defTab != NULL) {
4757 int i;
4758
4759 for (i = 0;i < ctxt->defNr;i++)
4760 xmlRelaxNGFreeDefine(ctxt->defTab[i]);
4761 xmlFree(ctxt->defTab);
4762 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004763 xmlFree(ctxt);
4764}
4765
Daniel Veillard6eadf632003-01-23 18:29:16 +00004766/**
Daniel Veillardd2298792003-02-14 16:54:11 +00004767 * xmlRelaxNGNormExtSpace:
4768 * @value: a value
4769 *
4770 * Removes the leading and ending spaces of the value
4771 * The string is modified "in situ"
4772 */
4773static void
4774xmlRelaxNGNormExtSpace(xmlChar *value) {
4775 xmlChar *start = value;
4776 xmlChar *cur = value;
4777 if (value == NULL)
4778 return;
4779
4780 while (IS_BLANK(*cur)) cur++;
4781 if (cur == start) {
4782 do {
4783 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
4784 if (*cur == 0)
4785 return;
4786 start = cur;
4787 while (IS_BLANK(*cur)) cur++;
4788 if (*cur == 0) {
4789 *start = 0;
4790 return;
4791 }
4792 } while (1);
4793 } else {
4794 do {
4795 while ((*cur != 0) && (!IS_BLANK(*cur)))
4796 *start++ = *cur++;
4797 if (*cur == 0) {
4798 *start = 0;
4799 return;
4800 }
4801 /* don't try to normalize the inner spaces */
4802 while (IS_BLANK(*cur)) cur++;
4803 *start++ = *cur++;
4804 if (*cur == 0) {
4805 *start = 0;
4806 return;
4807 }
4808 } while (1);
4809 }
4810}
4811
4812/**
4813 * xmlRelaxNGCheckAttributes:
4814 * @ctxt: a Relax-NG parser context
4815 * @node: a Relax-NG node
4816 *
4817 * Check all the attributes on the given node
4818 */
4819static void
4820xmlRelaxNGCleanupAttributes(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
4821 xmlAttrPtr cur, next;
4822
4823 cur = node->properties;
4824 while (cur != NULL) {
4825 next = cur->next;
4826 if ((cur->ns == NULL) ||
4827 (xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
4828 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
4829 if ((!xmlStrEqual(node->name, BAD_CAST "element")) &&
4830 (!xmlStrEqual(node->name, BAD_CAST "attribute")) &&
4831 (!xmlStrEqual(node->name, BAD_CAST "ref")) &&
4832 (!xmlStrEqual(node->name, BAD_CAST "parentRef")) &&
Daniel Veillard2df2de22003-02-17 23:34:33 +00004833 (!xmlStrEqual(node->name, BAD_CAST "param")) &&
Daniel Veillardd2298792003-02-14 16:54:11 +00004834 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
4835 if (ctxt->error != NULL)
4836 ctxt->error(ctxt->userData,
4837 "Attribute %s is not allowed on %s\n",
4838 cur->name, node->name);
4839 ctxt->nbErrors++;
4840 }
4841 } else if (xmlStrEqual(cur->name, BAD_CAST "type")) {
4842 if ((!xmlStrEqual(node->name, BAD_CAST "value")) &&
4843 (!xmlStrEqual(node->name, BAD_CAST "data"))) {
4844 if (ctxt->error != NULL)
4845 ctxt->error(ctxt->userData,
4846 "Attribute %s is not allowed on %s\n",
4847 cur->name, node->name);
4848 ctxt->nbErrors++;
4849 }
4850 } else if (xmlStrEqual(cur->name, BAD_CAST "href")) {
4851 if ((!xmlStrEqual(node->name, BAD_CAST "externalRef")) &&
4852 (!xmlStrEqual(node->name, BAD_CAST "include"))) {
4853 if (ctxt->error != NULL)
4854 ctxt->error(ctxt->userData,
4855 "Attribute %s is not allowed on %s\n",
4856 cur->name, node->name);
4857 ctxt->nbErrors++;
4858 }
4859 } else if (xmlStrEqual(cur->name, BAD_CAST "combine")) {
4860 if ((!xmlStrEqual(node->name, BAD_CAST "start")) &&
4861 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
4862 if (ctxt->error != NULL)
4863 ctxt->error(ctxt->userData,
4864 "Attribute %s is not allowed on %s\n",
4865 cur->name, node->name);
4866 ctxt->nbErrors++;
4867 }
4868 } else if (xmlStrEqual(cur->name, BAD_CAST "datatypeLibrary")) {
4869 xmlChar *val;
4870 xmlURIPtr uri;
4871
4872 val = xmlNodeListGetString(node->doc, cur->children, 1);
4873 if (val != NULL) {
4874 if (val[0] != 0) {
4875 uri = xmlParseURI((const char *) val);
4876 if (uri == NULL) {
4877 if (ctxt->error != NULL)
4878 ctxt->error(ctxt->userData,
4879 "Attribute %s contains invalid URI %s\n",
4880 cur->name, val);
4881 ctxt->nbErrors++;
4882 } else {
4883 if (uri->scheme == NULL) {
4884 if (ctxt->error != NULL)
4885 ctxt->error(ctxt->userData,
4886 "Attribute %s URI %s is not absolute\n",
4887 cur->name, val);
4888 ctxt->nbErrors++;
4889 }
4890 if (uri->fragment != NULL) {
4891 if (ctxt->error != NULL)
4892 ctxt->error(ctxt->userData,
4893 "Attribute %s URI %s has a fragment ID\n",
4894 cur->name, val);
4895 ctxt->nbErrors++;
4896 }
4897 xmlFreeURI(uri);
4898 }
4899 }
4900 xmlFree(val);
4901 }
4902 } else if (!xmlStrEqual(cur->name, BAD_CAST "ns")) {
4903 if (ctxt->error != NULL)
4904 ctxt->error(ctxt->userData,
4905 "Unknown attribute %s on %s\n",
4906 cur->name, node->name);
4907 ctxt->nbErrors++;
4908 }
4909 }
4910 cur = next;
4911 }
4912}
4913
4914/**
Daniel Veillardc5312d72003-02-21 17:14:10 +00004915 * xmlRelaxNGCleanupTree:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004916 * @ctxt: a Relax-NG parser context
Daniel Veillardc5312d72003-02-21 17:14:10 +00004917 * @root: an xmlNodePtr subtree
Daniel Veillard6eadf632003-01-23 18:29:16 +00004918 *
Daniel Veillardc5312d72003-02-21 17:14:10 +00004919 * Cleanup the subtree from unwanted nodes for parsing, resolve
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004920 * Include and externalRef lookups.
Daniel Veillard6eadf632003-01-23 18:29:16 +00004921 */
Daniel Veillardc5312d72003-02-21 17:14:10 +00004922static void
4923xmlRelaxNGCleanupTree(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr root) {
4924 xmlNodePtr cur, delete;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004925
Daniel Veillard6eadf632003-01-23 18:29:16 +00004926 delete = NULL;
4927 cur = root;
4928 while (cur != NULL) {
4929 if (delete != NULL) {
4930 xmlUnlinkNode(delete);
4931 xmlFreeNode(delete);
4932 delete = NULL;
4933 }
4934 if (cur->type == XML_ELEMENT_NODE) {
4935 /*
4936 * Simplification 4.1. Annotations
4937 */
4938 if ((cur->ns == NULL) ||
4939 (!xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
Daniel Veillardd2298792003-02-14 16:54:11 +00004940 if ((cur->parent != NULL) &&
4941 (cur->parent->type == XML_ELEMENT_NODE) &&
4942 ((xmlStrEqual(cur->parent->name, BAD_CAST "name")) ||
4943 (xmlStrEqual(cur->parent->name, BAD_CAST "value")) ||
4944 (xmlStrEqual(cur->parent->name, BAD_CAST "param")))) {
4945 if (ctxt->error != NULL)
4946 ctxt->error(ctxt->userData,
4947 "element %s doesn't allow foreign elements\n",
4948 cur->parent->name);
4949 ctxt->nbErrors++;
4950 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004951 delete = cur;
4952 goto skip_children;
4953 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00004954 xmlRelaxNGCleanupAttributes(ctxt, cur);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004955 if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004956 xmlChar *href, *ns, *base, *URL;
4957 xmlRelaxNGDocumentPtr docu;
Daniel Veillard416589a2003-02-17 17:25:42 +00004958 xmlNodePtr tmp;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004959
4960 ns = xmlGetProp(cur, BAD_CAST "ns");
Daniel Veillard416589a2003-02-17 17:25:42 +00004961 if (ns == NULL) {
4962 tmp = cur->parent;
4963 while ((tmp != NULL) &&
4964 (tmp->type == XML_ELEMENT_NODE)) {
4965 ns = xmlGetProp(tmp, BAD_CAST "ns");
4966 if (ns != NULL)
4967 break;
4968 tmp = tmp->parent;
4969 }
4970 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004971 href = xmlGetProp(cur, BAD_CAST "href");
4972 if (href == NULL) {
4973 if (ctxt->error != NULL)
4974 ctxt->error(ctxt->userData,
4975 "xmlRelaxNGParse: externalRef has no href attribute\n");
4976 ctxt->nbErrors++;
4977 delete = cur;
4978 goto skip_children;
4979 }
4980 base = xmlNodeGetBase(cur->doc, cur);
4981 URL = xmlBuildURI(href, base);
4982 if (URL == NULL) {
4983 if (ctxt->error != NULL)
4984 ctxt->error(ctxt->userData,
4985 "Failed to compute URL for externalRef %s\n", href);
4986 ctxt->nbErrors++;
4987 if (href != NULL)
4988 xmlFree(href);
4989 if (base != NULL)
4990 xmlFree(base);
4991 delete = cur;
4992 goto skip_children;
4993 }
4994 if (href != NULL)
4995 xmlFree(href);
4996 if (base != NULL)
4997 xmlFree(base);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004998 docu = xmlRelaxNGLoadExternalRef(ctxt, URL, ns);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004999 if (docu == NULL) {
5000 if (ctxt->error != NULL)
5001 ctxt->error(ctxt->userData,
5002 "Failed to load externalRef %s\n", URL);
5003 ctxt->nbErrors++;
5004 xmlFree(URL);
5005 delete = cur;
5006 goto skip_children;
5007 }
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00005008 if (ns != NULL)
5009 xmlFree(ns);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005010 xmlFree(URL);
5011 cur->_private = docu;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005012 } else if (xmlStrEqual(cur->name, BAD_CAST "include")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005013 xmlChar *href, *ns, *base, *URL;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005014 xmlRelaxNGIncludePtr incl;
Daniel Veillard416589a2003-02-17 17:25:42 +00005015 xmlNodePtr tmp;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005016
5017 href = xmlGetProp(cur, BAD_CAST "href");
5018 if (href == NULL) {
5019 if (ctxt->error != NULL)
5020 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005021 "xmlRelaxNGParse: include has no href attribute\n");
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005022 ctxt->nbErrors++;
5023 delete = cur;
5024 goto skip_children;
5025 }
5026 base = xmlNodeGetBase(cur->doc, cur);
5027 URL = xmlBuildURI(href, base);
5028 if (URL == NULL) {
5029 if (ctxt->error != NULL)
5030 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005031 "Failed to compute URL for include %s\n", href);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005032 ctxt->nbErrors++;
5033 if (href != NULL)
5034 xmlFree(href);
5035 if (base != NULL)
5036 xmlFree(base);
5037 delete = cur;
5038 goto skip_children;
5039 }
5040 if (href != NULL)
5041 xmlFree(href);
5042 if (base != NULL)
5043 xmlFree(base);
Daniel Veillard416589a2003-02-17 17:25:42 +00005044 ns = xmlGetProp(cur, BAD_CAST "ns");
5045 if (ns == NULL) {
5046 tmp = cur->parent;
5047 while ((tmp != NULL) &&
5048 (tmp->type == XML_ELEMENT_NODE)) {
5049 ns = xmlGetProp(tmp, BAD_CAST "ns");
5050 if (ns != NULL)
5051 break;
5052 tmp = tmp->parent;
5053 }
5054 }
5055 incl = xmlRelaxNGLoadInclude(ctxt, URL, cur, ns);
5056 if (ns != NULL)
5057 xmlFree(ns);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005058 if (incl == NULL) {
5059 if (ctxt->error != NULL)
5060 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005061 "Failed to load include %s\n", URL);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005062 ctxt->nbErrors++;
5063 xmlFree(URL);
5064 delete = cur;
5065 goto skip_children;
5066 }
5067 xmlFree(URL);
5068 cur->_private = incl;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005069 } else if ((xmlStrEqual(cur->name, BAD_CAST "element")) ||
5070 (xmlStrEqual(cur->name, BAD_CAST "attribute"))) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00005071 xmlChar *name, *ns;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005072 xmlNodePtr text = NULL;
5073
5074 /*
5075 * Simplification 4.8. name attribute of element
5076 * and attribute elements
5077 */
5078 name = xmlGetProp(cur, BAD_CAST "name");
5079 if (name != NULL) {
5080 if (cur->children == NULL) {
5081 text = xmlNewChild(cur, cur->ns, BAD_CAST "name",
5082 name);
5083 } else {
5084 xmlNodePtr node;
5085 node = xmlNewNode(cur->ns, BAD_CAST "name");
5086 if (node != NULL) {
5087 xmlAddPrevSibling(cur->children, node);
5088 text = xmlNewText(name);
5089 xmlAddChild(node, text);
5090 text = node;
5091 }
5092 }
Daniel Veillardfebcca42003-02-16 15:44:18 +00005093 if (text == NULL) {
5094 if (ctxt->error != NULL)
5095 ctxt->error(ctxt->userData,
5096 "Failed to create a name %s element\n", name);
5097 ctxt->nbErrors++;
5098 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005099 xmlUnsetProp(cur, BAD_CAST "name");
5100 xmlFree(name);
Daniel Veillardfebcca42003-02-16 15:44:18 +00005101 ns = xmlGetProp(cur, BAD_CAST "ns");
5102 if (ns != NULL) {
5103 if (text != NULL) {
5104 xmlSetProp(text, BAD_CAST "ns", ns);
5105 /* xmlUnsetProp(cur, BAD_CAST "ns"); */
Daniel Veillard6eadf632003-01-23 18:29:16 +00005106 }
Daniel Veillardfebcca42003-02-16 15:44:18 +00005107 xmlFree(ns);
5108 } else if (xmlStrEqual(cur->name,
5109 BAD_CAST "attribute")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005110 xmlSetProp(text, BAD_CAST "ns", BAD_CAST "");
5111 }
5112 }
5113 } else if ((xmlStrEqual(cur->name, BAD_CAST "name")) ||
5114 (xmlStrEqual(cur->name, BAD_CAST "nsName")) ||
5115 (xmlStrEqual(cur->name, BAD_CAST "value"))) {
5116 /*
5117 * Simplification 4.8. name attribute of element
5118 * and attribute elements
5119 */
5120 if (xmlHasProp(cur, BAD_CAST "ns") == NULL) {
5121 xmlNodePtr node;
5122 xmlChar *ns = NULL;
5123
5124 node = cur->parent;
5125 while ((node != NULL) &&
5126 (node->type == XML_ELEMENT_NODE)) {
5127 ns = xmlGetProp(node, BAD_CAST "ns");
5128 if (ns != NULL) {
5129 break;
5130 }
5131 node = node->parent;
5132 }
5133 if (ns == NULL) {
5134 xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
5135 } else {
5136 xmlSetProp(cur, BAD_CAST "ns", ns);
5137 xmlFree(ns);
5138 }
5139 }
5140 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
5141 xmlChar *name, *local, *prefix;
5142
5143 /*
5144 * Simplification: 4.10. QNames
5145 */
5146 name = xmlNodeGetContent(cur);
5147 if (name != NULL) {
5148 local = xmlSplitQName2(name, &prefix);
5149 if (local != NULL) {
5150 xmlNsPtr ns;
5151
5152 ns = xmlSearchNs(cur->doc, cur, prefix);
5153 if (ns == NULL) {
5154 if (ctxt->error != NULL)
5155 ctxt->error(ctxt->userData,
5156 "xmlRelaxNGParse: no namespace for prefix %s\n", prefix);
5157 ctxt->nbErrors++;
5158 } else {
5159 xmlSetProp(cur, BAD_CAST "ns", ns->href);
5160 xmlNodeSetContent(cur, local);
5161 }
5162 xmlFree(local);
5163 xmlFree(prefix);
5164 }
5165 xmlFree(name);
5166 }
5167 }
Daniel Veillard44e1dd02003-02-21 23:23:28 +00005168 /*
5169 * 4.16
5170 */
Daniel Veillardc5312d72003-02-21 17:14:10 +00005171 if (xmlStrEqual(cur->name, BAD_CAST "nsName")) {
5172 if (ctxt->flags & XML_RELAXNG_IN_NSEXCEPT) {
5173 if (ctxt->error != NULL)
5174 ctxt->error(ctxt->userData,
5175 "Found nsName/except//nsName forbidden construct\n");
5176 ctxt->nbErrors++;
5177 }
5178 }
5179 } else if ((xmlStrEqual(cur->name, BAD_CAST "except")) &&
5180 (cur != root)) {
5181 int oldflags = ctxt->flags;
5182
Daniel Veillard44e1dd02003-02-21 23:23:28 +00005183 /*
5184 * 4.16
5185 */
Daniel Veillardc5312d72003-02-21 17:14:10 +00005186 if ((cur->parent != NULL) &&
5187 (xmlStrEqual(cur->parent->name, BAD_CAST "anyName"))) {
5188 ctxt->flags |= XML_RELAXNG_IN_ANYEXCEPT;
5189 xmlRelaxNGCleanupTree(ctxt, cur);
5190 ctxt->flags = oldflags;
5191 goto skip_children;
5192 } else if ((cur->parent != NULL) &&
5193 (xmlStrEqual(cur->parent->name, BAD_CAST "nsName"))) {
5194 ctxt->flags |= XML_RELAXNG_IN_NSEXCEPT;
5195 xmlRelaxNGCleanupTree(ctxt, cur);
5196 ctxt->flags = oldflags;
5197 goto skip_children;
5198 }
5199 } else if (xmlStrEqual(cur->name, BAD_CAST "anyName")) {
Daniel Veillard44e1dd02003-02-21 23:23:28 +00005200 /*
5201 * 4.16
5202 */
Daniel Veillardc5312d72003-02-21 17:14:10 +00005203 if (ctxt->flags & XML_RELAXNG_IN_ANYEXCEPT) {
5204 if (ctxt->error != NULL)
5205 ctxt->error(ctxt->userData,
5206 "Found anyName/except//anyName forbidden construct\n");
5207 ctxt->nbErrors++;
5208 } else if (ctxt->flags & XML_RELAXNG_IN_NSEXCEPT) {
5209 if (ctxt->error != NULL)
5210 ctxt->error(ctxt->userData,
5211 "Found nsName/except//anyName forbidden construct\n");
5212 ctxt->nbErrors++;
5213 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005214 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005215 /*
5216 * Thisd is not an else since "include" is transformed
5217 * into a div
5218 */
5219 if (xmlStrEqual(cur->name, BAD_CAST "div")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005220 xmlChar *ns;
5221 xmlNodePtr child, ins, tmp;
5222
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005223 /*
5224 * implements rule 4.11
5225 */
Daniel Veillard416589a2003-02-17 17:25:42 +00005226
5227 ns = xmlGetProp(cur, BAD_CAST "ns");
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005228
5229 child = cur->children;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005230 ins = cur;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005231 while (child != NULL) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005232 if (ns != NULL) {
5233 if (!xmlHasProp(child, BAD_CAST "ns")) {
5234 xmlSetProp(child, BAD_CAST "ns", ns);
5235 }
5236 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005237 tmp = child->next;
5238 xmlUnlinkNode(child);
5239 ins = xmlAddNextSibling(ins, child);
5240 child = tmp;
5241 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005242 if (ns != NULL)
5243 xmlFree(ns);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005244 delete = cur;
5245 goto skip_children;
5246 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005247 }
5248 }
5249 /*
5250 * Simplification 4.2 whitespaces
5251 */
5252 else if (cur->type == XML_TEXT_NODE) {
5253 if (IS_BLANK_NODE(cur)) {
5254 if (cur->parent->type == XML_ELEMENT_NODE) {
5255 if ((!xmlStrEqual(cur->parent->name, BAD_CAST "value")) &&
5256 (!xmlStrEqual(cur->parent->name, BAD_CAST "param")))
5257 delete = cur;
5258 } else {
5259 delete = cur;
5260 goto skip_children;
5261 }
5262 }
5263 } else if (cur->type != XML_CDATA_SECTION_NODE) {
5264 delete = cur;
5265 goto skip_children;
5266 }
5267
5268 /*
5269 * Skip to next node
5270 */
5271 if (cur->children != NULL) {
5272 if ((cur->children->type != XML_ENTITY_DECL) &&
5273 (cur->children->type != XML_ENTITY_REF_NODE) &&
5274 (cur->children->type != XML_ENTITY_NODE)) {
5275 cur = cur->children;
5276 continue;
5277 }
5278 }
5279skip_children:
5280 if (cur->next != NULL) {
5281 cur = cur->next;
5282 continue;
5283 }
5284
5285 do {
5286 cur = cur->parent;
5287 if (cur == NULL)
5288 break;
5289 if (cur == root) {
5290 cur = NULL;
5291 break;
5292 }
5293 if (cur->next != NULL) {
5294 cur = cur->next;
5295 break;
5296 }
5297 } while (cur != NULL);
5298 }
5299 if (delete != NULL) {
5300 xmlUnlinkNode(delete);
5301 xmlFreeNode(delete);
5302 delete = NULL;
5303 }
Daniel Veillardc5312d72003-02-21 17:14:10 +00005304}
Daniel Veillard6eadf632003-01-23 18:29:16 +00005305
Daniel Veillardc5312d72003-02-21 17:14:10 +00005306/**
5307 * xmlRelaxNGCleanupDoc:
5308 * @ctxt: a Relax-NG parser context
5309 * @doc: an xmldocPtr document pointer
5310 *
5311 * Cleanup the document from unwanted nodes for parsing, resolve
5312 * Include and externalRef lookups.
5313 *
5314 * Returns the cleaned up document or NULL in case of error
5315 */
5316static xmlDocPtr
5317xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt, xmlDocPtr doc) {
5318 xmlNodePtr root;
5319
5320 /*
5321 * Extract the root
5322 */
5323 root = xmlDocGetRootElement(doc);
5324 if (root == NULL) {
5325 if (ctxt->error != NULL)
5326 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
5327 ctxt->URL);
5328 ctxt->nbErrors++;
5329 return (NULL);
5330 }
5331 xmlRelaxNGCleanupTree(ctxt, root);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005332 return(doc);
5333}
5334
5335/**
5336 * xmlRelaxNGParse:
5337 * @ctxt: a Relax-NG parser context
5338 *
5339 * parse a schema definition resource and build an internal
5340 * XML Shema struture which can be used to validate instances.
5341 * *WARNING* this interface is highly subject to change
5342 *
5343 * Returns the internal XML RelaxNG structure built from the resource or
5344 * NULL in case of error
5345 */
5346xmlRelaxNGPtr
5347xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt)
5348{
5349 xmlRelaxNGPtr ret = NULL;
5350 xmlDocPtr doc;
5351 xmlNodePtr root;
5352
5353 xmlRelaxNGInitTypes();
5354
5355 if (ctxt == NULL)
5356 return (NULL);
5357
5358 /*
5359 * First step is to parse the input document into an DOM/Infoset
5360 */
5361 if (ctxt->URL != NULL) {
5362 doc = xmlParseFile((const char *) ctxt->URL);
5363 if (doc == NULL) {
5364 if (ctxt->error != NULL)
5365 ctxt->error(ctxt->userData,
5366 "xmlRelaxNGParse: could not load %s\n", ctxt->URL);
5367 ctxt->nbErrors++;
5368 return (NULL);
5369 }
5370 } else if (ctxt->buffer != NULL) {
5371 doc = xmlParseMemory(ctxt->buffer, ctxt->size);
5372 if (doc == NULL) {
5373 if (ctxt->error != NULL)
5374 ctxt->error(ctxt->userData,
5375 "xmlRelaxNGParse: could not parse schemas\n");
5376 ctxt->nbErrors++;
5377 return (NULL);
5378 }
5379 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
5380 ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
5381 } else {
5382 if (ctxt->error != NULL)
5383 ctxt->error(ctxt->userData,
5384 "xmlRelaxNGParse: nothing to parse\n");
5385 ctxt->nbErrors++;
5386 return (NULL);
5387 }
5388 ctxt->document = doc;
5389
5390 /*
5391 * Some preprocessing of the document content
5392 */
5393 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
5394 if (doc == NULL) {
5395 xmlFreeDoc(ctxt->document);
5396 ctxt->document = NULL;
5397 return(NULL);
5398 }
5399
Daniel Veillard6eadf632003-01-23 18:29:16 +00005400 /*
5401 * Then do the parsing for good
5402 */
5403 root = xmlDocGetRootElement(doc);
5404 if (root == NULL) {
5405 if (ctxt->error != NULL)
5406 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
5407 ctxt->URL);
5408 ctxt->nbErrors++;
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00005409 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005410 return (NULL);
5411 }
5412 ret = xmlRelaxNGParseDocument(ctxt, root);
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00005413 if (ret == NULL) {
5414 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005415 return(NULL);
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00005416 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005417
5418 /*
5419 * Check the ref/defines links
5420 */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005421 /*
5422 * try to preprocess interleaves
5423 */
5424 if (ctxt->interleaves != NULL) {
5425 xmlHashScan(ctxt->interleaves,
5426 (xmlHashScanner)xmlRelaxNGComputeInterleaves, ctxt);
5427 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005428
5429 /*
5430 * if there was a parsing error return NULL
5431 */
5432 if (ctxt->nbErrors > 0) {
5433 xmlRelaxNGFree(ret);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005434 ctxt->document = NULL;
5435 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005436 return(NULL);
5437 }
5438
5439 /*
5440 * Transfer the pointer for cleanup at the schema level.
5441 */
5442 ret->doc = doc;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005443 ctxt->document = NULL;
5444 ret->documents = ctxt->documents;
5445 ctxt->documents = NULL;
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00005446
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005447 ret->includes = ctxt->includes;
5448 ctxt->includes = NULL;
Daniel Veillard419a7682003-02-03 23:22:49 +00005449 ret->defNr = ctxt->defNr;
5450 ret->defTab = ctxt->defTab;
5451 ctxt->defTab = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005452
5453 return (ret);
5454}
5455
5456/**
5457 * xmlRelaxNGSetParserErrors:
5458 * @ctxt: a Relax-NG validation context
5459 * @err: the error callback
5460 * @warn: the warning callback
5461 * @ctx: contextual data for the callbacks
5462 *
5463 * Set the callback functions used to handle errors for a validation context
5464 */
5465void
5466xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
5467 xmlRelaxNGValidityErrorFunc err,
5468 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
5469 if (ctxt == NULL)
5470 return;
5471 ctxt->error = err;
5472 ctxt->warning = warn;
5473 ctxt->userData = ctx;
5474}
5475/************************************************************************
5476 * *
5477 * Dump back a compiled form *
5478 * *
5479 ************************************************************************/
5480static void xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define);
5481
5482/**
5483 * xmlRelaxNGDumpDefines:
5484 * @output: the file output
5485 * @defines: a list of define structures
5486 *
5487 * Dump a RelaxNG structure back
5488 */
5489static void
5490xmlRelaxNGDumpDefines(FILE * output, xmlRelaxNGDefinePtr defines) {
5491 while (defines != NULL) {
5492 xmlRelaxNGDumpDefine(output, defines);
5493 defines = defines->next;
5494 }
5495}
5496
5497/**
5498 * xmlRelaxNGDumpDefine:
5499 * @output: the file output
5500 * @define: a define structure
5501 *
5502 * Dump a RelaxNG structure back
5503 */
5504static void
5505xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define) {
5506 if (define == NULL)
5507 return;
5508 switch(define->type) {
5509 case XML_RELAXNG_EMPTY:
5510 fprintf(output, "<empty/>\n");
5511 break;
5512 case XML_RELAXNG_NOT_ALLOWED:
5513 fprintf(output, "<notAllowed/>\n");
5514 break;
5515 case XML_RELAXNG_TEXT:
5516 fprintf(output, "<text/>\n");
5517 break;
5518 case XML_RELAXNG_ELEMENT:
5519 fprintf(output, "<element>\n");
5520 if (define->name != NULL) {
5521 fprintf(output, "<name");
5522 if (define->ns != NULL)
5523 fprintf(output, " ns=\"%s\"", define->ns);
5524 fprintf(output, ">%s</name>\n", define->name);
5525 }
5526 xmlRelaxNGDumpDefines(output, define->attrs);
5527 xmlRelaxNGDumpDefines(output, define->content);
5528 fprintf(output, "</element>\n");
5529 break;
5530 case XML_RELAXNG_LIST:
5531 fprintf(output, "<list>\n");
5532 xmlRelaxNGDumpDefines(output, define->content);
5533 fprintf(output, "</list>\n");
5534 break;
5535 case XML_RELAXNG_ONEORMORE:
5536 fprintf(output, "<oneOrMore>\n");
5537 xmlRelaxNGDumpDefines(output, define->content);
5538 fprintf(output, "</oneOrMore>\n");
5539 break;
5540 case XML_RELAXNG_ZEROORMORE:
5541 fprintf(output, "<zeroOrMore>\n");
5542 xmlRelaxNGDumpDefines(output, define->content);
5543 fprintf(output, "</zeroOrMore>\n");
5544 break;
5545 case XML_RELAXNG_CHOICE:
5546 fprintf(output, "<choice>\n");
5547 xmlRelaxNGDumpDefines(output, define->content);
5548 fprintf(output, "</choice>\n");
5549 break;
5550 case XML_RELAXNG_GROUP:
5551 fprintf(output, "<group>\n");
5552 xmlRelaxNGDumpDefines(output, define->content);
5553 fprintf(output, "</group>\n");
5554 break;
5555 case XML_RELAXNG_INTERLEAVE:
5556 fprintf(output, "<interleave>\n");
5557 xmlRelaxNGDumpDefines(output, define->content);
5558 fprintf(output, "</interleave>\n");
5559 break;
5560 case XML_RELAXNG_OPTIONAL:
5561 fprintf(output, "<optional>\n");
5562 xmlRelaxNGDumpDefines(output, define->content);
5563 fprintf(output, "</optional>\n");
5564 break;
5565 case XML_RELAXNG_ATTRIBUTE:
5566 fprintf(output, "<attribute>\n");
5567 xmlRelaxNGDumpDefines(output, define->content);
5568 fprintf(output, "</attribute>\n");
5569 break;
5570 case XML_RELAXNG_DEF:
5571 fprintf(output, "<define");
5572 if (define->name != NULL)
5573 fprintf(output, " name=\"%s\"", define->name);
5574 fprintf(output, ">\n");
5575 xmlRelaxNGDumpDefines(output, define->content);
5576 fprintf(output, "</define>\n");
5577 break;
5578 case XML_RELAXNG_REF:
5579 fprintf(output, "<ref");
5580 if (define->name != NULL)
5581 fprintf(output, " name=\"%s\"", define->name);
5582 fprintf(output, ">\n");
5583 xmlRelaxNGDumpDefines(output, define->content);
5584 fprintf(output, "</ref>\n");
5585 break;
Daniel Veillard419a7682003-02-03 23:22:49 +00005586 case XML_RELAXNG_PARENTREF:
5587 fprintf(output, "<parentRef");
5588 if (define->name != NULL)
5589 fprintf(output, " name=\"%s\"", define->name);
5590 fprintf(output, ">\n");
5591 xmlRelaxNGDumpDefines(output, define->content);
5592 fprintf(output, "</parentRef>\n");
5593 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005594 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard416589a2003-02-17 17:25:42 +00005595 fprintf(output, "<externalRef>");
Daniel Veillarde431a272003-01-29 23:02:33 +00005596 xmlRelaxNGDumpDefines(output, define->content);
5597 fprintf(output, "</externalRef>\n");
5598 break;
5599 case XML_RELAXNG_DATATYPE:
Daniel Veillard6eadf632003-01-23 18:29:16 +00005600 case XML_RELAXNG_VALUE:
5601 TODO
5602 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005603 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00005604 case XML_RELAXNG_EXCEPT:
Daniel Veillard8fe98712003-02-19 00:19:14 +00005605 case XML_RELAXNG_PARAM:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005606 TODO
5607 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00005608 case XML_RELAXNG_NOOP:
5609 xmlRelaxNGDumpDefines(output, define->content);
5610 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005611 }
5612}
5613
5614/**
5615 * xmlRelaxNGDumpGrammar:
5616 * @output: the file output
5617 * @grammar: a grammar structure
5618 * @top: is this a top grammar
5619 *
5620 * Dump a RelaxNG structure back
5621 */
5622static void
5623xmlRelaxNGDumpGrammar(FILE * output, xmlRelaxNGGrammarPtr grammar, int top)
5624{
5625 if (grammar == NULL)
5626 return;
5627
5628 fprintf(output, "<grammar");
5629 if (top)
5630 fprintf(output,
5631 " xmlns=\"http://relaxng.org/ns/structure/1.0\"");
5632 switch(grammar->combine) {
5633 case XML_RELAXNG_COMBINE_UNDEFINED:
5634 break;
5635 case XML_RELAXNG_COMBINE_CHOICE:
5636 fprintf(output, " combine=\"choice\"");
5637 break;
5638 case XML_RELAXNG_COMBINE_INTERLEAVE:
5639 fprintf(output, " combine=\"interleave\"");
5640 break;
5641 default:
5642 fprintf(output, " <!-- invalid combine value -->");
5643 }
5644 fprintf(output, ">\n");
5645 if (grammar->start == NULL) {
5646 fprintf(output, " <!-- grammar had no start -->");
5647 } else {
5648 fprintf(output, "<start>\n");
5649 xmlRelaxNGDumpDefine(output, grammar->start);
5650 fprintf(output, "</start>\n");
5651 }
5652 /* TODO ? Dump the defines ? */
5653 fprintf(output, "</grammar>\n");
5654}
5655
5656/**
5657 * xmlRelaxNGDump:
5658 * @output: the file output
5659 * @schema: a schema structure
5660 *
5661 * Dump a RelaxNG structure back
5662 */
5663void
5664xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema)
5665{
5666 if (schema == NULL) {
5667 fprintf(output, "RelaxNG empty or failed to compile\n");
5668 return;
5669 }
5670 fprintf(output, "RelaxNG: ");
5671 if (schema->doc == NULL) {
5672 fprintf(output, "no document\n");
5673 } else if (schema->doc->URL != NULL) {
5674 fprintf(output, "%s\n", schema->doc->URL);
5675 } else {
5676 fprintf(output, "\n");
5677 }
5678 if (schema->topgrammar == NULL) {
5679 fprintf(output, "RelaxNG has no top grammar\n");
5680 return;
5681 }
5682 xmlRelaxNGDumpGrammar(output, schema->topgrammar, 1);
5683}
5684
Daniel Veillardfebcca42003-02-16 15:44:18 +00005685/**
5686 * xmlRelaxNGDumpTree:
5687 * @output: the file output
5688 * @schema: a schema structure
5689 *
5690 * Dump the transformed RelaxNG tree.
5691 */
5692void
5693xmlRelaxNGDumpTree(FILE * output, xmlRelaxNGPtr schema)
5694{
5695 if (schema == NULL) {
5696 fprintf(output, "RelaxNG empty or failed to compile\n");
5697 return;
5698 }
5699 if (schema->doc == NULL) {
5700 fprintf(output, "no document\n");
5701 } else {
5702 xmlDocDump(output, schema->doc);
5703 }
5704}
5705
Daniel Veillard6eadf632003-01-23 18:29:16 +00005706/************************************************************************
5707 * *
5708 * Validation implementation *
5709 * *
5710 ************************************************************************/
5711static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
5712 xmlRelaxNGDefinePtr define);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005713static int xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
5714 xmlRelaxNGDefinePtr define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005715
5716/**
5717 * xmlRelaxNGSkipIgnored:
5718 * @ctxt: a schema validation context
5719 * @node: the top node.
5720 *
5721 * Skip ignorable nodes in that context
5722 *
5723 * Returns the new sibling or NULL in case of error.
5724 */
5725static xmlNodePtr
5726xmlRelaxNGSkipIgnored(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
5727 xmlNodePtr node) {
5728 /*
5729 * TODO complete and handle entities
5730 */
5731 while ((node != NULL) &&
5732 ((node->type == XML_COMMENT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005733 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00005734 ((node->type == XML_TEXT_NODE) &&
5735 (IS_BLANK_NODE(node))))) {
5736 node = node->next;
5737 }
5738 return(node);
5739}
5740
5741/**
Daniel Veillardedc91922003-01-26 00:52:04 +00005742 * xmlRelaxNGNormalize:
5743 * @ctxt: a schema validation context
5744 * @str: the string to normalize
5745 *
5746 * Implements the normalizeWhiteSpace( s ) function from
5747 * section 6.2.9 of the spec
5748 *
5749 * Returns the new string or NULL in case of error.
5750 */
5751static xmlChar *
5752xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *str) {
5753 xmlChar *ret, *p;
5754 const xmlChar *tmp;
5755 int len;
5756
5757 if (str == NULL)
5758 return(NULL);
5759 tmp = str;
5760 while (*tmp != 0) tmp++;
5761 len = tmp - str;
5762
5763 ret = (xmlChar *) xmlMalloc((len + 1) * sizeof(xmlChar));
5764 if (ret == NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00005765 if (ctxt != NULL) {
5766 VALID_CTXT();
5767 VALID_ERROR("xmlRelaxNGNormalize: out of memory\n");
5768 } else {
5769 xmlGenericError(xmlGenericErrorContext,
5770 "xmlRelaxNGNormalize: out of memory\n");
5771 }
Daniel Veillardedc91922003-01-26 00:52:04 +00005772 return(NULL);
5773 }
5774 p = ret;
5775 while (IS_BLANK(*str)) str++;
5776 while (*str != 0) {
5777 if (IS_BLANK(*str)) {
5778 while (IS_BLANK(*str)) str++;
5779 if (*str == 0)
5780 break;
5781 *p++ = ' ';
5782 } else
5783 *p++ = *str++;
5784 }
5785 *p = 0;
5786 return(ret);
5787}
5788
5789/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005790 * xmlRelaxNGValidateDatatype:
5791 * @ctxt: a Relax-NG validation context
5792 * @value: the string value
5793 * @type: the datatype definition
5794 *
5795 * Validate the given value against the dataype
5796 *
5797 * Returns 0 if the validation succeeded or an error code.
5798 */
5799static int
5800xmlRelaxNGValidateDatatype(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *value,
5801 xmlRelaxNGDefinePtr define) {
5802 int ret;
5803 xmlRelaxNGTypeLibraryPtr lib;
5804
5805 if ((define == NULL) || (define->data == NULL)) {
5806 return(-1);
5807 }
5808 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
5809 if (lib->check != NULL)
5810 ret = lib->check(lib->data, define->name, value);
5811 else
5812 ret = -1;
5813 if (ret < 0) {
5814 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005815 VALID_ERROR2("Internal: failed to validate type %s\n", define->name);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005816 return(-1);
5817 } else if (ret == 1) {
5818 ret = 0;
5819 } else {
5820 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005821 VALID_ERROR3("Type %s doesn't allow value %s\n", define->name, value);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005822 return(-1);
5823 ret = -1;
5824 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005825 if ((ret == 0) && (define->content != NULL)) {
5826 const xmlChar *oldvalue, *oldendvalue;
5827
5828 oldvalue = ctxt->state->value;
5829 oldendvalue = ctxt->state->endvalue;
5830 ctxt->state->value = (xmlChar *) value;
5831 ctxt->state->endvalue = NULL;
5832 ret = xmlRelaxNGValidateValue(ctxt, define->content);
5833 ctxt->state->value = (xmlChar *) oldvalue;
5834 ctxt->state->endvalue = (xmlChar *) oldendvalue;
5835 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005836 return(ret);
5837}
5838
5839/**
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005840 * xmlRelaxNGNextValue:
5841 * @ctxt: a Relax-NG validation context
5842 *
5843 * Skip to the next value when validating within a list
5844 *
5845 * Returns 0 if the operation succeeded or an error code.
5846 */
5847static int
5848xmlRelaxNGNextValue(xmlRelaxNGValidCtxtPtr ctxt) {
5849 xmlChar *cur;
5850
5851 cur = ctxt->state->value;
5852 if ((cur == NULL) || (ctxt->state->endvalue == NULL)) {
5853 ctxt->state->value = NULL;
Daniel Veillarde5b110b2003-02-04 14:43:39 +00005854 ctxt->state->endvalue = NULL;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005855 return(0);
5856 }
5857 while (*cur != 0) cur++;
5858 while ((cur != ctxt->state->endvalue) && (*cur == 0)) cur++;
5859 if (cur == ctxt->state->endvalue)
5860 ctxt->state->value = NULL;
5861 else
5862 ctxt->state->value = cur;
5863 return(0);
5864}
5865
5866/**
5867 * xmlRelaxNGValidateValueList:
5868 * @ctxt: a Relax-NG validation context
5869 * @defines: the list of definitions to verify
5870 *
5871 * Validate the given set of definitions for the current value
5872 *
5873 * Returns 0 if the validation succeeded or an error code.
5874 */
5875static int
5876xmlRelaxNGValidateValueList(xmlRelaxNGValidCtxtPtr ctxt,
5877 xmlRelaxNGDefinePtr defines) {
5878 int ret = 0;
5879
5880 while (defines != NULL) {
5881 ret = xmlRelaxNGValidateValue(ctxt, defines);
5882 if (ret != 0)
5883 break;
5884 defines = defines->next;
5885 }
5886 return(ret);
5887}
5888
5889/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00005890 * xmlRelaxNGValidateValue:
5891 * @ctxt: a Relax-NG validation context
5892 * @define: the definition to verify
5893 *
5894 * Validate the given definition for the current value
5895 *
5896 * Returns 0 if the validation succeeded or an error code.
5897 */
5898static int
5899xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
5900 xmlRelaxNGDefinePtr define) {
Daniel Veillardedc91922003-01-26 00:52:04 +00005901 int ret = 0, oldflags;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005902 xmlChar *value;
5903
5904 value = ctxt->state->value;
5905 switch (define->type) {
Daniel Veillardd4310742003-02-18 21:12:46 +00005906 case XML_RELAXNG_EMPTY: {
5907 if ((value != NULL) && (value[0] != 0)) {
5908 int idx = 0;
5909
5910 while (IS_BLANK(value[idx]))
5911 idx++;
5912 if (value[idx] != 0)
5913 ret = -1;
5914 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005915 break;
Daniel Veillardd4310742003-02-18 21:12:46 +00005916 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005917 case XML_RELAXNG_TEXT:
5918 break;
Daniel Veillardedc91922003-01-26 00:52:04 +00005919 case XML_RELAXNG_VALUE: {
5920 if (!xmlStrEqual(value, define->value)) {
5921 if (define->name != NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00005922 xmlRelaxNGTypeLibraryPtr lib;
5923
5924 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
5925 if ((lib != NULL) && (lib->comp != NULL))
5926 ret = lib->comp(lib->data, define->name, value,
5927 define->value);
5928 else
5929 ret = -1;
5930 if (ret < 0) {
5931 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005932 VALID_ERROR2("Internal: failed to compare type %s\n",
Daniel Veillardea3f3982003-01-26 19:45:18 +00005933 define->name);
5934 return(-1);
5935 } else if (ret == 1) {
5936 ret = 0;
5937 } else {
5938 ret = -1;
5939 }
Daniel Veillardedc91922003-01-26 00:52:04 +00005940 } else {
5941 xmlChar *nval, *nvalue;
5942
5943 /*
5944 * TODO: trivial optimizations are possible by
5945 * computing at compile-time
5946 */
5947 nval = xmlRelaxNGNormalize(ctxt, define->value);
5948 nvalue = xmlRelaxNGNormalize(ctxt, value);
5949
Daniel Veillardea3f3982003-01-26 19:45:18 +00005950 if ((nval == NULL) || (nvalue == NULL) ||
5951 (!xmlStrEqual(nval, nvalue)))
Daniel Veillardedc91922003-01-26 00:52:04 +00005952 ret = -1;
5953 if (nval != NULL)
5954 xmlFree(nval);
5955 if (nvalue != NULL)
5956 xmlFree(nvalue);
5957 }
5958 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005959 if (ret == 0)
5960 xmlRelaxNGNextValue(ctxt);
Daniel Veillardedc91922003-01-26 00:52:04 +00005961 break;
5962 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005963 case XML_RELAXNG_DATATYPE: {
5964 ret = xmlRelaxNGValidateDatatype(ctxt, value, define);
5965 if (ret == 0)
5966 xmlRelaxNGNextValue(ctxt);
5967
5968 break;
5969 }
5970 case XML_RELAXNG_CHOICE: {
5971 xmlRelaxNGDefinePtr list = define->content;
5972 xmlChar *oldvalue;
5973
5974 oldflags = ctxt->flags;
5975 ctxt->flags |= FLAGS_IGNORABLE;
5976
5977 oldvalue = ctxt->state->value;
5978 while (list != NULL) {
5979 ret = xmlRelaxNGValidateValue(ctxt, list);
5980 if (ret == 0) {
5981 break;
5982 }
5983 ctxt->state->value = oldvalue;
5984 list = list->next;
5985 }
5986 ctxt->flags = oldflags;
Daniel Veillard416589a2003-02-17 17:25:42 +00005987 if (ret == 0)
5988 xmlRelaxNGNextValue(ctxt);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005989 break;
5990 }
5991 case XML_RELAXNG_LIST: {
5992 xmlRelaxNGDefinePtr list = define->content;
5993 xmlChar *oldvalue, *oldend, *val, *cur;
Daniel Veillard416589a2003-02-17 17:25:42 +00005994#ifdef DEBUG_LIST
5995 int nb_values = 0;
5996#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005997
5998 oldvalue = ctxt->state->value;
5999 oldend = ctxt->state->endvalue;
6000
6001 val = xmlStrdup(oldvalue);
6002 if (val == NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00006003 val = xmlStrdup(BAD_CAST "");
6004 }
6005 if (val == NULL) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006006 VALID_CTXT();
6007 VALID_ERROR("Internal: no state\n");
6008 return(-1);
6009 }
6010 cur = val;
6011 while (*cur != 0) {
Daniel Veillard416589a2003-02-17 17:25:42 +00006012 if (IS_BLANK(*cur)) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006013 *cur = 0;
Daniel Veillard416589a2003-02-17 17:25:42 +00006014 cur++;
6015#ifdef DEBUG_LIST
6016 nb_values++;
6017#endif
6018 while (IS_BLANK(*cur))
6019 *cur++ = 0;
6020 } else
6021 cur++;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006022 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006023#ifdef DEBUG_LIST
6024 xmlGenericError(xmlGenericErrorContext,
6025 "list value: '%s' found %d items\n", oldvalue, nb_values);
6026 nb_values = 0;
6027#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006028 ctxt->state->endvalue = cur;
6029 cur = val;
6030 while ((*cur == 0) && (cur != ctxt->state->endvalue)) cur++;
6031
6032 ctxt->state->value = cur;
6033
6034 while (list != NULL) {
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006035 if (ctxt->state->value == ctxt->state->endvalue)
6036 ctxt->state->value = NULL;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006037 ret = xmlRelaxNGValidateValue(ctxt, list);
6038 if (ret != 0) {
Daniel Veillard416589a2003-02-17 17:25:42 +00006039#ifdef DEBUG_LIST
6040 xmlGenericError(xmlGenericErrorContext,
6041 "Failed to validate value: '%s' with %d rule\n",
6042 ctxt->state->value, nb_values);
6043#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006044 break;
6045 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006046#ifdef DEBUG_LIST
6047 nb_values++;
6048#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006049 list = list->next;
6050 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006051
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006052 if ((ret == 0) && (ctxt->state->value != NULL) &&
6053 (ctxt->state->value != ctxt->state->endvalue)) {
6054 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006055 VALID_ERROR2("Extra data in list: %s\n", ctxt->state->value);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006056 ret = -1;
6057 }
6058 xmlFree(val);
6059 ctxt->state->value = oldvalue;
6060 ctxt->state->endvalue = oldend;
6061 break;
6062 }
6063 case XML_RELAXNG_ONEORMORE:
6064 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
6065 if (ret != 0) {
6066 break;
6067 }
6068 /* no break on purpose */
6069 case XML_RELAXNG_ZEROORMORE: {
6070 xmlChar *cur, *temp;
6071
6072 oldflags = ctxt->flags;
6073 ctxt->flags |= FLAGS_IGNORABLE;
6074 cur = ctxt->state->value;
6075 temp = NULL;
6076 while ((cur != NULL) && (cur != ctxt->state->endvalue) &&
6077 (temp != cur)) {
6078 temp = cur;
6079 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
6080 if (ret != 0) {
6081 ctxt->state->value = temp;
6082 ret = 0;
6083 break;
6084 }
6085 cur = ctxt->state->value;
6086 }
6087 ctxt->flags = oldflags;
6088 break;
6089 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006090 case XML_RELAXNG_EXCEPT: {
6091 xmlRelaxNGDefinePtr list;
6092
6093 list = define->content;
6094 while (list != NULL) {
6095 ret = xmlRelaxNGValidateValue(ctxt, list);
6096 if (ret == 0) {
6097 ret = -1;
6098 break;
6099 } else
6100 ret = 0;
6101 list = list->next;
6102 }
6103 break;
6104 }
Daniel Veillardd4310742003-02-18 21:12:46 +00006105 case XML_RELAXNG_GROUP: {
6106 xmlRelaxNGDefinePtr list;
6107
6108 list = define->content;
6109 while (list != NULL) {
6110 ret = xmlRelaxNGValidateValue(ctxt, list);
6111 if (ret != 0) {
6112 ret = -1;
6113 break;
6114 } else
6115 ret = 0;
6116 list = list->next;
6117 }
6118 break;
6119 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006120 default:
6121 TODO
6122 ret = -1;
6123 }
6124 return(ret);
6125}
6126
6127/**
6128 * xmlRelaxNGValidateValueContent:
6129 * @ctxt: a Relax-NG validation context
6130 * @defines: the list of definitions to verify
6131 *
6132 * Validate the given definitions for the current value
6133 *
6134 * Returns 0 if the validation succeeded or an error code.
6135 */
6136static int
6137xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt,
6138 xmlRelaxNGDefinePtr defines) {
6139 int ret = 0;
6140
6141 while (defines != NULL) {
6142 ret = xmlRelaxNGValidateValue(ctxt, defines);
6143 if (ret != 0)
6144 break;
6145 defines = defines->next;
6146 }
6147 return(ret);
6148}
6149
6150/**
Daniel Veillard144fae12003-02-03 13:17:57 +00006151 * xmlRelaxNGAttributeMatch:
6152 * @ctxt: a Relax-NG validation context
6153 * @define: the definition to check
6154 * @prop: the attribute
6155 *
6156 * Check if the attribute matches the definition nameClass
6157 *
6158 * Returns 1 if the attribute matches, 0 if no, or -1 in case of error
6159 */
6160static int
6161xmlRelaxNGAttributeMatch(xmlRelaxNGValidCtxtPtr ctxt,
6162 xmlRelaxNGDefinePtr define,
6163 xmlAttrPtr prop) {
6164 int ret;
6165
6166 if (define->name != NULL) {
6167 if (!xmlStrEqual(define->name, prop->name))
6168 return(0);
6169 }
6170 if (define->ns != NULL) {
6171 if (define->ns[0] == 0) {
6172 if (prop->ns != NULL)
6173 return(0);
6174 } else {
6175 if ((prop->ns == NULL) ||
6176 (!xmlStrEqual(define->ns, prop->ns->href)))
6177 return(0);
6178 }
6179 }
6180 if (define->nameClass == NULL)
6181 return(1);
6182 define = define->nameClass;
6183 if (define->type == XML_RELAXNG_EXCEPT) {
6184 xmlRelaxNGDefinePtr list;
6185
6186 list = define->content;
6187 while (list != NULL) {
6188 ret = xmlRelaxNGAttributeMatch(ctxt, list, prop);
6189 if (ret == 1)
6190 return(0);
6191 if (ret < 0)
6192 return(ret);
6193 list = list->next;
6194 }
6195 } else {
6196 TODO
6197 }
6198 return(1);
6199}
6200
6201/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00006202 * xmlRelaxNGValidateAttribute:
6203 * @ctxt: a Relax-NG validation context
6204 * @define: the definition to verify
6205 *
6206 * Validate the given attribute definition for that node
6207 *
6208 * Returns 0 if the validation succeeded or an error code.
6209 */
6210static int
6211xmlRelaxNGValidateAttribute(xmlRelaxNGValidCtxtPtr ctxt,
6212 xmlRelaxNGDefinePtr define) {
6213 int ret = 0, i;
6214 xmlChar *value, *oldvalue;
6215 xmlAttrPtr prop = NULL, tmp;
6216
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006217 if (ctxt->state->nbAttrLeft <= 0)
6218 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006219 if (define->name != NULL) {
6220 for (i = 0;i < ctxt->state->nbAttrs;i++) {
6221 tmp = ctxt->state->attrs[i];
6222 if ((tmp != NULL) && (xmlStrEqual(define->name, tmp->name))) {
6223 if ((((define->ns == NULL) || (define->ns[0] == 0)) &&
6224 (tmp->ns == NULL)) ||
6225 ((tmp->ns != NULL) &&
6226 (xmlStrEqual(define->ns, tmp->ns->href)))) {
6227 prop = tmp;
6228 break;
6229 }
6230 }
6231 }
6232 if (prop != NULL) {
6233 value = xmlNodeListGetString(prop->doc, prop->children, 1);
6234 oldvalue = ctxt->state->value;
6235 ctxt->state->value = value;
Daniel Veillard231d7912003-02-09 14:22:17 +00006236 ctxt->state->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006237 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00006238 if (ctxt->state->value != NULL)
6239 value = ctxt->state->value;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006240 if (value != NULL)
6241 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00006242 ctxt->state->value = oldvalue;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006243 if (ret == 0) {
6244 /*
6245 * flag the attribute as processed
6246 */
6247 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006248 ctxt->state->nbAttrLeft--;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006249 }
6250 } else {
6251 ret = -1;
6252 }
6253#ifdef DEBUG
6254 xmlGenericError(xmlGenericErrorContext,
6255 "xmlRelaxNGValidateAttribute(%s): %d\n", define->name, ret);
6256#endif
6257 } else {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006258 for (i = 0;i < ctxt->state->nbAttrs;i++) {
6259 tmp = ctxt->state->attrs[i];
Daniel Veillard144fae12003-02-03 13:17:57 +00006260 if ((tmp != NULL) &&
6261 (xmlRelaxNGAttributeMatch(ctxt, define, tmp) == 1)) {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006262 prop = tmp;
6263 break;
6264 }
6265 }
6266 if (prop != NULL) {
6267 value = xmlNodeListGetString(prop->doc, prop->children, 1);
6268 oldvalue = ctxt->state->value;
6269 ctxt->state->value = value;
6270 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00006271 if (ctxt->state->value != NULL)
6272 value = ctxt->state->value;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006273 if (value != NULL)
6274 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00006275 ctxt->state->value = oldvalue;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006276 if (ret == 0) {
6277 /*
6278 * flag the attribute as processed
6279 */
6280 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006281 ctxt->state->nbAttrLeft--;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006282 }
6283 } else {
6284 ret = -1;
6285 }
6286#ifdef DEBUG
Daniel Veillard144fae12003-02-03 13:17:57 +00006287 if (define->ns != NULL) {
6288 xmlGenericError(xmlGenericErrorContext,
6289 "xmlRelaxNGValidateAttribute(nsName ns = %s): %d\n",
6290 define->ns, ret);
6291 } else {
6292 xmlGenericError(xmlGenericErrorContext,
6293 "xmlRelaxNGValidateAttribute(anyName): %d\n",
6294 ret);
6295 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006296#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00006297 }
6298
6299 return(ret);
6300}
6301
6302/**
6303 * xmlRelaxNGValidateAttributeList:
6304 * @ctxt: a Relax-NG validation context
6305 * @define: the list of definition to verify
6306 *
6307 * Validate the given node against the list of attribute definitions
6308 *
6309 * Returns 0 if the validation succeeded or an error code.
6310 */
6311static int
6312xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt,
6313 xmlRelaxNGDefinePtr defines) {
6314 int ret = 0;
6315 while (defines != NULL) {
6316 if (xmlRelaxNGValidateAttribute(ctxt, defines) != 0)
6317 ret = -1;
6318 defines = defines->next;
6319 }
6320 return(ret);
6321}
6322
6323/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006324 * xmlRelaxNGNodeMatchesList:
6325 * @node: the node
6326 * @list: a NULL terminated array of definitions
6327 *
6328 * Check if a node can be matched by one of the definitions
6329 *
6330 * Returns 1 if matches 0 otherwise
6331 */
6332static int
6333xmlRelaxNGNodeMatchesList(xmlNodePtr node, xmlRelaxNGDefinePtr *list) {
6334 xmlRelaxNGDefinePtr cur;
6335 int i = 0;
6336
6337 if ((node == NULL) || (list == NULL))
6338 return(0);
6339
6340 cur = list[i++];
6341 while (cur != NULL) {
6342 if ((node->type == XML_ELEMENT_NODE) &&
6343 (cur->type == XML_RELAXNG_ELEMENT)) {
6344 if (cur->name == NULL) {
6345 if ((node->ns != NULL) &&
6346 (xmlStrEqual(node->ns->href, cur->ns)))
6347 return(1);
6348 } else if (xmlStrEqual(cur->name, node->name)) {
6349 if ((cur->ns == NULL) || (cur->ns[0] == 0)) {
6350 if (node->ns == NULL)
6351 return(1);
6352 } else {
6353 if ((node->ns != NULL) &&
6354 (xmlStrEqual(node->ns->href, cur->ns)))
6355 return(1);
6356 }
6357 }
6358 } else if ((node->type == XML_TEXT_NODE) &&
6359 (cur->type == XML_RELAXNG_TEXT)) {
6360 return(1);
6361 }
6362 cur = list[i++];
6363 }
6364 return(0);
6365}
6366
6367/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006368 * xmlRelaxNGValidateInterleave:
6369 * @ctxt: a Relax-NG validation context
6370 * @define: the definition to verify
6371 *
6372 * Validate an interleave definition for a node.
6373 *
6374 * Returns 0 if the validation succeeded or an error code.
6375 */
6376static int
6377xmlRelaxNGValidateInterleave(xmlRelaxNGValidCtxtPtr ctxt,
6378 xmlRelaxNGDefinePtr define) {
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006379 int ret = 0, i, nbgroups, left;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006380 xmlRelaxNGPartitionPtr partitions;
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006381 xmlRelaxNGInterleaveGroupPtr group = NULL;
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00006382 xmlNodePtr cur, start, last = NULL, lastchg = NULL, lastelem;
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006383 xmlNodePtr *list = NULL, *lasts = NULL;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006384
6385 if (define->data != NULL) {
6386 partitions = (xmlRelaxNGPartitionPtr) define->data;
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006387 nbgroups = partitions->nbgroups;
6388 left = nbgroups;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006389 } else {
6390 VALID_CTXT();
6391 VALID_ERROR("Internal: interleave block has no data\n");
6392 return(-1);
6393 }
6394
6395 /*
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006396 * Build arrays to store the first and last node of the chain
6397 * pertaining to each group
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006398 */
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006399 list = (xmlNodePtr *) xmlMalloc(nbgroups * sizeof(xmlNodePtr));
6400 if (list == NULL) {
6401 VALID_CTXT();
6402 VALID_ERROR("Internal: out of memory in interleave check\n");
6403 return(-1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006404 }
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006405 memset(list, 0, nbgroups * sizeof(xmlNodePtr));
6406 lasts = (xmlNodePtr *) xmlMalloc(nbgroups * sizeof(xmlNodePtr));
6407 if (lasts == NULL) {
6408 VALID_CTXT();
6409 VALID_ERROR("Internal: out of memory in interleave check\n");
6410 return(-1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006411 }
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006412 memset(lasts, 0, nbgroups * sizeof(xmlNodePtr));
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006413
6414 /*
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006415 * Walk the sequence of children finding the right group and
6416 * sorting them in sequences.
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006417 */
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006418 cur = ctxt->state->seq;
6419 cur = xmlRelaxNGSkipIgnored(ctxt, cur);
6420 start = cur;
6421 while (cur != NULL) {
6422 ctxt->state->seq = cur;
6423 for (i = 0;i < nbgroups;i++) {
6424 group = partitions->groups[i];
6425 if (group == NULL)
6426 continue;
6427 if (xmlRelaxNGNodeMatchesList(cur, group->defs))
6428 break;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006429 }
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006430 /*
6431 * We break as soon as an element not matched is found
6432 */
6433 if (i >= nbgroups) {
6434 break;
6435 }
6436 if (lasts[i] != NULL) {
6437 lasts[i]->next = cur;
6438 lasts[i] = cur;
6439 } else {
6440 list[i] = cur;
6441 lasts[i] = cur;
6442 }
6443 if (cur->next != NULL)
6444 lastchg = cur->next;
6445 else
6446 lastchg = cur;
6447 cur = xmlRelaxNGSkipIgnored(ctxt, cur->next);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006448 }
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006449 if (ret != 0) {
6450 VALID_CTXT();
6451 VALID_ERROR("Invalid sequence in interleave\n");
6452 ret = -1;
6453 goto done;
6454 }
6455 lastelem = cur;
6456 for (i = 0;i < nbgroups;i++) {
6457 group = partitions->groups[i];
6458 if (lasts[i] != NULL) {
6459 last = lasts[i]->next;
6460 lasts[i]->next = NULL;
6461 }
6462 ctxt->state->seq = list[i];
6463 ret = xmlRelaxNGValidateDefinition(ctxt, group->rule);
6464 if (ret != 0)
6465 break;
6466 cur = ctxt->state->seq;
6467 cur = xmlRelaxNGSkipIgnored(ctxt, cur);
6468 if (cur != NULL) {
6469 VALID_CTXT();
6470 VALID_ERROR2("Extra element %s in interleave\n", cur->name);
6471 ret = -1;
6472 goto done;
6473 }
6474 if (lasts[i] != NULL) {
6475 lasts[i]->next = last;
6476 }
6477 }
6478 ctxt->state->seq = lastelem;
6479 if (ret != 0) {
6480 VALID_CTXT();
6481 VALID_ERROR("Invalid sequence in interleave\n");
6482 ret = -1;
6483 goto done;
6484 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006485
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006486done:
6487 /*
6488 * builds the next links chain from the prev one
6489 */
6490 cur = lastchg;
6491 while (cur != NULL) {
6492 if ((cur == start) || (cur->prev == NULL))
6493 break;
6494 cur->prev->next = cur;
6495 cur = cur->prev;
6496 }
6497
6498 xmlFree(list);
6499 xmlFree(lasts);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006500 return(ret);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006501}
6502
6503/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00006504 * xmlRelaxNGValidateElementContent:
6505 * @ctxt: a Relax-NG validation context
6506 * @define: the list of definition to verify
6507 *
6508 * Validate the given node content against the (list) of definitions
6509 *
6510 * Returns 0 if the validation succeeded or an error code.
6511 */
6512static int
6513xmlRelaxNGValidateElementContent(xmlRelaxNGValidCtxtPtr ctxt,
6514 xmlRelaxNGDefinePtr defines) {
6515 int ret = 0, res;
6516
6517 if (ctxt->state == NULL) {
6518 VALID_CTXT();
6519 VALID_ERROR("Internal: no state\n");
6520 return(-1);
6521 }
6522 while (defines != NULL) {
6523 res = xmlRelaxNGValidateDefinition(ctxt, defines);
6524 if (res < 0)
6525 ret = -1;
6526 defines = defines->next;
6527 }
6528
6529 return(ret);
6530}
6531
6532/**
Daniel Veillard416589a2003-02-17 17:25:42 +00006533 * xmlRelaxNGElementMatch:
6534 * @ctxt: a Relax-NG validation context
6535 * @define: the definition to check
6536 * @elem: the element
6537 *
6538 * Check if the element matches the definition nameClass
6539 *
6540 * Returns 1 if the element matches, 0 if no, or -1 in case of error
6541 */
6542static int
6543xmlRelaxNGElementMatch(xmlRelaxNGValidCtxtPtr ctxt,
6544 xmlRelaxNGDefinePtr define,
6545 xmlNodePtr elem) {
6546 int ret, oldflags;
6547
6548 if (define->name != NULL) {
6549 if (!xmlStrEqual(elem->name, define->name)) {
6550 VALID_CTXT();
6551 VALID_ERROR3("Expecting element %s, got %s\n",
6552 define->name, elem->name);
6553 return(0);
6554 }
6555 }
6556 if ((define->ns != NULL) && (define->ns[0] != 0)) {
6557 if (elem->ns == NULL) {
6558 VALID_CTXT();
6559 VALID_ERROR2("Expecting a namespace for element %s\n",
6560 elem->name);
6561 return(0);
6562 } else if (!xmlStrEqual(elem->ns->href, define->ns)) {
6563 VALID_CTXT();
6564 VALID_ERROR3("Expecting element %s has wrong namespace: expecting %s\n",
6565 elem->name, define->ns);
6566 return(0);
6567 }
Daniel Veillard8fe98712003-02-19 00:19:14 +00006568 } else if ((elem->ns != NULL) && (define->ns != NULL) &&
6569 (define->name == NULL)) {
6570 VALID_CTXT();
6571 VALID_ERROR2("Expecting no namespace for element %s\n",
6572 define->name);
6573 return(0);
6574 } else if ((elem->ns != NULL) && (define->name != NULL)) {
6575 VALID_CTXT();
6576 VALID_ERROR2("Expecting no namespace for element %s\n",
6577 define->name);
6578 return(0);
Daniel Veillard416589a2003-02-17 17:25:42 +00006579 }
6580
6581 if (define->nameClass == NULL)
6582 return(1);
6583
6584 define = define->nameClass;
6585 if (define->type == XML_RELAXNG_EXCEPT) {
6586 xmlRelaxNGDefinePtr list;
6587 oldflags = ctxt->flags;
6588 ctxt->flags |= FLAGS_IGNORABLE;
6589
6590 list = define->content;
6591 while (list != NULL) {
6592 ret = xmlRelaxNGElementMatch(ctxt, list, elem);
6593 if (ret == 1) {
6594 ctxt->flags = oldflags;
6595 return(0);
6596 }
6597 if (ret < 0) {
6598 ctxt->flags = oldflags;
6599 return(ret);
6600 }
6601 list = list->next;
6602 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006603 ret = 1;
6604 ctxt->flags = oldflags;
6605 } else if (define->type == XML_RELAXNG_CHOICE) {
6606 xmlRelaxNGDefinePtr list;
6607 oldflags = ctxt->flags;
6608 ctxt->flags |= FLAGS_IGNORABLE;
6609
6610 list = define->nameClass;
6611 while (list != NULL) {
6612 ret = xmlRelaxNGElementMatch(ctxt, list, elem);
6613 if (ret == 1) {
6614 ctxt->flags = oldflags;
6615 return(1);
6616 }
6617 if (ret < 0) {
6618 ctxt->flags = oldflags;
6619 return(ret);
6620 }
6621 list = list->next;
6622 }
6623 ret = 0;
Daniel Veillard416589a2003-02-17 17:25:42 +00006624 ctxt->flags = oldflags;
6625 } else {
6626 TODO
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006627 ret = -1;
Daniel Veillard416589a2003-02-17 17:25:42 +00006628 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006629 return(ret);
Daniel Veillard416589a2003-02-17 17:25:42 +00006630}
6631
6632/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00006633 * xmlRelaxNGValidateDefinition:
6634 * @ctxt: a Relax-NG validation context
6635 * @define: the definition to verify
6636 *
6637 * Validate the current node against the definition
6638 *
6639 * Returns 0 if the validation succeeded or an error code.
6640 */
6641static int
6642xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
6643 xmlRelaxNGDefinePtr define) {
6644 xmlNodePtr node;
6645 int ret = 0, i, tmp, oldflags;
6646 xmlRelaxNGValidStatePtr oldstate, state;
6647
6648 if (define == NULL) {
6649 VALID_CTXT();
6650 VALID_ERROR("internal error: define == NULL\n");
6651 return(-1);
6652 }
6653 if (ctxt->state != NULL) {
6654 node = ctxt->state->seq;
6655 } else {
6656 node = NULL;
6657 }
Daniel Veillard231d7912003-02-09 14:22:17 +00006658#ifdef DEBUG
6659 for (i = 0;i < ctxt->depth;i++)
6660 xmlGenericError(xmlGenericErrorContext, " ");
6661 xmlGenericError(xmlGenericErrorContext,
6662 "Start validating %s ", xmlRelaxNGDefName(define));
6663 if (define->name != NULL)
6664 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
6665 if ((node != NULL) && (node->name != NULL))
6666 xmlGenericError(xmlGenericErrorContext, "on %s\n", node->name);
6667 else
6668 xmlGenericError(xmlGenericErrorContext, "\n");
6669#endif
6670 ctxt->depth++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006671 switch (define->type) {
6672 case XML_RELAXNG_EMPTY:
Daniel Veillardd4310742003-02-18 21:12:46 +00006673 node = xmlRelaxNGSkipIgnored(ctxt, node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006674 if (node != NULL) {
6675 VALID_CTXT();
6676 VALID_ERROR("Expecting an empty element\n");
Daniel Veillard231d7912003-02-09 14:22:17 +00006677 ret = -1;
6678 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006679 }
Daniel Veillard231d7912003-02-09 14:22:17 +00006680 ret = 0;
6681 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006682 case XML_RELAXNG_NOT_ALLOWED:
Daniel Veillard231d7912003-02-09 14:22:17 +00006683 ret = -1;
6684 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006685 case XML_RELAXNG_TEXT:
Daniel Veillardce14fa52003-02-19 17:32:48 +00006686#if 0
Daniel Veillard231d7912003-02-09 14:22:17 +00006687 if (node == NULL) {
6688 ret = 0;
6689 break;
6690 }
Daniel Veillardce14fa52003-02-19 17:32:48 +00006691#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00006692 while ((node != NULL) &&
6693 ((node->type == XML_TEXT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006694 (node->type == XML_COMMENT_NODE) ||
6695 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00006696 (node->type == XML_CDATA_SECTION_NODE)))
6697 node = node->next;
Daniel Veillardce14fa52003-02-19 17:32:48 +00006698#if 0
Daniel Veillard276be4a2003-01-24 01:03:34 +00006699 if (node == ctxt->state->seq) {
6700 VALID_CTXT();
6701 VALID_ERROR("Expecting text content\n");
6702 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006703 }
Daniel Veillardce14fa52003-02-19 17:32:48 +00006704#endif
Daniel Veillard276be4a2003-01-24 01:03:34 +00006705 ctxt->state->seq = node;
6706 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006707 case XML_RELAXNG_ELEMENT:
6708 node = xmlRelaxNGSkipIgnored(ctxt, node);
Daniel Veillard231d7912003-02-09 14:22:17 +00006709 if (node == NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006710 VALID_CTXT();
Daniel Veillard231d7912003-02-09 14:22:17 +00006711 VALID_ERROR("Expecting an element, got empty\n");
6712 ret = -1;
6713 break;
6714 }
6715 if (node->type != XML_ELEMENT_NODE) {
6716 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006717 VALID_ERROR2("Expecting an element got %d type\n", node->type);
Daniel Veillard231d7912003-02-09 14:22:17 +00006718 ret = -1;
6719 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006720 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00006721 /*
6722 * This node was already validated successfully against
6723 * this definition.
6724 */
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006725 if (node->_private == define) {
6726 ctxt->state->seq = xmlRelaxNGSkipIgnored(ctxt, node->next);
Daniel Veillarde5b110b2003-02-04 14:43:39 +00006727 break;
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006728 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006729
6730 ret = xmlRelaxNGElementMatch(ctxt, define, node);
6731 if (ret <= 0) {
6732 ret = -1;
6733 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006734 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006735 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006736
6737 state = xmlRelaxNGNewValidState(ctxt, node);
6738 if (state == NULL) {
Daniel Veillard231d7912003-02-09 14:22:17 +00006739 ret = -1;
6740 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006741 }
6742
6743 oldstate = ctxt->state;
6744 ctxt->state = state;
6745 if (define->attrs != NULL) {
6746 tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
Daniel Veillard231d7912003-02-09 14:22:17 +00006747 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006748 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00006749#ifdef DEBUG
6750 xmlGenericError(xmlGenericErrorContext,
6751 "E: Element %s failed to validate attributes\n",
6752 node->name);
6753#endif
6754 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006755 }
6756 if (define->content != NULL) {
6757 tmp = xmlRelaxNGValidateElementContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00006758 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006759 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00006760#ifdef DEBUG
6761 xmlGenericError(xmlGenericErrorContext,
6762 "E: Element %s failed to validate element content\n",
6763 node->name);
6764#endif
6765 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006766 }
6767 state = ctxt->state;
6768 if (state->seq != NULL) {
6769 state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq);
6770 if (state->seq != NULL) {
6771 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006772 VALID_ERROR3("Extra content for element %s: %s\n",
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006773 node->name, state->seq->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006774 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00006775#ifdef DEBUG
6776 xmlGenericError(xmlGenericErrorContext,
6777 "E: Element %s has extra content: %s\n",
6778 node->name, state->seq->name);
6779#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00006780 }
6781 }
6782 for (i = 0;i < state->nbAttrs;i++) {
6783 if (state->attrs[i] != NULL) {
6784 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006785 VALID_ERROR3("Invalid attribute %s for element %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00006786 state->attrs[i]->name, node->name);
6787 ret = -1;
6788 }
6789 }
6790 ctxt->state = oldstate;
6791 xmlRelaxNGFreeValidState(state);
6792 if (oldstate != NULL)
Daniel Veillarde5b110b2003-02-04 14:43:39 +00006793 oldstate->seq = xmlRelaxNGSkipIgnored(ctxt, node->next);
6794 if (ret == 0)
6795 node->_private = define;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006796
6797
6798#ifdef DEBUG
6799 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard231d7912003-02-09 14:22:17 +00006800 "xmlRelaxNGValidateDefinition(): validated %s : %d",
Daniel Veillard6eadf632003-01-23 18:29:16 +00006801 node->name, ret);
Daniel Veillard231d7912003-02-09 14:22:17 +00006802 if (oldstate == NULL)
6803 xmlGenericError(xmlGenericErrorContext, ": no state\n");
6804 else if (oldstate->seq == NULL)
6805 xmlGenericError(xmlGenericErrorContext, ": done\n");
6806 else if (oldstate->seq->type == XML_ELEMENT_NODE)
6807 xmlGenericError(xmlGenericErrorContext, ": next elem %s\n",
6808 oldstate->seq->name);
6809 else
6810 xmlGenericError(xmlGenericErrorContext, ": next %s %d\n",
6811 oldstate->seq->name, oldstate->seq->type);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006812#endif
6813 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006814 case XML_RELAXNG_OPTIONAL:
6815 oldflags = ctxt->flags;
6816 ctxt->flags |= FLAGS_IGNORABLE;
6817 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6818 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6819 if (ret != 0) {
6820 xmlRelaxNGFreeValidState(ctxt->state);
6821 ctxt->state = oldstate;
6822 ret = 0;
6823 break;
6824 }
6825 xmlRelaxNGFreeValidState(oldstate);
6826 ctxt->flags = oldflags;
6827 ret = 0;
6828 break;
6829 case XML_RELAXNG_ONEORMORE:
6830 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6831 if (ret != 0) {
6832 break;
6833 }
6834 /* no break on purpose */
Daniel Veillard276be4a2003-01-24 01:03:34 +00006835 case XML_RELAXNG_ZEROORMORE: {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006836 oldflags = ctxt->flags;
6837 ctxt->flags |= FLAGS_IGNORABLE;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006838 while (ctxt->state->nbAttrLeft != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006839 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6840 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6841 if (ret != 0) {
6842 xmlRelaxNGFreeValidState(ctxt->state);
6843 ctxt->state = oldstate;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006844 break;
6845 }
6846 xmlRelaxNGFreeValidState(oldstate);
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006847 }
6848 if (ret == 0) {
6849 /*
6850 * There is no attribute left to be consumed,
6851 * we can check the closure by looking at ctxt->state->seq
6852 */
6853 xmlNodePtr cur, temp;
6854
Daniel Veillard276be4a2003-01-24 01:03:34 +00006855 cur = ctxt->state->seq;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006856 temp = NULL;
6857 while ((cur != NULL) && (temp != cur)) {
6858 temp = cur;
6859 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6860 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6861 if (ret != 0) {
6862 xmlRelaxNGFreeValidState(ctxt->state);
6863 ctxt->state = oldstate;
6864 break;
6865 }
6866 xmlRelaxNGFreeValidState(oldstate);
6867 cur = ctxt->state->seq;
6868 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006869 }
6870 ctxt->flags = oldflags;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006871 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006872 break;
Daniel Veillard276be4a2003-01-24 01:03:34 +00006873 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006874 case XML_RELAXNG_CHOICE: {
6875 xmlRelaxNGDefinePtr list = define->content;
Daniel Veillardce14fa52003-02-19 17:32:48 +00006876 int success = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006877
6878 oldflags = ctxt->flags;
6879 ctxt->flags |= FLAGS_IGNORABLE;
6880
6881 while (list != NULL) {
6882 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6883 ret = xmlRelaxNGValidateDefinition(ctxt, list);
6884 if (ret == 0) {
Daniel Veillardce14fa52003-02-19 17:32:48 +00006885 if (xmlRelaxNGEqualValidState(ctxt, ctxt->state, oldstate)){
6886 /*
6887 * if that pattern was nullable flag it but try
6888 * to make more progresses
6889 */
6890 success = 1;
6891 } else {
6892 xmlRelaxNGFreeValidState(oldstate);
6893 break;
6894 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006895 }
6896 xmlRelaxNGFreeValidState(ctxt->state);
6897 ctxt->state = oldstate;
6898 list = list->next;
6899 }
6900 ctxt->flags = oldflags;
Daniel Veillardce14fa52003-02-19 17:32:48 +00006901 if (success == 1)
6902 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006903 break;
6904 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00006905 case XML_RELAXNG_DEF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00006906 case XML_RELAXNG_GROUP: {
6907 xmlRelaxNGDefinePtr list = define->content;
6908
6909 while (list != NULL) {
6910 ret = xmlRelaxNGValidateDefinition(ctxt, list);
6911 if (ret != 0)
6912 break;
6913 list = list->next;
6914 }
6915 break;
6916 }
6917 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006918 ret = xmlRelaxNGValidateInterleave(ctxt, define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006919 break;
6920 case XML_RELAXNG_ATTRIBUTE:
6921 ret = xmlRelaxNGValidateAttribute(ctxt, define);
6922 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00006923 case XML_RELAXNG_NOOP:
Daniel Veillard6eadf632003-01-23 18:29:16 +00006924 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00006925 case XML_RELAXNG_PARENTREF:
6926 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00006927 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6928 break;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006929 case XML_RELAXNG_DATATYPE: {
Daniel Veillardd4310742003-02-18 21:12:46 +00006930 xmlNodePtr child;
6931 xmlChar *content = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006932
Daniel Veillardd4310742003-02-18 21:12:46 +00006933 child = node;
6934 while (child != NULL) {
6935 if (child->type == XML_ELEMENT_NODE) {
6936 VALID_CTXT();
6937 VALID_ERROR2("Element %s has child elements\n",
6938 node->parent->name);
6939 ret = -1;
6940 break;
6941 } else if ((child->type == XML_TEXT_NODE) ||
6942 (child->type == XML_CDATA_SECTION_NODE)) {
6943 content = xmlStrcat(content, child->content);
6944 }
6945 /* TODO: handle entities ... */
6946 child = child->next;
6947 }
6948 if (ret == -1) {
6949 if (content != NULL)
6950 xmlFree(content);
6951 break;
6952 }
6953 if (content == NULL) {
6954 content = xmlStrdup(BAD_CAST "");
6955 if (content == NULL) {
6956 VALID_CTXT();
6957 VALID_ERROR("Allocation failure\n");
6958 ret = -1;
6959 break;
6960 }
6961 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006962 ret = xmlRelaxNGValidateDatatype(ctxt, content, define);
6963 if (ret == -1) {
6964 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006965 VALID_ERROR2("internal error validating %s\n", define->name);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006966 } else if (ret == 0) {
Daniel Veillardd4310742003-02-18 21:12:46 +00006967 ctxt->state->seq = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006968 }
6969 if (content != NULL)
6970 xmlFree(content);
6971 break;
6972 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00006973 case XML_RELAXNG_VALUE: {
Daniel Veillardd4310742003-02-18 21:12:46 +00006974 xmlChar *content = NULL;
Daniel Veillardea3f3982003-01-26 19:45:18 +00006975 xmlChar *oldvalue;
Daniel Veillardd4310742003-02-18 21:12:46 +00006976 xmlNodePtr child;
Daniel Veillardea3f3982003-01-26 19:45:18 +00006977
Daniel Veillardd4310742003-02-18 21:12:46 +00006978 child = node;
6979 while (child != NULL) {
6980 if (child->type == XML_ELEMENT_NODE) {
6981 VALID_CTXT();
6982 VALID_ERROR2("Element %s has child elements\n",
6983 node->parent->name);
6984 ret = -1;
6985 break;
6986 } else if ((child->type == XML_TEXT_NODE) ||
6987 (child->type == XML_CDATA_SECTION_NODE)) {
6988 content = xmlStrcat(content, child->content);
6989 }
6990 /* TODO: handle entities ... */
6991 child = child->next;
6992 }
6993 if (ret == -1) {
6994 if (content != NULL)
6995 xmlFree(content);
6996 break;
6997 }
6998 if (content == NULL) {
6999 content = xmlStrdup(BAD_CAST "");
7000 if (content == NULL) {
7001 VALID_CTXT();
7002 VALID_ERROR("Allocation failure\n");
7003 ret = -1;
7004 break;
7005 }
7006 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00007007 oldvalue = ctxt->state->value;
7008 ctxt->state->value = content;
7009 ret = xmlRelaxNGValidateValue(ctxt, define);
7010 ctxt->state->value = oldvalue;
7011 if (ret == -1) {
7012 VALID_CTXT();
Daniel Veillardd2298792003-02-14 16:54:11 +00007013 if (define->name != NULL) {
7014 VALID_ERROR2("error validating value %s\n", define->name);
7015 } else {
7016 VALID_ERROR("error validating value\n");
7017 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00007018 } else if (ret == 0) {
Daniel Veillardd4310742003-02-18 21:12:46 +00007019 ctxt->state->seq = NULL;
Daniel Veillardea3f3982003-01-26 19:45:18 +00007020 }
7021 if (content != NULL)
7022 xmlFree(content);
7023 break;
7024 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007025 case XML_RELAXNG_LIST: {
7026 xmlChar *content;
Daniel Veillardd4310742003-02-18 21:12:46 +00007027 xmlNodePtr child;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007028 xmlChar *oldvalue, *oldendvalue;
7029 int len;
Daniel Veillardea3f3982003-01-26 19:45:18 +00007030
Daniel Veillardd4310742003-02-18 21:12:46 +00007031 /*
7032 * Make sure it's only text nodes
7033 */
7034
7035 content = NULL;
7036 child = node;
7037 while (child != NULL) {
7038 if (child->type == XML_ELEMENT_NODE) {
7039 VALID_CTXT();
7040 VALID_ERROR2("Element %s has child elements\n",
7041 node->parent->name);
7042 ret = -1;
7043 break;
7044 } else if ((child->type == XML_TEXT_NODE) ||
7045 (child->type == XML_CDATA_SECTION_NODE)) {
7046 content = xmlStrcat(content, child->content);
7047 }
7048 /* TODO: handle entities ... */
7049 child = child->next;
7050 }
7051 if (ret == -1) {
7052 if (content != NULL)
7053 xmlFree(content);
7054 break;
7055 }
7056 if (content == NULL) {
7057 content = xmlStrdup(BAD_CAST "");
7058 if (content == NULL) {
7059 VALID_CTXT();
7060 VALID_ERROR("Allocation failure\n");
7061 ret = -1;
7062 break;
7063 }
7064 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007065 len = xmlStrlen(content);
7066 oldvalue = ctxt->state->value;
7067 oldendvalue = ctxt->state->endvalue;
7068 ctxt->state->value = content;
7069 ctxt->state->endvalue = content + len;
7070 ret = xmlRelaxNGValidateValue(ctxt, define);
7071 ctxt->state->value = oldvalue;
7072 ctxt->state->endvalue = oldendvalue;
7073 if (ret == -1) {
7074 VALID_CTXT();
Daniel Veillard2e9b1652003-02-19 13:29:45 +00007075 VALID_ERROR("error validating list\n");
Daniel Veillardd4310742003-02-18 21:12:46 +00007076 } else if ((ret == 0) && (node != NULL)) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007077 ctxt->state->seq = node->next;
7078 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007079 if (content != NULL)
7080 xmlFree(content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00007081 break;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007082 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00007083 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00007084 case XML_RELAXNG_EXCEPT:
Daniel Veillard8fe98712003-02-19 00:19:14 +00007085 case XML_RELAXNG_PARAM:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00007086 TODO
Daniel Veillard416589a2003-02-17 17:25:42 +00007087 ret = -1;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00007088 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007089 }
Daniel Veillard231d7912003-02-09 14:22:17 +00007090 ctxt->depth--;
7091#ifdef DEBUG
7092 for (i = 0;i < ctxt->depth;i++)
7093 xmlGenericError(xmlGenericErrorContext, " ");
7094 xmlGenericError(xmlGenericErrorContext,
7095 "Validating %s ", xmlRelaxNGDefName(define));
7096 if (define->name != NULL)
7097 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
7098 if (ret == 0)
7099 xmlGenericError(xmlGenericErrorContext, "suceeded\n");
7100 else
7101 xmlGenericError(xmlGenericErrorContext, "failed\n");
7102#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00007103 return(ret);
7104}
7105
7106/**
7107 * xmlRelaxNGValidateDocument:
7108 * @ctxt: a Relax-NG validation context
7109 * @doc: the document
7110 *
7111 * Validate the given document
7112 *
7113 * Returns 0 if the validation succeeded or an error code.
7114 */
7115static int
7116xmlRelaxNGValidateDocument(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
7117 int ret;
7118 xmlRelaxNGPtr schema;
7119 xmlRelaxNGGrammarPtr grammar;
7120 xmlRelaxNGValidStatePtr state;
7121
7122 if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL))
7123 return(-1);
7124
7125 schema = ctxt->schema;
7126 grammar = schema->topgrammar;
7127 if (grammar == NULL) {
7128 VALID_CTXT();
7129 VALID_ERROR("No top grammar defined\n");
7130 return(-1);
7131 }
7132 state = xmlRelaxNGNewValidState(ctxt, NULL);
7133 ctxt->state = state;
7134 ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start);
7135 state = ctxt->state;
7136 if ((state != NULL) && (state->seq != NULL)) {
7137 xmlNodePtr node;
7138
7139 node = state->seq;
7140 node = xmlRelaxNGSkipIgnored(ctxt, node);
7141 if (node != NULL) {
7142 VALID_CTXT();
7143 VALID_ERROR("extra data on the document\n");
7144 ret = -1;
7145 }
7146 }
7147 xmlRelaxNGFreeValidState(state);
7148
7149 return(ret);
7150}
7151
7152/************************************************************************
7153 * *
7154 * Validation interfaces *
7155 * *
7156 ************************************************************************/
7157/**
7158 * xmlRelaxNGNewValidCtxt:
7159 * @schema: a precompiled XML RelaxNGs
7160 *
7161 * Create an XML RelaxNGs validation context based on the given schema
7162 *
7163 * Returns the validation context or NULL in case of error
7164 */
7165xmlRelaxNGValidCtxtPtr
7166xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema) {
7167 xmlRelaxNGValidCtxtPtr ret;
7168
7169 ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt));
7170 if (ret == NULL) {
7171 xmlGenericError(xmlGenericErrorContext,
7172 "Failed to allocate new schama validation context\n");
7173 return (NULL);
7174 }
7175 memset(ret, 0, sizeof(xmlRelaxNGValidCtxt));
7176 ret->schema = schema;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00007177 ret->error = xmlGenericError;
7178 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007179 return (ret);
7180}
7181
7182/**
7183 * xmlRelaxNGFreeValidCtxt:
7184 * @ctxt: the schema validation context
7185 *
7186 * Free the resources associated to the schema validation context
7187 */
7188void
7189xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt) {
7190 if (ctxt == NULL)
7191 return;
7192 xmlFree(ctxt);
7193}
7194
7195/**
7196 * xmlRelaxNGSetValidErrors:
7197 * @ctxt: a Relax-NG validation context
7198 * @err: the error function
7199 * @warn: the warning function
7200 * @ctx: the functions context
7201 *
7202 * Set the error and warning callback informations
7203 */
7204void
7205xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
7206 xmlRelaxNGValidityErrorFunc err,
7207 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
7208 if (ctxt == NULL)
7209 return;
7210 ctxt->error = err;
7211 ctxt->warning = warn;
7212 ctxt->userData = ctx;
7213}
7214
7215/**
7216 * xmlRelaxNGValidateDoc:
7217 * @ctxt: a Relax-NG validation context
7218 * @doc: a parsed document tree
7219 *
7220 * Validate a document tree in memory.
7221 *
7222 * Returns 0 if the document is valid, a positive error code
7223 * number otherwise and -1 in case of internal or API error.
7224 */
7225int
7226xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
7227 int ret;
7228
7229 if ((ctxt == NULL) || (doc == NULL))
7230 return(-1);
7231
7232 ctxt->doc = doc;
7233
7234 ret = xmlRelaxNGValidateDocument(ctxt, doc);
Daniel Veillard71531f32003-02-05 13:19:53 +00007235 /*
7236 * TODO: build error codes
7237 */
7238 if (ret == -1)
7239 return(1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00007240 return(ret);
7241}
7242
7243#endif /* LIBXML_SCHEMAS_ENABLED */
7244