blob: 1d3a3f6defaaeba525eb882bb38eb2c65fc90e7a [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 Veillard6eadf632003-01-23 18:29:16 +0000178
179struct _xmlRelaxNGParserCtxt {
180 void *userData; /* user specific data block */
181 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
182 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
183 xmlRelaxNGValidError err;
184
185 xmlRelaxNGPtr schema; /* The schema in use */
186 xmlRelaxNGGrammarPtr grammar; /* the current grammar */
Daniel Veillard419a7682003-02-03 23:22:49 +0000187 xmlRelaxNGGrammarPtr parentgrammar;/* the parent grammar */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000188 int flags; /* parser flags */
189 int nbErrors; /* number of errors at parse time */
190 int nbWarnings; /* number of warnings at parse time */
Daniel Veillard276be4a2003-01-24 01:03:34 +0000191 const xmlChar *define; /* the current define scope */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000192 xmlRelaxNGDefinePtr def; /* the current define */
193
194 int nbInterleaves;
195 xmlHashTablePtr interleaves; /* keep track of all the interleaves */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000196
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000197 xmlHashTablePtr documents; /* all the documents loaded */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000198 xmlHashTablePtr includes; /* all the includes loaded */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000199 xmlChar *URL;
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000200 xmlDocPtr document;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000201
Daniel Veillard419a7682003-02-03 23:22:49 +0000202 int defNr; /* number of defines used */
203 int defMax; /* number of defines aloocated */
204 xmlRelaxNGDefinePtr *defTab; /* pointer to the allocated definitions */
205
Daniel Veillard6eadf632003-01-23 18:29:16 +0000206 const char *buffer;
207 int size;
208
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000209 /* the document stack */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000210 xmlRelaxNGDocumentPtr doc; /* Current parsed external ref */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000211 int docNr; /* Depth of the parsing stack */
212 int docMax; /* Max depth of the parsing stack */
213 xmlRelaxNGDocumentPtr *docTab; /* array of docs */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000214
215 /* the include stack */
Daniel Veillardd4310742003-02-18 21:12:46 +0000216 xmlRelaxNGIncludePtr inc; /* Current parsed include */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000217 int incNr; /* Depth of the include parsing stack */
218 int incMax; /* Max depth of the parsing stack */
219 xmlRelaxNGIncludePtr *incTab; /* array of incs */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000220};
221
222#define FLAGS_IGNORABLE 1
223#define FLAGS_NEGATIVE 2
224
225/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000226 * xmlRelaxNGInterleaveGroup:
227 *
228 * A RelaxNGs partition set associated to lists of definitions
229 */
230typedef struct _xmlRelaxNGInterleaveGroup xmlRelaxNGInterleaveGroup;
231typedef xmlRelaxNGInterleaveGroup *xmlRelaxNGInterleaveGroupPtr;
232struct _xmlRelaxNGInterleaveGroup {
233 xmlRelaxNGDefinePtr rule; /* the rule to satisfy */
234 xmlRelaxNGDefinePtr *defs; /* the array of element definitions */
235};
236
237/**
238 * xmlRelaxNGPartitions:
239 *
240 * A RelaxNGs partition associated to an interleave group
241 */
242typedef struct _xmlRelaxNGPartition xmlRelaxNGPartition;
243typedef xmlRelaxNGPartition *xmlRelaxNGPartitionPtr;
244struct _xmlRelaxNGPartition {
245 int nbgroups; /* number of groups in the partitions */
246 xmlRelaxNGInterleaveGroupPtr *groups;
247};
248
249/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000250 * xmlRelaxNGValidState:
251 *
252 * A RelaxNGs validation state
253 */
254#define MAX_ATTR 20
255typedef struct _xmlRelaxNGValidState xmlRelaxNGValidState;
256typedef xmlRelaxNGValidState *xmlRelaxNGValidStatePtr;
257struct _xmlRelaxNGValidState {
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000258 xmlNodePtr node; /* the current node */
259 xmlNodePtr seq; /* the sequence of children left to validate */
260 int nbAttrs; /* the number of attributes */
Daniel Veillard1ed7f362003-02-03 10:57:45 +0000261 int nbAttrLeft; /* the number of attributes left to validate */
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000262 xmlChar *value; /* the value when operating on string */
263 xmlChar *endvalue; /* the end value when operating on string */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000264 xmlAttrPtr attrs[1]; /* the array of attributes */
265};
266
267/**
268 * xmlRelaxNGValidCtxt:
269 *
270 * A RelaxNGs validation context
271 */
272
273struct _xmlRelaxNGValidCtxt {
274 void *userData; /* user specific data block */
275 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
276 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
277
278 xmlRelaxNGPtr schema; /* The schema in use */
279 xmlDocPtr doc; /* the document being validated */
280 xmlRelaxNGValidStatePtr state; /* the current validation state */
281 int flags; /* validation flags */
Daniel Veillard231d7912003-02-09 14:22:17 +0000282 int depth; /* validation depth */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000283};
284
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000285/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000286 * xmlRelaxNGInclude:
287 *
288 * Structure associated to a RelaxNGs document element
289 */
290struct _xmlRelaxNGInclude {
291 xmlChar *href; /* the normalized href value */
292 xmlDocPtr doc; /* the associated XML document */
293 xmlRelaxNGDefinePtr content;/* the definitions */
294 xmlRelaxNGPtr schema; /* the schema */
295};
296
297/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000298 * xmlRelaxNGDocument:
299 *
300 * Structure associated to a RelaxNGs document element
301 */
302struct _xmlRelaxNGDocument {
303 xmlChar *href; /* the normalized href value */
304 xmlDocPtr doc; /* the associated XML document */
305 xmlRelaxNGDefinePtr content;/* the definitions */
306 xmlRelaxNGPtr schema; /* the schema */
307};
308
Daniel Veillard6eadf632003-01-23 18:29:16 +0000309/************************************************************************
310 * *
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000311 * Preliminary type checking interfaces *
312 * *
313 ************************************************************************/
314/**
315 * xmlRelaxNGTypeHave:
316 * @data: data needed for the library
317 * @type: the type name
318 * @value: the value to check
319 *
320 * Function provided by a type library to check if a type is exported
321 *
322 * Returns 1 if yes, 0 if no and -1 in case of error.
323 */
324typedef int (*xmlRelaxNGTypeHave) (void *data, const xmlChar *type);
325
326/**
327 * xmlRelaxNGTypeCheck:
328 * @data: data needed for the library
329 * @type: the type name
330 * @value: the value to check
331 *
332 * Function provided by a type library to check if a value match a type
333 *
334 * Returns 1 if yes, 0 if no and -1 in case of error.
335 */
336typedef int (*xmlRelaxNGTypeCheck) (void *data, const xmlChar *type,
337 const xmlChar *value);
338
339/**
340 * xmlRelaxNGTypeCompare:
341 * @data: data needed for the library
342 * @type: the type name
343 * @value1: the first value
344 * @value2: the second value
345 *
346 * Function provided by a type library to compare two values accordingly
347 * to a type.
348 *
349 * Returns 1 if yes, 0 if no and -1 in case of error.
350 */
351typedef int (*xmlRelaxNGTypeCompare) (void *data, const xmlChar *type,
352 const xmlChar *value1,
353 const xmlChar *value2);
354typedef struct _xmlRelaxNGTypeLibrary xmlRelaxNGTypeLibrary;
355typedef xmlRelaxNGTypeLibrary *xmlRelaxNGTypeLibraryPtr;
356struct _xmlRelaxNGTypeLibrary {
357 const xmlChar *namespace; /* the datatypeLibrary value */
358 void *data; /* data needed for the library */
359 xmlRelaxNGTypeHave have; /* the export function */
360 xmlRelaxNGTypeCheck check; /* the checking function */
361 xmlRelaxNGTypeCompare comp; /* the compare function */
362};
363
364/************************************************************************
365 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +0000366 * Allocation functions *
367 * *
368 ************************************************************************/
Daniel Veillard6eadf632003-01-23 18:29:16 +0000369static void xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar);
370static void xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define);
Daniel Veillardd2298792003-02-14 16:54:11 +0000371static void xmlRelaxNGNormExtSpace(xmlChar *value);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000372
373/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000374 * xmlRelaxNGFreeDocument:
375 * @docu: a document structure
376 *
377 * Deallocate a RelaxNG document structure.
378 */
379static void
380xmlRelaxNGFreeDocument(xmlRelaxNGDocumentPtr docu)
381{
382 if (docu == NULL)
383 return;
384
385 if (docu->href != NULL)
386 xmlFree(docu->href);
387 if (docu->doc != NULL)
388 xmlFreeDoc(docu->doc);
389 if (docu->schema != NULL)
390 xmlRelaxNGFree(docu->schema);
391 xmlFree(docu);
392}
393
394/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000395 * xmlRelaxNGFreeInclude:
396 * @incl: a include structure
397 *
398 * Deallocate a RelaxNG include structure.
399 */
400static void
401xmlRelaxNGFreeInclude(xmlRelaxNGIncludePtr incl)
402{
403 if (incl == NULL)
404 return;
405
406 if (incl->href != NULL)
407 xmlFree(incl->href);
408 if (incl->doc != NULL)
409 xmlFreeDoc(incl->doc);
410 if (incl->schema != NULL)
411 xmlRelaxNGFree(incl->schema);
412 xmlFree(incl);
413}
414
415/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000416 * xmlRelaxNGNewRelaxNG:
417 * @ctxt: a Relax-NG validation context (optional)
418 *
419 * Allocate a new RelaxNG structure.
420 *
421 * Returns the newly allocated structure or NULL in case or error
422 */
423static xmlRelaxNGPtr
424xmlRelaxNGNewRelaxNG(xmlRelaxNGParserCtxtPtr ctxt)
425{
426 xmlRelaxNGPtr ret;
427
428 ret = (xmlRelaxNGPtr) xmlMalloc(sizeof(xmlRelaxNG));
429 if (ret == NULL) {
430 if ((ctxt != NULL) && (ctxt->error != NULL))
431 ctxt->error(ctxt->userData, "Out of memory\n");
432 ctxt->nbErrors++;
433 return (NULL);
434 }
435 memset(ret, 0, sizeof(xmlRelaxNG));
436
437 return (ret);
438}
439
440/**
441 * xmlRelaxNGFree:
442 * @schema: a schema structure
443 *
444 * Deallocate a RelaxNG structure.
445 */
446void
447xmlRelaxNGFree(xmlRelaxNGPtr schema)
448{
449 if (schema == NULL)
450 return;
451
Daniel Veillard6eadf632003-01-23 18:29:16 +0000452 if (schema->topgrammar != NULL)
453 xmlRelaxNGFreeGrammar(schema->topgrammar);
454 if (schema->doc != NULL)
455 xmlFreeDoc(schema->doc);
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000456 if (schema->documents != NULL)
457 xmlHashFree(schema->documents, (xmlHashDeallocator)
458 xmlRelaxNGFreeDocument);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000459 if (schema->includes != NULL)
460 xmlHashFree(schema->includes, (xmlHashDeallocator)
461 xmlRelaxNGFreeInclude);
Daniel Veillard419a7682003-02-03 23:22:49 +0000462 if (schema->defTab != NULL) {
463 int i;
464
465 for (i = 0;i < schema->defNr;i++)
466 xmlRelaxNGFreeDefine(schema->defTab[i]);
467 xmlFree(schema->defTab);
468 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000469
470 xmlFree(schema);
471}
472
473/**
474 * xmlRelaxNGNewGrammar:
475 * @ctxt: a Relax-NG validation context (optional)
476 *
477 * Allocate a new RelaxNG grammar.
478 *
479 * Returns the newly allocated structure or NULL in case or error
480 */
481static xmlRelaxNGGrammarPtr
482xmlRelaxNGNewGrammar(xmlRelaxNGParserCtxtPtr ctxt)
483{
484 xmlRelaxNGGrammarPtr ret;
485
486 ret = (xmlRelaxNGGrammarPtr) xmlMalloc(sizeof(xmlRelaxNGGrammar));
487 if (ret == NULL) {
488 if ((ctxt != NULL) && (ctxt->error != NULL))
489 ctxt->error(ctxt->userData, "Out of memory\n");
490 ctxt->nbErrors++;
491 return (NULL);
492 }
493 memset(ret, 0, sizeof(xmlRelaxNGGrammar));
494
495 return (ret);
496}
497
498/**
499 * xmlRelaxNGFreeGrammar:
500 * @grammar: a grammar structure
501 *
502 * Deallocate a RelaxNG grammar structure.
503 */
504static void
505xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar)
506{
507 if (grammar == NULL)
508 return;
509
Daniel Veillard419a7682003-02-03 23:22:49 +0000510 if (grammar->next != NULL) {
511 xmlRelaxNGFreeGrammar(grammar->next);
512 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000513 if (grammar->refs != NULL) {
514 xmlHashFree(grammar->refs, NULL);
515 }
516 if (grammar->defs != NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000517 xmlHashFree(grammar->defs, NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000518 }
519
520 xmlFree(grammar);
521}
522
523/**
524 * xmlRelaxNGNewDefine:
525 * @ctxt: a Relax-NG validation context
526 * @node: the node in the input document.
527 *
528 * Allocate a new RelaxNG define.
529 *
530 * Returns the newly allocated structure or NULL in case or error
531 */
532static xmlRelaxNGDefinePtr
533xmlRelaxNGNewDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
534{
535 xmlRelaxNGDefinePtr ret;
536
Daniel Veillard419a7682003-02-03 23:22:49 +0000537 if (ctxt->defMax == 0) {
538 ctxt->defMax = 16;
539 ctxt->defNr = 0;
540 ctxt->defTab = (xmlRelaxNGDefinePtr *)
541 xmlMalloc(ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
542 if (ctxt->defTab == NULL) {
543 if ((ctxt != NULL) && (ctxt->error != NULL))
544 ctxt->error(ctxt->userData, "Out of memory\n");
545 ctxt->nbErrors++;
546 return (NULL);
547 }
548 } else if (ctxt->defMax <= ctxt->defNr) {
549 xmlRelaxNGDefinePtr *tmp;
550 ctxt->defMax *= 2;
551 tmp = (xmlRelaxNGDefinePtr *) xmlRealloc(ctxt->defTab,
552 ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
553 if (tmp == NULL) {
554 if ((ctxt != NULL) && (ctxt->error != NULL))
555 ctxt->error(ctxt->userData, "Out of memory\n");
556 ctxt->nbErrors++;
557 return (NULL);
558 }
559 ctxt->defTab = tmp;
560 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000561 ret = (xmlRelaxNGDefinePtr) xmlMalloc(sizeof(xmlRelaxNGDefine));
562 if (ret == NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000563 if ((ctxt != NULL) && (ctxt->error != NULL))
564 ctxt->error(ctxt->userData, "Out of memory\n");
Daniel Veillard6eadf632003-01-23 18:29:16 +0000565 ctxt->nbErrors++;
Daniel Veillard419a7682003-02-03 23:22:49 +0000566 return(NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000567 }
568 memset(ret, 0, sizeof(xmlRelaxNGDefine));
Daniel Veillard419a7682003-02-03 23:22:49 +0000569 ctxt->defTab[ctxt->defNr++] = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000570 ret->node = node;
Daniel Veillardd4310742003-02-18 21:12:46 +0000571 ret->depth = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000572 return (ret);
573}
574
575/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000576 * xmlRelaxNGFreePartition:
577 * @partitions: a partition set structure
578 *
579 * Deallocate RelaxNG partition set structures.
580 */
581static void
582xmlRelaxNGFreePartition(xmlRelaxNGPartitionPtr partitions) {
583 xmlRelaxNGInterleaveGroupPtr group;
584 int j;
585
586 if (partitions != NULL) {
587 if (partitions->groups != NULL) {
588 for (j = 0;j < partitions->nbgroups;j++) {
589 group = partitions->groups[j];
590 if (group != NULL) {
591 if (group->defs != NULL)
592 xmlFree(group->defs);
593 xmlFree(group);
594 }
595 }
596 xmlFree(partitions->groups);
597 }
598 xmlFree(partitions);
599 }
600}
601/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000602 * xmlRelaxNGFreeDefine:
603 * @define: a define structure
604 *
605 * Deallocate a RelaxNG define structure.
606 */
607static void
608xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define)
609{
610 if (define == NULL)
611 return;
612
Daniel Veillard419a7682003-02-03 23:22:49 +0000613 if ((define->data != NULL) &&
614 (define->type == XML_RELAXNG_INTERLEAVE))
615 xmlRelaxNGFreePartition((xmlRelaxNGPartitionPtr) define->data);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000616 if (define->name != NULL)
617 xmlFree(define->name);
618 if (define->ns != NULL)
619 xmlFree(define->ns);
Daniel Veillardedc91922003-01-26 00:52:04 +0000620 if (define->value != NULL)
621 xmlFree(define->value);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000622 xmlFree(define);
623}
624
625/**
626 * xmlRelaxNGNewValidState:
627 * @ctxt: a Relax-NG validation context
628 * @node: the current node or NULL for the document
629 *
630 * Allocate a new RelaxNG validation state
631 *
632 * Returns the newly allocated structure or NULL in case or error
633 */
634static xmlRelaxNGValidStatePtr
635xmlRelaxNGNewValidState(xmlRelaxNGValidCtxtPtr ctxt, xmlNodePtr node)
636{
637 xmlRelaxNGValidStatePtr ret;
638 xmlAttrPtr attr;
639 xmlAttrPtr attrs[MAX_ATTR];
640 int nbAttrs = 0;
641 xmlNodePtr root = NULL;
642
643 if (node == NULL) {
644 root = xmlDocGetRootElement(ctxt->doc);
645 if (root == NULL)
646 return(NULL);
647 } else {
648 attr = node->properties;
649 while (attr != NULL) {
650 if (nbAttrs < MAX_ATTR)
651 attrs[nbAttrs++] = attr;
652 else
653 nbAttrs++;
654 attr = attr->next;
655 }
656 }
657
658 if (nbAttrs < MAX_ATTR)
659 attrs[nbAttrs] = NULL;
660 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(sizeof(xmlRelaxNGValidState) +
661 nbAttrs * sizeof(xmlAttrPtr));
662 if (ret == NULL) {
663 if ((ctxt != NULL) && (ctxt->error != NULL))
664 ctxt->error(ctxt->userData, "Out of memory\n");
665 return (NULL);
666 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +0000667 ret->value = NULL;
668 ret->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000669 if (node == NULL) {
670 ret->node = (xmlNodePtr) ctxt->doc;
671 ret->seq = root;
672 ret->nbAttrs = 0;
673 } else {
674 ret->node = node;
675 ret->seq = node->children;
676 ret->nbAttrs = nbAttrs;
677 if (nbAttrs > 0) {
678 if (nbAttrs < MAX_ATTR) {
679 memcpy(&(ret->attrs[0]), attrs,
680 sizeof(xmlAttrPtr) * (nbAttrs + 1));
681 } else {
682 attr = node->properties;
683 nbAttrs = 0;
684 while (attr != NULL) {
685 ret->attrs[nbAttrs++] = attr;
686 attr = attr->next;
687 }
688 ret->attrs[nbAttrs] = NULL;
689 }
690 }
691 }
Daniel Veillard1ed7f362003-02-03 10:57:45 +0000692 ret->nbAttrLeft = ret->nbAttrs;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000693 return (ret);
694}
695
696/**
697 * xmlRelaxNGCopyValidState:
698 * @ctxt: a Relax-NG validation context
699 * @state: a validation state
700 *
701 * Copy the validation state
702 *
703 * Returns the newly allocated structure or NULL in case or error
704 */
705static xmlRelaxNGValidStatePtr
706xmlRelaxNGCopyValidState(xmlRelaxNGValidCtxtPtr ctxt,
707 xmlRelaxNGValidStatePtr state)
708{
709 xmlRelaxNGValidStatePtr ret;
710 unsigned int size;
711
712 if (state == NULL)
713 return(NULL);
714
715 size = sizeof(xmlRelaxNGValidState) +
716 state->nbAttrs * sizeof(xmlAttrPtr);
717 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(size);
718 if (ret == NULL) {
719 if ((ctxt != NULL) && (ctxt->error != NULL))
720 ctxt->error(ctxt->userData, "Out of memory\n");
721 return (NULL);
722 }
723 memcpy(ret, state, size);
724 return(ret);
725}
726
727/**
Daniel Veillardce14fa52003-02-19 17:32:48 +0000728 * xmlRelaxNGEqualValidState:
729 * @ctxt: a Relax-NG validation context
730 * @state1: a validation state
731 * @state2: a validation state
732 *
733 * Compare the validation states for equality
734 *
735 * Returns 1 if equald, 0 otherwise
736 */
737static int
738xmlRelaxNGEqualValidState(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
739 xmlRelaxNGValidStatePtr state1,
740 xmlRelaxNGValidStatePtr state2)
741{
742 int i;
743
744 if ((state1 == NULL) || (state2 == NULL))
745 return(0);
746 if (state1 == state2)
747 return(1);
748 if (state1->node != state2->node)
749 return(0);
750 if (state1->seq != state2->seq)
751 return(0);
752 if (state1->nbAttrLeft != state2->nbAttrLeft)
753 return(0);
754 if (state1->nbAttrs != state2->nbAttrs)
755 return(0);
756 if (state1->endvalue != state2->endvalue)
757 return(0);
758 if ((state1->value != state2->value) &&
759 (!xmlStrEqual(state1->value, state2->value)))
760 return(0);
761 for (i = 0;i < state1->nbAttrs;i++) {
762 if (state1->attrs[i] != state2->attrs[i])
763 return(0);
764 }
765 return(1);
766}
767
768/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000769 * xmlRelaxNGFreeValidState:
770 * @state: a validation state structure
771 *
772 * Deallocate a RelaxNG validation state structure.
773 */
774static void
775xmlRelaxNGFreeValidState(xmlRelaxNGValidStatePtr state)
776{
777 if (state == NULL)
778 return;
779
780 xmlFree(state);
781}
782
783/************************************************************************
784 * *
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000785 * Document functions *
786 * *
787 ************************************************************************/
788static xmlDocPtr xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt,
789 xmlDocPtr doc);
790
791/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000792 * xmlRelaxNGIncludePush:
793 * @ctxt: the parser context
794 * @value: the element doc
795 *
796 * Pushes a new include on top of the include stack
797 *
798 * Returns 0 in case of error, the index in the stack otherwise
799 */
800static int
801xmlRelaxNGIncludePush(xmlRelaxNGParserCtxtPtr ctxt,
802 xmlRelaxNGIncludePtr value)
803{
804 if (ctxt->incTab == NULL) {
805 ctxt->incMax = 4;
806 ctxt->incNr = 0;
807 ctxt->incTab = (xmlRelaxNGIncludePtr *) xmlMalloc(
808 ctxt->incMax * sizeof(ctxt->incTab[0]));
809 if (ctxt->incTab == NULL) {
810 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
811 return (0);
812 }
813 }
814 if (ctxt->incNr >= ctxt->incMax) {
815 ctxt->incMax *= 2;
816 ctxt->incTab =
817 (xmlRelaxNGIncludePtr *) xmlRealloc(ctxt->incTab,
818 ctxt->incMax *
819 sizeof(ctxt->incTab[0]));
820 if (ctxt->incTab == NULL) {
821 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
822 return (0);
823 }
824 }
825 ctxt->incTab[ctxt->incNr] = value;
826 ctxt->inc = value;
827 return (ctxt->incNr++);
828}
829
830/**
831 * xmlRelaxNGIncludePop:
832 * @ctxt: the parser context
833 *
834 * Pops the top include from the include stack
835 *
836 * Returns the include just removed
837 */
838static xmlRelaxNGIncludePtr
839xmlRelaxNGIncludePop(xmlRelaxNGParserCtxtPtr ctxt)
840{
841 xmlRelaxNGIncludePtr ret;
842
843 if (ctxt->incNr <= 0)
844 return (0);
845 ctxt->incNr--;
846 if (ctxt->incNr > 0)
847 ctxt->inc = ctxt->incTab[ctxt->incNr - 1];
848 else
849 ctxt->inc = NULL;
850 ret = ctxt->incTab[ctxt->incNr];
851 ctxt->incTab[ctxt->incNr] = 0;
852 return (ret);
853}
854
855/**
856 * xmlRelaxNGLoadInclude:
857 * @ctxt: the parser context
858 * @URL: the normalized URL
859 * @node: the include node.
Daniel Veillard416589a2003-02-17 17:25:42 +0000860 * @ns: the namespace passed from the context.
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000861 *
862 * First lookup if the document is already loaded into the parser context,
863 * check against recursion. If not found the resource is loaded and
864 * the content is preprocessed before being returned back to the caller.
865 *
866 * Returns the xmlRelaxNGIncludePtr or NULL in case of error
867 */
868static xmlRelaxNGIncludePtr
869xmlRelaxNGLoadInclude(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillard416589a2003-02-17 17:25:42 +0000870 xmlNodePtr node, const xmlChar *ns) {
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000871 xmlRelaxNGIncludePtr ret = NULL;
872 xmlDocPtr doc;
873 int i;
874 xmlNodePtr root, tmp, tmp2, cur;
875
876 /*
877 * check against recursion in the stack
878 */
879 for (i = 0;i < ctxt->incNr;i++) {
880 if (xmlStrEqual(ctxt->incTab[i]->href, URL)) {
881 if (ctxt->error != NULL)
882 ctxt->error(ctxt->userData,
883 "Detected an externalRef recursion for %s\n",
884 URL);
885 ctxt->nbErrors++;
886 return(NULL);
887 }
888 }
889
890 /*
891 * Lookup in the hash table
892 */
893 if (ctxt->includes == NULL) {
894 ctxt->includes = xmlHashCreate(10);
895 if (ctxt->includes == NULL) {
896 if (ctxt->error != NULL)
897 ctxt->error(ctxt->userData,
898 "Failed to allocate hash table for document\n");
899 ctxt->nbErrors++;
900 return(NULL);
901 }
902 } else {
Daniel Veillard416589a2003-02-17 17:25:42 +0000903 if (ns == NULL)
904 ret = xmlHashLookup2(ctxt->includes, BAD_CAST "", URL);
905 else
906 ret = xmlHashLookup2(ctxt->includes, ns, URL);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000907 if (ret != NULL)
908 return(ret);
909 }
910
911
912 /*
913 * load the document
914 */
915 doc = xmlParseFile((const char *) URL);
916 if (doc == NULL) {
917 if (ctxt->error != NULL)
918 ctxt->error(ctxt->userData,
919 "xmlRelaxNG: could not load %s\n", URL);
920 ctxt->nbErrors++;
921 return (NULL);
922 }
923
924 /*
925 * Allocate the document structures and register it first.
926 */
927 ret = (xmlRelaxNGIncludePtr) xmlMalloc(sizeof(xmlRelaxNGInclude));
928 if (ret == NULL) {
929 if (ctxt->error != NULL)
930 ctxt->error(ctxt->userData,
931 "xmlRelaxNG: allocate memory for doc %s\n", URL);
932 ctxt->nbErrors++;
933 xmlFreeDoc(doc);
934 return (NULL);
935 }
936 memset(ret, 0, sizeof(xmlRelaxNGInclude));
937 ret->doc = doc;
938 ret->href = xmlStrdup(URL);
939
940 /*
Daniel Veillard416589a2003-02-17 17:25:42 +0000941 * transmit the ns if needed
942 */
943 if (ns != NULL) {
944 root = xmlDocGetRootElement(doc);
945 if (root != NULL) {
946 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
947 xmlSetProp(root, BAD_CAST"ns", ns);
948 }
949 }
950 }
951
952 /*
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000953 * push it on the stack and register it in the hash table
954 */
Daniel Veillard416589a2003-02-17 17:25:42 +0000955 if (ns == NULL)
956 xmlHashAddEntry2(ctxt->includes, BAD_CAST "", URL, ret);
957 else
958 xmlHashAddEntry2(ctxt->includes, ns, URL, ret);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000959 xmlRelaxNGIncludePush(ctxt, ret);
960
961 /*
962 * Some preprocessing of the document content, this include recursing
963 * in the include stack.
964 */
965 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
966 if (doc == NULL) {
967 /* xmlFreeDoc(ctxt->include); */
968 ctxt->inc = NULL;
969 return(NULL);
970 }
971
972 /*
973 * Pop up the include from the stack
974 */
975 xmlRelaxNGIncludePop(ctxt);
976
977 /*
978 * Check that the top element is a grammar
979 */
980 root = xmlDocGetRootElement(doc);
981 if (root == NULL) {
982 if (ctxt->error != NULL)
983 ctxt->error(ctxt->userData,
984 "xmlRelaxNG: included document is empty %s\n", URL);
985 ctxt->nbErrors++;
986 xmlFreeDoc(doc);
987 return (NULL);
988 }
989 if (!IS_RELAXNG(root, "grammar")) {
990 if (ctxt->error != NULL)
991 ctxt->error(ctxt->userData,
992 "xmlRelaxNG: included document %s root is not a grammar\n",
993 URL);
994 ctxt->nbErrors++;
995 xmlFreeDoc(doc);
996 return (NULL);
997 }
998
999 /*
1000 * Elimination of redefined rules in the include.
1001 */
1002 cur = node->children;
1003 while (cur != NULL) {
1004 if (IS_RELAXNG(cur, "start")) {
1005 int found = 0;
1006
1007 tmp = root->children;
1008 while (tmp != NULL) {
1009 tmp2 = tmp->next;
1010 if (IS_RELAXNG(tmp, "start")) {
1011 found = 1;
1012 xmlUnlinkNode(tmp);
1013 xmlFreeNode(tmp);
1014 }
1015 tmp = tmp2;
1016 }
1017 if (!found) {
1018 if (ctxt->error != NULL)
1019 ctxt->error(ctxt->userData,
1020 "xmlRelaxNG: include %s has a start but not the included grammar\n",
1021 URL);
1022 ctxt->nbErrors++;
1023 }
1024 } else if (IS_RELAXNG(cur, "define")) {
1025 xmlChar *name, *name2;
1026
1027 name = xmlGetProp(cur, BAD_CAST "name");
1028 if (name == NULL) {
1029 if (ctxt->error != NULL)
1030 ctxt->error(ctxt->userData,
1031 "xmlRelaxNG: include %s has define without name\n",
1032 URL);
1033 ctxt->nbErrors++;
1034 } else {
1035 int found = 0;
1036
Daniel Veillardd2298792003-02-14 16:54:11 +00001037 xmlRelaxNGNormExtSpace(name);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001038 tmp = root->children;
1039 while (tmp != NULL) {
1040 tmp2 = tmp->next;
1041 if (IS_RELAXNG(tmp, "define")) {
1042 name2 = xmlGetProp(tmp, BAD_CAST "name");
Daniel Veillardd2298792003-02-14 16:54:11 +00001043 xmlRelaxNGNormExtSpace(name2);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001044 if (name2 != NULL) {
1045 if (xmlStrEqual(name, name2)) {
1046 found = 1;
1047 xmlUnlinkNode(tmp);
1048 xmlFreeNode(tmp);
1049 }
1050 xmlFree(name2);
1051 }
1052 }
1053 tmp = tmp2;
1054 }
1055 if (!found) {
1056 if (ctxt->error != NULL)
1057 ctxt->error(ctxt->userData,
1058 "xmlRelaxNG: include %s has a define %s but not the included grammar\n",
1059 URL, name);
1060 ctxt->nbErrors++;
1061 }
1062 xmlFree(name);
1063 }
1064 }
1065 cur = cur->next;
1066 }
1067
1068
1069 return(ret);
1070}
1071
1072/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001073 * xmlRelaxNGDocumentPush:
1074 * @ctxt: the parser context
1075 * @value: the element doc
1076 *
1077 * Pushes a new doc on top of the doc stack
1078 *
1079 * Returns 0 in case of error, the index in the stack otherwise
1080 */
1081static int
1082xmlRelaxNGDocumentPush(xmlRelaxNGParserCtxtPtr ctxt,
1083 xmlRelaxNGDocumentPtr value)
1084{
1085 if (ctxt->docTab == NULL) {
1086 ctxt->docMax = 4;
1087 ctxt->docNr = 0;
1088 ctxt->docTab = (xmlRelaxNGDocumentPtr *) xmlMalloc(
1089 ctxt->docMax * sizeof(ctxt->docTab[0]));
1090 if (ctxt->docTab == NULL) {
1091 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
1092 return (0);
1093 }
1094 }
1095 if (ctxt->docNr >= ctxt->docMax) {
1096 ctxt->docMax *= 2;
1097 ctxt->docTab =
1098 (xmlRelaxNGDocumentPtr *) xmlRealloc(ctxt->docTab,
1099 ctxt->docMax *
1100 sizeof(ctxt->docTab[0]));
1101 if (ctxt->docTab == NULL) {
1102 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
1103 return (0);
1104 }
1105 }
1106 ctxt->docTab[ctxt->docNr] = value;
1107 ctxt->doc = value;
1108 return (ctxt->docNr++);
1109}
1110
1111/**
1112 * xmlRelaxNGDocumentPop:
1113 * @ctxt: the parser context
1114 *
1115 * Pops the top doc from the doc stack
1116 *
1117 * Returns the doc just removed
1118 */
1119static xmlRelaxNGDocumentPtr
1120xmlRelaxNGDocumentPop(xmlRelaxNGParserCtxtPtr ctxt)
1121{
1122 xmlRelaxNGDocumentPtr ret;
1123
1124 if (ctxt->docNr <= 0)
1125 return (0);
1126 ctxt->docNr--;
1127 if (ctxt->docNr > 0)
1128 ctxt->doc = ctxt->docTab[ctxt->docNr - 1];
1129 else
1130 ctxt->doc = NULL;
1131 ret = ctxt->docTab[ctxt->docNr];
1132 ctxt->docTab[ctxt->docNr] = 0;
1133 return (ret);
1134}
1135
1136/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001137 * xmlRelaxNGLoadExternalRef:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001138 * @ctxt: the parser context
1139 * @URL: the normalized URL
1140 * @ns: the inherited ns if any
1141 *
1142 * First lookup if the document is already loaded into the parser context,
1143 * check against recursion. If not found the resource is loaded and
1144 * the content is preprocessed before being returned back to the caller.
1145 *
1146 * Returns the xmlRelaxNGDocumentPtr or NULL in case of error
1147 */
1148static xmlRelaxNGDocumentPtr
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001149xmlRelaxNGLoadExternalRef(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001150 const xmlChar *ns) {
1151 xmlRelaxNGDocumentPtr ret = NULL;
1152 xmlDocPtr doc;
1153 xmlNodePtr root;
1154 int i;
1155
1156 /*
1157 * check against recursion in the stack
1158 */
1159 for (i = 0;i < ctxt->docNr;i++) {
1160 if (xmlStrEqual(ctxt->docTab[i]->href, URL)) {
1161 if (ctxt->error != NULL)
1162 ctxt->error(ctxt->userData,
1163 "Detected an externalRef recursion for %s\n",
1164 URL);
1165 ctxt->nbErrors++;
1166 return(NULL);
1167 }
1168 }
1169
1170 /*
1171 * Lookup in the hash table
1172 */
1173 if (ctxt->documents == NULL) {
1174 ctxt->documents = xmlHashCreate(10);
1175 if (ctxt->documents == NULL) {
1176 if (ctxt->error != NULL)
1177 ctxt->error(ctxt->userData,
1178 "Failed to allocate hash table for document\n");
1179 ctxt->nbErrors++;
1180 return(NULL);
1181 }
1182 } else {
1183 if (ns == NULL)
1184 ret = xmlHashLookup2(ctxt->documents, BAD_CAST "", URL);
1185 else
1186 ret = xmlHashLookup2(ctxt->documents, ns, URL);
1187 if (ret != NULL)
1188 return(ret);
1189 }
1190
1191
1192 /*
1193 * load the document
1194 */
1195 doc = xmlParseFile((const char *) URL);
1196 if (doc == NULL) {
1197 if (ctxt->error != NULL)
1198 ctxt->error(ctxt->userData,
1199 "xmlRelaxNG: could not load %s\n", URL);
1200 ctxt->nbErrors++;
1201 return (NULL);
1202 }
1203
1204 /*
1205 * Allocate the document structures and register it first.
1206 */
1207 ret = (xmlRelaxNGDocumentPtr) xmlMalloc(sizeof(xmlRelaxNGDocument));
1208 if (ret == NULL) {
1209 if (ctxt->error != NULL)
1210 ctxt->error(ctxt->userData,
1211 "xmlRelaxNG: allocate memory for doc %s\n", URL);
1212 ctxt->nbErrors++;
1213 xmlFreeDoc(doc);
1214 return (NULL);
1215 }
1216 memset(ret, 0, sizeof(xmlRelaxNGDocument));
1217 ret->doc = doc;
1218 ret->href = xmlStrdup(URL);
1219
1220 /*
1221 * transmit the ns if needed
1222 */
1223 if (ns != NULL) {
1224 root = xmlDocGetRootElement(doc);
1225 if (root != NULL) {
1226 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
1227 xmlSetProp(root, BAD_CAST"ns", ns);
1228 }
1229 }
1230 }
1231
1232 /*
1233 * push it on the stack and register it in the hash table
1234 */
1235 if (ns == NULL)
1236 xmlHashAddEntry2(ctxt->documents, BAD_CAST "", URL, ret);
1237 else
1238 xmlHashAddEntry2(ctxt->documents, ns, URL, ret);
1239 xmlRelaxNGDocumentPush(ctxt, ret);
1240
1241 /*
1242 * Some preprocessing of the document content
1243 */
1244 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
1245 if (doc == NULL) {
1246 xmlFreeDoc(ctxt->document);
1247 ctxt->doc = NULL;
1248 return(NULL);
1249 }
1250
1251 xmlRelaxNGDocumentPop(ctxt);
1252
1253 return(ret);
1254}
1255
1256/************************************************************************
1257 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +00001258 * Error functions *
1259 * *
1260 ************************************************************************/
1261
1262#define VALID_CTXT() \
Daniel Veillard231d7912003-02-09 14:22:17 +00001263 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1264 xmlGenericError(xmlGenericErrorContext, \
Daniel Veillard6eadf632003-01-23 18:29:16 +00001265 "error detected at %s:%d\n", \
1266 __FILE__, __LINE__);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001267
1268#define VALID_ERROR(a) \
Daniel Veillard231d7912003-02-09 14:22:17 +00001269 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001270 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a)
1271#define VALID_ERROR2(a, b) \
1272 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1273 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a, b)
1274#define VALID_ERROR3(a, b, c) \
1275 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1276 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a, b, c)
Daniel Veillard6eadf632003-01-23 18:29:16 +00001277
Daniel Veillardd2298792003-02-14 16:54:11 +00001278#ifdef DEBUG
Daniel Veillard231d7912003-02-09 14:22:17 +00001279static const char *
1280xmlRelaxNGDefName(xmlRelaxNGDefinePtr def) {
1281 if (def == NULL)
1282 return("none");
1283 switch(def->type) {
1284 case XML_RELAXNG_EMPTY: return("empty");
1285 case XML_RELAXNG_NOT_ALLOWED: return("notAllowed");
1286 case XML_RELAXNG_EXCEPT: return("except");
1287 case XML_RELAXNG_TEXT: return("text");
1288 case XML_RELAXNG_ELEMENT: return("element");
1289 case XML_RELAXNG_DATATYPE: return("datatype");
1290 case XML_RELAXNG_VALUE: return("value");
1291 case XML_RELAXNG_LIST: return("list");
1292 case XML_RELAXNG_ATTRIBUTE: return("attribute");
1293 case XML_RELAXNG_DEF: return("def");
1294 case XML_RELAXNG_REF: return("ref");
1295 case XML_RELAXNG_EXTERNALREF: return("externalRef");
1296 case XML_RELAXNG_PARENTREF: return("parentRef");
1297 case XML_RELAXNG_OPTIONAL: return("optional");
1298 case XML_RELAXNG_ZEROORMORE: return("zeroOrMore");
1299 case XML_RELAXNG_ONEORMORE: return("oneOrMore");
1300 case XML_RELAXNG_CHOICE: return("choice");
1301 case XML_RELAXNG_GROUP: return("group");
1302 case XML_RELAXNG_INTERLEAVE: return("interleave");
1303 case XML_RELAXNG_START: return("start");
1304 }
1305 return("unknown");
1306}
Daniel Veillardd2298792003-02-14 16:54:11 +00001307#endif
1308
Daniel Veillard6eadf632003-01-23 18:29:16 +00001309#if 0
1310/**
1311 * xmlRelaxNGErrorContext:
1312 * @ctxt: the parsing context
1313 * @schema: the schema being built
1314 * @node: the node being processed
1315 * @child: the child being processed
1316 *
1317 * Dump a RelaxNGType structure
1318 */
1319static void
1320xmlRelaxNGErrorContext(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGPtr schema,
1321 xmlNodePtr node, xmlNodePtr child)
1322{
1323 int line = 0;
1324 const xmlChar *file = NULL;
1325 const xmlChar *name = NULL;
1326 const char *type = "error";
1327
1328 if ((ctxt == NULL) || (ctxt->error == NULL))
1329 return;
1330
1331 if (child != NULL)
1332 node = child;
1333
1334 if (node != NULL) {
1335 if ((node->type == XML_DOCUMENT_NODE) ||
1336 (node->type == XML_HTML_DOCUMENT_NODE)) {
1337 xmlDocPtr doc = (xmlDocPtr) node;
1338
1339 file = doc->URL;
1340 } else {
1341 /*
1342 * Try to find contextual informations to report
1343 */
1344 if (node->type == XML_ELEMENT_NODE) {
1345 line = (int) node->content;
1346 } else if ((node->prev != NULL) &&
1347 (node->prev->type == XML_ELEMENT_NODE)) {
1348 line = (int) node->prev->content;
1349 } else if ((node->parent != NULL) &&
1350 (node->parent->type == XML_ELEMENT_NODE)) {
1351 line = (int) node->parent->content;
1352 }
1353 if ((node->doc != NULL) && (node->doc->URL != NULL))
1354 file = node->doc->URL;
1355 if (node->name != NULL)
1356 name = node->name;
1357 }
1358 }
1359
1360 if (ctxt != NULL)
1361 type = "compilation error";
1362 else if (schema != NULL)
1363 type = "runtime error";
1364
1365 if ((file != NULL) && (line != 0) && (name != NULL))
1366 ctxt->error(ctxt->userData, "%s: file %s line %d element %s\n",
1367 type, file, line, name);
1368 else if ((file != NULL) && (name != NULL))
1369 ctxt->error(ctxt->userData, "%s: file %s element %s\n",
1370 type, file, name);
1371 else if ((file != NULL) && (line != 0))
1372 ctxt->error(ctxt->userData, "%s: file %s line %d\n", type, file, line);
1373 else if (file != NULL)
1374 ctxt->error(ctxt->userData, "%s: file %s\n", type, file);
1375 else if (name != NULL)
1376 ctxt->error(ctxt->userData, "%s: element %s\n", type, name);
1377 else
1378 ctxt->error(ctxt->userData, "%s\n", type);
1379}
1380#endif
1381
1382/************************************************************************
1383 * *
1384 * Type library hooks *
1385 * *
1386 ************************************************************************/
Daniel Veillardea3f3982003-01-26 19:45:18 +00001387static xmlChar *xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt,
1388 const xmlChar *str);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001389
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001390/**
1391 * xmlRelaxNGSchemaTypeHave:
1392 * @data: data needed for the library
1393 * @type: the type name
1394 *
1395 * Check if the given type is provided by
1396 * the W3C XMLSchema Datatype library.
1397 *
1398 * Returns 1 if yes, 0 if no and -1 in case of error.
1399 */
1400static int
1401xmlRelaxNGSchemaTypeHave(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001402 const xmlChar *type) {
1403 xmlSchemaTypePtr typ;
1404
1405 if (type == NULL)
1406 return(-1);
1407 typ = xmlSchemaGetPredefinedType(type,
1408 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1409 if (typ == NULL)
1410 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001411 return(1);
1412}
1413
1414/**
1415 * xmlRelaxNGSchemaTypeCheck:
1416 * @data: data needed for the library
1417 * @type: the type name
1418 * @value: the value to check
1419 *
1420 * Check if the given type and value are validated by
1421 * the W3C XMLSchema Datatype library.
1422 *
1423 * Returns 1 if yes, 0 if no and -1 in case of error.
1424 */
1425static int
1426xmlRelaxNGSchemaTypeCheck(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001427 const xmlChar *type,
1428 const xmlChar *value) {
1429 xmlSchemaTypePtr typ;
1430 int ret;
1431
1432 /*
1433 * TODO: the type should be cached ab provided back, interface subject
1434 * to changes.
1435 * TODO: handle facets, may require an additional interface and keep
1436 * the value returned from the validation.
1437 */
1438 if ((type == NULL) || (value == NULL))
1439 return(-1);
1440 typ = xmlSchemaGetPredefinedType(type,
1441 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1442 if (typ == NULL)
1443 return(-1);
1444 ret = xmlSchemaValidatePredefinedType(typ, value, NULL);
1445 if (ret == 0)
1446 return(1);
1447 if (ret > 0)
1448 return(0);
1449 return(-1);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001450}
1451
1452/**
1453 * xmlRelaxNGSchemaTypeCompare:
1454 * @data: data needed for the library
1455 * @type: the type name
1456 * @value1: the first value
1457 * @value2: the second value
1458 *
1459 * Compare two values accordingly a type from the W3C XMLSchema
1460 * Datatype library.
1461 *
1462 * Returns 1 if yes, 0 if no and -1 in case of error.
1463 */
1464static int
1465xmlRelaxNGSchemaTypeCompare(void *data ATTRIBUTE_UNUSED,
1466 const xmlChar *type ATTRIBUTE_UNUSED,
1467 const xmlChar *value1 ATTRIBUTE_UNUSED,
1468 const xmlChar *value2 ATTRIBUTE_UNUSED) {
1469 TODO
1470 return(1);
1471}
1472
1473/**
1474 * xmlRelaxNGDefaultTypeHave:
1475 * @data: data needed for the library
1476 * @type: the type name
1477 *
1478 * Check if the given type is provided by
1479 * the default datatype library.
1480 *
1481 * Returns 1 if yes, 0 if no and -1 in case of error.
1482 */
1483static int
1484xmlRelaxNGDefaultTypeHave(void *data ATTRIBUTE_UNUSED, const xmlChar *type) {
1485 if (type == NULL)
1486 return(-1);
1487 if (xmlStrEqual(type, BAD_CAST "string"))
1488 return(1);
1489 if (xmlStrEqual(type, BAD_CAST "token"))
1490 return(1);
1491 return(0);
1492}
1493
1494/**
1495 * xmlRelaxNGDefaultTypeCheck:
1496 * @data: data needed for the library
1497 * @type: the type name
1498 * @value: the value to check
1499 *
1500 * Check if the given type and value are validated by
1501 * the default datatype library.
1502 *
1503 * Returns 1 if yes, 0 if no and -1 in case of error.
1504 */
1505static int
1506xmlRelaxNGDefaultTypeCheck(void *data ATTRIBUTE_UNUSED,
1507 const xmlChar *type ATTRIBUTE_UNUSED,
1508 const xmlChar *value ATTRIBUTE_UNUSED) {
Daniel Veillardd4310742003-02-18 21:12:46 +00001509 if (value == NULL)
1510 return(-1);
1511 if (xmlStrEqual(type, BAD_CAST "string"))
1512 return(1);
1513 if (xmlStrEqual(type, BAD_CAST "token")) {
1514#if 0
1515 const xmlChar *cur = value;
1516
1517 while (*cur != 0) {
1518 if (!IS_BLANK(*cur))
1519 return(1);
1520 cur++;
1521 }
1522#endif
1523 return(1);
1524 }
1525
1526 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001527}
1528
1529/**
1530 * xmlRelaxNGDefaultTypeCompare:
1531 * @data: data needed for the library
1532 * @type: the type name
1533 * @value1: the first value
1534 * @value2: the second value
1535 *
1536 * Compare two values accordingly a type from the default
1537 * datatype library.
1538 *
1539 * Returns 1 if yes, 0 if no and -1 in case of error.
1540 */
1541static int
1542xmlRelaxNGDefaultTypeCompare(void *data ATTRIBUTE_UNUSED,
1543 const xmlChar *type ATTRIBUTE_UNUSED,
1544 const xmlChar *value1 ATTRIBUTE_UNUSED,
1545 const xmlChar *value2 ATTRIBUTE_UNUSED) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00001546 int ret = -1;
1547
1548 if (xmlStrEqual(type, BAD_CAST "string")) {
1549 ret = xmlStrEqual(value1, value2);
1550 } else if (xmlStrEqual(type, BAD_CAST "token")) {
1551 if (!xmlStrEqual(value1, value2)) {
1552 xmlChar *nval, *nvalue;
1553
1554 /*
1555 * TODO: trivial optimizations are possible by
1556 * computing at compile-time
1557 */
1558 nval = xmlRelaxNGNormalize(NULL, value1);
1559 nvalue = xmlRelaxNGNormalize(NULL, value2);
1560
Daniel Veillardd4310742003-02-18 21:12:46 +00001561 if ((nval == NULL) || (nvalue == NULL))
Daniel Veillardea3f3982003-01-26 19:45:18 +00001562 ret = -1;
Daniel Veillardd4310742003-02-18 21:12:46 +00001563 else if (xmlStrEqual(nval, nvalue))
1564 ret = 1;
1565 else
1566 ret = 0;
Daniel Veillardea3f3982003-01-26 19:45:18 +00001567 if (nval != NULL)
1568 xmlFree(nval);
1569 if (nvalue != NULL)
1570 xmlFree(nvalue);
Daniel Veillardd4310742003-02-18 21:12:46 +00001571 } else
1572 ret = 1;
Daniel Veillardea3f3982003-01-26 19:45:18 +00001573 }
1574 return(ret);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001575}
1576
1577static int xmlRelaxNGTypeInitialized = 0;
1578static xmlHashTablePtr xmlRelaxNGRegisteredTypes = NULL;
1579
1580/**
1581 * xmlRelaxNGFreeTypeLibrary:
1582 * @lib: the type library structure
1583 * @namespace: the URI bound to the library
1584 *
1585 * Free the structure associated to the type library
1586 */
Daniel Veillard6eadf632003-01-23 18:29:16 +00001587static void
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001588xmlRelaxNGFreeTypeLibrary(xmlRelaxNGTypeLibraryPtr lib,
1589 const xmlChar *namespace ATTRIBUTE_UNUSED) {
1590 if (lib == NULL)
1591 return;
1592 if (lib->namespace != NULL)
1593 xmlFree((xmlChar *)lib->namespace);
1594 xmlFree(lib);
1595}
1596
1597/**
1598 * xmlRelaxNGRegisterTypeLibrary:
1599 * @namespace: the URI bound to the library
1600 * @data: data associated to the library
1601 * @have: the provide function
1602 * @check: the checking function
1603 * @comp: the comparison function
1604 *
1605 * Register a new type library
1606 *
1607 * Returns 0 in case of success and -1 in case of error.
1608 */
1609static int
1610xmlRelaxNGRegisterTypeLibrary(const xmlChar *namespace, void *data,
1611 xmlRelaxNGTypeHave have, xmlRelaxNGTypeCheck check,
1612 xmlRelaxNGTypeCompare comp) {
1613 xmlRelaxNGTypeLibraryPtr lib;
1614 int ret;
1615
1616 if ((xmlRelaxNGRegisteredTypes == NULL) || (namespace == NULL) ||
1617 (check == NULL) || (comp == NULL))
1618 return(-1);
1619 if (xmlHashLookup(xmlRelaxNGRegisteredTypes, namespace) != NULL) {
1620 xmlGenericError(xmlGenericErrorContext,
1621 "Relax-NG types library '%s' already registered\n",
1622 namespace);
1623 return(-1);
1624 }
1625 lib = (xmlRelaxNGTypeLibraryPtr) xmlMalloc(sizeof(xmlRelaxNGTypeLibrary));
1626 if (lib == NULL) {
1627 xmlGenericError(xmlGenericErrorContext,
1628 "Relax-NG types library '%s' malloc() failed\n",
1629 namespace);
1630 return (-1);
1631 }
1632 memset(lib, 0, sizeof(xmlRelaxNGTypeLibrary));
1633 lib->namespace = xmlStrdup(namespace);
1634 lib->data = data;
1635 lib->have = have;
1636 lib->comp = comp;
1637 lib->check = check;
1638 ret = xmlHashAddEntry(xmlRelaxNGRegisteredTypes, namespace, lib);
1639 if (ret < 0) {
1640 xmlGenericError(xmlGenericErrorContext,
1641 "Relax-NG types library failed to register '%s'\n",
1642 namespace);
1643 xmlRelaxNGFreeTypeLibrary(lib, namespace);
1644 return(-1);
1645 }
1646 return(0);
1647}
1648
1649/**
1650 * xmlRelaxNGInitTypes:
1651 *
1652 * Initilize the default type libraries.
1653 *
1654 * Returns 0 in case of success and -1 in case of error.
1655 */
1656static int
Daniel Veillard6eadf632003-01-23 18:29:16 +00001657xmlRelaxNGInitTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001658 if (xmlRelaxNGTypeInitialized != 0)
1659 return(0);
1660 xmlRelaxNGRegisteredTypes = xmlHashCreate(10);
1661 if (xmlRelaxNGRegisteredTypes == NULL) {
1662 xmlGenericError(xmlGenericErrorContext,
1663 "Failed to allocate sh table for Relax-NG types\n");
1664 return(-1);
1665 }
1666 xmlRelaxNGRegisterTypeLibrary(
1667 BAD_CAST "http://www.w3.org/2001/XMLSchema-datatypes",
1668 NULL,
1669 xmlRelaxNGSchemaTypeHave,
1670 xmlRelaxNGSchemaTypeCheck,
1671 xmlRelaxNGSchemaTypeCompare);
1672 xmlRelaxNGRegisterTypeLibrary(
1673 xmlRelaxNGNs,
1674 NULL,
1675 xmlRelaxNGDefaultTypeHave,
1676 xmlRelaxNGDefaultTypeCheck,
1677 xmlRelaxNGDefaultTypeCompare);
1678 xmlRelaxNGTypeInitialized = 1;
1679 return(0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001680}
1681
1682/**
1683 * xmlRelaxNGCleanupTypes:
1684 *
1685 * Cleanup the default Schemas type library associated to RelaxNG
1686 */
1687void
1688xmlRelaxNGCleanupTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001689 if (xmlRelaxNGTypeInitialized == 0)
1690 return;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001691 xmlSchemaCleanupTypes();
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001692 xmlHashFree(xmlRelaxNGRegisteredTypes, (xmlHashDeallocator)
1693 xmlRelaxNGFreeTypeLibrary);
1694 xmlRelaxNGTypeInitialized = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001695}
1696
1697/************************************************************************
1698 * *
1699 * Parsing functions *
1700 * *
1701 ************************************************************************/
1702
1703static xmlRelaxNGDefinePtr xmlRelaxNGParseAttribute(
1704 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1705static xmlRelaxNGDefinePtr xmlRelaxNGParseElement(
1706 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1707static xmlRelaxNGDefinePtr xmlRelaxNGParsePatterns(
Daniel Veillard154877e2003-01-30 12:17:05 +00001708 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes, int group);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001709static xmlRelaxNGDefinePtr xmlRelaxNGParsePattern(
1710 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001711static xmlRelaxNGPtr xmlRelaxNGParseDocument(
1712 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00001713static int xmlRelaxNGParseGrammarContent(
1714 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00001715static xmlRelaxNGDefinePtr xmlRelaxNGParseNameClass(
1716 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
1717 xmlRelaxNGDefinePtr def);
Daniel Veillard419a7682003-02-03 23:22:49 +00001718static xmlRelaxNGGrammarPtr xmlRelaxNGParseGrammar(
1719 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001720
1721
1722#define IS_BLANK_NODE(n) \
1723 (((n)->type == XML_TEXT_NODE) && (xmlRelaxNGIsBlank((n)->content)))
1724
1725/**
1726 * xmlRelaxNGIsBlank:
1727 * @str: a string
1728 *
1729 * Check if a string is ignorable c.f. 4.2. Whitespace
1730 *
1731 * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
1732 */
1733static int
1734xmlRelaxNGIsBlank(xmlChar *str) {
1735 if (str == NULL)
1736 return(1);
1737 while (*str != 0) {
1738 if (!(IS_BLANK(*str))) return(0);
1739 str++;
1740 }
1741 return(1);
1742}
1743
Daniel Veillard6eadf632003-01-23 18:29:16 +00001744/**
1745 * xmlRelaxNGGetDataTypeLibrary:
1746 * @ctxt: a Relax-NG parser context
1747 * @node: the current data or value element
1748 *
1749 * Applies algorithm from 4.3. datatypeLibrary attribute
1750 *
1751 * Returns the datatypeLibary value or NULL if not found
1752 */
1753static xmlChar *
1754xmlRelaxNGGetDataTypeLibrary(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1755 xmlNodePtr node) {
1756 xmlChar *ret, *escape;
1757
Daniel Veillard6eadf632003-01-23 18:29:16 +00001758 if ((IS_RELAXNG(node, "data")) || (IS_RELAXNG(node, "value"))) {
1759 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1760 if (ret != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001761 if (ret[0] == 0) {
1762 xmlFree(ret);
1763 return(NULL);
1764 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001765 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +00001766 if (escape == NULL) {
1767 return(ret);
1768 }
1769 xmlFree(ret);
1770 return(escape);
1771 }
1772 }
1773 node = node->parent;
1774 while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) {
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001775 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1776 if (ret != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001777 if (ret[0] == 0) {
1778 xmlFree(ret);
1779 return(NULL);
1780 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001781 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
1782 if (escape == NULL) {
1783 return(ret);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001784 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001785 xmlFree(ret);
1786 return(escape);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001787 }
1788 node = node->parent;
1789 }
1790 return(NULL);
1791}
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001792
1793/**
Daniel Veillardedc91922003-01-26 00:52:04 +00001794 * xmlRelaxNGParseValue:
1795 * @ctxt: a Relax-NG parser context
1796 * @node: the data node.
1797 *
1798 * parse the content of a RelaxNG value node.
1799 *
1800 * Returns the definition pointer or NULL in case of error
1801 */
1802static xmlRelaxNGDefinePtr
1803xmlRelaxNGParseValue(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1804 xmlRelaxNGDefinePtr def = NULL;
1805 xmlRelaxNGTypeLibraryPtr lib;
1806 xmlChar *type;
1807 xmlChar *library;
1808 int tmp;
1809
1810 def = xmlRelaxNGNewDefine(ctxt, node);
1811 if (def == NULL)
1812 return(NULL);
1813 def->type = XML_RELAXNG_VALUE;
1814
1815 type = xmlGetProp(node, BAD_CAST "type");
1816 if (type != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001817 xmlRelaxNGNormExtSpace(type);
1818 if (xmlValidateNCName(type, 0)) {
1819 if (ctxt->error != NULL)
1820 ctxt->error(ctxt->userData,
1821 "value type '%s' is not an NCName\n",
1822 type);
1823 ctxt->nbErrors++;
1824 }
Daniel Veillardedc91922003-01-26 00:52:04 +00001825 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1826 if (library == NULL)
1827 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1828
1829 def->name = type;
1830 def->ns = library;
1831
1832 lib = (xmlRelaxNGTypeLibraryPtr)
1833 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1834 if (lib == NULL) {
1835 if (ctxt->error != NULL)
1836 ctxt->error(ctxt->userData,
1837 "Use of unregistered type library '%s'\n",
1838 library);
1839 ctxt->nbErrors++;
1840 def->data = NULL;
1841 } else {
1842 def->data = lib;
1843 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001844 if (ctxt->error != NULL)
1845 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00001846 "Internal error with type library '%s': no 'have'\n",
1847 library);
1848 ctxt->nbErrors++;
1849 } else {
1850 tmp = lib->have(lib->data, def->name);
1851 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001852 if (ctxt->error != NULL)
1853 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00001854 "Error type '%s' is not exported by type library '%s'\n",
1855 def->name, library);
1856 ctxt->nbErrors++;
1857 }
1858 }
1859 }
1860 }
1861 if (node->children == NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00001862 def->value = xmlStrdup(BAD_CAST "");
Daniel Veillardedc91922003-01-26 00:52:04 +00001863 } else if ((node->children->type != XML_TEXT_NODE) ||
1864 (node->children->next != NULL)) {
1865 if (ctxt->error != NULL)
1866 ctxt->error(ctxt->userData,
1867 "Expecting a single text value for <value>content\n");
1868 ctxt->nbErrors++;
1869 } else {
1870 def->value = xmlNodeGetContent(node);
1871 if (def->value == NULL) {
1872 if (ctxt->error != NULL)
1873 ctxt->error(ctxt->userData,
1874 "Element <value> has no content\n");
1875 ctxt->nbErrors++;
1876 }
1877 }
1878 /* TODO check ahead of time that the value is okay per the type */
1879 return(def);
1880}
1881
1882/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001883 * xmlRelaxNGParseData:
1884 * @ctxt: a Relax-NG parser context
1885 * @node: the data node.
1886 *
1887 * parse the content of a RelaxNG data node.
1888 *
1889 * Returns the definition pointer or NULL in case of error
1890 */
1891static xmlRelaxNGDefinePtr
1892xmlRelaxNGParseData(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
Daniel Veillard416589a2003-02-17 17:25:42 +00001893 xmlRelaxNGDefinePtr def = NULL, except, last = NULL;
Daniel Veillard8fe98712003-02-19 00:19:14 +00001894 xmlRelaxNGDefinePtr param, lastparam = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001895 xmlRelaxNGTypeLibraryPtr lib;
1896 xmlChar *type;
1897 xmlChar *library;
1898 xmlNodePtr content;
1899 int tmp;
1900
1901 type = xmlGetProp(node, BAD_CAST "type");
1902 if (type == NULL) {
1903 if (ctxt->error != NULL)
1904 ctxt->error(ctxt->userData,
1905 "data has no type\n");
1906 ctxt->nbErrors++;
1907 return(NULL);
1908 }
Daniel Veillardd2298792003-02-14 16:54:11 +00001909 xmlRelaxNGNormExtSpace(type);
1910 if (xmlValidateNCName(type, 0)) {
1911 if (ctxt->error != NULL)
1912 ctxt->error(ctxt->userData,
1913 "data type '%s' is not an NCName\n",
1914 type);
1915 ctxt->nbErrors++;
1916 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001917 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1918 if (library == NULL)
1919 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1920
1921 def = xmlRelaxNGNewDefine(ctxt, node);
1922 if (def == NULL) {
1923 xmlFree(type);
1924 return(NULL);
1925 }
1926 def->type = XML_RELAXNG_DATATYPE;
1927 def->name = type;
1928 def->ns = library;
1929
1930 lib = (xmlRelaxNGTypeLibraryPtr)
1931 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1932 if (lib == NULL) {
1933 if (ctxt->error != NULL)
1934 ctxt->error(ctxt->userData,
1935 "Use of unregistered type library '%s'\n",
1936 library);
1937 ctxt->nbErrors++;
1938 def->data = NULL;
1939 } else {
1940 def->data = lib;
1941 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001942 if (ctxt->error != NULL)
1943 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001944 "Internal error with type library '%s': no 'have'\n",
1945 library);
1946 ctxt->nbErrors++;
1947 } else {
1948 tmp = lib->have(lib->data, def->name);
1949 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001950 if (ctxt->error != NULL)
1951 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001952 "Error type '%s' is not exported by type library '%s'\n",
1953 def->name, library);
1954 ctxt->nbErrors++;
1955 }
1956 }
1957 }
1958 content = node->children;
Daniel Veillard416589a2003-02-17 17:25:42 +00001959
1960 /*
1961 * Handle optional params
1962 */
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001963 while (content != NULL) {
Daniel Veillard416589a2003-02-17 17:25:42 +00001964 if (!xmlStrEqual(content->name, BAD_CAST "param"))
1965 break;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00001966 if (xmlStrEqual(library,
1967 BAD_CAST"http://relaxng.org/ns/structure/1.0")) {
1968 if (ctxt->error != NULL)
1969 ctxt->error(ctxt->userData,
1970 "Type library '%s' does not allow type parameters\n",
1971 library);
1972 ctxt->nbErrors++;
1973 content = content->next;
1974 while ((content != NULL) &&
1975 (xmlStrEqual(content->name, BAD_CAST "param")))
1976 content = content->next;
1977 } else {
1978 param = xmlRelaxNGNewDefine(ctxt, node);
1979 if (param != NULL) {
1980 param->type = XML_RELAXNG_PARAM;
1981 param->name = xmlGetProp(content, BAD_CAST "name");
1982 if (param->name == NULL) {
1983 if (ctxt->error != NULL)
1984 ctxt->error(ctxt->userData,
1985 "param has no name\n");
1986 ctxt->nbErrors++;
1987 }
1988 param->value = xmlNodeGetContent(content);
1989 if (lastparam == NULL) {
1990 def->attrs = lastparam = param;
1991 } else {
1992 lastparam->next = param;
1993 lastparam = param;
1994 }
Daniel Veillard8fe98712003-02-19 00:19:14 +00001995 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00001996 content = content->next;
Daniel Veillard8fe98712003-02-19 00:19:14 +00001997 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001998 }
Daniel Veillard416589a2003-02-17 17:25:42 +00001999 /*
2000 * Handle optional except
2001 */
2002 if ((content != NULL) && (xmlStrEqual(content->name, BAD_CAST "except"))) {
2003 xmlNodePtr child;
2004 xmlRelaxNGDefinePtr tmp2, last2 = NULL;
2005
2006 except = xmlRelaxNGNewDefine(ctxt, node);
2007 if (except == NULL) {
2008 return(def);
2009 }
2010 except->type = XML_RELAXNG_EXCEPT;
2011 child = content->children;
2012 if (last == NULL) {
2013 def->content = except;
2014 } else {
2015 last->next = except;
2016 }
2017 if (child == NULL) {
2018 if (ctxt->error != NULL)
2019 ctxt->error(ctxt->userData,
2020 "except has no content\n");
2021 ctxt->nbErrors++;
2022 }
2023 while (child != NULL) {
2024 tmp2 = xmlRelaxNGParsePattern(ctxt, child);
2025 if (tmp2 != NULL) {
2026 if (last2 == NULL) {
2027 except->content = last2 = tmp2;
2028 } else {
2029 last2->next = tmp2;
2030 last2 = tmp2;
2031 }
2032 }
2033 child = child->next;
2034 }
2035 content = content->next;
2036 }
2037 /*
2038 * Check there is no unhandled data
2039 */
2040 if (content != NULL) {
2041 if (ctxt->error != NULL)
2042 ctxt->error(ctxt->userData,
2043 "Element data has unexpected content %s\n", content->name);
2044 ctxt->nbErrors++;
2045 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002046
2047 return(def);
2048}
2049
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002050/**
2051 * xmlRelaxNGCompareElemDefLists:
2052 * @ctxt: a Relax-NG parser context
2053 * @defs1: the first list of element defs
2054 * @defs2: the second list of element defs
2055 *
2056 * Compare the 2 lists of element definitions. The comparison is
2057 * that if both lists do not accept the same QNames, it returns 1
2058 * If the 2 lists can accept the same QName the comparison returns 0
2059 *
2060 * Returns 1 disttinct, 0 if equal
2061 */
2062static int
2063xmlRelaxNGCompareElemDefLists(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
2064 xmlRelaxNGDefinePtr *def1,
2065 xmlRelaxNGDefinePtr *def2) {
2066 xmlRelaxNGDefinePtr *basedef2 = def2;
2067
Daniel Veillard154877e2003-01-30 12:17:05 +00002068 if ((def1 == NULL) || (def2 == NULL))
2069 return(1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002070 if ((*def1 == NULL) || (*def2 == NULL))
2071 return(1);
2072 while (*def1 != NULL) {
2073 while ((*def2) != NULL) {
2074 if ((*def1)->name == NULL) {
2075 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
2076 return(0);
2077 } else if ((*def2)->name == NULL) {
2078 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
2079 return(0);
2080 } else if (xmlStrEqual((*def1)->name, (*def2)->name)) {
2081 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
2082 return(0);
2083 }
2084 def2++;
2085 }
2086 def2 = basedef2;
2087 def1++;
2088 }
2089 return(1);
2090}
2091
2092/**
2093 * xmlRelaxNGGetElements:
2094 * @ctxt: a Relax-NG parser context
2095 * @def: the interleave definition
2096 *
2097 * Compute the list of top elements a definition can generate
2098 *
2099 * Returns a list of elements or NULL if none was found.
2100 */
2101static xmlRelaxNGDefinePtr *
2102xmlRelaxNGGetElements(xmlRelaxNGParserCtxtPtr ctxt,
2103 xmlRelaxNGDefinePtr def) {
2104 xmlRelaxNGDefinePtr *ret = NULL, parent, cur, tmp;
2105 int len = 0;
2106 int max = 0;
2107
2108 parent = NULL;
2109 cur = def;
2110 while (cur != NULL) {
Daniel Veillardb08c9812003-01-28 23:09:49 +00002111 if ((cur->type == XML_RELAXNG_ELEMENT) ||
2112 (cur->type == XML_RELAXNG_TEXT)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002113 if (ret == NULL) {
2114 max = 10;
2115 ret = (xmlRelaxNGDefinePtr *)
2116 xmlMalloc((max + 1) * sizeof(xmlRelaxNGDefinePtr));
2117 if (ret == NULL) {
2118 if (ctxt->error != NULL)
2119 ctxt->error(ctxt->userData,
2120 "Out of memory in element search\n");
2121 ctxt->nbErrors++;
2122 return(NULL);
2123 }
2124 } else if (max <= len) {
2125 max *= 2;
2126 ret = xmlRealloc(ret, (max + 1) * sizeof(xmlRelaxNGDefinePtr));
2127 if (ret == NULL) {
2128 if (ctxt->error != NULL)
2129 ctxt->error(ctxt->userData,
2130 "Out of memory in element search\n");
2131 ctxt->nbErrors++;
2132 return(NULL);
2133 }
2134 }
Daniel Veillardb08c9812003-01-28 23:09:49 +00002135 ret[len++] = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002136 ret[len] = NULL;
2137 } else if ((cur->type == XML_RELAXNG_CHOICE) ||
2138 (cur->type == XML_RELAXNG_INTERLEAVE) ||
2139 (cur->type == XML_RELAXNG_GROUP) ||
2140 (cur->type == XML_RELAXNG_ONEORMORE) ||
Daniel Veillardb08c9812003-01-28 23:09:49 +00002141 (cur->type == XML_RELAXNG_ZEROORMORE) ||
2142 (cur->type == XML_RELAXNG_OPTIONAL) ||
2143 (cur->type == XML_RELAXNG_REF) ||
2144 (cur->type == XML_RELAXNG_DEF)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002145 /*
2146 * Don't go within elements or attributes or string values.
2147 * Just gather the element top list
2148 */
2149 if (cur->content != NULL) {
2150 parent = cur;
2151 cur = cur->content;
2152 tmp = cur;
2153 while (tmp != NULL) {
2154 tmp->parent = parent;
2155 tmp = tmp->next;
2156 }
2157 continue;
2158 }
2159 }
Daniel Veillard154877e2003-01-30 12:17:05 +00002160 if (cur == def)
2161 return(ret);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002162 if (cur->next != NULL) {
2163 cur = cur->next;
2164 continue;
2165 }
2166 do {
2167 cur = cur->parent;
2168 if (cur == NULL) break;
2169 if (cur == def) return(ret);
2170 if (cur->next != NULL) {
2171 cur = cur->next;
2172 break;
2173 }
2174 } while (cur != NULL);
2175 }
2176 return(ret);
2177}
2178
2179/**
2180 * xmlRelaxNGComputeInterleaves:
2181 * @def: the interleave definition
2182 * @ctxt: a Relax-NG parser context
2183 * @node: the data node.
2184 *
2185 * A lot of work for preprocessing interleave definitions
2186 * is potentially needed to get a decent execution speed at runtime
2187 * - trying to get a total order on the element nodes generated
2188 * by the interleaves, order the list of interleave definitions
2189 * following that order.
2190 * - if <text/> is used to handle mixed content, it is better to
2191 * flag this in the define and simplify the runtime checking
2192 * algorithm
2193 */
2194static void
2195xmlRelaxNGComputeInterleaves(xmlRelaxNGDefinePtr def,
2196 xmlRelaxNGParserCtxtPtr ctxt,
2197 xmlChar *name ATTRIBUTE_UNUSED) {
2198 xmlRelaxNGDefinePtr cur;
2199
2200 xmlRelaxNGDefinePtr *list = NULL;
2201 xmlRelaxNGPartitionPtr partitions = NULL;
2202 xmlRelaxNGInterleaveGroupPtr *groups = NULL;
2203 xmlRelaxNGInterleaveGroupPtr group;
2204 int i,j,ret;
2205 int nbgroups = 0;
2206 int nbchild = 0;
2207
2208#ifdef DEBUG_INTERLEAVE
2209 xmlGenericError(xmlGenericErrorContext,
2210 "xmlRelaxNGComputeInterleaves(%s)\n",
2211 name);
2212#endif
2213 cur = def->content;
2214 while (cur != NULL) {
2215 nbchild++;
2216 cur = cur->next;
2217 }
2218
2219#ifdef DEBUG_INTERLEAVE
2220 xmlGenericError(xmlGenericErrorContext, " %d child\n", nbchild);
2221#endif
2222 groups = (xmlRelaxNGInterleaveGroupPtr *)
2223 xmlMalloc(nbchild * sizeof(xmlRelaxNGInterleaveGroupPtr));
2224 if (groups == NULL)
2225 goto error;
2226 cur = def->content;
2227 while (cur != NULL) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002228 groups[nbgroups] = (xmlRelaxNGInterleaveGroupPtr)
2229 xmlMalloc(sizeof(xmlRelaxNGInterleaveGroup));
2230 if (groups[nbgroups] == NULL)
2231 goto error;
2232 groups[nbgroups]->rule = cur;
2233 groups[nbgroups]->defs = xmlRelaxNGGetElements(ctxt, cur);
2234 nbgroups++;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002235 cur = cur->next;
2236 }
2237 list = NULL;
2238#ifdef DEBUG_INTERLEAVE
2239 xmlGenericError(xmlGenericErrorContext, " %d groups\n", nbgroups);
2240#endif
2241
2242 /*
2243 * Let's check that all rules makes a partitions according to 7.4
2244 */
2245 partitions = (xmlRelaxNGPartitionPtr)
2246 xmlMalloc(sizeof(xmlRelaxNGPartition));
2247 if (partitions == NULL)
2248 goto error;
2249 partitions->nbgroups = nbgroups;
2250 for (i = 0;i < nbgroups;i++) {
2251 group = groups[i];
2252 for (j = i+1;j < nbgroups;j++) {
2253 if (groups[j] == NULL)
2254 continue;
2255 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->defs,
2256 groups[j]->defs);
2257 if (ret == 0) {
2258 if (ctxt->error != NULL)
2259 ctxt->error(ctxt->userData,
2260 "Element or text conflicts in interleave\n");
2261 ctxt->nbErrors++;
2262 }
2263 }
2264 }
2265 partitions->groups = groups;
2266
2267 /*
2268 * Free Up the child list, and save the partition list back in the def
2269 */
2270 def->data = partitions;
2271 return;
2272
2273error:
2274 if (ctxt->error != NULL)
2275 ctxt->error(ctxt->userData,
2276 "Out of memory in interleave computation\n");
2277 ctxt->nbErrors++;
2278 if (list == NULL)
2279 xmlFree(list);
2280 if (groups != NULL) {
2281 for (i = 0;i < nbgroups;i++)
2282 if (groups[i] != NULL) {
2283 if (groups[i]->defs != NULL)
2284 xmlFree(groups[i]->defs);
2285 xmlFree(groups[i]);
2286 }
2287 xmlFree(groups);
2288 }
2289 xmlRelaxNGFreePartition(partitions);
2290}
2291
2292/**
2293 * xmlRelaxNGParseInterleave:
2294 * @ctxt: a Relax-NG parser context
2295 * @node: the data node.
2296 *
2297 * parse the content of a RelaxNG interleave node.
2298 *
2299 * Returns the definition pointer or NULL in case of error
2300 */
2301static xmlRelaxNGDefinePtr
2302xmlRelaxNGParseInterleave(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2303 xmlRelaxNGDefinePtr def = NULL;
2304 xmlRelaxNGDefinePtr last = NULL, cur;
2305 xmlNodePtr child;
2306
2307 def = xmlRelaxNGNewDefine(ctxt, node);
2308 if (def == NULL) {
2309 return(NULL);
2310 }
2311 def->type = XML_RELAXNG_INTERLEAVE;
2312
2313 if (ctxt->interleaves == NULL)
2314 ctxt->interleaves = xmlHashCreate(10);
2315 if (ctxt->interleaves == NULL) {
2316 if (ctxt->error != NULL)
2317 ctxt->error(ctxt->userData,
2318 "Failed to create interleaves hash table\n");
2319 ctxt->nbErrors++;
2320 } else {
2321 char name[32];
2322
2323 snprintf(name, 32, "interleave%d", ctxt->nbInterleaves++);
2324 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST name, def) < 0) {
2325 if (ctxt->error != NULL)
2326 ctxt->error(ctxt->userData,
2327 "Failed to add %s to hash table\n", name);
2328 ctxt->nbErrors++;
2329 }
2330 }
2331 child = node->children;
Daniel Veillardd2298792003-02-14 16:54:11 +00002332 if (child == NULL) {
2333 if (ctxt->error != NULL)
2334 ctxt->error(ctxt->userData, "Element interleave is empty\n");
2335 ctxt->nbErrors++;
2336 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002337 while (child != NULL) {
2338 if (IS_RELAXNG(child, "element")) {
2339 cur = xmlRelaxNGParseElement(ctxt, child);
2340 } else {
2341 cur = xmlRelaxNGParsePattern(ctxt, child);
2342 }
2343 if (cur != NULL) {
2344 cur->parent = def;
2345 if (last == NULL) {
2346 def->content = last = cur;
2347 } else {
2348 last->next = cur;
2349 last = cur;
2350 }
2351 }
2352 child = child->next;
2353 }
2354
2355 return(def);
2356}
Daniel Veillard6eadf632003-01-23 18:29:16 +00002357
2358/**
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002359 * xmlRelaxNGParseInclude:
2360 * @ctxt: a Relax-NG parser context
2361 * @node: the include node
2362 *
2363 * Integrate the content of an include node in the current grammar
2364 *
2365 * Returns 0 in case of success or -1 in case of error
2366 */
2367static int
2368xmlRelaxNGParseInclude(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2369 xmlRelaxNGIncludePtr incl;
2370 xmlNodePtr root;
2371 int ret = 0, tmp;
2372
2373 incl = node->_private;
2374 if (incl == NULL) {
2375 if (ctxt->error != NULL)
2376 ctxt->error(ctxt->userData,
2377 "Include node has no data\n");
2378 ctxt->nbErrors++;
2379 return(-1);
2380 }
2381 root = xmlDocGetRootElement(incl->doc);
2382 if (root == NULL) {
2383 if (ctxt->error != NULL)
2384 ctxt->error(ctxt->userData,
2385 "Include document is empty\n");
2386 ctxt->nbErrors++;
2387 return(-1);
2388 }
2389 if (!xmlStrEqual(root->name, BAD_CAST "grammar")) {
2390 if (ctxt->error != NULL)
2391 ctxt->error(ctxt->userData,
2392 "Include document root is not a grammar\n");
2393 ctxt->nbErrors++;
2394 return(-1);
2395 }
2396
2397 /*
2398 * Merge the definition from both the include and the internal list
2399 */
2400 if (root->children != NULL) {
2401 tmp = xmlRelaxNGParseGrammarContent(ctxt, root->children);
2402 if (tmp != 0)
2403 ret = -1;
2404 }
2405 if (node->children != NULL) {
2406 tmp = xmlRelaxNGParseGrammarContent(ctxt, node->children);
2407 if (tmp != 0)
2408 ret = -1;
2409 }
2410 return(ret);
2411}
2412
2413/**
Daniel Veillard276be4a2003-01-24 01:03:34 +00002414 * xmlRelaxNGParseDefine:
2415 * @ctxt: a Relax-NG parser context
2416 * @node: the define node
2417 *
2418 * parse the content of a RelaxNG define element node.
2419 *
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002420 * Returns 0 in case of success or -1 in case of error
Daniel Veillard276be4a2003-01-24 01:03:34 +00002421 */
2422static int
2423xmlRelaxNGParseDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2424 xmlChar *name;
2425 int ret = 0, tmp;
2426 xmlRelaxNGDefinePtr def;
2427 const xmlChar *olddefine;
2428
2429 name = xmlGetProp(node, BAD_CAST "name");
2430 if (name == NULL) {
2431 if (ctxt->error != NULL)
2432 ctxt->error(ctxt->userData,
2433 "define has no name\n");
2434 ctxt->nbErrors++;
2435 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00002436 xmlRelaxNGNormExtSpace(name);
2437 if (xmlValidateNCName(name, 0)) {
2438 if (ctxt->error != NULL)
2439 ctxt->error(ctxt->userData,
2440 "define name '%s' is not an NCName\n",
2441 name);
2442 ctxt->nbErrors++;
2443 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002444 def = xmlRelaxNGNewDefine(ctxt, node);
2445 if (def == NULL) {
2446 xmlFree(name);
2447 return(-1);
2448 }
2449 def->type = XML_RELAXNG_DEF;
2450 def->name = name;
2451 if (node->children == NULL) {
2452 if (ctxt->error != NULL)
2453 ctxt->error(ctxt->userData,
2454 "define has no children\n");
2455 ctxt->nbErrors++;
2456 } else {
2457 olddefine = ctxt->define;
2458 ctxt->define = name;
Daniel Veillard154877e2003-01-30 12:17:05 +00002459 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard276be4a2003-01-24 01:03:34 +00002460 ctxt->define = olddefine;
2461 }
2462 if (ctxt->grammar->defs == NULL)
2463 ctxt->grammar->defs = xmlHashCreate(10);
2464 if (ctxt->grammar->defs == NULL) {
2465 if (ctxt->error != NULL)
2466 ctxt->error(ctxt->userData,
2467 "Could not create definition hash\n");
2468 ctxt->nbErrors++;
2469 ret = -1;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002470 } else {
2471 tmp = xmlHashAddEntry(ctxt->grammar->defs, name, def);
2472 if (tmp < 0) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002473 xmlRelaxNGDefinePtr prev;
2474
2475 prev = xmlHashLookup(ctxt->grammar->defs, name);
2476 if (prev == NULL) {
2477 if (ctxt->error != NULL)
2478 ctxt->error(ctxt->userData,
2479 "Internal error on define aggregation of %s\n",
2480 name);
2481 ctxt->nbErrors++;
2482 ret = -1;
Daniel Veillard154877e2003-01-30 12:17:05 +00002483 } else {
2484 while (prev->nextHash != NULL)
2485 prev = prev->nextHash;
2486 prev->nextHash = def;
2487 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002488 }
2489 }
2490 }
2491 return(ret);
2492}
2493
2494/**
Daniel Veillardfebcca42003-02-16 15:44:18 +00002495 * xmlRelaxNGProcessExternalRef:
2496 * @ctxt: the parser context
2497 * @node: the externlRef node
2498 *
2499 * Process and compile an externlRef node
2500 *
2501 * Returns the xmlRelaxNGDefinePtr or NULL in case of error
2502 */
2503static xmlRelaxNGDefinePtr
2504xmlRelaxNGProcessExternalRef(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2505 xmlRelaxNGDocumentPtr docu;
2506 xmlNodePtr root, tmp;
2507 xmlChar *ns;
Daniel Veillard77648bb2003-02-20 15:03:22 +00002508 int newNs = 0, oldflags;
Daniel Veillardfebcca42003-02-16 15:44:18 +00002509 xmlRelaxNGDefinePtr def;
2510
2511 docu = node->_private;
2512 if (docu != NULL) {
2513 def = xmlRelaxNGNewDefine(ctxt, node);
2514 if (def == NULL)
2515 return(NULL);
2516 def->type = XML_RELAXNG_EXTERNALREF;
2517
2518 if (docu->content == NULL) {
2519 /*
2520 * Then do the parsing for good
2521 */
2522 root = xmlDocGetRootElement(docu->doc);
2523 if (root == NULL) {
2524 if (ctxt->error != NULL)
2525 ctxt->error(ctxt->userData,
2526 "xmlRelaxNGParse: %s is empty\n",
2527 ctxt->URL);
2528 ctxt->nbErrors++;
2529 return (NULL);
2530 }
2531 /*
2532 * ns transmission rules
2533 */
2534 ns = xmlGetProp(root, BAD_CAST "ns");
2535 if (ns == NULL) {
2536 tmp = node;
2537 while ((tmp != NULL) &&
2538 (tmp->type == XML_ELEMENT_NODE)) {
2539 ns = xmlGetProp(tmp, BAD_CAST "ns");
2540 if (ns != NULL) {
2541 break;
2542 }
2543 tmp = tmp->parent;
2544 }
2545 if (ns != NULL) {
2546 xmlSetProp(root, BAD_CAST "ns", ns);
2547 newNs = 1;
2548 xmlFree(ns);
2549 }
2550 } else {
2551 xmlFree(ns);
2552 }
2553
2554 /*
2555 * Parsing to get a precompiled schemas.
2556 */
Daniel Veillard77648bb2003-02-20 15:03:22 +00002557 oldflags = ctxt->flags;
2558 ctxt->flags |= XML_RELAXNG_IN_EXTERNALREF;
Daniel Veillardfebcca42003-02-16 15:44:18 +00002559 docu->schema = xmlRelaxNGParseDocument(ctxt, root);
Daniel Veillard77648bb2003-02-20 15:03:22 +00002560 ctxt->flags = oldflags;
Daniel Veillardfebcca42003-02-16 15:44:18 +00002561 if ((docu->schema != NULL) &&
2562 (docu->schema->topgrammar != NULL)) {
2563 docu->content = docu->schema->topgrammar->start;
2564 }
2565
2566 /*
2567 * the externalRef may be reused in a different ns context
2568 */
2569 if (newNs == 1) {
2570 xmlUnsetProp(root, BAD_CAST "ns");
2571 }
2572 }
2573 def->content = docu->content;
2574 } else {
2575 def = NULL;
2576 }
2577 return(def);
2578}
2579
2580/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00002581 * xmlRelaxNGParsePattern:
2582 * @ctxt: a Relax-NG parser context
2583 * @node: the pattern node.
2584 *
2585 * parse the content of a RelaxNG pattern node.
2586 *
Daniel Veillard276be4a2003-01-24 01:03:34 +00002587 * Returns the definition pointer or NULL in case of error or if no
2588 * pattern is generated.
Daniel Veillard6eadf632003-01-23 18:29:16 +00002589 */
2590static xmlRelaxNGDefinePtr
2591xmlRelaxNGParsePattern(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2592 xmlRelaxNGDefinePtr def = NULL;
2593
Daniel Veillardd2298792003-02-14 16:54:11 +00002594 if (node == NULL) {
2595 return(NULL);
2596 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002597 if (IS_RELAXNG(node, "element")) {
2598 def = xmlRelaxNGParseElement(ctxt, node);
2599 } else if (IS_RELAXNG(node, "attribute")) {
2600 def = xmlRelaxNGParseAttribute(ctxt, node);
2601 } else if (IS_RELAXNG(node, "empty")) {
2602 def = xmlRelaxNGNewDefine(ctxt, node);
2603 if (def == NULL)
2604 return(NULL);
2605 def->type = XML_RELAXNG_EMPTY;
Daniel Veillardd2298792003-02-14 16:54:11 +00002606 if (node->children != NULL) {
2607 if (ctxt->error != NULL)
2608 ctxt->error(ctxt->userData, "empty: had a child node\n");
2609 ctxt->nbErrors++;
2610 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002611 } else if (IS_RELAXNG(node, "text")) {
2612 def = xmlRelaxNGNewDefine(ctxt, node);
2613 if (def == NULL)
2614 return(NULL);
2615 def->type = XML_RELAXNG_TEXT;
2616 if (node->children != NULL) {
2617 if (ctxt->error != NULL)
2618 ctxt->error(ctxt->userData, "text: had a child node\n");
2619 ctxt->nbErrors++;
2620 }
2621 } else if (IS_RELAXNG(node, "zeroOrMore")) {
2622 def = xmlRelaxNGNewDefine(ctxt, node);
2623 if (def == NULL)
2624 return(NULL);
2625 def->type = XML_RELAXNG_ZEROORMORE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002626 if (node->children == NULL) {
2627 if (ctxt->error != NULL)
2628 ctxt->error(ctxt->userData,
2629 "Element %s is empty\n", node->name);
2630 ctxt->nbErrors++;
2631 } else {
2632 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2633 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002634 } else if (IS_RELAXNG(node, "oneOrMore")) {
2635 def = xmlRelaxNGNewDefine(ctxt, node);
2636 if (def == NULL)
2637 return(NULL);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002638 def->type = XML_RELAXNG_ONEORMORE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002639 if (node->children == NULL) {
2640 if (ctxt->error != NULL)
2641 ctxt->error(ctxt->userData,
2642 "Element %s is empty\n", node->name);
2643 ctxt->nbErrors++;
2644 } else {
2645 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2646 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002647 } else if (IS_RELAXNG(node, "optional")) {
2648 def = xmlRelaxNGNewDefine(ctxt, node);
2649 if (def == NULL)
2650 return(NULL);
2651 def->type = XML_RELAXNG_OPTIONAL;
Daniel Veillardd2298792003-02-14 16:54:11 +00002652 if (node->children == NULL) {
2653 if (ctxt->error != NULL)
2654 ctxt->error(ctxt->userData,
2655 "Element %s is empty\n", node->name);
2656 ctxt->nbErrors++;
2657 } else {
2658 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2659 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002660 } else if (IS_RELAXNG(node, "choice")) {
2661 def = xmlRelaxNGNewDefine(ctxt, node);
2662 if (def == NULL)
2663 return(NULL);
2664 def->type = XML_RELAXNG_CHOICE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002665 if (node->children == NULL) {
2666 if (ctxt->error != NULL)
2667 ctxt->error(ctxt->userData,
2668 "Element %s is empty\n", node->name);
2669 ctxt->nbErrors++;
2670 } else {
2671 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2672 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002673 } else if (IS_RELAXNG(node, "group")) {
2674 def = xmlRelaxNGNewDefine(ctxt, node);
2675 if (def == NULL)
2676 return(NULL);
2677 def->type = XML_RELAXNG_GROUP;
Daniel Veillardd2298792003-02-14 16:54:11 +00002678 if (node->children == NULL) {
2679 if (ctxt->error != NULL)
2680 ctxt->error(ctxt->userData,
2681 "Element %s is empty\n", node->name);
2682 ctxt->nbErrors++;
2683 } else {
2684 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2685 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002686 } else if (IS_RELAXNG(node, "ref")) {
2687 def = xmlRelaxNGNewDefine(ctxt, node);
2688 if (def == NULL)
2689 return(NULL);
2690 def->type = XML_RELAXNG_REF;
2691 def->name = xmlGetProp(node, BAD_CAST "name");
2692 if (def->name == NULL) {
2693 if (ctxt->error != NULL)
2694 ctxt->error(ctxt->userData,
2695 "ref has no name\n");
2696 ctxt->nbErrors++;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002697 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00002698 xmlRelaxNGNormExtSpace(def->name);
2699 if (xmlValidateNCName(def->name, 0)) {
2700 if (ctxt->error != NULL)
2701 ctxt->error(ctxt->userData,
2702 "ref name '%s' is not an NCName\n",
2703 def->name);
2704 ctxt->nbErrors++;
2705 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002706 }
2707 if (node->children != NULL) {
2708 if (ctxt->error != NULL)
2709 ctxt->error(ctxt->userData,
2710 "ref is not empty\n");
2711 ctxt->nbErrors++;
2712 }
2713 if (ctxt->grammar->refs == NULL)
2714 ctxt->grammar->refs = xmlHashCreate(10);
2715 if (ctxt->grammar->refs == NULL) {
2716 if (ctxt->error != NULL)
2717 ctxt->error(ctxt->userData,
2718 "Could not create references hash\n");
2719 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002720 def = NULL;
2721 } else {
2722 int tmp;
2723
2724 tmp = xmlHashAddEntry(ctxt->grammar->refs, def->name, def);
2725 if (tmp < 0) {
2726 xmlRelaxNGDefinePtr prev;
2727
2728 prev = (xmlRelaxNGDefinePtr)
2729 xmlHashLookup(ctxt->grammar->refs, def->name);
2730 if (prev == NULL) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00002731 if (def->name != NULL) {
2732 if (ctxt->error != NULL)
2733 ctxt->error(ctxt->userData,
2734 "Error refs definitions '%s'\n",
2735 def->name);
2736 } else {
2737 if (ctxt->error != NULL)
2738 ctxt->error(ctxt->userData,
2739 "Error refs definitions\n");
2740 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002741 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002742 def = NULL;
2743 } else {
2744 def->nextHash = prev->nextHash;
2745 prev->nextHash = def;
2746 }
2747 }
2748 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002749 } else if (IS_RELAXNG(node, "data")) {
2750 def = xmlRelaxNGParseData(ctxt, node);
Daniel Veillardd2298792003-02-14 16:54:11 +00002751#if 0
Daniel Veillard276be4a2003-01-24 01:03:34 +00002752 } else if (IS_RELAXNG(node, "define")) {
2753 xmlRelaxNGParseDefine(ctxt, node);
2754 def = NULL;
Daniel Veillardd2298792003-02-14 16:54:11 +00002755#endif
Daniel Veillardedc91922003-01-26 00:52:04 +00002756 } else if (IS_RELAXNG(node, "value")) {
2757 def = xmlRelaxNGParseValue(ctxt, node);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002758 } else if (IS_RELAXNG(node, "list")) {
2759 def = xmlRelaxNGNewDefine(ctxt, node);
2760 if (def == NULL)
2761 return(NULL);
2762 def->type = XML_RELAXNG_LIST;
Daniel Veillardd2298792003-02-14 16:54:11 +00002763 if (node->children == NULL) {
2764 if (ctxt->error != NULL)
2765 ctxt->error(ctxt->userData,
2766 "Element %s is empty\n", node->name);
2767 ctxt->nbErrors++;
2768 } else {
2769 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2770 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002771 } else if (IS_RELAXNG(node, "interleave")) {
2772 def = xmlRelaxNGParseInterleave(ctxt, node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002773 } else if (IS_RELAXNG(node, "externalRef")) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00002774 def = xmlRelaxNGProcessExternalRef(ctxt, node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002775 } else if (IS_RELAXNG(node, "notAllowed")) {
2776 def = xmlRelaxNGNewDefine(ctxt, node);
2777 if (def == NULL)
2778 return(NULL);
2779 def->type = XML_RELAXNG_NOT_ALLOWED;
2780 if (node->children != NULL) {
2781 if (ctxt->error != NULL)
2782 ctxt->error(ctxt->userData,
2783 "xmlRelaxNGParse: notAllowed element is not empty\n");
2784 ctxt->nbErrors++;
2785 }
Daniel Veillard419a7682003-02-03 23:22:49 +00002786 } else if (IS_RELAXNG(node, "grammar")) {
2787 xmlRelaxNGGrammarPtr grammar, old;
2788 xmlRelaxNGGrammarPtr oldparent;
2789
2790 oldparent = ctxt->parentgrammar;
2791 old = ctxt->grammar;
2792 ctxt->parentgrammar = old;
2793 grammar = xmlRelaxNGParseGrammar(ctxt, node->children);
2794 if (old != NULL) {
2795 ctxt->grammar = old;
2796 ctxt->parentgrammar = oldparent;
2797 if (grammar != NULL) {
2798 grammar->next = old->next;
2799 old->next = grammar;
2800 }
2801 }
2802 if (grammar != NULL)
2803 def = grammar->start;
2804 else
2805 def = NULL;
2806 } else if (IS_RELAXNG(node, "parentRef")) {
2807 if (ctxt->parentgrammar == NULL) {
2808 if (ctxt->error != NULL)
2809 ctxt->error(ctxt->userData,
2810 "Use of parentRef without a parent grammar\n");
2811 ctxt->nbErrors++;
2812 return(NULL);
2813 }
2814 def = xmlRelaxNGNewDefine(ctxt, node);
2815 if (def == NULL)
2816 return(NULL);
2817 def->type = XML_RELAXNG_PARENTREF;
2818 def->name = xmlGetProp(node, BAD_CAST "name");
2819 if (def->name == NULL) {
2820 if (ctxt->error != NULL)
2821 ctxt->error(ctxt->userData,
2822 "parentRef has no name\n");
2823 ctxt->nbErrors++;
Daniel Veillardd2298792003-02-14 16:54:11 +00002824 } else {
2825 xmlRelaxNGNormExtSpace(def->name);
2826 if (xmlValidateNCName(def->name, 0)) {
2827 if (ctxt->error != NULL)
2828 ctxt->error(ctxt->userData,
2829 "parentRef name '%s' is not an NCName\n",
2830 def->name);
2831 ctxt->nbErrors++;
2832 }
Daniel Veillard419a7682003-02-03 23:22:49 +00002833 }
2834 if (node->children != NULL) {
2835 if (ctxt->error != NULL)
2836 ctxt->error(ctxt->userData,
2837 "parentRef is not empty\n");
2838 ctxt->nbErrors++;
2839 }
2840 if (ctxt->parentgrammar->refs == NULL)
2841 ctxt->parentgrammar->refs = xmlHashCreate(10);
2842 if (ctxt->parentgrammar->refs == NULL) {
2843 if (ctxt->error != NULL)
2844 ctxt->error(ctxt->userData,
2845 "Could not create references hash\n");
2846 ctxt->nbErrors++;
2847 def = NULL;
Daniel Veillardd2298792003-02-14 16:54:11 +00002848 } else if (def->name != NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +00002849 int tmp;
2850
2851 tmp = xmlHashAddEntry(ctxt->parentgrammar->refs, def->name, def);
2852 if (tmp < 0) {
2853 xmlRelaxNGDefinePtr prev;
2854
2855 prev = (xmlRelaxNGDefinePtr)
2856 xmlHashLookup(ctxt->parentgrammar->refs, def->name);
2857 if (prev == NULL) {
2858 if (ctxt->error != NULL)
2859 ctxt->error(ctxt->userData,
2860 "Internal error parentRef definitions '%s'\n",
2861 def->name);
2862 ctxt->nbErrors++;
2863 def = NULL;
2864 } else {
2865 def->nextHash = prev->nextHash;
2866 prev->nextHash = def;
2867 }
2868 }
2869 }
Daniel Veillardd2298792003-02-14 16:54:11 +00002870 } else if (IS_RELAXNG(node, "mixed")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00002871 if (node->children == NULL) {
2872 if (ctxt->error != NULL)
2873 ctxt->error(ctxt->userData,
2874 "Mixed is empty\n");
2875 ctxt->nbErrors++;
2876 def = NULL;
Daniel Veillard416589a2003-02-17 17:25:42 +00002877 } else {
2878 def = xmlRelaxNGParseInterleave(ctxt, node);
2879 if (def != NULL) {
2880 xmlRelaxNGDefinePtr tmp;
Daniel Veillard2df2de22003-02-17 23:34:33 +00002881
2882 if ((def->content != NULL) && (def->content->next != NULL)) {
2883 tmp = xmlRelaxNGNewDefine(ctxt, node);
2884 if (tmp != NULL) {
2885 tmp->type = XML_RELAXNG_GROUP;
2886 tmp->content = def->content;
2887 def->content = tmp;
2888 }
2889 }
2890
Daniel Veillard416589a2003-02-17 17:25:42 +00002891 tmp = xmlRelaxNGNewDefine(ctxt, node);
2892 if (tmp == NULL)
2893 return(def);
2894 tmp->type = XML_RELAXNG_TEXT;
2895 tmp->next = def->content;
2896 def->content = tmp;
2897 }
2898 }
Daniel Veillardd2298792003-02-14 16:54:11 +00002899 } else {
2900 if (ctxt->error != NULL)
2901 ctxt->error(ctxt->userData,
2902 "Unexpected node %s is not a pattern\n",
2903 node->name);
2904 ctxt->nbErrors++;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002905 def = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002906 }
2907 return(def);
2908}
2909
2910/**
2911 * xmlRelaxNGParseAttribute:
2912 * @ctxt: a Relax-NG parser context
2913 * @node: the element node
2914 *
2915 * parse the content of a RelaxNG attribute node.
2916 *
2917 * Returns the definition pointer or NULL in case of error.
2918 */
2919static xmlRelaxNGDefinePtr
2920xmlRelaxNGParseAttribute(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
Daniel Veillardd2298792003-02-14 16:54:11 +00002921 xmlRelaxNGDefinePtr ret, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002922 xmlNodePtr child;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002923 int old_flags;
2924
2925 ret = xmlRelaxNGNewDefine(ctxt, node);
2926 if (ret == NULL)
2927 return(NULL);
2928 ret->type = XML_RELAXNG_ATTRIBUTE;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002929 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002930 child = node->children;
2931 if (child == NULL) {
2932 if (ctxt->error != NULL)
2933 ctxt->error(ctxt->userData,
2934 "xmlRelaxNGParseattribute: attribute has no children\n");
2935 ctxt->nbErrors++;
2936 return(ret);
2937 }
2938 old_flags = ctxt->flags;
2939 ctxt->flags |= XML_RELAXNG_IN_ATTRIBUTE;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002940 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
2941 if (cur != NULL)
2942 child = child->next;
2943
Daniel Veillardd2298792003-02-14 16:54:11 +00002944 if (child != NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00002945 cur = xmlRelaxNGParsePattern(ctxt, child);
2946 if (cur != NULL) {
2947 switch (cur->type) {
2948 case XML_RELAXNG_EMPTY:
2949 case XML_RELAXNG_NOT_ALLOWED:
2950 case XML_RELAXNG_TEXT:
2951 case XML_RELAXNG_ELEMENT:
2952 case XML_RELAXNG_DATATYPE:
2953 case XML_RELAXNG_VALUE:
2954 case XML_RELAXNG_LIST:
2955 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00002956 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002957 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00002958 case XML_RELAXNG_DEF:
2959 case XML_RELAXNG_ONEORMORE:
2960 case XML_RELAXNG_ZEROORMORE:
2961 case XML_RELAXNG_OPTIONAL:
2962 case XML_RELAXNG_CHOICE:
2963 case XML_RELAXNG_GROUP:
2964 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard77648bb2003-02-20 15:03:22 +00002965 case XML_RELAXNG_ATTRIBUTE:
Daniel Veillardd2298792003-02-14 16:54:11 +00002966 ret->content = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002967 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002968 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002969 case XML_RELAXNG_START:
Daniel Veillard8fe98712003-02-19 00:19:14 +00002970 case XML_RELAXNG_PARAM:
Daniel Veillard144fae12003-02-03 13:17:57 +00002971 case XML_RELAXNG_EXCEPT:
Daniel Veillardd2298792003-02-14 16:54:11 +00002972 if (ctxt->error != NULL)
2973 ctxt->error(ctxt->userData,
2974 "attribute has invalid content\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002975 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002976 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00002977 case XML_RELAXNG_NOOP:
2978 TODO
2979 if (ctxt->error != NULL)
2980 ctxt->error(ctxt->userData,
2981 "Internal error, noop found\n");
2982 ctxt->nbErrors++;
2983 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002984 }
2985 }
2986 child = child->next;
2987 }
Daniel Veillardd2298792003-02-14 16:54:11 +00002988 if (child != NULL) {
2989 if (ctxt->error != NULL)
2990 ctxt->error(ctxt->userData, "attribute has multiple children\n");
2991 ctxt->nbErrors++;
2992 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002993 ctxt->flags = old_flags;
2994 return(ret);
2995}
2996
2997/**
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002998 * xmlRelaxNGParseExceptNameClass:
2999 * @ctxt: a Relax-NG parser context
3000 * @node: the except node
Daniel Veillard144fae12003-02-03 13:17:57 +00003001 * @attr: 1 if within an attribute, 0 if within an element
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003002 *
3003 * parse the content of a RelaxNG nameClass node.
3004 *
3005 * Returns the definition pointer or NULL in case of error.
3006 */
3007static xmlRelaxNGDefinePtr
Daniel Veillard144fae12003-02-03 13:17:57 +00003008xmlRelaxNGParseExceptNameClass(xmlRelaxNGParserCtxtPtr ctxt,
3009 xmlNodePtr node, int attr) {
3010 xmlRelaxNGDefinePtr ret, cur, last = NULL;
3011 xmlNodePtr child;
3012
Daniel Veillardd2298792003-02-14 16:54:11 +00003013 if (!IS_RELAXNG(node, "except")) {
3014 if (ctxt->error != NULL)
3015 ctxt->error(ctxt->userData,
3016 "Expecting an except node\n");
3017 ctxt->nbErrors++;
Daniel Veillard144fae12003-02-03 13:17:57 +00003018 return(NULL);
Daniel Veillardd2298792003-02-14 16:54:11 +00003019 }
3020 if (node->next != NULL) {
3021 if (ctxt->error != NULL)
3022 ctxt->error(ctxt->userData,
3023 "exceptNameClass allows only a single except node\n");
3024 ctxt->nbErrors++;
3025 }
Daniel Veillard144fae12003-02-03 13:17:57 +00003026 if (node->children == NULL) {
3027 if (ctxt->error != NULL)
3028 ctxt->error(ctxt->userData,
3029 "except has no content\n");
3030 ctxt->nbErrors++;
3031 return(NULL);
3032 }
3033
3034 ret = xmlRelaxNGNewDefine(ctxt, node);
3035 if (ret == NULL)
3036 return(NULL);
3037 ret->type = XML_RELAXNG_EXCEPT;
3038 child = node->children;
3039 while (child != NULL) {
3040 cur = xmlRelaxNGNewDefine(ctxt, child);
3041 if (cur == NULL)
3042 break;
3043 if (attr)
3044 cur->type = XML_RELAXNG_ATTRIBUTE;
3045 else
3046 cur->type = XML_RELAXNG_ELEMENT;
3047
Daniel Veillard419a7682003-02-03 23:22:49 +00003048 if (xmlRelaxNGParseNameClass(ctxt, child, cur) != NULL) {
Daniel Veillard144fae12003-02-03 13:17:57 +00003049 if (last == NULL) {
3050 ret->content = cur;
3051 } else {
3052 last->next = cur;
3053 }
3054 last = cur;
3055 }
3056 child = child->next;
3057 }
3058
3059 return(ret);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003060}
3061
3062/**
3063 * xmlRelaxNGParseNameClass:
3064 * @ctxt: a Relax-NG parser context
3065 * @node: the nameClass node
3066 * @def: the current definition
3067 *
3068 * parse the content of a RelaxNG nameClass node.
3069 *
3070 * Returns the definition pointer or NULL in case of error.
3071 */
3072static xmlRelaxNGDefinePtr
3073xmlRelaxNGParseNameClass(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
3074 xmlRelaxNGDefinePtr def) {
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003075 xmlRelaxNGDefinePtr ret, tmp;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003076 xmlChar *val;
3077
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003078 ret = def;
3079 if ((IS_RELAXNG(node, "name")) || (IS_RELAXNG(node, "anyName")) ||
3080 (IS_RELAXNG(node, "nsName"))) {
3081 if ((def->type != XML_RELAXNG_ELEMENT) &&
3082 (def->type != XML_RELAXNG_ATTRIBUTE)) {
3083 ret = xmlRelaxNGNewDefine(ctxt, node);
3084 if (ret == NULL)
3085 return(NULL);
3086 ret->parent = def;
3087 if (ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE)
3088 ret->type = XML_RELAXNG_ATTRIBUTE;
3089 else
3090 ret->type = XML_RELAXNG_ELEMENT;
3091 }
3092 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003093 if (IS_RELAXNG(node, "name")) {
3094 val = xmlNodeGetContent(node);
Daniel Veillardd2298792003-02-14 16:54:11 +00003095 xmlRelaxNGNormExtSpace(val);
3096 if (xmlValidateNCName(val, 0)) {
3097 if (ctxt->error != NULL) {
3098 if (node->parent != NULL)
3099 ctxt->error(ctxt->userData,
3100 "Element %s name '%s' is not an NCName\n",
3101 node->parent->name, val);
3102 else
3103 ctxt->error(ctxt->userData,
3104 "name '%s' is not an NCName\n",
3105 val);
3106 }
3107 ctxt->nbErrors++;
3108 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003109 ret->name = val;
3110 val = xmlGetProp(node, BAD_CAST "ns");
3111 ret->ns = val;
Daniel Veillard416589a2003-02-17 17:25:42 +00003112 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3113 (val != NULL) &&
3114 (xmlStrEqual(val, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
3115 ctxt->error(ctxt->userData,
3116 "Attribute with namespace '%s' is not allowed\n",
3117 val);
3118 ctxt->nbErrors++;
3119 }
3120 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3121 (val != NULL) &&
3122 (val[0] == 0) &&
3123 (xmlStrEqual(ret->name, BAD_CAST "xmlns"))) {
3124 ctxt->error(ctxt->userData,
3125 "Attribute with QName 'xmlns' is not allowed\n",
3126 val);
3127 ctxt->nbErrors++;
3128 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003129 } else if (IS_RELAXNG(node, "anyName")) {
3130 ret->name = NULL;
3131 ret->ns = NULL;
3132 if (node->children != NULL) {
3133 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00003134 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
3135 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003136 }
3137 } else if (IS_RELAXNG(node, "nsName")) {
3138 ret->name = NULL;
3139 ret->ns = xmlGetProp(node, BAD_CAST "ns");
3140 if (ret->ns == NULL) {
3141 if (ctxt->error != NULL)
3142 ctxt->error(ctxt->userData,
3143 "nsName has no ns attribute\n");
3144 ctxt->nbErrors++;
3145 }
Daniel Veillard416589a2003-02-17 17:25:42 +00003146 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3147 (ret->ns != NULL) &&
3148 (xmlStrEqual(ret->ns, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
3149 ctxt->error(ctxt->userData,
3150 "Attribute with namespace '%s' is not allowed\n",
3151 ret->ns);
3152 ctxt->nbErrors++;
3153 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003154 if (node->children != NULL) {
3155 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00003156 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
3157 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003158 }
3159 } else if (IS_RELAXNG(node, "choice")) {
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003160 xmlNodePtr child;
3161 xmlRelaxNGDefinePtr last = NULL;
3162
Daniel Veillardd4310742003-02-18 21:12:46 +00003163 ret = xmlRelaxNGNewDefine(ctxt, node);
3164 if (ret == NULL)
3165 return(NULL);
3166 ret->parent = def;
3167 ret->type = XML_RELAXNG_CHOICE;
3168
Daniel Veillardd2298792003-02-14 16:54:11 +00003169 if (node->children == NULL) {
3170 if (ctxt->error != NULL)
3171 ctxt->error(ctxt->userData,
3172 "Element choice is empty\n");
3173 ctxt->nbErrors++;
3174 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00003175
3176 child = node->children;
3177 while (child != NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00003178 tmp = xmlRelaxNGParseNameClass(ctxt, child, ret);
Daniel Veillardd2298792003-02-14 16:54:11 +00003179 if (tmp != NULL) {
3180 if (last == NULL) {
3181 last = ret->nameClass = tmp;
3182 } else {
3183 last->next = tmp;
3184 last = tmp;
3185 }
3186 }
3187 child = child->next;
3188 }
3189 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003190 } else {
3191 if (ctxt->error != NULL)
3192 ctxt->error(ctxt->userData,
3193 "expecting name, anyName, nsName or choice : got %s\n",
3194 node->name);
3195 ctxt->nbErrors++;
3196 return(NULL);
3197 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003198 if (ret != def) {
3199 if (def->nameClass == NULL) {
3200 def->nameClass = ret;
3201 } else {
3202 tmp = def->nameClass;
3203 while (tmp->next != NULL) {
3204 tmp = tmp->next;
3205 }
3206 tmp->next = ret;
3207 }
3208 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003209 return(ret);
3210}
3211
3212/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00003213 * xmlRelaxNGParseElement:
3214 * @ctxt: a Relax-NG parser context
3215 * @node: the element node
3216 *
3217 * parse the content of a RelaxNG element node.
3218 *
3219 * Returns the definition pointer or NULL in case of error.
3220 */
3221static xmlRelaxNGDefinePtr
3222xmlRelaxNGParseElement(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3223 xmlRelaxNGDefinePtr ret, cur, last;
3224 xmlNodePtr child;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003225 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003226
3227 ret = xmlRelaxNGNewDefine(ctxt, node);
3228 if (ret == NULL)
3229 return(NULL);
3230 ret->type = XML_RELAXNG_ELEMENT;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003231 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003232 child = node->children;
3233 if (child == NULL) {
3234 if (ctxt->error != NULL)
3235 ctxt->error(ctxt->userData,
3236 "xmlRelaxNGParseElement: element has no children\n");
3237 ctxt->nbErrors++;
3238 return(ret);
3239 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003240 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
3241 if (cur != NULL)
3242 child = child->next;
3243
Daniel Veillard6eadf632003-01-23 18:29:16 +00003244 if (child == NULL) {
3245 if (ctxt->error != NULL)
3246 ctxt->error(ctxt->userData,
3247 "xmlRelaxNGParseElement: element has no content\n");
3248 ctxt->nbErrors++;
3249 return(ret);
3250 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003251 olddefine = ctxt->define;
3252 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003253 last = NULL;
3254 while (child != NULL) {
3255 cur = xmlRelaxNGParsePattern(ctxt, child);
3256 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003257 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003258 switch (cur->type) {
3259 case XML_RELAXNG_EMPTY:
3260 case XML_RELAXNG_NOT_ALLOWED:
3261 case XML_RELAXNG_TEXT:
3262 case XML_RELAXNG_ELEMENT:
3263 case XML_RELAXNG_DATATYPE:
3264 case XML_RELAXNG_VALUE:
3265 case XML_RELAXNG_LIST:
3266 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00003267 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003268 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00003269 case XML_RELAXNG_DEF:
3270 case XML_RELAXNG_ZEROORMORE:
3271 case XML_RELAXNG_ONEORMORE:
3272 case XML_RELAXNG_OPTIONAL:
3273 case XML_RELAXNG_CHOICE:
3274 case XML_RELAXNG_GROUP:
3275 case XML_RELAXNG_INTERLEAVE:
3276 if (last == NULL) {
3277 ret->content = last = cur;
3278 } else {
3279 if ((last->type == XML_RELAXNG_ELEMENT) &&
3280 (ret->content == last)) {
3281 ret->content = xmlRelaxNGNewDefine(ctxt, node);
3282 if (ret->content != NULL) {
3283 ret->content->type = XML_RELAXNG_GROUP;
3284 ret->content->content = last;
3285 } else {
3286 ret->content = last;
3287 }
3288 }
3289 last->next = cur;
3290 last = cur;
3291 }
3292 break;
3293 case XML_RELAXNG_ATTRIBUTE:
3294 cur->next = ret->attrs;
3295 ret->attrs = cur;
3296 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003297 case XML_RELAXNG_START:
Daniel Veillard8fe98712003-02-19 00:19:14 +00003298 case XML_RELAXNG_PARAM:
Daniel Veillard144fae12003-02-03 13:17:57 +00003299 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003300 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003301 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003302 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00003303 case XML_RELAXNG_NOOP:
3304 TODO
3305 if (ctxt->error != NULL)
3306 ctxt->error(ctxt->userData,
3307 "Internal error, noop found\n");
3308 ctxt->nbErrors++;
3309 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003310 }
3311 }
3312 child = child->next;
3313 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003314 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003315 return(ret);
3316}
3317
3318/**
3319 * xmlRelaxNGParsePatterns:
3320 * @ctxt: a Relax-NG parser context
3321 * @nodes: list of nodes
Daniel Veillard154877e2003-01-30 12:17:05 +00003322 * @group: use an implicit <group> for elements
Daniel Veillard6eadf632003-01-23 18:29:16 +00003323 *
3324 * parse the content of a RelaxNG start node.
3325 *
3326 * Returns the definition pointer or NULL in case of error.
3327 */
3328static xmlRelaxNGDefinePtr
Daniel Veillard154877e2003-01-30 12:17:05 +00003329xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes,
3330 int group) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003331 xmlRelaxNGDefinePtr def = NULL, last = NULL, cur, parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003332
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003333 parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003334 while (nodes != NULL) {
3335 if (IS_RELAXNG(nodes, "element")) {
3336 cur = xmlRelaxNGParseElement(ctxt, nodes);
3337 if (def == NULL) {
3338 def = last = cur;
3339 } else {
Daniel Veillard154877e2003-01-30 12:17:05 +00003340 if ((group == 1) && (def->type == XML_RELAXNG_ELEMENT) &&
3341 (def == last)) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003342 def = xmlRelaxNGNewDefine(ctxt, nodes);
3343 def->type = XML_RELAXNG_GROUP;
3344 def->content = last;
3345 }
3346 last->next = cur;
3347 last = cur;
3348 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003349 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003350 } else {
3351 cur = xmlRelaxNGParsePattern(ctxt, nodes);
Daniel Veillard419a7682003-02-03 23:22:49 +00003352 if (cur != NULL) {
3353 if (def == NULL) {
3354 def = last = cur;
3355 } else {
3356 last->next = cur;
3357 last = cur;
3358 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003359 }
3360 }
3361 nodes = nodes->next;
3362 }
3363 return(def);
3364}
3365
3366/**
3367 * xmlRelaxNGParseStart:
3368 * @ctxt: a Relax-NG parser context
3369 * @nodes: start children nodes
3370 *
3371 * parse the content of a RelaxNG start node.
3372 *
3373 * Returns 0 in case of success, -1 in case of error
3374 */
3375static int
3376xmlRelaxNGParseStart(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
3377 int ret = 0;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003378 xmlRelaxNGDefinePtr def = NULL, last;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003379
Daniel Veillardd2298792003-02-14 16:54:11 +00003380 if (nodes == NULL) {
3381 if (ctxt->error != NULL)
3382 ctxt->error(ctxt->userData,
3383 "start has no children\n");
3384 ctxt->nbErrors++;
3385 return(-1);
3386 }
3387 if (IS_RELAXNG(nodes, "empty")) {
3388 def = xmlRelaxNGNewDefine(ctxt, nodes);
3389 if (def == NULL)
3390 return(-1);
3391 def->type = XML_RELAXNG_EMPTY;
3392 if (nodes->children != NULL) {
3393 if (ctxt->error != NULL)
3394 ctxt->error(ctxt->userData, "element empty is not empty\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003395 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003396 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003397 } else if (IS_RELAXNG(nodes, "notAllowed")) {
3398 def = xmlRelaxNGNewDefine(ctxt, nodes);
3399 if (def == NULL)
3400 return(-1);
3401 def->type = XML_RELAXNG_NOT_ALLOWED;
3402 if (nodes->children != NULL) {
3403 if (ctxt->error != NULL)
3404 ctxt->error(ctxt->userData,
3405 "element notAllowed is not empty\n");
3406 ctxt->nbErrors++;
3407 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003408 } else {
3409 def = xmlRelaxNGParsePatterns(ctxt, nodes, 1);
Daniel Veillard2df2de22003-02-17 23:34:33 +00003410 }
3411 if (ctxt->grammar->start != NULL) {
3412 last = ctxt->grammar->start;
3413 while (last->next != NULL)
3414 last = last->next;
3415 last->next = def;
3416 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00003417 ctxt->grammar->start = def;
3418 }
3419 nodes = nodes->next;
3420 if (nodes != NULL) {
3421 if (ctxt->error != NULL)
3422 ctxt->error(ctxt->userData,
3423 "start more than one children\n");
3424 ctxt->nbErrors++;
3425 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003426 }
3427 return(ret);
3428}
3429
3430/**
3431 * xmlRelaxNGParseGrammarContent:
3432 * @ctxt: a Relax-NG parser context
3433 * @nodes: grammar children nodes
3434 *
3435 * parse the content of a RelaxNG grammar node.
3436 *
3437 * Returns 0 in case of success, -1 in case of error
3438 */
3439static int
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003440xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003441{
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003442 int ret = 0, tmp;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003443
3444 if (nodes == NULL) {
3445 if (ctxt->error != NULL)
3446 ctxt->error(ctxt->userData,
3447 "grammar has no children\n");
3448 ctxt->nbErrors++;
3449 return(-1);
3450 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003451 while (nodes != NULL) {
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003452 if (IS_RELAXNG(nodes, "start")) {
3453 if (nodes->children == NULL) {
3454 if (ctxt->error != NULL)
3455 ctxt->error(ctxt->userData,
Daniel Veillardd2298792003-02-14 16:54:11 +00003456 "start has no children\n");
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003457 ctxt->nbErrors++;
3458 } else {
3459 tmp = xmlRelaxNGParseStart(ctxt, nodes->children);
3460 if (tmp != 0)
3461 ret = -1;
3462 }
3463 } else if (IS_RELAXNG(nodes, "define")) {
3464 tmp = xmlRelaxNGParseDefine(ctxt, nodes);
3465 if (tmp != 0)
3466 ret = -1;
3467 } else if (IS_RELAXNG(nodes, "include")) {
3468 tmp = xmlRelaxNGParseInclude(ctxt, nodes);
3469 if (tmp != 0)
3470 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003471 } else {
3472 if (ctxt->error != NULL)
3473 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003474 "grammar has unexpected child %s\n", nodes->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003475 ctxt->nbErrors++;
3476 ret = -1;
3477 }
3478 nodes = nodes->next;
3479 }
3480 return (ret);
3481}
3482
3483/**
3484 * xmlRelaxNGCheckReference:
3485 * @ref: the ref
3486 * @ctxt: a Relax-NG parser context
3487 * @name: the name associated to the defines
3488 *
3489 * Applies the 4.17. combine attribute rule for all the define
3490 * element of a given grammar using the same name.
3491 */
3492static void
3493xmlRelaxNGCheckReference(xmlRelaxNGDefinePtr ref,
3494 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
3495 xmlRelaxNGGrammarPtr grammar;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003496 xmlRelaxNGDefinePtr def, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003497
3498 grammar = ctxt->grammar;
3499 if (grammar == NULL) {
3500 if (ctxt->error != NULL)
3501 ctxt->error(ctxt->userData,
3502 "Internal error: no grammar in CheckReference %s\n",
3503 name);
3504 ctxt->nbErrors++;
3505 return;
3506 }
3507 if (ref->content != NULL) {
3508 if (ctxt->error != NULL)
3509 ctxt->error(ctxt->userData,
3510 "Internal error: reference has content in CheckReference %s\n",
3511 name);
3512 ctxt->nbErrors++;
3513 return;
3514 }
3515 if (grammar->defs != NULL) {
3516 def = xmlHashLookup(grammar->defs, name);
3517 if (def != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00003518 cur = ref;
3519 while (cur != NULL) {
3520 cur->content = def;
3521 cur = cur->nextHash;
3522 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003523 } else {
Daniel Veillardd4310742003-02-18 21:12:46 +00003524 if (ctxt->error != NULL)
3525 ctxt->error(ctxt->userData,
3526 "Reference %s has no matching definition\n",
3527 name);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003528 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003529 }
Daniel Veillardd4310742003-02-18 21:12:46 +00003530 } else {
3531 if (ctxt->error != NULL)
3532 ctxt->error(ctxt->userData,
3533 "Reference %s has no matching definition\n",
3534 name);
3535 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003536 }
3537 /*
3538 * TODO: make a closure and verify there is no loop !
3539 */
3540}
3541
3542/**
3543 * xmlRelaxNGCheckCombine:
3544 * @define: the define(s) list
3545 * @ctxt: a Relax-NG parser context
3546 * @name: the name associated to the defines
3547 *
3548 * Applies the 4.17. combine attribute rule for all the define
3549 * element of a given grammar using the same name.
3550 */
3551static void
3552xmlRelaxNGCheckCombine(xmlRelaxNGDefinePtr define,
3553 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
3554 xmlChar *combine;
3555 int choiceOrInterleave = -1;
3556 int missing = 0;
3557 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
3558
3559 if (define->nextHash == NULL)
3560 return;
3561 cur = define;
3562 while (cur != NULL) {
3563 combine = xmlGetProp(cur->node, BAD_CAST "combine");
3564 if (combine != NULL) {
3565 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3566 if (choiceOrInterleave == -1)
3567 choiceOrInterleave = 1;
3568 else if (choiceOrInterleave == 0) {
3569 if (ctxt->error != NULL)
3570 ctxt->error(ctxt->userData,
3571 "Defines for %s use both 'choice' and 'interleave'\n",
3572 name);
3573 ctxt->nbErrors++;
3574 }
Daniel Veillard154877e2003-01-30 12:17:05 +00003575 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003576 if (choiceOrInterleave == -1)
3577 choiceOrInterleave = 0;
3578 else if (choiceOrInterleave == 1) {
3579 if (ctxt->error != NULL)
3580 ctxt->error(ctxt->userData,
3581 "Defines for %s use both 'choice' and 'interleave'\n",
3582 name);
3583 ctxt->nbErrors++;
3584 }
3585 } else {
3586 if (ctxt->error != NULL)
3587 ctxt->error(ctxt->userData,
3588 "Defines for %s use unknown combine value '%s''\n",
3589 name, combine);
3590 ctxt->nbErrors++;
3591 }
3592 xmlFree(combine);
3593 } else {
3594 if (missing == 0)
3595 missing = 1;
3596 else {
3597 if (ctxt->error != NULL)
3598 ctxt->error(ctxt->userData,
3599 "Some defines for %s lacks the combine attribute\n",
3600 name);
3601 ctxt->nbErrors++;
3602 }
3603 }
3604
3605 cur = cur->nextHash;
3606 }
3607#ifdef DEBUG
3608 xmlGenericError(xmlGenericErrorContext,
3609 "xmlRelaxNGCheckCombine(): merging %s defines: %d\n",
3610 name, choiceOrInterleave);
3611#endif
3612 if (choiceOrInterleave == -1)
3613 choiceOrInterleave = 0;
3614 cur = xmlRelaxNGNewDefine(ctxt, define->node);
3615 if (cur == NULL)
3616 return;
3617 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003618 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard154877e2003-01-30 12:17:05 +00003619 else
3620 cur->type = XML_RELAXNG_CHOICE;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003621 tmp = define;
3622 last = NULL;
3623 while (tmp != NULL) {
3624 if (tmp->content != NULL) {
3625 if (tmp->content->next != NULL) {
3626 /*
3627 * we need first to create a wrapper.
3628 */
3629 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
3630 if (tmp2 == NULL)
3631 break;
3632 tmp2->type = XML_RELAXNG_GROUP;
3633 tmp2->content = tmp->content;
3634 } else {
3635 tmp2 = tmp->content;
3636 }
3637 if (last == NULL) {
3638 cur->content = tmp2;
3639 } else {
3640 last->next = tmp2;
3641 }
3642 last = tmp2;
3643 tmp->content = NULL;
3644 }
3645 tmp = tmp->nextHash;
3646 }
3647 define->content = cur;
Daniel Veillard154877e2003-01-30 12:17:05 +00003648 if (choiceOrInterleave == 0) {
3649 if (ctxt->interleaves == NULL)
3650 ctxt->interleaves = xmlHashCreate(10);
3651 if (ctxt->interleaves == NULL) {
3652 if (ctxt->error != NULL)
3653 ctxt->error(ctxt->userData,
3654 "Failed to create interleaves hash table\n");
3655 ctxt->nbErrors++;
3656 } else {
3657 char tmpname[32];
3658
3659 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
3660 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
3661 if (ctxt->error != NULL)
3662 ctxt->error(ctxt->userData,
3663 "Failed to add %s to hash table\n", tmpname);
3664 ctxt->nbErrors++;
3665 }
3666 }
3667 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003668}
3669
3670/**
3671 * xmlRelaxNGCombineStart:
3672 * @ctxt: a Relax-NG parser context
3673 * @grammar: the grammar
3674 *
3675 * Applies the 4.17. combine rule for all the start
3676 * element of a given grammar.
3677 */
3678static void
3679xmlRelaxNGCombineStart(xmlRelaxNGParserCtxtPtr ctxt,
3680 xmlRelaxNGGrammarPtr grammar) {
3681 xmlRelaxNGDefinePtr starts;
3682 xmlChar *combine;
3683 int choiceOrInterleave = -1;
3684 int missing = 0;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003685 xmlRelaxNGDefinePtr cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003686
Daniel Veillard2df2de22003-02-17 23:34:33 +00003687 starts = grammar->start;
3688 if ((starts == NULL) || (starts->next == NULL))
Daniel Veillard6eadf632003-01-23 18:29:16 +00003689 return;
3690 cur = starts;
3691 while (cur != NULL) {
Daniel Veillard2df2de22003-02-17 23:34:33 +00003692 if ((cur->node == NULL) || (cur->node->parent == NULL) ||
3693 (!xmlStrEqual(cur->node->parent->name, BAD_CAST "start"))) {
3694 combine = NULL;
3695 if (ctxt->error != NULL)
3696 ctxt->error(ctxt->userData,
3697 "Internal error: start element not found\n");
3698 ctxt->nbErrors++;
3699 } else {
3700 combine = xmlGetProp(cur->node->parent, BAD_CAST "combine");
3701 }
3702
Daniel Veillard6eadf632003-01-23 18:29:16 +00003703 if (combine != NULL) {
3704 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3705 if (choiceOrInterleave == -1)
3706 choiceOrInterleave = 1;
3707 else if (choiceOrInterleave == 0) {
3708 if (ctxt->error != NULL)
3709 ctxt->error(ctxt->userData,
3710 "<start> use both 'choice' and 'interleave'\n");
3711 ctxt->nbErrors++;
3712 }
Daniel Veillard2df2de22003-02-17 23:34:33 +00003713 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003714 if (choiceOrInterleave == -1)
3715 choiceOrInterleave = 0;
3716 else if (choiceOrInterleave == 1) {
3717 if (ctxt->error != NULL)
3718 ctxt->error(ctxt->userData,
3719 "<start> use both 'choice' and 'interleave'\n");
3720 ctxt->nbErrors++;
3721 }
3722 } else {
3723 if (ctxt->error != NULL)
3724 ctxt->error(ctxt->userData,
3725 "<start> uses unknown combine value '%s''\n", combine);
3726 ctxt->nbErrors++;
3727 }
3728 xmlFree(combine);
3729 } else {
3730 if (missing == 0)
3731 missing = 1;
3732 else {
3733 if (ctxt->error != NULL)
3734 ctxt->error(ctxt->userData,
3735 "Some <start> elements lacks the combine attribute\n");
3736 ctxt->nbErrors++;
3737 }
3738 }
3739
Daniel Veillard2df2de22003-02-17 23:34:33 +00003740 cur = cur->next;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003741 }
3742#ifdef DEBUG
3743 xmlGenericError(xmlGenericErrorContext,
3744 "xmlRelaxNGCombineStart(): merging <start>: %d\n",
3745 choiceOrInterleave);
3746#endif
3747 if (choiceOrInterleave == -1)
3748 choiceOrInterleave = 0;
3749 cur = xmlRelaxNGNewDefine(ctxt, starts->node);
3750 if (cur == NULL)
3751 return;
3752 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003753 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003754 else
3755 cur->type = XML_RELAXNG_CHOICE;
3756 cur->content = grammar->start;
3757 grammar->start = cur;
3758 if (choiceOrInterleave == 0) {
3759 if (ctxt->interleaves == NULL)
3760 ctxt->interleaves = xmlHashCreate(10);
3761 if (ctxt->interleaves == NULL) {
3762 if (ctxt->error != NULL)
3763 ctxt->error(ctxt->userData,
3764 "Failed to create interleaves hash table\n");
3765 ctxt->nbErrors++;
3766 } else {
3767 char tmpname[32];
3768
3769 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
3770 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
3771 if (ctxt->error != NULL)
3772 ctxt->error(ctxt->userData,
3773 "Failed to add %s to hash table\n", tmpname);
3774 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003775 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003776 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003777 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003778}
3779
3780/**
Daniel Veillardd4310742003-02-18 21:12:46 +00003781 * xmlRelaxNGCheckCycles:
3782 * @ctxt: a Relax-NG parser context
3783 * @nodes: grammar children nodes
3784 * @depth: the counter
3785 *
3786 * Check for cycles.
3787 *
3788 * Returns 0 if check passed, and -1 in case of error
3789 */
3790static int
3791xmlRelaxNGCheckCycles(xmlRelaxNGParserCtxtPtr ctxt,
3792 xmlRelaxNGDefinePtr cur, int depth) {
3793 int ret = 0;
3794
3795 while ((ret == 0) && (cur != NULL)) {
3796 if ((cur->type == XML_RELAXNG_REF) ||
3797 (cur->type == XML_RELAXNG_PARENTREF)) {
3798 if (cur->depth == -1) {
3799 cur->depth = depth;
3800 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth);
3801 cur->depth = -2;
3802 } else if (depth == cur->depth) {
3803 if (ctxt->error != NULL)
3804 ctxt->error(ctxt->userData,
3805 "Detected a cycle in %s references\n", cur->name);
3806 ctxt->nbErrors++;
3807 return(-1);
3808 }
3809 } else if (cur->type == XML_RELAXNG_ELEMENT) {
3810 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth + 1);
3811 } else {
3812 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth);
3813 }
3814 cur = cur->next;
3815 }
3816 return(ret);
3817}
3818
3819/**
Daniel Veillard77648bb2003-02-20 15:03:22 +00003820 * xmlRelaxNGTryUnlink:
3821 * @ctxt: a Relax-NG parser context
3822 * @cur: the definition to unlink
3823 * @parent: the parent definition
3824 * @prev: the previous sibling definition
3825 *
3826 * Try to unlink a definition. If not possble make it a NOOP
3827 *
3828 * Returns the new prev definition
3829 */
3830static xmlRelaxNGDefinePtr
3831xmlRelaxNGTryUnlink(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
3832 xmlRelaxNGDefinePtr cur,
3833 xmlRelaxNGDefinePtr parent,
3834 xmlRelaxNGDefinePtr prev) {
3835 if (prev != NULL) {
3836 prev->next = cur->next;
3837 } else {
3838 if (parent != NULL) {
3839 if (parent->content == cur)
3840 parent->content = cur->next;
3841 else if (parent->attrs == cur)
3842 parent->attrs = cur->next;
3843 else if (parent->nameClass == cur)
3844 parent->nameClass = cur->next;
3845 } else {
3846 cur->type = XML_RELAXNG_NOOP;
3847 prev = cur;
3848 }
3849 }
3850 return(prev);
3851}
3852
3853/**
Daniel Veillard4c5cf702003-02-21 15:40:34 +00003854 * xmlRelaxNGSimplify:
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003855 * @ctxt: a Relax-NG parser context
3856 * @nodes: grammar children nodes
3857 *
3858 * Check for simplification of empty and notAllowed
3859 */
3860static void
3861xmlRelaxNGSimplify(xmlRelaxNGParserCtxtPtr ctxt,
3862 xmlRelaxNGDefinePtr cur,
3863 xmlRelaxNGDefinePtr parent) {
3864 xmlRelaxNGDefinePtr prev = NULL;
3865
3866 while (cur != NULL) {
3867 if ((cur->type == XML_RELAXNG_REF) ||
3868 (cur->type == XML_RELAXNG_PARENTREF)) {
3869 if (cur->depth != -3) {
3870 cur->depth = -3;
3871 xmlRelaxNGSimplify(ctxt, cur->content, cur);
3872 }
3873 } else if (cur->type == XML_RELAXNG_NOT_ALLOWED) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00003874 cur->parent = parent;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003875 if ((parent != NULL) &&
3876 ((parent->type == XML_RELAXNG_ATTRIBUTE) ||
3877 (parent->type == XML_RELAXNG_LIST) ||
3878 (parent->type == XML_RELAXNG_GROUP) ||
3879 (parent->type == XML_RELAXNG_INTERLEAVE) ||
3880 (parent->type == XML_RELAXNG_ONEORMORE) ||
3881 (parent->type == XML_RELAXNG_ZEROORMORE))) {
3882 parent->type = XML_RELAXNG_NOT_ALLOWED;
3883 break;
3884 }
3885 if ((parent != NULL) &&
3886 (parent->type == XML_RELAXNG_CHOICE)) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00003887 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003888 } else
3889 prev = cur;
3890 } else if (cur->type == XML_RELAXNG_EMPTY){
Daniel Veillard77648bb2003-02-20 15:03:22 +00003891 cur->parent = parent;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003892 if ((parent != NULL) &&
3893 ((parent->type == XML_RELAXNG_ONEORMORE) ||
3894 (parent->type == XML_RELAXNG_ZEROORMORE))) {
3895 parent->type = XML_RELAXNG_EMPTY;
3896 break;
3897 }
3898 if ((parent != NULL) &&
3899 ((parent->type == XML_RELAXNG_GROUP) ||
3900 (parent->type == XML_RELAXNG_INTERLEAVE))) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00003901 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003902 } else
3903 prev = cur;
3904 } else {
Daniel Veillard77648bb2003-02-20 15:03:22 +00003905 cur->parent = parent;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003906 if (cur->content != NULL)
3907 xmlRelaxNGSimplify(ctxt, cur->content, cur);
Daniel Veillard77648bb2003-02-20 15:03:22 +00003908 if (cur->attrs != NULL)
3909 xmlRelaxNGSimplify(ctxt, cur->attrs, cur);
3910 if (cur->nameClass != NULL)
3911 xmlRelaxNGSimplify(ctxt, cur->nameClass, cur);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003912 /*
3913 * This may result in a simplification
3914 */
3915 if ((cur->type == XML_RELAXNG_GROUP) ||
3916 (cur->type == XML_RELAXNG_INTERLEAVE)) {
3917 if (cur->content == NULL)
3918 cur->type = XML_RELAXNG_EMPTY;
3919 else if (cur->content->next == NULL) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00003920 if ((parent == NULL) && (prev == NULL)) {
3921 cur->type = XML_RELAXNG_NOOP;
3922 } else if (prev == NULL) {
3923 parent->content = cur->content;
3924 cur->content->next = cur->next;
3925 cur = cur->content;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003926 } else {
Daniel Veillard77648bb2003-02-20 15:03:22 +00003927 cur->content->next = cur->next;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003928 prev->next = cur->content;
Daniel Veillard77648bb2003-02-20 15:03:22 +00003929 cur = cur->content;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003930 }
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003931 }
3932 }
3933 /*
3934 * the current node may have been transformed back
3935 */
3936 if ((cur->type == XML_RELAXNG_EXCEPT) &&
3937 (cur->content != NULL) &&
3938 (cur->content->type == XML_RELAXNG_NOT_ALLOWED)) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00003939 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003940 } else if (cur->type == XML_RELAXNG_NOT_ALLOWED) {
3941 if ((parent != NULL) &&
3942 ((parent->type == XML_RELAXNG_ATTRIBUTE) ||
3943 (parent->type == XML_RELAXNG_LIST) ||
3944 (parent->type == XML_RELAXNG_GROUP) ||
3945 (parent->type == XML_RELAXNG_INTERLEAVE) ||
3946 (parent->type == XML_RELAXNG_ONEORMORE) ||
3947 (parent->type == XML_RELAXNG_ZEROORMORE))) {
3948 parent->type = XML_RELAXNG_NOT_ALLOWED;
3949 break;
3950 }
3951 if ((parent != NULL) &&
3952 (parent->type == XML_RELAXNG_CHOICE)) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00003953 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003954 } else
3955 prev = cur;
3956 } else if (cur->type == XML_RELAXNG_EMPTY){
3957 if ((parent != NULL) &&
3958 ((parent->type == XML_RELAXNG_ONEORMORE) ||
3959 (parent->type == XML_RELAXNG_ZEROORMORE))) {
3960 parent->type = XML_RELAXNG_EMPTY;
3961 break;
3962 }
3963 if ((parent != NULL) &&
3964 ((parent->type == XML_RELAXNG_GROUP) ||
3965 (parent->type == XML_RELAXNG_INTERLEAVE) ||
3966 (parent->type == XML_RELAXNG_CHOICE))) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00003967 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003968 } else
3969 prev = cur;
3970 } else {
3971 prev = cur;
3972 }
3973 }
3974 cur = cur->next;
3975 }
3976}
3977
Daniel Veillard4c5cf702003-02-21 15:40:34 +00003978/**
3979 * xmlRelaxNGGroupContentType:
3980 * @ct1: the first content type
3981 * @ct2: the second content type
3982 *
3983 * Try to group 2 content types
3984 *
3985 * Returns the content type
3986 */
3987static xmlRelaxNGContentType
3988xmlRelaxNGGroupContentType(xmlRelaxNGContentType ct1,
3989 xmlRelaxNGContentType ct2) {
3990 if ((ct1 == XML_RELAXNG_CONTENT_ERROR) ||
3991 (ct2 == XML_RELAXNG_CONTENT_ERROR))
3992 return(XML_RELAXNG_CONTENT_ERROR);
3993 if (ct1 == XML_RELAXNG_CONTENT_EMPTY)
3994 return(ct2);
3995 if (ct2 == XML_RELAXNG_CONTENT_EMPTY)
3996 return(ct1);
3997 if ((ct1 == XML_RELAXNG_CONTENT_COMPLEX) &&
3998 (ct2 == XML_RELAXNG_CONTENT_COMPLEX))
3999 return(XML_RELAXNG_CONTENT_COMPLEX);
4000 return(XML_RELAXNG_CONTENT_ERROR);
4001}
4002
4003/**
4004 * xmlRelaxNGMaxContentType:
4005 * @ct1: the first content type
4006 * @ct2: the second content type
4007 *
4008 * Compute the max content-type
4009 *
4010 * Returns the content type
4011 */
4012static xmlRelaxNGContentType
4013xmlRelaxNGMaxContentType(xmlRelaxNGContentType ct1,
4014 xmlRelaxNGContentType ct2) {
4015 if ((ct1 == XML_RELAXNG_CONTENT_ERROR) ||
4016 (ct2 == XML_RELAXNG_CONTENT_ERROR))
4017 return(XML_RELAXNG_CONTENT_ERROR);
4018 if ((ct1 == XML_RELAXNG_CONTENT_SIMPLE) ||
4019 (ct2 == XML_RELAXNG_CONTENT_SIMPLE))
4020 return(XML_RELAXNG_CONTENT_SIMPLE);
4021 if ((ct1 == XML_RELAXNG_CONTENT_COMPLEX) ||
4022 (ct2 == XML_RELAXNG_CONTENT_COMPLEX))
4023 return(XML_RELAXNG_CONTENT_COMPLEX);
4024 return(XML_RELAXNG_CONTENT_EMPTY);
4025}
Daniel Veillard77648bb2003-02-20 15:03:22 +00004026
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004027/**
4028 * xmlRelaxNGCheckRules:
4029 * @ctxt: a Relax-NG parser context
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004030 * @cur: the current definition
Daniel Veillard77648bb2003-02-20 15:03:22 +00004031 * @flags: some accumulated flags
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004032 * @ptype: the parent type
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004033 *
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004034 * Check for rules in section 7.1 and 7.2
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004035 *
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004036 * Returns the content type of @cur
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004037 */
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004038static xmlRelaxNGContentType
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004039xmlRelaxNGCheckRules(xmlRelaxNGParserCtxtPtr ctxt,
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004040 xmlRelaxNGDefinePtr cur, int flags,
4041 xmlRelaxNGType ptype) {
4042 int nflags = flags;
4043 xmlRelaxNGContentType ret, tmp, val = XML_RELAXNG_CONTENT_EMPTY;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004044
Daniel Veillard77648bb2003-02-20 15:03:22 +00004045 while (cur != NULL) {
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004046 ret = XML_RELAXNG_CONTENT_EMPTY;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004047 if ((cur->type == XML_RELAXNG_REF) ||
4048 (cur->type == XML_RELAXNG_PARENTREF)) {
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004049 ret = XML_RELAXNG_CONTENT_COMPLEX;
4050
Daniel Veillard77648bb2003-02-20 15:03:22 +00004051 if (flags & XML_RELAXNG_IN_LIST) {
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004052 if (ctxt->error != NULL)
4053 ctxt->error(ctxt->userData,
Daniel Veillard77648bb2003-02-20 15:03:22 +00004054 "Found forbidden pattern list//ref\n");
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004055 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004056 }
4057 if (flags & XML_RELAXNG_IN_ATTRIBUTE) {
4058 if (ctxt->error != NULL)
4059 ctxt->error(ctxt->userData,
4060 "Found forbidden pattern attribute//ref\n");
4061 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004062 }
4063 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4064 if (ctxt->error != NULL)
4065 ctxt->error(ctxt->userData,
4066 "Found forbidden pattern data/except//ref\n");
4067 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004068 }
4069 if (cur->depth != -4) {
4070 cur->depth = -4;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004071 xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004072 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004073 if (ret != XML_RELAXNG_CONTENT_ERROR)
4074 ret = XML_RELAXNG_CONTENT_COMPLEX;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004075 } else if (cur->type == XML_RELAXNG_ELEMENT) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004076 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4077 if (ctxt->error != NULL)
4078 ctxt->error(ctxt->userData,
4079 "Found forbidden pattern data/except//element(ref)\n");
4080 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004081 }
4082 if (flags & XML_RELAXNG_IN_LIST) {
4083 if (ctxt->error != NULL)
4084 ctxt->error(ctxt->userData,
4085 "Found forbidden pattern list//element(ref)\n");
4086 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004087 }
4088 if (flags & XML_RELAXNG_IN_ATTRIBUTE) {
4089 if (ctxt->error != NULL)
4090 ctxt->error(ctxt->userData,
4091 "Found forbidden pattern attribute//element(ref)\n");
4092 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004093 }
4094 /*
4095 * reset since in the simple form elements are only child
4096 * of grammar/define
4097 */
4098 nflags = 0;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004099 ret = xmlRelaxNGCheckRules(ctxt, cur->attrs, nflags, cur->type);
4100 if (ret != XML_RELAXNG_CONTENT_EMPTY) {
4101 if (ctxt->error != NULL)
4102 ctxt->error(ctxt->userData,
4103 "Element %s attributes have a content type error\n",
4104 cur->name);
4105 ctxt->nbErrors++;
4106 }
4107 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
4108 if (ret == XML_RELAXNG_CONTENT_ERROR) {
4109 if (ctxt->error != NULL)
4110 ctxt->error(ctxt->userData,
4111 "Element %s has a content type error\n",
4112 cur->name);
4113 ctxt->nbErrors++;
4114 } else {
4115 ret = XML_RELAXNG_CONTENT_COMPLEX;
4116 }
Daniel Veillard77648bb2003-02-20 15:03:22 +00004117 } else if (cur->type == XML_RELAXNG_ATTRIBUTE) {
4118 if (flags & XML_RELAXNG_IN_ATTRIBUTE) {
4119 if (ctxt->error != NULL)
4120 ctxt->error(ctxt->userData,
4121 "Found forbidden pattern attribute//attribute\n");
4122 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004123 }
4124 if (flags & XML_RELAXNG_IN_LIST) {
4125 if (ctxt->error != NULL)
4126 ctxt->error(ctxt->userData,
4127 "Found forbidden pattern list//attribute\n");
4128 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004129 }
4130 if (flags & XML_RELAXNG_IN_OOMGROUP) {
4131 if (ctxt->error != NULL)
4132 ctxt->error(ctxt->userData,
4133 "Found forbidden pattern oneOrMore//group//attribute\n");
4134 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004135 }
4136 if (flags & XML_RELAXNG_IN_OOMINTERLEAVE) {
4137 if (ctxt->error != NULL)
4138 ctxt->error(ctxt->userData,
4139 "Found forbidden pattern oneOrMore//interleave//attribute\n");
4140 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004141 }
4142 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4143 if (ctxt->error != NULL)
4144 ctxt->error(ctxt->userData,
4145 "Found forbidden pattern data/except//attribute\n");
4146 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004147 }
4148 if (flags & XML_RELAXNG_IN_START) {
4149 if (ctxt->error != NULL)
4150 ctxt->error(ctxt->userData,
4151 "Found forbidden pattern start//attribute\n");
4152 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004153 }
4154 nflags = flags | XML_RELAXNG_IN_ATTRIBUTE;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004155 xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
4156 ret = XML_RELAXNG_CONTENT_EMPTY;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004157 } else if ((cur->type == XML_RELAXNG_ONEORMORE) ||
4158 (cur->type == XML_RELAXNG_ZEROORMORE)) {
4159 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4160 if (ctxt->error != NULL)
4161 ctxt->error(ctxt->userData,
4162 "Found forbidden pattern data/except//oneOrMore\n");
4163 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004164 }
4165 if (flags & XML_RELAXNG_IN_START) {
4166 if (ctxt->error != NULL)
4167 ctxt->error(ctxt->userData,
4168 "Found forbidden pattern start//oneOrMore\n");
4169 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004170 }
4171 nflags = flags | XML_RELAXNG_IN_ONEORMORE;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004172 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
4173 ret = xmlRelaxNGGroupContentType(ret, ret);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004174 } else if (cur->type == XML_RELAXNG_LIST) {
4175 if (flags & XML_RELAXNG_IN_LIST) {
4176 if (ctxt->error != NULL)
4177 ctxt->error(ctxt->userData,
4178 "Found forbidden pattern list//list\n");
4179 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004180 }
4181 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4182 if (ctxt->error != NULL)
4183 ctxt->error(ctxt->userData,
4184 "Found forbidden pattern data/except//list\n");
4185 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004186 }
4187 if (flags & XML_RELAXNG_IN_START) {
4188 if (ctxt->error != NULL)
4189 ctxt->error(ctxt->userData,
4190 "Found forbidden pattern start//list\n");
4191 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004192 }
4193 nflags = flags | XML_RELAXNG_IN_LIST;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004194 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004195 } else if (cur->type == XML_RELAXNG_GROUP) {
4196 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4197 if (ctxt->error != NULL)
4198 ctxt->error(ctxt->userData,
4199 "Found forbidden pattern data/except//group\n");
4200 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004201 }
4202 if (flags & XML_RELAXNG_IN_START) {
4203 if (ctxt->error != NULL)
4204 ctxt->error(ctxt->userData,
4205 "Found forbidden pattern start//group\n");
4206 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004207 }
4208 if (flags & XML_RELAXNG_IN_ONEORMORE)
4209 nflags = flags | XML_RELAXNG_IN_OOMGROUP;
4210 else
4211 nflags = flags;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004212 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004213 } else if (cur->type == XML_RELAXNG_INTERLEAVE) {
4214 if (flags & XML_RELAXNG_IN_LIST) {
4215 if (ctxt->error != NULL)
4216 ctxt->error(ctxt->userData,
4217 "Found forbidden pattern list//interleave\n");
4218 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004219 }
4220 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4221 if (ctxt->error != NULL)
4222 ctxt->error(ctxt->userData,
4223 "Found forbidden pattern data/except//interleave\n");
4224 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004225 }
4226 if (flags & XML_RELAXNG_IN_START) {
4227 if (ctxt->error != NULL)
4228 ctxt->error(ctxt->userData,
4229 "Found forbidden pattern start//interleave\n");
4230 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004231 }
4232 if (flags & XML_RELAXNG_IN_ONEORMORE)
4233 nflags = flags | XML_RELAXNG_IN_OOMINTERLEAVE;
4234 else
4235 nflags = flags;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004236 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004237 } else if (cur->type == XML_RELAXNG_EXCEPT) {
4238 if ((cur->parent != NULL) &&
4239 (cur->parent->type == XML_RELAXNG_DATATYPE))
4240 nflags = flags | XML_RELAXNG_IN_DATAEXCEPT;
4241 else
4242 nflags = flags;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004243 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004244 } else if (cur->type == XML_RELAXNG_DATATYPE) {
4245 if (flags & XML_RELAXNG_IN_START) {
4246 if (ctxt->error != NULL)
4247 ctxt->error(ctxt->userData,
4248 "Found forbidden pattern start//data\n");
4249 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004250 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004251 xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type);
4252 ret = XML_RELAXNG_CONTENT_SIMPLE;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004253 } else if (cur->type == XML_RELAXNG_VALUE) {
4254 if (flags & XML_RELAXNG_IN_START) {
4255 if (ctxt->error != NULL)
4256 ctxt->error(ctxt->userData,
4257 "Found forbidden pattern start//value\n");
4258 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004259 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004260 xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type);
4261 ret = XML_RELAXNG_CONTENT_SIMPLE;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004262 } else if (cur->type == XML_RELAXNG_TEXT) {
4263 if (flags & XML_RELAXNG_IN_LIST) {
4264 if (ctxt->error != NULL)
4265 ctxt->error(ctxt->userData,
4266 "Found forbidden pattern list//text\n");
4267 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004268 }
4269 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4270 if (ctxt->error != NULL)
4271 ctxt->error(ctxt->userData,
4272 "Found forbidden pattern data/except//text\n");
4273 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004274 }
4275 if (flags & XML_RELAXNG_IN_START) {
4276 if (ctxt->error != NULL)
4277 ctxt->error(ctxt->userData,
4278 "Found forbidden pattern start//text\n");
4279 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004280 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004281 ret = XML_RELAXNG_CONTENT_COMPLEX;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004282 } else if (cur->type == XML_RELAXNG_EMPTY) {
4283 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4284 if (ctxt->error != NULL)
4285 ctxt->error(ctxt->userData,
4286 "Found forbidden pattern data/except//empty\n");
4287 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004288 }
4289 if (flags & XML_RELAXNG_IN_START) {
4290 if (ctxt->error != NULL)
4291 ctxt->error(ctxt->userData,
4292 "Found forbidden pattern start//empty\n");
4293 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004294 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004295 ret = XML_RELAXNG_CONTENT_EMPTY;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004296 } else {
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004297 ret = xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004298 }
4299 cur = cur->next;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004300 if (ptype == XML_RELAXNG_GROUP) {
4301 val = xmlRelaxNGGroupContentType(val, ret);
4302 } else if (ptype == XML_RELAXNG_INTERLEAVE) {
4303 tmp = xmlRelaxNGGroupContentType(val, ret);
4304 if (tmp != XML_RELAXNG_CONTENT_ERROR)
4305 tmp = xmlRelaxNGMaxContentType(val, ret);
4306 } else if (ptype == XML_RELAXNG_CHOICE) {
4307 val = xmlRelaxNGMaxContentType(val, ret);
4308 } else if (ptype == XML_RELAXNG_LIST) {
4309 val = XML_RELAXNG_CONTENT_SIMPLE;
4310 } else if (ptype == XML_RELAXNG_EXCEPT) {
4311 if (ret == XML_RELAXNG_CONTENT_ERROR)
4312 val = XML_RELAXNG_CONTENT_ERROR;
4313 else
4314 val = XML_RELAXNG_CONTENT_SIMPLE;
4315 } else {
4316 val = xmlRelaxNGGroupContentType(val, ret);
4317 }
4318
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004319 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004320 return(val);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004321}
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004322
4323/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004324 * xmlRelaxNGParseGrammar:
4325 * @ctxt: a Relax-NG parser context
4326 * @nodes: grammar children nodes
4327 *
4328 * parse a Relax-NG <grammar> node
4329 *
4330 * Returns the internal xmlRelaxNGGrammarPtr built or
4331 * NULL in case of error
4332 */
4333static xmlRelaxNGGrammarPtr
4334xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
4335 xmlRelaxNGGrammarPtr ret, tmp, old;
4336
Daniel Veillard6eadf632003-01-23 18:29:16 +00004337 ret = xmlRelaxNGNewGrammar(ctxt);
4338 if (ret == NULL)
4339 return(NULL);
4340
4341 /*
4342 * Link the new grammar in the tree
4343 */
4344 ret->parent = ctxt->grammar;
4345 if (ctxt->grammar != NULL) {
4346 tmp = ctxt->grammar->children;
4347 if (tmp == NULL) {
4348 ctxt->grammar->children = ret;
4349 } else {
4350 while (tmp->next != NULL)
4351 tmp = tmp->next;
4352 tmp->next = ret;
4353 }
4354 }
4355
4356 old = ctxt->grammar;
4357 ctxt->grammar = ret;
4358 xmlRelaxNGParseGrammarContent(ctxt, nodes);
4359 ctxt->grammar = ret;
Daniel Veillard2df2de22003-02-17 23:34:33 +00004360 if (ctxt->grammar == NULL) {
4361 if (ctxt->error != NULL)
4362 ctxt->error(ctxt->userData,
4363 "Failed to parse <grammar> content\n");
4364 ctxt->nbErrors++;
4365 } else if (ctxt->grammar->start == NULL) {
4366 if (ctxt->error != NULL)
4367 ctxt->error(ctxt->userData,
4368 "Element <grammar> has no <start>\n");
4369 ctxt->nbErrors++;
4370 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004371
4372 /*
4373 * Apply 4.17 mergingd rules to defines and starts
4374 */
4375 xmlRelaxNGCombineStart(ctxt, ret);
4376 if (ret->defs != NULL) {
4377 xmlHashScan(ret->defs, (xmlHashScanner) xmlRelaxNGCheckCombine,
4378 ctxt);
4379 }
4380
4381 /*
4382 * link together defines and refs in this grammar
4383 */
4384 if (ret->refs != NULL) {
4385 xmlHashScan(ret->refs, (xmlHashScanner) xmlRelaxNGCheckReference,
4386 ctxt);
4387 }
4388 ctxt->grammar = old;
4389 return(ret);
4390}
4391
4392/**
4393 * xmlRelaxNGParseDocument:
4394 * @ctxt: a Relax-NG parser context
4395 * @node: the root node of the RelaxNG schema
4396 *
4397 * parse a Relax-NG definition resource and build an internal
4398 * xmlRelaxNG struture which can be used to validate instances.
4399 *
4400 * Returns the internal XML RelaxNG structure built or
4401 * NULL in case of error
4402 */
4403static xmlRelaxNGPtr
4404xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
4405 xmlRelaxNGPtr schema = NULL;
Daniel Veillard276be4a2003-01-24 01:03:34 +00004406 const xmlChar *olddefine;
Daniel Veillarde431a272003-01-29 23:02:33 +00004407 xmlRelaxNGGrammarPtr old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004408
4409 if ((ctxt == NULL) || (node == NULL))
4410 return (NULL);
4411
4412 schema = xmlRelaxNGNewRelaxNG(ctxt);
4413 if (schema == NULL)
4414 return(NULL);
4415
Daniel Veillard276be4a2003-01-24 01:03:34 +00004416 olddefine = ctxt->define;
4417 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004418 if (IS_RELAXNG(node, "grammar")) {
4419 schema->topgrammar = xmlRelaxNGParseGrammar(ctxt, node->children);
4420 } else {
4421 schema->topgrammar = xmlRelaxNGNewGrammar(ctxt);
4422 if (schema->topgrammar == NULL) {
4423 return(schema);
4424 }
4425 schema->topgrammar->parent = NULL;
Daniel Veillarde431a272003-01-29 23:02:33 +00004426 old = ctxt->grammar;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004427 ctxt->grammar = schema->topgrammar;
4428 xmlRelaxNGParseStart(ctxt, node);
Daniel Veillarde431a272003-01-29 23:02:33 +00004429 if (old != NULL)
4430 ctxt->grammar = old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004431 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00004432 ctxt->define = olddefine;
Daniel Veillardd4310742003-02-18 21:12:46 +00004433 if (schema->topgrammar->start != NULL) {
4434 xmlRelaxNGCheckCycles(ctxt, schema->topgrammar->start, 0);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004435 if ((ctxt->flags & XML_RELAXNG_IN_EXTERNALREF) == 0) {
4436 xmlRelaxNGSimplify(ctxt, schema->topgrammar->start, NULL);
4437 while ((schema->topgrammar->start != NULL) &&
4438 (schema->topgrammar->start->type == XML_RELAXNG_NOOP) &&
4439 (schema->topgrammar->start->next != NULL))
4440 schema->topgrammar->start = schema->topgrammar->start->content;
4441 xmlRelaxNGCheckRules(ctxt, schema->topgrammar->start,
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004442 XML_RELAXNG_IN_START, XML_RELAXNG_NOOP);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004443 }
Daniel Veillardd4310742003-02-18 21:12:46 +00004444 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004445
4446#ifdef DEBUG
4447 if (schema == NULL)
4448 xmlGenericError(xmlGenericErrorContext,
4449 "xmlRelaxNGParseDocument() failed\n");
4450#endif
4451
4452 return (schema);
4453}
4454
4455/************************************************************************
4456 * *
4457 * Reading RelaxNGs *
4458 * *
4459 ************************************************************************/
4460
4461/**
4462 * xmlRelaxNGNewParserCtxt:
4463 * @URL: the location of the schema
4464 *
4465 * Create an XML RelaxNGs parse context for that file/resource expected
4466 * to contain an XML RelaxNGs file.
4467 *
4468 * Returns the parser context or NULL in case of error
4469 */
4470xmlRelaxNGParserCtxtPtr
4471xmlRelaxNGNewParserCtxt(const char *URL) {
4472 xmlRelaxNGParserCtxtPtr ret;
4473
4474 if (URL == NULL)
4475 return(NULL);
4476
4477 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
4478 if (ret == NULL) {
4479 xmlGenericError(xmlGenericErrorContext,
4480 "Failed to allocate new schama parser context for %s\n", URL);
4481 return (NULL);
4482 }
4483 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
4484 ret->URL = xmlStrdup((const xmlChar *)URL);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004485 ret->error = xmlGenericError;
4486 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004487 return (ret);
4488}
4489
4490/**
4491 * xmlRelaxNGNewMemParserCtxt:
4492 * @buffer: a pointer to a char array containing the schemas
4493 * @size: the size of the array
4494 *
4495 * Create an XML RelaxNGs parse context for that memory buffer expected
4496 * to contain an XML RelaxNGs file.
4497 *
4498 * Returns the parser context or NULL in case of error
4499 */
4500xmlRelaxNGParserCtxtPtr
4501xmlRelaxNGNewMemParserCtxt(const char *buffer, int size) {
4502 xmlRelaxNGParserCtxtPtr ret;
4503
4504 if ((buffer == NULL) || (size <= 0))
4505 return(NULL);
4506
4507 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
4508 if (ret == NULL) {
4509 xmlGenericError(xmlGenericErrorContext,
4510 "Failed to allocate new schama parser context\n");
4511 return (NULL);
4512 }
4513 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
4514 ret->buffer = buffer;
4515 ret->size = size;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004516 ret->error = xmlGenericError;
4517 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004518 return (ret);
4519}
4520
4521/**
4522 * xmlRelaxNGFreeParserCtxt:
4523 * @ctxt: the schema parser context
4524 *
4525 * Free the resources associated to the schema parser context
4526 */
4527void
4528xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxtPtr ctxt) {
4529 if (ctxt == NULL)
4530 return;
4531 if (ctxt->URL != NULL)
4532 xmlFree(ctxt->URL);
4533 if (ctxt->doc != NULL)
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004534 xmlFreeDoc(ctxt->document);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004535 if (ctxt->interleaves != NULL)
4536 xmlHashFree(ctxt->interleaves, NULL);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004537 if (ctxt->documents != NULL)
4538 xmlHashFree(ctxt->documents, (xmlHashDeallocator)
4539 xmlRelaxNGFreeDocument);
4540 if (ctxt->docTab != NULL)
4541 xmlFree(ctxt->docTab);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004542 if (ctxt->incTab != NULL)
4543 xmlFree(ctxt->incTab);
Daniel Veillard419a7682003-02-03 23:22:49 +00004544 if (ctxt->defTab != NULL) {
4545 int i;
4546
4547 for (i = 0;i < ctxt->defNr;i++)
4548 xmlRelaxNGFreeDefine(ctxt->defTab[i]);
4549 xmlFree(ctxt->defTab);
4550 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004551 xmlFree(ctxt);
4552}
4553
Daniel Veillard6eadf632003-01-23 18:29:16 +00004554/**
Daniel Veillardd2298792003-02-14 16:54:11 +00004555 * xmlRelaxNGNormExtSpace:
4556 * @value: a value
4557 *
4558 * Removes the leading and ending spaces of the value
4559 * The string is modified "in situ"
4560 */
4561static void
4562xmlRelaxNGNormExtSpace(xmlChar *value) {
4563 xmlChar *start = value;
4564 xmlChar *cur = value;
4565 if (value == NULL)
4566 return;
4567
4568 while (IS_BLANK(*cur)) cur++;
4569 if (cur == start) {
4570 do {
4571 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
4572 if (*cur == 0)
4573 return;
4574 start = cur;
4575 while (IS_BLANK(*cur)) cur++;
4576 if (*cur == 0) {
4577 *start = 0;
4578 return;
4579 }
4580 } while (1);
4581 } else {
4582 do {
4583 while ((*cur != 0) && (!IS_BLANK(*cur)))
4584 *start++ = *cur++;
4585 if (*cur == 0) {
4586 *start = 0;
4587 return;
4588 }
4589 /* don't try to normalize the inner spaces */
4590 while (IS_BLANK(*cur)) cur++;
4591 *start++ = *cur++;
4592 if (*cur == 0) {
4593 *start = 0;
4594 return;
4595 }
4596 } while (1);
4597 }
4598}
4599
4600/**
4601 * xmlRelaxNGCheckAttributes:
4602 * @ctxt: a Relax-NG parser context
4603 * @node: a Relax-NG node
4604 *
4605 * Check all the attributes on the given node
4606 */
4607static void
4608xmlRelaxNGCleanupAttributes(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
4609 xmlAttrPtr cur, next;
4610
4611 cur = node->properties;
4612 while (cur != NULL) {
4613 next = cur->next;
4614 if ((cur->ns == NULL) ||
4615 (xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
4616 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
4617 if ((!xmlStrEqual(node->name, BAD_CAST "element")) &&
4618 (!xmlStrEqual(node->name, BAD_CAST "attribute")) &&
4619 (!xmlStrEqual(node->name, BAD_CAST "ref")) &&
4620 (!xmlStrEqual(node->name, BAD_CAST "parentRef")) &&
Daniel Veillard2df2de22003-02-17 23:34:33 +00004621 (!xmlStrEqual(node->name, BAD_CAST "param")) &&
Daniel Veillardd2298792003-02-14 16:54:11 +00004622 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
4623 if (ctxt->error != NULL)
4624 ctxt->error(ctxt->userData,
4625 "Attribute %s is not allowed on %s\n",
4626 cur->name, node->name);
4627 ctxt->nbErrors++;
4628 }
4629 } else if (xmlStrEqual(cur->name, BAD_CAST "type")) {
4630 if ((!xmlStrEqual(node->name, BAD_CAST "value")) &&
4631 (!xmlStrEqual(node->name, BAD_CAST "data"))) {
4632 if (ctxt->error != NULL)
4633 ctxt->error(ctxt->userData,
4634 "Attribute %s is not allowed on %s\n",
4635 cur->name, node->name);
4636 ctxt->nbErrors++;
4637 }
4638 } else if (xmlStrEqual(cur->name, BAD_CAST "href")) {
4639 if ((!xmlStrEqual(node->name, BAD_CAST "externalRef")) &&
4640 (!xmlStrEqual(node->name, BAD_CAST "include"))) {
4641 if (ctxt->error != NULL)
4642 ctxt->error(ctxt->userData,
4643 "Attribute %s is not allowed on %s\n",
4644 cur->name, node->name);
4645 ctxt->nbErrors++;
4646 }
4647 } else if (xmlStrEqual(cur->name, BAD_CAST "combine")) {
4648 if ((!xmlStrEqual(node->name, BAD_CAST "start")) &&
4649 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
4650 if (ctxt->error != NULL)
4651 ctxt->error(ctxt->userData,
4652 "Attribute %s is not allowed on %s\n",
4653 cur->name, node->name);
4654 ctxt->nbErrors++;
4655 }
4656 } else if (xmlStrEqual(cur->name, BAD_CAST "datatypeLibrary")) {
4657 xmlChar *val;
4658 xmlURIPtr uri;
4659
4660 val = xmlNodeListGetString(node->doc, cur->children, 1);
4661 if (val != NULL) {
4662 if (val[0] != 0) {
4663 uri = xmlParseURI((const char *) val);
4664 if (uri == NULL) {
4665 if (ctxt->error != NULL)
4666 ctxt->error(ctxt->userData,
4667 "Attribute %s contains invalid URI %s\n",
4668 cur->name, val);
4669 ctxt->nbErrors++;
4670 } else {
4671 if (uri->scheme == NULL) {
4672 if (ctxt->error != NULL)
4673 ctxt->error(ctxt->userData,
4674 "Attribute %s URI %s is not absolute\n",
4675 cur->name, val);
4676 ctxt->nbErrors++;
4677 }
4678 if (uri->fragment != NULL) {
4679 if (ctxt->error != NULL)
4680 ctxt->error(ctxt->userData,
4681 "Attribute %s URI %s has a fragment ID\n",
4682 cur->name, val);
4683 ctxt->nbErrors++;
4684 }
4685 xmlFreeURI(uri);
4686 }
4687 }
4688 xmlFree(val);
4689 }
4690 } else if (!xmlStrEqual(cur->name, BAD_CAST "ns")) {
4691 if (ctxt->error != NULL)
4692 ctxt->error(ctxt->userData,
4693 "Unknown attribute %s on %s\n",
4694 cur->name, node->name);
4695 ctxt->nbErrors++;
4696 }
4697 }
4698 cur = next;
4699 }
4700}
4701
4702/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004703 * xmlRelaxNGCleanupDoc:
4704 * @ctxt: a Relax-NG parser context
4705 * @doc: an xmldocPtr document pointer
Daniel Veillard6eadf632003-01-23 18:29:16 +00004706 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004707 * Cleanup the document from unwanted nodes for parsing, resolve
4708 * Include and externalRef lookups.
Daniel Veillard6eadf632003-01-23 18:29:16 +00004709 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004710 * Returns the cleaned up document or NULL in case of error
Daniel Veillard6eadf632003-01-23 18:29:16 +00004711 */
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004712static xmlDocPtr
4713xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt, xmlDocPtr doc) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00004714 xmlNodePtr root, cur, delete;
4715
Daniel Veillard6eadf632003-01-23 18:29:16 +00004716 /*
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004717 * Extract the root
Daniel Veillard6eadf632003-01-23 18:29:16 +00004718 */
4719 root = xmlDocGetRootElement(doc);
4720 if (root == NULL) {
4721 if (ctxt->error != NULL)
4722 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
4723 ctxt->URL);
4724 ctxt->nbErrors++;
4725 return (NULL);
4726 }
4727
4728 /*
4729 * Remove all the blank text nodes
4730 */
4731 delete = NULL;
4732 cur = root;
4733 while (cur != NULL) {
4734 if (delete != NULL) {
4735 xmlUnlinkNode(delete);
4736 xmlFreeNode(delete);
4737 delete = NULL;
4738 }
4739 if (cur->type == XML_ELEMENT_NODE) {
4740 /*
4741 * Simplification 4.1. Annotations
4742 */
4743 if ((cur->ns == NULL) ||
4744 (!xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
Daniel Veillardd2298792003-02-14 16:54:11 +00004745 if ((cur->parent != NULL) &&
4746 (cur->parent->type == XML_ELEMENT_NODE) &&
4747 ((xmlStrEqual(cur->parent->name, BAD_CAST "name")) ||
4748 (xmlStrEqual(cur->parent->name, BAD_CAST "value")) ||
4749 (xmlStrEqual(cur->parent->name, BAD_CAST "param")))) {
4750 if (ctxt->error != NULL)
4751 ctxt->error(ctxt->userData,
4752 "element %s doesn't allow foreign elements\n",
4753 cur->parent->name);
4754 ctxt->nbErrors++;
4755 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004756 delete = cur;
4757 goto skip_children;
4758 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00004759 xmlRelaxNGCleanupAttributes(ctxt, cur);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004760 if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004761 xmlChar *href, *ns, *base, *URL;
4762 xmlRelaxNGDocumentPtr docu;
Daniel Veillard416589a2003-02-17 17:25:42 +00004763 xmlNodePtr tmp;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004764
4765 ns = xmlGetProp(cur, BAD_CAST "ns");
Daniel Veillard416589a2003-02-17 17:25:42 +00004766 if (ns == NULL) {
4767 tmp = cur->parent;
4768 while ((tmp != NULL) &&
4769 (tmp->type == XML_ELEMENT_NODE)) {
4770 ns = xmlGetProp(tmp, BAD_CAST "ns");
4771 if (ns != NULL)
4772 break;
4773 tmp = tmp->parent;
4774 }
4775 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004776 href = xmlGetProp(cur, BAD_CAST "href");
4777 if (href == NULL) {
4778 if (ctxt->error != NULL)
4779 ctxt->error(ctxt->userData,
4780 "xmlRelaxNGParse: externalRef has no href attribute\n");
4781 ctxt->nbErrors++;
4782 delete = cur;
4783 goto skip_children;
4784 }
4785 base = xmlNodeGetBase(cur->doc, cur);
4786 URL = xmlBuildURI(href, base);
4787 if (URL == NULL) {
4788 if (ctxt->error != NULL)
4789 ctxt->error(ctxt->userData,
4790 "Failed to compute URL for externalRef %s\n", href);
4791 ctxt->nbErrors++;
4792 if (href != NULL)
4793 xmlFree(href);
4794 if (base != NULL)
4795 xmlFree(base);
4796 delete = cur;
4797 goto skip_children;
4798 }
4799 if (href != NULL)
4800 xmlFree(href);
4801 if (base != NULL)
4802 xmlFree(base);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004803 docu = xmlRelaxNGLoadExternalRef(ctxt, URL, ns);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004804 if (docu == NULL) {
4805 if (ctxt->error != NULL)
4806 ctxt->error(ctxt->userData,
4807 "Failed to load externalRef %s\n", URL);
4808 ctxt->nbErrors++;
4809 xmlFree(URL);
4810 delete = cur;
4811 goto skip_children;
4812 }
4813 xmlFree(URL);
4814 cur->_private = docu;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004815 } else if (xmlStrEqual(cur->name, BAD_CAST "include")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00004816 xmlChar *href, *ns, *base, *URL;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004817 xmlRelaxNGIncludePtr incl;
Daniel Veillard416589a2003-02-17 17:25:42 +00004818 xmlNodePtr tmp;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004819
4820 href = xmlGetProp(cur, BAD_CAST "href");
4821 if (href == NULL) {
4822 if (ctxt->error != NULL)
4823 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004824 "xmlRelaxNGParse: include has no href attribute\n");
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004825 ctxt->nbErrors++;
4826 delete = cur;
4827 goto skip_children;
4828 }
4829 base = xmlNodeGetBase(cur->doc, cur);
4830 URL = xmlBuildURI(href, base);
4831 if (URL == NULL) {
4832 if (ctxt->error != NULL)
4833 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004834 "Failed to compute URL for include %s\n", href);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004835 ctxt->nbErrors++;
4836 if (href != NULL)
4837 xmlFree(href);
4838 if (base != NULL)
4839 xmlFree(base);
4840 delete = cur;
4841 goto skip_children;
4842 }
4843 if (href != NULL)
4844 xmlFree(href);
4845 if (base != NULL)
4846 xmlFree(base);
Daniel Veillard416589a2003-02-17 17:25:42 +00004847 ns = xmlGetProp(cur, BAD_CAST "ns");
4848 if (ns == NULL) {
4849 tmp = cur->parent;
4850 while ((tmp != NULL) &&
4851 (tmp->type == XML_ELEMENT_NODE)) {
4852 ns = xmlGetProp(tmp, BAD_CAST "ns");
4853 if (ns != NULL)
4854 break;
4855 tmp = tmp->parent;
4856 }
4857 }
4858 incl = xmlRelaxNGLoadInclude(ctxt, URL, cur, ns);
4859 if (ns != NULL)
4860 xmlFree(ns);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004861 if (incl == NULL) {
4862 if (ctxt->error != NULL)
4863 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004864 "Failed to load include %s\n", URL);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004865 ctxt->nbErrors++;
4866 xmlFree(URL);
4867 delete = cur;
4868 goto skip_children;
4869 }
4870 xmlFree(URL);
4871 cur->_private = incl;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004872 } else if ((xmlStrEqual(cur->name, BAD_CAST "element")) ||
4873 (xmlStrEqual(cur->name, BAD_CAST "attribute"))) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00004874 xmlChar *name, *ns;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004875 xmlNodePtr text = NULL;
4876
4877 /*
4878 * Simplification 4.8. name attribute of element
4879 * and attribute elements
4880 */
4881 name = xmlGetProp(cur, BAD_CAST "name");
4882 if (name != NULL) {
4883 if (cur->children == NULL) {
4884 text = xmlNewChild(cur, cur->ns, BAD_CAST "name",
4885 name);
4886 } else {
4887 xmlNodePtr node;
4888 node = xmlNewNode(cur->ns, BAD_CAST "name");
4889 if (node != NULL) {
4890 xmlAddPrevSibling(cur->children, node);
4891 text = xmlNewText(name);
4892 xmlAddChild(node, text);
4893 text = node;
4894 }
4895 }
Daniel Veillardfebcca42003-02-16 15:44:18 +00004896 if (text == NULL) {
4897 if (ctxt->error != NULL)
4898 ctxt->error(ctxt->userData,
4899 "Failed to create a name %s element\n", name);
4900 ctxt->nbErrors++;
4901 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004902 xmlUnsetProp(cur, BAD_CAST "name");
4903 xmlFree(name);
Daniel Veillardfebcca42003-02-16 15:44:18 +00004904 ns = xmlGetProp(cur, BAD_CAST "ns");
4905 if (ns != NULL) {
4906 if (text != NULL) {
4907 xmlSetProp(text, BAD_CAST "ns", ns);
4908 /* xmlUnsetProp(cur, BAD_CAST "ns"); */
Daniel Veillard6eadf632003-01-23 18:29:16 +00004909 }
Daniel Veillardfebcca42003-02-16 15:44:18 +00004910 xmlFree(ns);
4911 } else if (xmlStrEqual(cur->name,
4912 BAD_CAST "attribute")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00004913 xmlSetProp(text, BAD_CAST "ns", BAD_CAST "");
4914 }
4915 }
4916 } else if ((xmlStrEqual(cur->name, BAD_CAST "name")) ||
4917 (xmlStrEqual(cur->name, BAD_CAST "nsName")) ||
4918 (xmlStrEqual(cur->name, BAD_CAST "value"))) {
4919 /*
4920 * Simplification 4.8. name attribute of element
4921 * and attribute elements
4922 */
4923 if (xmlHasProp(cur, BAD_CAST "ns") == NULL) {
4924 xmlNodePtr node;
4925 xmlChar *ns = NULL;
4926
4927 node = cur->parent;
4928 while ((node != NULL) &&
4929 (node->type == XML_ELEMENT_NODE)) {
4930 ns = xmlGetProp(node, BAD_CAST "ns");
4931 if (ns != NULL) {
4932 break;
4933 }
4934 node = node->parent;
4935 }
4936 if (ns == NULL) {
4937 xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
4938 } else {
4939 xmlSetProp(cur, BAD_CAST "ns", ns);
4940 xmlFree(ns);
4941 }
4942 }
4943 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
4944 xmlChar *name, *local, *prefix;
4945
4946 /*
4947 * Simplification: 4.10. QNames
4948 */
4949 name = xmlNodeGetContent(cur);
4950 if (name != NULL) {
4951 local = xmlSplitQName2(name, &prefix);
4952 if (local != NULL) {
4953 xmlNsPtr ns;
4954
4955 ns = xmlSearchNs(cur->doc, cur, prefix);
4956 if (ns == NULL) {
4957 if (ctxt->error != NULL)
4958 ctxt->error(ctxt->userData,
4959 "xmlRelaxNGParse: no namespace for prefix %s\n", prefix);
4960 ctxt->nbErrors++;
4961 } else {
4962 xmlSetProp(cur, BAD_CAST "ns", ns->href);
4963 xmlNodeSetContent(cur, local);
4964 }
4965 xmlFree(local);
4966 xmlFree(prefix);
4967 }
4968 xmlFree(name);
4969 }
4970 }
4971 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004972 /*
4973 * Thisd is not an else since "include" is transformed
4974 * into a div
4975 */
4976 if (xmlStrEqual(cur->name, BAD_CAST "div")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00004977 xmlChar *ns;
4978 xmlNodePtr child, ins, tmp;
4979
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004980 /*
4981 * implements rule 4.11
4982 */
Daniel Veillard416589a2003-02-17 17:25:42 +00004983
4984 ns = xmlGetProp(cur, BAD_CAST "ns");
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004985
4986 child = cur->children;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004987 ins = cur;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004988 while (child != NULL) {
Daniel Veillard416589a2003-02-17 17:25:42 +00004989 if (ns != NULL) {
4990 if (!xmlHasProp(child, BAD_CAST "ns")) {
4991 xmlSetProp(child, BAD_CAST "ns", ns);
4992 }
4993 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004994 tmp = child->next;
4995 xmlUnlinkNode(child);
4996 ins = xmlAddNextSibling(ins, child);
4997 child = tmp;
4998 }
Daniel Veillard416589a2003-02-17 17:25:42 +00004999 if (ns != NULL)
5000 xmlFree(ns);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005001 delete = cur;
5002 goto skip_children;
5003 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005004 }
5005 }
5006 /*
5007 * Simplification 4.2 whitespaces
5008 */
5009 else if (cur->type == XML_TEXT_NODE) {
5010 if (IS_BLANK_NODE(cur)) {
5011 if (cur->parent->type == XML_ELEMENT_NODE) {
5012 if ((!xmlStrEqual(cur->parent->name, BAD_CAST "value")) &&
5013 (!xmlStrEqual(cur->parent->name, BAD_CAST "param")))
5014 delete = cur;
5015 } else {
5016 delete = cur;
5017 goto skip_children;
5018 }
5019 }
5020 } else if (cur->type != XML_CDATA_SECTION_NODE) {
5021 delete = cur;
5022 goto skip_children;
5023 }
5024
5025 /*
5026 * Skip to next node
5027 */
5028 if (cur->children != NULL) {
5029 if ((cur->children->type != XML_ENTITY_DECL) &&
5030 (cur->children->type != XML_ENTITY_REF_NODE) &&
5031 (cur->children->type != XML_ENTITY_NODE)) {
5032 cur = cur->children;
5033 continue;
5034 }
5035 }
5036skip_children:
5037 if (cur->next != NULL) {
5038 cur = cur->next;
5039 continue;
5040 }
5041
5042 do {
5043 cur = cur->parent;
5044 if (cur == NULL)
5045 break;
5046 if (cur == root) {
5047 cur = NULL;
5048 break;
5049 }
5050 if (cur->next != NULL) {
5051 cur = cur->next;
5052 break;
5053 }
5054 } while (cur != NULL);
5055 }
5056 if (delete != NULL) {
5057 xmlUnlinkNode(delete);
5058 xmlFreeNode(delete);
5059 delete = NULL;
5060 }
5061
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005062 return(doc);
5063}
5064
5065/**
5066 * xmlRelaxNGParse:
5067 * @ctxt: a Relax-NG parser context
5068 *
5069 * parse a schema definition resource and build an internal
5070 * XML Shema struture which can be used to validate instances.
5071 * *WARNING* this interface is highly subject to change
5072 *
5073 * Returns the internal XML RelaxNG structure built from the resource or
5074 * NULL in case of error
5075 */
5076xmlRelaxNGPtr
5077xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt)
5078{
5079 xmlRelaxNGPtr ret = NULL;
5080 xmlDocPtr doc;
5081 xmlNodePtr root;
5082
5083 xmlRelaxNGInitTypes();
5084
5085 if (ctxt == NULL)
5086 return (NULL);
5087
5088 /*
5089 * First step is to parse the input document into an DOM/Infoset
5090 */
5091 if (ctxt->URL != NULL) {
5092 doc = xmlParseFile((const char *) ctxt->URL);
5093 if (doc == NULL) {
5094 if (ctxt->error != NULL)
5095 ctxt->error(ctxt->userData,
5096 "xmlRelaxNGParse: could not load %s\n", ctxt->URL);
5097 ctxt->nbErrors++;
5098 return (NULL);
5099 }
5100 } else if (ctxt->buffer != NULL) {
5101 doc = xmlParseMemory(ctxt->buffer, ctxt->size);
5102 if (doc == NULL) {
5103 if (ctxt->error != NULL)
5104 ctxt->error(ctxt->userData,
5105 "xmlRelaxNGParse: could not parse schemas\n");
5106 ctxt->nbErrors++;
5107 return (NULL);
5108 }
5109 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
5110 ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
5111 } else {
5112 if (ctxt->error != NULL)
5113 ctxt->error(ctxt->userData,
5114 "xmlRelaxNGParse: nothing to parse\n");
5115 ctxt->nbErrors++;
5116 return (NULL);
5117 }
5118 ctxt->document = doc;
5119
5120 /*
5121 * Some preprocessing of the document content
5122 */
5123 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
5124 if (doc == NULL) {
5125 xmlFreeDoc(ctxt->document);
5126 ctxt->document = NULL;
5127 return(NULL);
5128 }
5129
Daniel Veillard6eadf632003-01-23 18:29:16 +00005130 /*
5131 * Then do the parsing for good
5132 */
5133 root = xmlDocGetRootElement(doc);
5134 if (root == NULL) {
5135 if (ctxt->error != NULL)
5136 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
5137 ctxt->URL);
5138 ctxt->nbErrors++;
5139 return (NULL);
5140 }
5141 ret = xmlRelaxNGParseDocument(ctxt, root);
5142 if (ret == NULL)
5143 return(NULL);
5144
5145 /*
5146 * Check the ref/defines links
5147 */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005148 /*
5149 * try to preprocess interleaves
5150 */
5151 if (ctxt->interleaves != NULL) {
5152 xmlHashScan(ctxt->interleaves,
5153 (xmlHashScanner)xmlRelaxNGComputeInterleaves, ctxt);
5154 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005155
5156 /*
5157 * if there was a parsing error return NULL
5158 */
5159 if (ctxt->nbErrors > 0) {
5160 xmlRelaxNGFree(ret);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005161 ctxt->document = NULL;
5162 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005163 return(NULL);
5164 }
5165
5166 /*
5167 * Transfer the pointer for cleanup at the schema level.
5168 */
5169 ret->doc = doc;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005170 ctxt->document = NULL;
5171 ret->documents = ctxt->documents;
5172 ctxt->documents = NULL;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005173 ret->includes = ctxt->includes;
5174 ctxt->includes = NULL;
Daniel Veillard419a7682003-02-03 23:22:49 +00005175 ret->defNr = ctxt->defNr;
5176 ret->defTab = ctxt->defTab;
5177 ctxt->defTab = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005178
5179 return (ret);
5180}
5181
5182/**
5183 * xmlRelaxNGSetParserErrors:
5184 * @ctxt: a Relax-NG validation context
5185 * @err: the error callback
5186 * @warn: the warning callback
5187 * @ctx: contextual data for the callbacks
5188 *
5189 * Set the callback functions used to handle errors for a validation context
5190 */
5191void
5192xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
5193 xmlRelaxNGValidityErrorFunc err,
5194 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
5195 if (ctxt == NULL)
5196 return;
5197 ctxt->error = err;
5198 ctxt->warning = warn;
5199 ctxt->userData = ctx;
5200}
5201/************************************************************************
5202 * *
5203 * Dump back a compiled form *
5204 * *
5205 ************************************************************************/
5206static void xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define);
5207
5208/**
5209 * xmlRelaxNGDumpDefines:
5210 * @output: the file output
5211 * @defines: a list of define structures
5212 *
5213 * Dump a RelaxNG structure back
5214 */
5215static void
5216xmlRelaxNGDumpDefines(FILE * output, xmlRelaxNGDefinePtr defines) {
5217 while (defines != NULL) {
5218 xmlRelaxNGDumpDefine(output, defines);
5219 defines = defines->next;
5220 }
5221}
5222
5223/**
5224 * xmlRelaxNGDumpDefine:
5225 * @output: the file output
5226 * @define: a define structure
5227 *
5228 * Dump a RelaxNG structure back
5229 */
5230static void
5231xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define) {
5232 if (define == NULL)
5233 return;
5234 switch(define->type) {
5235 case XML_RELAXNG_EMPTY:
5236 fprintf(output, "<empty/>\n");
5237 break;
5238 case XML_RELAXNG_NOT_ALLOWED:
5239 fprintf(output, "<notAllowed/>\n");
5240 break;
5241 case XML_RELAXNG_TEXT:
5242 fprintf(output, "<text/>\n");
5243 break;
5244 case XML_RELAXNG_ELEMENT:
5245 fprintf(output, "<element>\n");
5246 if (define->name != NULL) {
5247 fprintf(output, "<name");
5248 if (define->ns != NULL)
5249 fprintf(output, " ns=\"%s\"", define->ns);
5250 fprintf(output, ">%s</name>\n", define->name);
5251 }
5252 xmlRelaxNGDumpDefines(output, define->attrs);
5253 xmlRelaxNGDumpDefines(output, define->content);
5254 fprintf(output, "</element>\n");
5255 break;
5256 case XML_RELAXNG_LIST:
5257 fprintf(output, "<list>\n");
5258 xmlRelaxNGDumpDefines(output, define->content);
5259 fprintf(output, "</list>\n");
5260 break;
5261 case XML_RELAXNG_ONEORMORE:
5262 fprintf(output, "<oneOrMore>\n");
5263 xmlRelaxNGDumpDefines(output, define->content);
5264 fprintf(output, "</oneOrMore>\n");
5265 break;
5266 case XML_RELAXNG_ZEROORMORE:
5267 fprintf(output, "<zeroOrMore>\n");
5268 xmlRelaxNGDumpDefines(output, define->content);
5269 fprintf(output, "</zeroOrMore>\n");
5270 break;
5271 case XML_RELAXNG_CHOICE:
5272 fprintf(output, "<choice>\n");
5273 xmlRelaxNGDumpDefines(output, define->content);
5274 fprintf(output, "</choice>\n");
5275 break;
5276 case XML_RELAXNG_GROUP:
5277 fprintf(output, "<group>\n");
5278 xmlRelaxNGDumpDefines(output, define->content);
5279 fprintf(output, "</group>\n");
5280 break;
5281 case XML_RELAXNG_INTERLEAVE:
5282 fprintf(output, "<interleave>\n");
5283 xmlRelaxNGDumpDefines(output, define->content);
5284 fprintf(output, "</interleave>\n");
5285 break;
5286 case XML_RELAXNG_OPTIONAL:
5287 fprintf(output, "<optional>\n");
5288 xmlRelaxNGDumpDefines(output, define->content);
5289 fprintf(output, "</optional>\n");
5290 break;
5291 case XML_RELAXNG_ATTRIBUTE:
5292 fprintf(output, "<attribute>\n");
5293 xmlRelaxNGDumpDefines(output, define->content);
5294 fprintf(output, "</attribute>\n");
5295 break;
5296 case XML_RELAXNG_DEF:
5297 fprintf(output, "<define");
5298 if (define->name != NULL)
5299 fprintf(output, " name=\"%s\"", define->name);
5300 fprintf(output, ">\n");
5301 xmlRelaxNGDumpDefines(output, define->content);
5302 fprintf(output, "</define>\n");
5303 break;
5304 case XML_RELAXNG_REF:
5305 fprintf(output, "<ref");
5306 if (define->name != NULL)
5307 fprintf(output, " name=\"%s\"", define->name);
5308 fprintf(output, ">\n");
5309 xmlRelaxNGDumpDefines(output, define->content);
5310 fprintf(output, "</ref>\n");
5311 break;
Daniel Veillard419a7682003-02-03 23:22:49 +00005312 case XML_RELAXNG_PARENTREF:
5313 fprintf(output, "<parentRef");
5314 if (define->name != NULL)
5315 fprintf(output, " name=\"%s\"", define->name);
5316 fprintf(output, ">\n");
5317 xmlRelaxNGDumpDefines(output, define->content);
5318 fprintf(output, "</parentRef>\n");
5319 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005320 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard416589a2003-02-17 17:25:42 +00005321 fprintf(output, "<externalRef>");
Daniel Veillarde431a272003-01-29 23:02:33 +00005322 xmlRelaxNGDumpDefines(output, define->content);
5323 fprintf(output, "</externalRef>\n");
5324 break;
5325 case XML_RELAXNG_DATATYPE:
Daniel Veillard6eadf632003-01-23 18:29:16 +00005326 case XML_RELAXNG_VALUE:
5327 TODO
5328 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005329 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00005330 case XML_RELAXNG_EXCEPT:
Daniel Veillard8fe98712003-02-19 00:19:14 +00005331 case XML_RELAXNG_PARAM:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005332 TODO
5333 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00005334 case XML_RELAXNG_NOOP:
5335 xmlRelaxNGDumpDefines(output, define->content);
5336 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005337 }
5338}
5339
5340/**
5341 * xmlRelaxNGDumpGrammar:
5342 * @output: the file output
5343 * @grammar: a grammar structure
5344 * @top: is this a top grammar
5345 *
5346 * Dump a RelaxNG structure back
5347 */
5348static void
5349xmlRelaxNGDumpGrammar(FILE * output, xmlRelaxNGGrammarPtr grammar, int top)
5350{
5351 if (grammar == NULL)
5352 return;
5353
5354 fprintf(output, "<grammar");
5355 if (top)
5356 fprintf(output,
5357 " xmlns=\"http://relaxng.org/ns/structure/1.0\"");
5358 switch(grammar->combine) {
5359 case XML_RELAXNG_COMBINE_UNDEFINED:
5360 break;
5361 case XML_RELAXNG_COMBINE_CHOICE:
5362 fprintf(output, " combine=\"choice\"");
5363 break;
5364 case XML_RELAXNG_COMBINE_INTERLEAVE:
5365 fprintf(output, " combine=\"interleave\"");
5366 break;
5367 default:
5368 fprintf(output, " <!-- invalid combine value -->");
5369 }
5370 fprintf(output, ">\n");
5371 if (grammar->start == NULL) {
5372 fprintf(output, " <!-- grammar had no start -->");
5373 } else {
5374 fprintf(output, "<start>\n");
5375 xmlRelaxNGDumpDefine(output, grammar->start);
5376 fprintf(output, "</start>\n");
5377 }
5378 /* TODO ? Dump the defines ? */
5379 fprintf(output, "</grammar>\n");
5380}
5381
5382/**
5383 * xmlRelaxNGDump:
5384 * @output: the file output
5385 * @schema: a schema structure
5386 *
5387 * Dump a RelaxNG structure back
5388 */
5389void
5390xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema)
5391{
5392 if (schema == NULL) {
5393 fprintf(output, "RelaxNG empty or failed to compile\n");
5394 return;
5395 }
5396 fprintf(output, "RelaxNG: ");
5397 if (schema->doc == NULL) {
5398 fprintf(output, "no document\n");
5399 } else if (schema->doc->URL != NULL) {
5400 fprintf(output, "%s\n", schema->doc->URL);
5401 } else {
5402 fprintf(output, "\n");
5403 }
5404 if (schema->topgrammar == NULL) {
5405 fprintf(output, "RelaxNG has no top grammar\n");
5406 return;
5407 }
5408 xmlRelaxNGDumpGrammar(output, schema->topgrammar, 1);
5409}
5410
Daniel Veillardfebcca42003-02-16 15:44:18 +00005411/**
5412 * xmlRelaxNGDumpTree:
5413 * @output: the file output
5414 * @schema: a schema structure
5415 *
5416 * Dump the transformed RelaxNG tree.
5417 */
5418void
5419xmlRelaxNGDumpTree(FILE * output, xmlRelaxNGPtr schema)
5420{
5421 if (schema == NULL) {
5422 fprintf(output, "RelaxNG empty or failed to compile\n");
5423 return;
5424 }
5425 if (schema->doc == NULL) {
5426 fprintf(output, "no document\n");
5427 } else {
5428 xmlDocDump(output, schema->doc);
5429 }
5430}
5431
Daniel Veillard6eadf632003-01-23 18:29:16 +00005432/************************************************************************
5433 * *
5434 * Validation implementation *
5435 * *
5436 ************************************************************************/
5437static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
5438 xmlRelaxNGDefinePtr define);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005439static int xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
5440 xmlRelaxNGDefinePtr define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005441
5442/**
5443 * xmlRelaxNGSkipIgnored:
5444 * @ctxt: a schema validation context
5445 * @node: the top node.
5446 *
5447 * Skip ignorable nodes in that context
5448 *
5449 * Returns the new sibling or NULL in case of error.
5450 */
5451static xmlNodePtr
5452xmlRelaxNGSkipIgnored(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
5453 xmlNodePtr node) {
5454 /*
5455 * TODO complete and handle entities
5456 */
5457 while ((node != NULL) &&
5458 ((node->type == XML_COMMENT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005459 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00005460 ((node->type == XML_TEXT_NODE) &&
5461 (IS_BLANK_NODE(node))))) {
5462 node = node->next;
5463 }
5464 return(node);
5465}
5466
5467/**
Daniel Veillardedc91922003-01-26 00:52:04 +00005468 * xmlRelaxNGNormalize:
5469 * @ctxt: a schema validation context
5470 * @str: the string to normalize
5471 *
5472 * Implements the normalizeWhiteSpace( s ) function from
5473 * section 6.2.9 of the spec
5474 *
5475 * Returns the new string or NULL in case of error.
5476 */
5477static xmlChar *
5478xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *str) {
5479 xmlChar *ret, *p;
5480 const xmlChar *tmp;
5481 int len;
5482
5483 if (str == NULL)
5484 return(NULL);
5485 tmp = str;
5486 while (*tmp != 0) tmp++;
5487 len = tmp - str;
5488
5489 ret = (xmlChar *) xmlMalloc((len + 1) * sizeof(xmlChar));
5490 if (ret == NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00005491 if (ctxt != NULL) {
5492 VALID_CTXT();
5493 VALID_ERROR("xmlRelaxNGNormalize: out of memory\n");
5494 } else {
5495 xmlGenericError(xmlGenericErrorContext,
5496 "xmlRelaxNGNormalize: out of memory\n");
5497 }
Daniel Veillardedc91922003-01-26 00:52:04 +00005498 return(NULL);
5499 }
5500 p = ret;
5501 while (IS_BLANK(*str)) str++;
5502 while (*str != 0) {
5503 if (IS_BLANK(*str)) {
5504 while (IS_BLANK(*str)) str++;
5505 if (*str == 0)
5506 break;
5507 *p++ = ' ';
5508 } else
5509 *p++ = *str++;
5510 }
5511 *p = 0;
5512 return(ret);
5513}
5514
5515/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005516 * xmlRelaxNGValidateDatatype:
5517 * @ctxt: a Relax-NG validation context
5518 * @value: the string value
5519 * @type: the datatype definition
5520 *
5521 * Validate the given value against the dataype
5522 *
5523 * Returns 0 if the validation succeeded or an error code.
5524 */
5525static int
5526xmlRelaxNGValidateDatatype(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *value,
5527 xmlRelaxNGDefinePtr define) {
5528 int ret;
5529 xmlRelaxNGTypeLibraryPtr lib;
5530
5531 if ((define == NULL) || (define->data == NULL)) {
5532 return(-1);
5533 }
5534 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
5535 if (lib->check != NULL)
5536 ret = lib->check(lib->data, define->name, value);
5537 else
5538 ret = -1;
5539 if (ret < 0) {
5540 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005541 VALID_ERROR2("Internal: failed to validate type %s\n", define->name);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005542 return(-1);
5543 } else if (ret == 1) {
5544 ret = 0;
5545 } else {
5546 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005547 VALID_ERROR3("Type %s doesn't allow value %s\n", define->name, value);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005548 return(-1);
5549 ret = -1;
5550 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005551 if ((ret == 0) && (define->content != NULL)) {
5552 const xmlChar *oldvalue, *oldendvalue;
5553
5554 oldvalue = ctxt->state->value;
5555 oldendvalue = ctxt->state->endvalue;
5556 ctxt->state->value = (xmlChar *) value;
5557 ctxt->state->endvalue = NULL;
5558 ret = xmlRelaxNGValidateValue(ctxt, define->content);
5559 ctxt->state->value = (xmlChar *) oldvalue;
5560 ctxt->state->endvalue = (xmlChar *) oldendvalue;
5561 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005562 return(ret);
5563}
5564
5565/**
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005566 * xmlRelaxNGNextValue:
5567 * @ctxt: a Relax-NG validation context
5568 *
5569 * Skip to the next value when validating within a list
5570 *
5571 * Returns 0 if the operation succeeded or an error code.
5572 */
5573static int
5574xmlRelaxNGNextValue(xmlRelaxNGValidCtxtPtr ctxt) {
5575 xmlChar *cur;
5576
5577 cur = ctxt->state->value;
5578 if ((cur == NULL) || (ctxt->state->endvalue == NULL)) {
5579 ctxt->state->value = NULL;
Daniel Veillarde5b110b2003-02-04 14:43:39 +00005580 ctxt->state->endvalue = NULL;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005581 return(0);
5582 }
5583 while (*cur != 0) cur++;
5584 while ((cur != ctxt->state->endvalue) && (*cur == 0)) cur++;
5585 if (cur == ctxt->state->endvalue)
5586 ctxt->state->value = NULL;
5587 else
5588 ctxt->state->value = cur;
5589 return(0);
5590}
5591
5592/**
5593 * xmlRelaxNGValidateValueList:
5594 * @ctxt: a Relax-NG validation context
5595 * @defines: the list of definitions to verify
5596 *
5597 * Validate the given set of definitions for the current value
5598 *
5599 * Returns 0 if the validation succeeded or an error code.
5600 */
5601static int
5602xmlRelaxNGValidateValueList(xmlRelaxNGValidCtxtPtr ctxt,
5603 xmlRelaxNGDefinePtr defines) {
5604 int ret = 0;
5605
5606 while (defines != NULL) {
5607 ret = xmlRelaxNGValidateValue(ctxt, defines);
5608 if (ret != 0)
5609 break;
5610 defines = defines->next;
5611 }
5612 return(ret);
5613}
5614
5615/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00005616 * xmlRelaxNGValidateValue:
5617 * @ctxt: a Relax-NG validation context
5618 * @define: the definition to verify
5619 *
5620 * Validate the given definition for the current value
5621 *
5622 * Returns 0 if the validation succeeded or an error code.
5623 */
5624static int
5625xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
5626 xmlRelaxNGDefinePtr define) {
Daniel Veillardedc91922003-01-26 00:52:04 +00005627 int ret = 0, oldflags;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005628 xmlChar *value;
5629
5630 value = ctxt->state->value;
5631 switch (define->type) {
Daniel Veillardd4310742003-02-18 21:12:46 +00005632 case XML_RELAXNG_EMPTY: {
5633 if ((value != NULL) && (value[0] != 0)) {
5634 int idx = 0;
5635
5636 while (IS_BLANK(value[idx]))
5637 idx++;
5638 if (value[idx] != 0)
5639 ret = -1;
5640 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005641 break;
Daniel Veillardd4310742003-02-18 21:12:46 +00005642 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005643 case XML_RELAXNG_TEXT:
5644 break;
Daniel Veillardedc91922003-01-26 00:52:04 +00005645 case XML_RELAXNG_VALUE: {
5646 if (!xmlStrEqual(value, define->value)) {
5647 if (define->name != NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00005648 xmlRelaxNGTypeLibraryPtr lib;
5649
5650 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
5651 if ((lib != NULL) && (lib->comp != NULL))
5652 ret = lib->comp(lib->data, define->name, value,
5653 define->value);
5654 else
5655 ret = -1;
5656 if (ret < 0) {
5657 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005658 VALID_ERROR2("Internal: failed to compare type %s\n",
Daniel Veillardea3f3982003-01-26 19:45:18 +00005659 define->name);
5660 return(-1);
5661 } else if (ret == 1) {
5662 ret = 0;
5663 } else {
5664 ret = -1;
5665 }
Daniel Veillardedc91922003-01-26 00:52:04 +00005666 } else {
5667 xmlChar *nval, *nvalue;
5668
5669 /*
5670 * TODO: trivial optimizations are possible by
5671 * computing at compile-time
5672 */
5673 nval = xmlRelaxNGNormalize(ctxt, define->value);
5674 nvalue = xmlRelaxNGNormalize(ctxt, value);
5675
Daniel Veillardea3f3982003-01-26 19:45:18 +00005676 if ((nval == NULL) || (nvalue == NULL) ||
5677 (!xmlStrEqual(nval, nvalue)))
Daniel Veillardedc91922003-01-26 00:52:04 +00005678 ret = -1;
5679 if (nval != NULL)
5680 xmlFree(nval);
5681 if (nvalue != NULL)
5682 xmlFree(nvalue);
5683 }
5684 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005685 if (ret == 0)
5686 xmlRelaxNGNextValue(ctxt);
Daniel Veillardedc91922003-01-26 00:52:04 +00005687 break;
5688 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005689 case XML_RELAXNG_DATATYPE: {
5690 ret = xmlRelaxNGValidateDatatype(ctxt, value, define);
5691 if (ret == 0)
5692 xmlRelaxNGNextValue(ctxt);
5693
5694 break;
5695 }
5696 case XML_RELAXNG_CHOICE: {
5697 xmlRelaxNGDefinePtr list = define->content;
5698 xmlChar *oldvalue;
5699
5700 oldflags = ctxt->flags;
5701 ctxt->flags |= FLAGS_IGNORABLE;
5702
5703 oldvalue = ctxt->state->value;
5704 while (list != NULL) {
5705 ret = xmlRelaxNGValidateValue(ctxt, list);
5706 if (ret == 0) {
5707 break;
5708 }
5709 ctxt->state->value = oldvalue;
5710 list = list->next;
5711 }
5712 ctxt->flags = oldflags;
Daniel Veillard416589a2003-02-17 17:25:42 +00005713 if (ret == 0)
5714 xmlRelaxNGNextValue(ctxt);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005715 break;
5716 }
5717 case XML_RELAXNG_LIST: {
5718 xmlRelaxNGDefinePtr list = define->content;
5719 xmlChar *oldvalue, *oldend, *val, *cur;
Daniel Veillard416589a2003-02-17 17:25:42 +00005720#ifdef DEBUG_LIST
5721 int nb_values = 0;
5722#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005723
5724 oldvalue = ctxt->state->value;
5725 oldend = ctxt->state->endvalue;
5726
5727 val = xmlStrdup(oldvalue);
5728 if (val == NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00005729 val = xmlStrdup(BAD_CAST "");
5730 }
5731 if (val == NULL) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005732 VALID_CTXT();
5733 VALID_ERROR("Internal: no state\n");
5734 return(-1);
5735 }
5736 cur = val;
5737 while (*cur != 0) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005738 if (IS_BLANK(*cur)) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005739 *cur = 0;
Daniel Veillard416589a2003-02-17 17:25:42 +00005740 cur++;
5741#ifdef DEBUG_LIST
5742 nb_values++;
5743#endif
5744 while (IS_BLANK(*cur))
5745 *cur++ = 0;
5746 } else
5747 cur++;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005748 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005749#ifdef DEBUG_LIST
5750 xmlGenericError(xmlGenericErrorContext,
5751 "list value: '%s' found %d items\n", oldvalue, nb_values);
5752 nb_values = 0;
5753#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005754 ctxt->state->endvalue = cur;
5755 cur = val;
5756 while ((*cur == 0) && (cur != ctxt->state->endvalue)) cur++;
5757
5758 ctxt->state->value = cur;
5759
5760 while (list != NULL) {
Daniel Veillard2e9b1652003-02-19 13:29:45 +00005761 if (ctxt->state->value == ctxt->state->endvalue)
5762 ctxt->state->value = NULL;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005763 ret = xmlRelaxNGValidateValue(ctxt, list);
5764 if (ret != 0) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005765#ifdef DEBUG_LIST
5766 xmlGenericError(xmlGenericErrorContext,
5767 "Failed to validate value: '%s' with %d rule\n",
5768 ctxt->state->value, nb_values);
5769#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005770 break;
5771 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005772#ifdef DEBUG_LIST
5773 nb_values++;
5774#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005775 list = list->next;
5776 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00005777
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005778 if ((ret == 0) && (ctxt->state->value != NULL) &&
5779 (ctxt->state->value != ctxt->state->endvalue)) {
5780 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005781 VALID_ERROR2("Extra data in list: %s\n", ctxt->state->value);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005782 ret = -1;
5783 }
5784 xmlFree(val);
5785 ctxt->state->value = oldvalue;
5786 ctxt->state->endvalue = oldend;
5787 break;
5788 }
5789 case XML_RELAXNG_ONEORMORE:
5790 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
5791 if (ret != 0) {
5792 break;
5793 }
5794 /* no break on purpose */
5795 case XML_RELAXNG_ZEROORMORE: {
5796 xmlChar *cur, *temp;
5797
5798 oldflags = ctxt->flags;
5799 ctxt->flags |= FLAGS_IGNORABLE;
5800 cur = ctxt->state->value;
5801 temp = NULL;
5802 while ((cur != NULL) && (cur != ctxt->state->endvalue) &&
5803 (temp != cur)) {
5804 temp = cur;
5805 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
5806 if (ret != 0) {
5807 ctxt->state->value = temp;
5808 ret = 0;
5809 break;
5810 }
5811 cur = ctxt->state->value;
5812 }
5813 ctxt->flags = oldflags;
5814 break;
5815 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005816 case XML_RELAXNG_EXCEPT: {
5817 xmlRelaxNGDefinePtr list;
5818
5819 list = define->content;
5820 while (list != NULL) {
5821 ret = xmlRelaxNGValidateValue(ctxt, list);
5822 if (ret == 0) {
5823 ret = -1;
5824 break;
5825 } else
5826 ret = 0;
5827 list = list->next;
5828 }
5829 break;
5830 }
Daniel Veillardd4310742003-02-18 21:12:46 +00005831 case XML_RELAXNG_GROUP: {
5832 xmlRelaxNGDefinePtr list;
5833
5834 list = define->content;
5835 while (list != NULL) {
5836 ret = xmlRelaxNGValidateValue(ctxt, list);
5837 if (ret != 0) {
5838 ret = -1;
5839 break;
5840 } else
5841 ret = 0;
5842 list = list->next;
5843 }
5844 break;
5845 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005846 default:
5847 TODO
5848 ret = -1;
5849 }
5850 return(ret);
5851}
5852
5853/**
5854 * xmlRelaxNGValidateValueContent:
5855 * @ctxt: a Relax-NG validation context
5856 * @defines: the list of definitions to verify
5857 *
5858 * Validate the given definitions for the current value
5859 *
5860 * Returns 0 if the validation succeeded or an error code.
5861 */
5862static int
5863xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt,
5864 xmlRelaxNGDefinePtr defines) {
5865 int ret = 0;
5866
5867 while (defines != NULL) {
5868 ret = xmlRelaxNGValidateValue(ctxt, defines);
5869 if (ret != 0)
5870 break;
5871 defines = defines->next;
5872 }
5873 return(ret);
5874}
5875
5876/**
Daniel Veillard144fae12003-02-03 13:17:57 +00005877 * xmlRelaxNGAttributeMatch:
5878 * @ctxt: a Relax-NG validation context
5879 * @define: the definition to check
5880 * @prop: the attribute
5881 *
5882 * Check if the attribute matches the definition nameClass
5883 *
5884 * Returns 1 if the attribute matches, 0 if no, or -1 in case of error
5885 */
5886static int
5887xmlRelaxNGAttributeMatch(xmlRelaxNGValidCtxtPtr ctxt,
5888 xmlRelaxNGDefinePtr define,
5889 xmlAttrPtr prop) {
5890 int ret;
5891
5892 if (define->name != NULL) {
5893 if (!xmlStrEqual(define->name, prop->name))
5894 return(0);
5895 }
5896 if (define->ns != NULL) {
5897 if (define->ns[0] == 0) {
5898 if (prop->ns != NULL)
5899 return(0);
5900 } else {
5901 if ((prop->ns == NULL) ||
5902 (!xmlStrEqual(define->ns, prop->ns->href)))
5903 return(0);
5904 }
5905 }
5906 if (define->nameClass == NULL)
5907 return(1);
5908 define = define->nameClass;
5909 if (define->type == XML_RELAXNG_EXCEPT) {
5910 xmlRelaxNGDefinePtr list;
5911
5912 list = define->content;
5913 while (list != NULL) {
5914 ret = xmlRelaxNGAttributeMatch(ctxt, list, prop);
5915 if (ret == 1)
5916 return(0);
5917 if (ret < 0)
5918 return(ret);
5919 list = list->next;
5920 }
5921 } else {
5922 TODO
5923 }
5924 return(1);
5925}
5926
5927/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00005928 * xmlRelaxNGValidateAttribute:
5929 * @ctxt: a Relax-NG validation context
5930 * @define: the definition to verify
5931 *
5932 * Validate the given attribute definition for that node
5933 *
5934 * Returns 0 if the validation succeeded or an error code.
5935 */
5936static int
5937xmlRelaxNGValidateAttribute(xmlRelaxNGValidCtxtPtr ctxt,
5938 xmlRelaxNGDefinePtr define) {
5939 int ret = 0, i;
5940 xmlChar *value, *oldvalue;
5941 xmlAttrPtr prop = NULL, tmp;
5942
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005943 if (ctxt->state->nbAttrLeft <= 0)
5944 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005945 if (define->name != NULL) {
5946 for (i = 0;i < ctxt->state->nbAttrs;i++) {
5947 tmp = ctxt->state->attrs[i];
5948 if ((tmp != NULL) && (xmlStrEqual(define->name, tmp->name))) {
5949 if ((((define->ns == NULL) || (define->ns[0] == 0)) &&
5950 (tmp->ns == NULL)) ||
5951 ((tmp->ns != NULL) &&
5952 (xmlStrEqual(define->ns, tmp->ns->href)))) {
5953 prop = tmp;
5954 break;
5955 }
5956 }
5957 }
5958 if (prop != NULL) {
5959 value = xmlNodeListGetString(prop->doc, prop->children, 1);
5960 oldvalue = ctxt->state->value;
5961 ctxt->state->value = value;
Daniel Veillard231d7912003-02-09 14:22:17 +00005962 ctxt->state->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005963 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00005964 if (ctxt->state->value != NULL)
5965 value = ctxt->state->value;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005966 if (value != NULL)
5967 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00005968 ctxt->state->value = oldvalue;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005969 if (ret == 0) {
5970 /*
5971 * flag the attribute as processed
5972 */
5973 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005974 ctxt->state->nbAttrLeft--;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005975 }
5976 } else {
5977 ret = -1;
5978 }
5979#ifdef DEBUG
5980 xmlGenericError(xmlGenericErrorContext,
5981 "xmlRelaxNGValidateAttribute(%s): %d\n", define->name, ret);
5982#endif
5983 } else {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005984 for (i = 0;i < ctxt->state->nbAttrs;i++) {
5985 tmp = ctxt->state->attrs[i];
Daniel Veillard144fae12003-02-03 13:17:57 +00005986 if ((tmp != NULL) &&
5987 (xmlRelaxNGAttributeMatch(ctxt, define, tmp) == 1)) {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005988 prop = tmp;
5989 break;
5990 }
5991 }
5992 if (prop != NULL) {
5993 value = xmlNodeListGetString(prop->doc, prop->children, 1);
5994 oldvalue = ctxt->state->value;
5995 ctxt->state->value = value;
5996 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00005997 if (ctxt->state->value != NULL)
5998 value = ctxt->state->value;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005999 if (value != NULL)
6000 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00006001 ctxt->state->value = oldvalue;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006002 if (ret == 0) {
6003 /*
6004 * flag the attribute as processed
6005 */
6006 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006007 ctxt->state->nbAttrLeft--;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006008 }
6009 } else {
6010 ret = -1;
6011 }
6012#ifdef DEBUG
Daniel Veillard144fae12003-02-03 13:17:57 +00006013 if (define->ns != NULL) {
6014 xmlGenericError(xmlGenericErrorContext,
6015 "xmlRelaxNGValidateAttribute(nsName ns = %s): %d\n",
6016 define->ns, ret);
6017 } else {
6018 xmlGenericError(xmlGenericErrorContext,
6019 "xmlRelaxNGValidateAttribute(anyName): %d\n",
6020 ret);
6021 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006022#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00006023 }
6024
6025 return(ret);
6026}
6027
6028/**
6029 * xmlRelaxNGValidateAttributeList:
6030 * @ctxt: a Relax-NG validation context
6031 * @define: the list of definition to verify
6032 *
6033 * Validate the given node against the list of attribute definitions
6034 *
6035 * Returns 0 if the validation succeeded or an error code.
6036 */
6037static int
6038xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt,
6039 xmlRelaxNGDefinePtr defines) {
6040 int ret = 0;
6041 while (defines != NULL) {
6042 if (xmlRelaxNGValidateAttribute(ctxt, defines) != 0)
6043 ret = -1;
6044 defines = defines->next;
6045 }
6046 return(ret);
6047}
6048
6049/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006050 * xmlRelaxNGValidateTryPermutation:
6051 * @ctxt: a Relax-NG validation context
6052 * @groups: the array of groups
6053 * @nbgroups: the number of groups in the array
6054 * @array: the permutation to try
6055 * @len: the size of the set
6056 *
6057 * Try to validate a permutation for the group of definitions.
6058 *
6059 * Returns 0 if the validation succeeded or an error code.
6060 */
6061static int
6062xmlRelaxNGValidateTryPermutation(xmlRelaxNGValidCtxtPtr ctxt,
6063 xmlRelaxNGDefinePtr rule,
6064 xmlNodePtr *array, int len) {
6065 int i, ret;
6066
6067 if (len > 0) {
6068 /*
6069 * One only need the next pointer set-up to do the validation
6070 */
6071 for (i = 0;i < (len - 1);i++)
6072 array[i]->next = array[i + 1];
6073 array[i]->next = NULL;
6074
6075 /*
6076 * Now try to validate the sequence
6077 */
6078 ctxt->state->seq = array[0];
6079 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
6080 } else {
6081 ctxt->state->seq = NULL;
6082 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
6083 }
6084
6085 /*
6086 * the sequence must be fully consumed
6087 */
6088 if (ctxt->state->seq != NULL)
6089 return(-1);
6090
6091 return(ret);
6092}
6093
6094/**
6095 * xmlRelaxNGValidateWalkPermutations:
6096 * @ctxt: a Relax-NG validation context
6097 * @groups: the array of groups
6098 * @nbgroups: the number of groups in the array
6099 * @nodes: the set of nodes
6100 * @array: the current state of the parmutation
6101 * @len: the size of the set
6102 * @level: a pointer to the level variable
6103 * @k: the index in the array to fill
6104 *
6105 * Validate a set of nodes for a groups of definitions, will try the
6106 * full set of permutations
6107 *
6108 * Returns 0 if the validation succeeded or an error code.
6109 */
6110static int
6111xmlRelaxNGValidateWalkPermutations(xmlRelaxNGValidCtxtPtr ctxt,
6112 xmlRelaxNGDefinePtr rule, xmlNodePtr *nodes,
6113 xmlNodePtr *array, int len,
6114 int *level, int k) {
6115 int i, ret;
6116
6117 if ((k >= 0) && (k < len))
6118 array[k] = nodes[*level];
6119 *level = *level + 1;
6120 if (*level == len) {
6121 ret = xmlRelaxNGValidateTryPermutation(ctxt, rule, array, len);
6122 if (ret == 0)
6123 return(0);
6124 } else {
6125 for (i = 0;i < len;i++) {
6126 if (array[i] == NULL) {
6127 ret = xmlRelaxNGValidateWalkPermutations(ctxt, rule,
6128 nodes, array, len, level, i);
6129 if (ret == 0)
6130 return(0);
6131 }
6132 }
6133 }
6134 *level = *level - 1;
6135 array[k] = NULL;
6136 return(-1);
6137}
6138
6139/**
6140 * xmlRelaxNGNodeMatchesList:
6141 * @node: the node
6142 * @list: a NULL terminated array of definitions
6143 *
6144 * Check if a node can be matched by one of the definitions
6145 *
6146 * Returns 1 if matches 0 otherwise
6147 */
6148static int
6149xmlRelaxNGNodeMatchesList(xmlNodePtr node, xmlRelaxNGDefinePtr *list) {
6150 xmlRelaxNGDefinePtr cur;
6151 int i = 0;
6152
6153 if ((node == NULL) || (list == NULL))
6154 return(0);
6155
6156 cur = list[i++];
6157 while (cur != NULL) {
6158 if ((node->type == XML_ELEMENT_NODE) &&
6159 (cur->type == XML_RELAXNG_ELEMENT)) {
6160 if (cur->name == NULL) {
6161 if ((node->ns != NULL) &&
6162 (xmlStrEqual(node->ns->href, cur->ns)))
6163 return(1);
6164 } else if (xmlStrEqual(cur->name, node->name)) {
6165 if ((cur->ns == NULL) || (cur->ns[0] == 0)) {
6166 if (node->ns == NULL)
6167 return(1);
6168 } else {
6169 if ((node->ns != NULL) &&
6170 (xmlStrEqual(node->ns->href, cur->ns)))
6171 return(1);
6172 }
6173 }
6174 } else if ((node->type == XML_TEXT_NODE) &&
6175 (cur->type == XML_RELAXNG_TEXT)) {
6176 return(1);
6177 }
6178 cur = list[i++];
6179 }
6180 return(0);
6181}
6182
6183/**
6184 * xmlRelaxNGValidatePartGroup:
6185 * @ctxt: a Relax-NG validation context
6186 * @groups: the array of groups
6187 * @nbgroups: the number of groups in the array
6188 * @nodes: the set of nodes
6189 * @len: the size of the set of nodes
6190 *
6191 * Validate a set of nodes for a groups of definitions
6192 *
6193 * Returns 0 if the validation succeeded or an error code.
6194 */
6195static int
6196xmlRelaxNGValidatePartGroup(xmlRelaxNGValidCtxtPtr ctxt,
6197 xmlRelaxNGInterleaveGroupPtr *groups,
6198 int nbgroups, xmlNodePtr *nodes, int len) {
Daniel Veillard231d7912003-02-09 14:22:17 +00006199 int level, ret = -1, i, j, k, top_j, max_j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006200 xmlNodePtr *array = NULL, *list, oldseq;
6201 xmlRelaxNGInterleaveGroupPtr group;
6202
6203 list = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
6204 if (list == NULL) {
6205 return(-1);
6206 }
6207 array = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
6208 if (array == NULL) {
6209 xmlFree(list);
6210 return(-1);
6211 }
6212 memset(array, 0, len * sizeof(xmlNodePtr));
6213
6214 /*
6215 * Partition the elements and validate the subsets.
6216 */
6217 oldseq = ctxt->state->seq;
Daniel Veillard231d7912003-02-09 14:22:17 +00006218 max_j = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006219 for (i = 0;i < nbgroups;i++) {
6220 group = groups[i];
6221 if (group == NULL)
6222 continue;
6223 k = 0;
Daniel Veillard231d7912003-02-09 14:22:17 +00006224 top_j = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006225 for (j = 0;j < len;j++) {
6226 if (nodes[j] == NULL)
6227 continue;
6228 if (xmlRelaxNGNodeMatchesList(nodes[j], group->defs)) {
6229 list[k++] = nodes[j];
6230 nodes[j] = NULL;
Daniel Veillard231d7912003-02-09 14:22:17 +00006231 top_j = j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006232 }
6233 }
Daniel Veillard231d7912003-02-09 14:22:17 +00006234 if (top_j > max_j)
6235 max_j = top_j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006236 ctxt->state->seq = oldseq;
6237 if (k > 1) {
6238 memset(array, 0, k * sizeof(xmlNodePtr));
Daniel Veillardb08c9812003-01-28 23:09:49 +00006239 level = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006240 ret = xmlRelaxNGValidateWalkPermutations(ctxt, group->rule,
6241 list, array, k, &level, -1);
6242 } else {
6243 ret = xmlRelaxNGValidateTryPermutation(ctxt, group->rule, list, k);
6244 }
6245 if (ret != 0) {
6246 ctxt->state->seq = oldseq;
6247 break;
6248 }
6249 }
6250
Daniel Veillard231d7912003-02-09 14:22:17 +00006251 for (j = 0;j < max_j;j++) {
6252 if (nodes[j] != NULL) {
6253 TODO /* problem, one of the nodes didn't got a match */
6254 }
6255 }
6256 if (ret == 0) {
6257 if (max_j + 1 < len)
6258 ctxt->state->seq = nodes[max_j + 1];
6259 else
6260 ctxt->state->seq = NULL;
6261 }
6262
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006263 xmlFree(list);
6264 xmlFree(array);
6265 return(ret);
6266}
6267
6268/**
6269 * xmlRelaxNGValidateInterleave:
6270 * @ctxt: a Relax-NG validation context
6271 * @define: the definition to verify
6272 *
6273 * Validate an interleave definition for a node.
6274 *
6275 * Returns 0 if the validation succeeded or an error code.
6276 */
6277static int
6278xmlRelaxNGValidateInterleave(xmlRelaxNGValidCtxtPtr ctxt,
6279 xmlRelaxNGDefinePtr define) {
6280 int ret = 0, nbchildren, nbtot, i, j;
6281 xmlRelaxNGPartitionPtr partitions;
6282 xmlNodePtr *children = NULL;
6283 xmlNodePtr *order = NULL;
Daniel Veillard231d7912003-02-09 14:22:17 +00006284 xmlNodePtr cur, oldseq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006285
6286 if (define->data != NULL) {
6287 partitions = (xmlRelaxNGPartitionPtr) define->data;
6288 } else {
6289 VALID_CTXT();
6290 VALID_ERROR("Internal: interleave block has no data\n");
6291 return(-1);
6292 }
6293
6294 /*
6295 * Build the sequence of child and an array preserving the children
6296 * initial order.
6297 */
6298 cur = ctxt->state->seq;
Daniel Veillard231d7912003-02-09 14:22:17 +00006299 oldseq = ctxt->state->seq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006300 nbchildren = 0;
6301 nbtot = 0;
6302 while (cur != NULL) {
6303 if ((cur->type == XML_COMMENT_NODE) ||
6304 (cur->type == XML_PI_NODE) ||
6305 ((cur->type == XML_TEXT_NODE) &&
6306 (IS_BLANK_NODE(cur)))) {
6307 nbtot++;
6308 } else {
6309 nbchildren++;
6310 nbtot++;
6311 }
6312 cur = cur->next;
6313 }
6314 children = (xmlNodePtr *) xmlMalloc(nbchildren * sizeof(xmlNodePtr));
6315 if (children == NULL)
6316 goto error;
6317 order = (xmlNodePtr *) xmlMalloc(nbtot * sizeof(xmlNodePtr));
6318 if (order == NULL)
6319 goto error;
6320 cur = ctxt->state->seq;
6321 i = 0;
6322 j = 0;
6323 while (cur != NULL) {
6324 if ((cur->type == XML_COMMENT_NODE) ||
6325 (cur->type == XML_PI_NODE) ||
6326 ((cur->type == XML_TEXT_NODE) &&
6327 (IS_BLANK_NODE(cur)))) {
6328 order[j++] = cur;
6329 } else {
6330 order[j++] = cur;
6331 children[i++] = cur;
6332 }
6333 cur = cur->next;
6334 }
6335
6336 /* TODO: retry with a maller set of child if there is a next... */
6337 ret = xmlRelaxNGValidatePartGroup(ctxt, partitions->groups,
6338 partitions->nbgroups, children, nbchildren);
Daniel Veillard231d7912003-02-09 14:22:17 +00006339 if (ret != 0)
6340 ctxt->state->seq = oldseq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006341
6342 /*
6343 * Cleanup: rebuid the child sequence and free the structure
6344 */
6345 if (order != NULL) {
6346 for (i = 0;i < nbtot;i++) {
6347 if (i == 0)
6348 order[i]->prev = NULL;
6349 else
6350 order[i]->prev = order[i - 1];
6351 if (i == nbtot - 1)
6352 order[i]->next = NULL;
6353 else
6354 order[i]->next = order[i + 1];
6355 }
6356 xmlFree(order);
6357 }
6358 if (children != NULL)
6359 xmlFree(children);
6360
6361 return(ret);
6362
6363error:
6364 if (order != NULL) {
6365 for (i = 0;i < nbtot;i++) {
6366 if (i == 0)
6367 order[i]->prev = NULL;
6368 else
6369 order[i]->prev = order[i - 1];
6370 if (i == nbtot - 1)
6371 order[i]->next = NULL;
6372 else
6373 order[i]->next = order[i + 1];
6374 }
6375 xmlFree(order);
6376 }
6377 if (children != NULL)
6378 xmlFree(children);
6379 return(-1);
6380}
6381
6382/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00006383 * xmlRelaxNGValidateElementContent:
6384 * @ctxt: a Relax-NG validation context
6385 * @define: the list of definition to verify
6386 *
6387 * Validate the given node content against the (list) of definitions
6388 *
6389 * Returns 0 if the validation succeeded or an error code.
6390 */
6391static int
6392xmlRelaxNGValidateElementContent(xmlRelaxNGValidCtxtPtr ctxt,
6393 xmlRelaxNGDefinePtr defines) {
6394 int ret = 0, res;
6395
6396 if (ctxt->state == NULL) {
6397 VALID_CTXT();
6398 VALID_ERROR("Internal: no state\n");
6399 return(-1);
6400 }
6401 while (defines != NULL) {
6402 res = xmlRelaxNGValidateDefinition(ctxt, defines);
6403 if (res < 0)
6404 ret = -1;
6405 defines = defines->next;
6406 }
6407
6408 return(ret);
6409}
6410
6411/**
Daniel Veillard416589a2003-02-17 17:25:42 +00006412 * xmlRelaxNGElementMatch:
6413 * @ctxt: a Relax-NG validation context
6414 * @define: the definition to check
6415 * @elem: the element
6416 *
6417 * Check if the element matches the definition nameClass
6418 *
6419 * Returns 1 if the element matches, 0 if no, or -1 in case of error
6420 */
6421static int
6422xmlRelaxNGElementMatch(xmlRelaxNGValidCtxtPtr ctxt,
6423 xmlRelaxNGDefinePtr define,
6424 xmlNodePtr elem) {
6425 int ret, oldflags;
6426
6427 if (define->name != NULL) {
6428 if (!xmlStrEqual(elem->name, define->name)) {
6429 VALID_CTXT();
6430 VALID_ERROR3("Expecting element %s, got %s\n",
6431 define->name, elem->name);
6432 return(0);
6433 }
6434 }
6435 if ((define->ns != NULL) && (define->ns[0] != 0)) {
6436 if (elem->ns == NULL) {
6437 VALID_CTXT();
6438 VALID_ERROR2("Expecting a namespace for element %s\n",
6439 elem->name);
6440 return(0);
6441 } else if (!xmlStrEqual(elem->ns->href, define->ns)) {
6442 VALID_CTXT();
6443 VALID_ERROR3("Expecting element %s has wrong namespace: expecting %s\n",
6444 elem->name, define->ns);
6445 return(0);
6446 }
Daniel Veillard8fe98712003-02-19 00:19:14 +00006447 } else if ((elem->ns != NULL) && (define->ns != NULL) &&
6448 (define->name == NULL)) {
6449 VALID_CTXT();
6450 VALID_ERROR2("Expecting no namespace for element %s\n",
6451 define->name);
6452 return(0);
6453 } else if ((elem->ns != NULL) && (define->name != NULL)) {
6454 VALID_CTXT();
6455 VALID_ERROR2("Expecting no namespace for element %s\n",
6456 define->name);
6457 return(0);
Daniel Veillard416589a2003-02-17 17:25:42 +00006458 }
6459
6460 if (define->nameClass == NULL)
6461 return(1);
6462
6463 define = define->nameClass;
6464 if (define->type == XML_RELAXNG_EXCEPT) {
6465 xmlRelaxNGDefinePtr list;
6466 oldflags = ctxt->flags;
6467 ctxt->flags |= FLAGS_IGNORABLE;
6468
6469 list = define->content;
6470 while (list != NULL) {
6471 ret = xmlRelaxNGElementMatch(ctxt, list, elem);
6472 if (ret == 1) {
6473 ctxt->flags = oldflags;
6474 return(0);
6475 }
6476 if (ret < 0) {
6477 ctxt->flags = oldflags;
6478 return(ret);
6479 }
6480 list = list->next;
6481 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006482 ret = 1;
6483 ctxt->flags = oldflags;
6484 } else if (define->type == XML_RELAXNG_CHOICE) {
6485 xmlRelaxNGDefinePtr list;
6486 oldflags = ctxt->flags;
6487 ctxt->flags |= FLAGS_IGNORABLE;
6488
6489 list = define->nameClass;
6490 while (list != NULL) {
6491 ret = xmlRelaxNGElementMatch(ctxt, list, elem);
6492 if (ret == 1) {
6493 ctxt->flags = oldflags;
6494 return(1);
6495 }
6496 if (ret < 0) {
6497 ctxt->flags = oldflags;
6498 return(ret);
6499 }
6500 list = list->next;
6501 }
6502 ret = 0;
Daniel Veillard416589a2003-02-17 17:25:42 +00006503 ctxt->flags = oldflags;
6504 } else {
6505 TODO
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006506 ret = -1;
Daniel Veillard416589a2003-02-17 17:25:42 +00006507 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006508 return(ret);
Daniel Veillard416589a2003-02-17 17:25:42 +00006509}
6510
6511/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00006512 * xmlRelaxNGValidateDefinition:
6513 * @ctxt: a Relax-NG validation context
6514 * @define: the definition to verify
6515 *
6516 * Validate the current node against the definition
6517 *
6518 * Returns 0 if the validation succeeded or an error code.
6519 */
6520static int
6521xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
6522 xmlRelaxNGDefinePtr define) {
6523 xmlNodePtr node;
6524 int ret = 0, i, tmp, oldflags;
6525 xmlRelaxNGValidStatePtr oldstate, state;
6526
6527 if (define == NULL) {
6528 VALID_CTXT();
6529 VALID_ERROR("internal error: define == NULL\n");
6530 return(-1);
6531 }
6532 if (ctxt->state != NULL) {
6533 node = ctxt->state->seq;
6534 } else {
6535 node = NULL;
6536 }
Daniel Veillard231d7912003-02-09 14:22:17 +00006537#ifdef DEBUG
6538 for (i = 0;i < ctxt->depth;i++)
6539 xmlGenericError(xmlGenericErrorContext, " ");
6540 xmlGenericError(xmlGenericErrorContext,
6541 "Start validating %s ", xmlRelaxNGDefName(define));
6542 if (define->name != NULL)
6543 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
6544 if ((node != NULL) && (node->name != NULL))
6545 xmlGenericError(xmlGenericErrorContext, "on %s\n", node->name);
6546 else
6547 xmlGenericError(xmlGenericErrorContext, "\n");
6548#endif
6549 ctxt->depth++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006550 switch (define->type) {
6551 case XML_RELAXNG_EMPTY:
Daniel Veillardd4310742003-02-18 21:12:46 +00006552 node = xmlRelaxNGSkipIgnored(ctxt, node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006553 if (node != NULL) {
6554 VALID_CTXT();
6555 VALID_ERROR("Expecting an empty element\n");
Daniel Veillard231d7912003-02-09 14:22:17 +00006556 ret = -1;
6557 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006558 }
Daniel Veillard231d7912003-02-09 14:22:17 +00006559 ret = 0;
6560 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006561 case XML_RELAXNG_NOT_ALLOWED:
Daniel Veillard231d7912003-02-09 14:22:17 +00006562 ret = -1;
6563 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006564 case XML_RELAXNG_TEXT:
Daniel Veillardce14fa52003-02-19 17:32:48 +00006565#if 0
Daniel Veillard231d7912003-02-09 14:22:17 +00006566 if (node == NULL) {
6567 ret = 0;
6568 break;
6569 }
Daniel Veillardce14fa52003-02-19 17:32:48 +00006570#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00006571 while ((node != NULL) &&
6572 ((node->type == XML_TEXT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006573 (node->type == XML_COMMENT_NODE) ||
6574 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00006575 (node->type == XML_CDATA_SECTION_NODE)))
6576 node = node->next;
Daniel Veillardce14fa52003-02-19 17:32:48 +00006577#if 0
Daniel Veillard276be4a2003-01-24 01:03:34 +00006578 if (node == ctxt->state->seq) {
6579 VALID_CTXT();
6580 VALID_ERROR("Expecting text content\n");
6581 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006582 }
Daniel Veillardce14fa52003-02-19 17:32:48 +00006583#endif
Daniel Veillard276be4a2003-01-24 01:03:34 +00006584 ctxt->state->seq = node;
6585 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006586 case XML_RELAXNG_ELEMENT:
6587 node = xmlRelaxNGSkipIgnored(ctxt, node);
Daniel Veillard231d7912003-02-09 14:22:17 +00006588 if (node == NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006589 VALID_CTXT();
Daniel Veillard231d7912003-02-09 14:22:17 +00006590 VALID_ERROR("Expecting an element, got empty\n");
6591 ret = -1;
6592 break;
6593 }
6594 if (node->type != XML_ELEMENT_NODE) {
6595 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006596 VALID_ERROR2("Expecting an element got %d type\n", node->type);
Daniel Veillard231d7912003-02-09 14:22:17 +00006597 ret = -1;
6598 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006599 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00006600 /*
6601 * This node was already validated successfully against
6602 * this definition.
6603 */
6604 if (node->_private == define)
6605 break;
Daniel Veillard416589a2003-02-17 17:25:42 +00006606
6607 ret = xmlRelaxNGElementMatch(ctxt, define, node);
6608 if (ret <= 0) {
6609 ret = -1;
6610 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006611 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006612 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006613
6614 state = xmlRelaxNGNewValidState(ctxt, node);
6615 if (state == NULL) {
Daniel Veillard231d7912003-02-09 14:22:17 +00006616 ret = -1;
6617 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006618 }
6619
6620 oldstate = ctxt->state;
6621 ctxt->state = state;
6622 if (define->attrs != NULL) {
6623 tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
Daniel Veillard231d7912003-02-09 14:22:17 +00006624 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006625 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00006626#ifdef DEBUG
6627 xmlGenericError(xmlGenericErrorContext,
6628 "E: Element %s failed to validate attributes\n",
6629 node->name);
6630#endif
6631 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006632 }
6633 if (define->content != NULL) {
6634 tmp = xmlRelaxNGValidateElementContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00006635 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006636 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00006637#ifdef DEBUG
6638 xmlGenericError(xmlGenericErrorContext,
6639 "E: Element %s failed to validate element content\n",
6640 node->name);
6641#endif
6642 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006643 }
6644 state = ctxt->state;
6645 if (state->seq != NULL) {
6646 state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq);
6647 if (state->seq != NULL) {
6648 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006649 VALID_ERROR3("Extra content for element %s: %s\n",
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006650 node->name, state->seq->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006651 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00006652#ifdef DEBUG
6653 xmlGenericError(xmlGenericErrorContext,
6654 "E: Element %s has extra content: %s\n",
6655 node->name, state->seq->name);
6656#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00006657 }
6658 }
6659 for (i = 0;i < state->nbAttrs;i++) {
6660 if (state->attrs[i] != NULL) {
6661 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006662 VALID_ERROR3("Invalid attribute %s for element %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00006663 state->attrs[i]->name, node->name);
6664 ret = -1;
6665 }
6666 }
6667 ctxt->state = oldstate;
6668 xmlRelaxNGFreeValidState(state);
6669 if (oldstate != NULL)
Daniel Veillarde5b110b2003-02-04 14:43:39 +00006670 oldstate->seq = xmlRelaxNGSkipIgnored(ctxt, node->next);
6671 if (ret == 0)
6672 node->_private = define;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006673
6674
6675#ifdef DEBUG
6676 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard231d7912003-02-09 14:22:17 +00006677 "xmlRelaxNGValidateDefinition(): validated %s : %d",
Daniel Veillard6eadf632003-01-23 18:29:16 +00006678 node->name, ret);
Daniel Veillard231d7912003-02-09 14:22:17 +00006679 if (oldstate == NULL)
6680 xmlGenericError(xmlGenericErrorContext, ": no state\n");
6681 else if (oldstate->seq == NULL)
6682 xmlGenericError(xmlGenericErrorContext, ": done\n");
6683 else if (oldstate->seq->type == XML_ELEMENT_NODE)
6684 xmlGenericError(xmlGenericErrorContext, ": next elem %s\n",
6685 oldstate->seq->name);
6686 else
6687 xmlGenericError(xmlGenericErrorContext, ": next %s %d\n",
6688 oldstate->seq->name, oldstate->seq->type);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006689#endif
6690 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006691 case XML_RELAXNG_OPTIONAL:
6692 oldflags = ctxt->flags;
6693 ctxt->flags |= FLAGS_IGNORABLE;
6694 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6695 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6696 if (ret != 0) {
6697 xmlRelaxNGFreeValidState(ctxt->state);
6698 ctxt->state = oldstate;
6699 ret = 0;
6700 break;
6701 }
6702 xmlRelaxNGFreeValidState(oldstate);
6703 ctxt->flags = oldflags;
6704 ret = 0;
6705 break;
6706 case XML_RELAXNG_ONEORMORE:
6707 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6708 if (ret != 0) {
6709 break;
6710 }
6711 /* no break on purpose */
Daniel Veillard276be4a2003-01-24 01:03:34 +00006712 case XML_RELAXNG_ZEROORMORE: {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006713 oldflags = ctxt->flags;
6714 ctxt->flags |= FLAGS_IGNORABLE;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006715 while (ctxt->state->nbAttrLeft != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006716 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6717 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6718 if (ret != 0) {
6719 xmlRelaxNGFreeValidState(ctxt->state);
6720 ctxt->state = oldstate;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006721 break;
6722 }
6723 xmlRelaxNGFreeValidState(oldstate);
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006724 }
6725 if (ret == 0) {
6726 /*
6727 * There is no attribute left to be consumed,
6728 * we can check the closure by looking at ctxt->state->seq
6729 */
6730 xmlNodePtr cur, temp;
6731
Daniel Veillard276be4a2003-01-24 01:03:34 +00006732 cur = ctxt->state->seq;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006733 temp = NULL;
6734 while ((cur != NULL) && (temp != cur)) {
6735 temp = cur;
6736 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6737 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6738 if (ret != 0) {
6739 xmlRelaxNGFreeValidState(ctxt->state);
6740 ctxt->state = oldstate;
6741 break;
6742 }
6743 xmlRelaxNGFreeValidState(oldstate);
6744 cur = ctxt->state->seq;
6745 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006746 }
6747 ctxt->flags = oldflags;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006748 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006749 break;
Daniel Veillard276be4a2003-01-24 01:03:34 +00006750 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006751 case XML_RELAXNG_CHOICE: {
6752 xmlRelaxNGDefinePtr list = define->content;
Daniel Veillardce14fa52003-02-19 17:32:48 +00006753 int success = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006754
6755 oldflags = ctxt->flags;
6756 ctxt->flags |= FLAGS_IGNORABLE;
6757
6758 while (list != NULL) {
6759 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6760 ret = xmlRelaxNGValidateDefinition(ctxt, list);
6761 if (ret == 0) {
Daniel Veillardce14fa52003-02-19 17:32:48 +00006762 if (xmlRelaxNGEqualValidState(ctxt, ctxt->state, oldstate)){
6763 /*
6764 * if that pattern was nullable flag it but try
6765 * to make more progresses
6766 */
6767 success = 1;
6768 } else {
6769 xmlRelaxNGFreeValidState(oldstate);
6770 break;
6771 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006772 }
6773 xmlRelaxNGFreeValidState(ctxt->state);
6774 ctxt->state = oldstate;
6775 list = list->next;
6776 }
6777 ctxt->flags = oldflags;
Daniel Veillardce14fa52003-02-19 17:32:48 +00006778 if (success == 1)
6779 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006780 break;
6781 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00006782 case XML_RELAXNG_DEF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00006783 case XML_RELAXNG_GROUP: {
6784 xmlRelaxNGDefinePtr list = define->content;
6785
6786 while (list != NULL) {
6787 ret = xmlRelaxNGValidateDefinition(ctxt, list);
6788 if (ret != 0)
6789 break;
6790 list = list->next;
6791 }
6792 break;
6793 }
6794 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006795 ret = xmlRelaxNGValidateInterleave(ctxt, define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006796 break;
6797 case XML_RELAXNG_ATTRIBUTE:
6798 ret = xmlRelaxNGValidateAttribute(ctxt, define);
6799 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00006800 case XML_RELAXNG_NOOP:
Daniel Veillard6eadf632003-01-23 18:29:16 +00006801 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00006802 case XML_RELAXNG_PARENTREF:
6803 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00006804 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6805 break;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006806 case XML_RELAXNG_DATATYPE: {
Daniel Veillardd4310742003-02-18 21:12:46 +00006807 xmlNodePtr child;
6808 xmlChar *content = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006809
Daniel Veillardd4310742003-02-18 21:12:46 +00006810 child = node;
6811 while (child != NULL) {
6812 if (child->type == XML_ELEMENT_NODE) {
6813 VALID_CTXT();
6814 VALID_ERROR2("Element %s has child elements\n",
6815 node->parent->name);
6816 ret = -1;
6817 break;
6818 } else if ((child->type == XML_TEXT_NODE) ||
6819 (child->type == XML_CDATA_SECTION_NODE)) {
6820 content = xmlStrcat(content, child->content);
6821 }
6822 /* TODO: handle entities ... */
6823 child = child->next;
6824 }
6825 if (ret == -1) {
6826 if (content != NULL)
6827 xmlFree(content);
6828 break;
6829 }
6830 if (content == NULL) {
6831 content = xmlStrdup(BAD_CAST "");
6832 if (content == NULL) {
6833 VALID_CTXT();
6834 VALID_ERROR("Allocation failure\n");
6835 ret = -1;
6836 break;
6837 }
6838 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006839 ret = xmlRelaxNGValidateDatatype(ctxt, content, define);
6840 if (ret == -1) {
6841 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006842 VALID_ERROR2("internal error validating %s\n", define->name);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006843 } else if (ret == 0) {
Daniel Veillardd4310742003-02-18 21:12:46 +00006844 ctxt->state->seq = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006845 }
6846 if (content != NULL)
6847 xmlFree(content);
6848 break;
6849 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00006850 case XML_RELAXNG_VALUE: {
Daniel Veillardd4310742003-02-18 21:12:46 +00006851 xmlChar *content = NULL;
Daniel Veillardea3f3982003-01-26 19:45:18 +00006852 xmlChar *oldvalue;
Daniel Veillardd4310742003-02-18 21:12:46 +00006853 xmlNodePtr child;
Daniel Veillardea3f3982003-01-26 19:45:18 +00006854
Daniel Veillardd4310742003-02-18 21:12:46 +00006855 child = node;
6856 while (child != NULL) {
6857 if (child->type == XML_ELEMENT_NODE) {
6858 VALID_CTXT();
6859 VALID_ERROR2("Element %s has child elements\n",
6860 node->parent->name);
6861 ret = -1;
6862 break;
6863 } else if ((child->type == XML_TEXT_NODE) ||
6864 (child->type == XML_CDATA_SECTION_NODE)) {
6865 content = xmlStrcat(content, child->content);
6866 }
6867 /* TODO: handle entities ... */
6868 child = child->next;
6869 }
6870 if (ret == -1) {
6871 if (content != NULL)
6872 xmlFree(content);
6873 break;
6874 }
6875 if (content == NULL) {
6876 content = xmlStrdup(BAD_CAST "");
6877 if (content == NULL) {
6878 VALID_CTXT();
6879 VALID_ERROR("Allocation failure\n");
6880 ret = -1;
6881 break;
6882 }
6883 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00006884 oldvalue = ctxt->state->value;
6885 ctxt->state->value = content;
6886 ret = xmlRelaxNGValidateValue(ctxt, define);
6887 ctxt->state->value = oldvalue;
6888 if (ret == -1) {
6889 VALID_CTXT();
Daniel Veillardd2298792003-02-14 16:54:11 +00006890 if (define->name != NULL) {
6891 VALID_ERROR2("error validating value %s\n", define->name);
6892 } else {
6893 VALID_ERROR("error validating value\n");
6894 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00006895 } else if (ret == 0) {
Daniel Veillardd4310742003-02-18 21:12:46 +00006896 ctxt->state->seq = NULL;
Daniel Veillardea3f3982003-01-26 19:45:18 +00006897 }
6898 if (content != NULL)
6899 xmlFree(content);
6900 break;
6901 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006902 case XML_RELAXNG_LIST: {
6903 xmlChar *content;
Daniel Veillardd4310742003-02-18 21:12:46 +00006904 xmlNodePtr child;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006905 xmlChar *oldvalue, *oldendvalue;
6906 int len;
Daniel Veillardea3f3982003-01-26 19:45:18 +00006907
Daniel Veillardd4310742003-02-18 21:12:46 +00006908 /*
6909 * Make sure it's only text nodes
6910 */
6911
6912 content = NULL;
6913 child = node;
6914 while (child != NULL) {
6915 if (child->type == XML_ELEMENT_NODE) {
6916 VALID_CTXT();
6917 VALID_ERROR2("Element %s has child elements\n",
6918 node->parent->name);
6919 ret = -1;
6920 break;
6921 } else if ((child->type == XML_TEXT_NODE) ||
6922 (child->type == XML_CDATA_SECTION_NODE)) {
6923 content = xmlStrcat(content, child->content);
6924 }
6925 /* TODO: handle entities ... */
6926 child = child->next;
6927 }
6928 if (ret == -1) {
6929 if (content != NULL)
6930 xmlFree(content);
6931 break;
6932 }
6933 if (content == NULL) {
6934 content = xmlStrdup(BAD_CAST "");
6935 if (content == NULL) {
6936 VALID_CTXT();
6937 VALID_ERROR("Allocation failure\n");
6938 ret = -1;
6939 break;
6940 }
6941 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006942 len = xmlStrlen(content);
6943 oldvalue = ctxt->state->value;
6944 oldendvalue = ctxt->state->endvalue;
6945 ctxt->state->value = content;
6946 ctxt->state->endvalue = content + len;
6947 ret = xmlRelaxNGValidateValue(ctxt, define);
6948 ctxt->state->value = oldvalue;
6949 ctxt->state->endvalue = oldendvalue;
6950 if (ret == -1) {
6951 VALID_CTXT();
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006952 VALID_ERROR("error validating list\n");
Daniel Veillardd4310742003-02-18 21:12:46 +00006953 } else if ((ret == 0) && (node != NULL)) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006954 ctxt->state->seq = node->next;
6955 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006956 if (content != NULL)
6957 xmlFree(content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006958 break;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006959 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006960 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00006961 case XML_RELAXNG_EXCEPT:
Daniel Veillard8fe98712003-02-19 00:19:14 +00006962 case XML_RELAXNG_PARAM:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006963 TODO
Daniel Veillard416589a2003-02-17 17:25:42 +00006964 ret = -1;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006965 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006966 }
Daniel Veillard231d7912003-02-09 14:22:17 +00006967 ctxt->depth--;
6968#ifdef DEBUG
6969 for (i = 0;i < ctxt->depth;i++)
6970 xmlGenericError(xmlGenericErrorContext, " ");
6971 xmlGenericError(xmlGenericErrorContext,
6972 "Validating %s ", xmlRelaxNGDefName(define));
6973 if (define->name != NULL)
6974 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
6975 if (ret == 0)
6976 xmlGenericError(xmlGenericErrorContext, "suceeded\n");
6977 else
6978 xmlGenericError(xmlGenericErrorContext, "failed\n");
6979#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00006980 return(ret);
6981}
6982
6983/**
6984 * xmlRelaxNGValidateDocument:
6985 * @ctxt: a Relax-NG validation context
6986 * @doc: the document
6987 *
6988 * Validate the given document
6989 *
6990 * Returns 0 if the validation succeeded or an error code.
6991 */
6992static int
6993xmlRelaxNGValidateDocument(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
6994 int ret;
6995 xmlRelaxNGPtr schema;
6996 xmlRelaxNGGrammarPtr grammar;
6997 xmlRelaxNGValidStatePtr state;
6998
6999 if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL))
7000 return(-1);
7001
7002 schema = ctxt->schema;
7003 grammar = schema->topgrammar;
7004 if (grammar == NULL) {
7005 VALID_CTXT();
7006 VALID_ERROR("No top grammar defined\n");
7007 return(-1);
7008 }
7009 state = xmlRelaxNGNewValidState(ctxt, NULL);
7010 ctxt->state = state;
7011 ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start);
7012 state = ctxt->state;
7013 if ((state != NULL) && (state->seq != NULL)) {
7014 xmlNodePtr node;
7015
7016 node = state->seq;
7017 node = xmlRelaxNGSkipIgnored(ctxt, node);
7018 if (node != NULL) {
7019 VALID_CTXT();
7020 VALID_ERROR("extra data on the document\n");
7021 ret = -1;
7022 }
7023 }
7024 xmlRelaxNGFreeValidState(state);
7025
7026 return(ret);
7027}
7028
7029/************************************************************************
7030 * *
7031 * Validation interfaces *
7032 * *
7033 ************************************************************************/
7034/**
7035 * xmlRelaxNGNewValidCtxt:
7036 * @schema: a precompiled XML RelaxNGs
7037 *
7038 * Create an XML RelaxNGs validation context based on the given schema
7039 *
7040 * Returns the validation context or NULL in case of error
7041 */
7042xmlRelaxNGValidCtxtPtr
7043xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema) {
7044 xmlRelaxNGValidCtxtPtr ret;
7045
7046 ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt));
7047 if (ret == NULL) {
7048 xmlGenericError(xmlGenericErrorContext,
7049 "Failed to allocate new schama validation context\n");
7050 return (NULL);
7051 }
7052 memset(ret, 0, sizeof(xmlRelaxNGValidCtxt));
7053 ret->schema = schema;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00007054 ret->error = xmlGenericError;
7055 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007056 return (ret);
7057}
7058
7059/**
7060 * xmlRelaxNGFreeValidCtxt:
7061 * @ctxt: the schema validation context
7062 *
7063 * Free the resources associated to the schema validation context
7064 */
7065void
7066xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt) {
7067 if (ctxt == NULL)
7068 return;
7069 xmlFree(ctxt);
7070}
7071
7072/**
7073 * xmlRelaxNGSetValidErrors:
7074 * @ctxt: a Relax-NG validation context
7075 * @err: the error function
7076 * @warn: the warning function
7077 * @ctx: the functions context
7078 *
7079 * Set the error and warning callback informations
7080 */
7081void
7082xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
7083 xmlRelaxNGValidityErrorFunc err,
7084 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
7085 if (ctxt == NULL)
7086 return;
7087 ctxt->error = err;
7088 ctxt->warning = warn;
7089 ctxt->userData = ctx;
7090}
7091
7092/**
7093 * xmlRelaxNGValidateDoc:
7094 * @ctxt: a Relax-NG validation context
7095 * @doc: a parsed document tree
7096 *
7097 * Validate a document tree in memory.
7098 *
7099 * Returns 0 if the document is valid, a positive error code
7100 * number otherwise and -1 in case of internal or API error.
7101 */
7102int
7103xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
7104 int ret;
7105
7106 if ((ctxt == NULL) || (doc == NULL))
7107 return(-1);
7108
7109 ctxt->doc = doc;
7110
7111 ret = xmlRelaxNGValidateDocument(ctxt, doc);
Daniel Veillard71531f32003-02-05 13:19:53 +00007112 /*
7113 * TODO: build error codes
7114 */
7115 if (ret == -1)
7116 return(1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00007117 return(ret);
7118}
7119
7120#endif /* LIBXML_SCHEMAS_ENABLED */
7121