blob: 2fd70af074aee62c7a3d95b180ddb7c3b7903432 [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
83typedef struct _xmlRelaxNGGrammar xmlRelaxNGGrammar;
84typedef xmlRelaxNGGrammar *xmlRelaxNGGrammarPtr;
85
86struct _xmlRelaxNGGrammar {
87 xmlRelaxNGGrammarPtr parent;/* the parent grammar if any */
88 xmlRelaxNGGrammarPtr children;/* the children grammar if any */
89 xmlRelaxNGGrammarPtr next; /* the next grammar if any */
90 xmlRelaxNGDefinePtr start; /* <start> content */
91 xmlRelaxNGCombine combine; /* the default combine value */
Daniel Veillardd41f4f42003-01-29 21:07:52 +000092 xmlRelaxNGDefinePtr startList;/* list of <start> definitions */
Daniel Veillard6eadf632003-01-23 18:29:16 +000093 xmlHashTablePtr defs; /* define* */
94 xmlHashTablePtr refs; /* references */
95};
96
97
Daniel Veillard6eadf632003-01-23 18:29:16 +000098typedef enum {
99 XML_RELAXNG_EMPTY = 0, /* an empty pattern */
100 XML_RELAXNG_NOT_ALLOWED, /* not allowed top */
Daniel Veillard144fae12003-02-03 13:17:57 +0000101 XML_RELAXNG_EXCEPT, /* except present in nameclass defs */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000102 XML_RELAXNG_TEXT, /* textual content */
103 XML_RELAXNG_ELEMENT, /* an element */
104 XML_RELAXNG_DATATYPE, /* extenal data type definition */
Daniel Veillard8fe98712003-02-19 00:19:14 +0000105 XML_RELAXNG_PARAM, /* extenal data type parameter */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000106 XML_RELAXNG_VALUE, /* value from an extenal data type definition */
107 XML_RELAXNG_LIST, /* a list of patterns */
108 XML_RELAXNG_ATTRIBUTE, /* an attrbute following a pattern */
109 XML_RELAXNG_DEF, /* a definition */
110 XML_RELAXNG_REF, /* reference to a definition */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000111 XML_RELAXNG_EXTERNALREF, /* reference to an external def */
Daniel Veillard419a7682003-02-03 23:22:49 +0000112 XML_RELAXNG_PARENTREF, /* reference to a def in the parent grammar */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000113 XML_RELAXNG_OPTIONAL, /* optional patterns */
114 XML_RELAXNG_ZEROORMORE, /* zero or more non empty patterns */
115 XML_RELAXNG_ONEORMORE, /* one or more non empty patterns */
116 XML_RELAXNG_CHOICE, /* a choice between non empty patterns */
117 XML_RELAXNG_GROUP, /* a pair/group of non empty patterns */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000118 XML_RELAXNG_INTERLEAVE, /* interleaving choice of non-empty patterns */
119 XML_RELAXNG_START /* Used to keep track of starts on grammars */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000120} xmlRelaxNGType;
121
122struct _xmlRelaxNGDefine {
123 xmlRelaxNGType type; /* the type of definition */
124 xmlNodePtr node; /* the node in the source */
125 xmlChar *name; /* the element local name if present */
126 xmlChar *ns; /* the namespace local name if present */
Daniel Veillardedc91922003-01-26 00:52:04 +0000127 xmlChar *value; /* value when available */
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000128 void *data; /* data lib or specific pointer */
Daniel Veillardd4310742003-02-18 21:12:46 +0000129 int depth; /* used for the cycle detection */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000130 xmlRelaxNGDefinePtr content;/* the expected content */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000131 xmlRelaxNGDefinePtr parent; /* the parent definition, if any */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000132 xmlRelaxNGDefinePtr next; /* list within grouping sequences */
133 xmlRelaxNGDefinePtr attrs; /* list of attributes for elements */
Daniel Veillard3b2e4e12003-02-03 08:52:58 +0000134 xmlRelaxNGDefinePtr nameClass;/* the nameClass definition if any */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000135 xmlRelaxNGDefinePtr nextHash;/* next define in defs/refs hash tables */
136};
137
138/**
139 * _xmlRelaxNG:
140 *
141 * A RelaxNGs definition
142 */
143struct _xmlRelaxNG {
144 xmlRelaxNGGrammarPtr topgrammar;
145 xmlDocPtr doc;
146
147 xmlHashTablePtr defs; /* define */
148 xmlHashTablePtr refs; /* references */
Daniel Veillard419a7682003-02-03 23:22:49 +0000149 xmlHashTablePtr documents; /* all the documents loaded */
150 xmlHashTablePtr includes; /* all the includes loaded */
151 int defNr; /* number of defines used */
152 xmlRelaxNGDefinePtr *defTab;/* pointer to the allocated definitions */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000153 void *_private; /* unused by the library for users or bindings */
154};
155
156typedef enum {
157 XML_RELAXNG_ERR_OK = 0,
158 XML_RELAXNG_ERR_NOROOT = 1,
159 XML_RELAXNG_ERR_
160} xmlRelaxNGValidError;
161
162#define XML_RELAXNG_IN_ATTRIBUTE 1
163
164struct _xmlRelaxNGParserCtxt {
165 void *userData; /* user specific data block */
166 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
167 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
168 xmlRelaxNGValidError err;
169
170 xmlRelaxNGPtr schema; /* The schema in use */
171 xmlRelaxNGGrammarPtr grammar; /* the current grammar */
Daniel Veillard419a7682003-02-03 23:22:49 +0000172 xmlRelaxNGGrammarPtr parentgrammar;/* the parent grammar */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000173 int flags; /* parser flags */
174 int nbErrors; /* number of errors at parse time */
175 int nbWarnings; /* number of warnings at parse time */
Daniel Veillard276be4a2003-01-24 01:03:34 +0000176 const xmlChar *define; /* the current define scope */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000177 xmlRelaxNGDefinePtr def; /* the current define */
178
179 int nbInterleaves;
180 xmlHashTablePtr interleaves; /* keep track of all the interleaves */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000181
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000182 xmlHashTablePtr documents; /* all the documents loaded */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000183 xmlHashTablePtr includes; /* all the includes loaded */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000184 xmlChar *URL;
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000185 xmlDocPtr document;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000186
Daniel Veillard419a7682003-02-03 23:22:49 +0000187 int defNr; /* number of defines used */
188 int defMax; /* number of defines aloocated */
189 xmlRelaxNGDefinePtr *defTab; /* pointer to the allocated definitions */
190
Daniel Veillard6eadf632003-01-23 18:29:16 +0000191 const char *buffer;
192 int size;
193
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000194 /* the document stack */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000195 xmlRelaxNGDocumentPtr doc; /* Current parsed external ref */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000196 int docNr; /* Depth of the parsing stack */
197 int docMax; /* Max depth of the parsing stack */
198 xmlRelaxNGDocumentPtr *docTab; /* array of docs */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000199
200 /* the include stack */
Daniel Veillardd4310742003-02-18 21:12:46 +0000201 xmlRelaxNGIncludePtr inc; /* Current parsed include */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000202 int incNr; /* Depth of the include parsing stack */
203 int incMax; /* Max depth of the parsing stack */
204 xmlRelaxNGIncludePtr *incTab; /* array of incs */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000205};
206
207#define FLAGS_IGNORABLE 1
208#define FLAGS_NEGATIVE 2
209
210/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000211 * xmlRelaxNGInterleaveGroup:
212 *
213 * A RelaxNGs partition set associated to lists of definitions
214 */
215typedef struct _xmlRelaxNGInterleaveGroup xmlRelaxNGInterleaveGroup;
216typedef xmlRelaxNGInterleaveGroup *xmlRelaxNGInterleaveGroupPtr;
217struct _xmlRelaxNGInterleaveGroup {
218 xmlRelaxNGDefinePtr rule; /* the rule to satisfy */
219 xmlRelaxNGDefinePtr *defs; /* the array of element definitions */
220};
221
222/**
223 * xmlRelaxNGPartitions:
224 *
225 * A RelaxNGs partition associated to an interleave group
226 */
227typedef struct _xmlRelaxNGPartition xmlRelaxNGPartition;
228typedef xmlRelaxNGPartition *xmlRelaxNGPartitionPtr;
229struct _xmlRelaxNGPartition {
230 int nbgroups; /* number of groups in the partitions */
231 xmlRelaxNGInterleaveGroupPtr *groups;
232};
233
234/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000235 * xmlRelaxNGValidState:
236 *
237 * A RelaxNGs validation state
238 */
239#define MAX_ATTR 20
240typedef struct _xmlRelaxNGValidState xmlRelaxNGValidState;
241typedef xmlRelaxNGValidState *xmlRelaxNGValidStatePtr;
242struct _xmlRelaxNGValidState {
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000243 xmlNodePtr node; /* the current node */
244 xmlNodePtr seq; /* the sequence of children left to validate */
245 int nbAttrs; /* the number of attributes */
Daniel Veillard1ed7f362003-02-03 10:57:45 +0000246 int nbAttrLeft; /* the number of attributes left to validate */
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000247 xmlChar *value; /* the value when operating on string */
248 xmlChar *endvalue; /* the end value when operating on string */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000249 xmlAttrPtr attrs[1]; /* the array of attributes */
250};
251
252/**
253 * xmlRelaxNGValidCtxt:
254 *
255 * A RelaxNGs validation context
256 */
257
258struct _xmlRelaxNGValidCtxt {
259 void *userData; /* user specific data block */
260 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
261 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
262
263 xmlRelaxNGPtr schema; /* The schema in use */
264 xmlDocPtr doc; /* the document being validated */
265 xmlRelaxNGValidStatePtr state; /* the current validation state */
266 int flags; /* validation flags */
Daniel Veillard231d7912003-02-09 14:22:17 +0000267 int depth; /* validation depth */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000268};
269
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000270/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000271 * xmlRelaxNGInclude:
272 *
273 * Structure associated to a RelaxNGs document element
274 */
275struct _xmlRelaxNGInclude {
276 xmlChar *href; /* the normalized href value */
277 xmlDocPtr doc; /* the associated XML document */
278 xmlRelaxNGDefinePtr content;/* the definitions */
279 xmlRelaxNGPtr schema; /* the schema */
280};
281
282/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000283 * xmlRelaxNGDocument:
284 *
285 * Structure associated to a RelaxNGs document element
286 */
287struct _xmlRelaxNGDocument {
288 xmlChar *href; /* the normalized href value */
289 xmlDocPtr doc; /* the associated XML document */
290 xmlRelaxNGDefinePtr content;/* the definitions */
291 xmlRelaxNGPtr schema; /* the schema */
292};
293
Daniel Veillard6eadf632003-01-23 18:29:16 +0000294/************************************************************************
295 * *
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000296 * Preliminary type checking interfaces *
297 * *
298 ************************************************************************/
299/**
300 * xmlRelaxNGTypeHave:
301 * @data: data needed for the library
302 * @type: the type name
303 * @value: the value to check
304 *
305 * Function provided by a type library to check if a type is exported
306 *
307 * Returns 1 if yes, 0 if no and -1 in case of error.
308 */
309typedef int (*xmlRelaxNGTypeHave) (void *data, const xmlChar *type);
310
311/**
312 * xmlRelaxNGTypeCheck:
313 * @data: data needed for the library
314 * @type: the type name
315 * @value: the value to check
316 *
317 * Function provided by a type library to check if a value match a type
318 *
319 * Returns 1 if yes, 0 if no and -1 in case of error.
320 */
321typedef int (*xmlRelaxNGTypeCheck) (void *data, const xmlChar *type,
322 const xmlChar *value);
323
324/**
325 * xmlRelaxNGTypeCompare:
326 * @data: data needed for the library
327 * @type: the type name
328 * @value1: the first value
329 * @value2: the second value
330 *
331 * Function provided by a type library to compare two values accordingly
332 * to a type.
333 *
334 * Returns 1 if yes, 0 if no and -1 in case of error.
335 */
336typedef int (*xmlRelaxNGTypeCompare) (void *data, const xmlChar *type,
337 const xmlChar *value1,
338 const xmlChar *value2);
339typedef struct _xmlRelaxNGTypeLibrary xmlRelaxNGTypeLibrary;
340typedef xmlRelaxNGTypeLibrary *xmlRelaxNGTypeLibraryPtr;
341struct _xmlRelaxNGTypeLibrary {
342 const xmlChar *namespace; /* the datatypeLibrary value */
343 void *data; /* data needed for the library */
344 xmlRelaxNGTypeHave have; /* the export function */
345 xmlRelaxNGTypeCheck check; /* the checking function */
346 xmlRelaxNGTypeCompare comp; /* the compare function */
347};
348
349/************************************************************************
350 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +0000351 * Allocation functions *
352 * *
353 ************************************************************************/
Daniel Veillard6eadf632003-01-23 18:29:16 +0000354static void xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar);
355static void xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define);
Daniel Veillardd2298792003-02-14 16:54:11 +0000356static void xmlRelaxNGNormExtSpace(xmlChar *value);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000357
358/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000359 * xmlRelaxNGFreeDocument:
360 * @docu: a document structure
361 *
362 * Deallocate a RelaxNG document structure.
363 */
364static void
365xmlRelaxNGFreeDocument(xmlRelaxNGDocumentPtr docu)
366{
367 if (docu == NULL)
368 return;
369
370 if (docu->href != NULL)
371 xmlFree(docu->href);
372 if (docu->doc != NULL)
373 xmlFreeDoc(docu->doc);
374 if (docu->schema != NULL)
375 xmlRelaxNGFree(docu->schema);
376 xmlFree(docu);
377}
378
379/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000380 * xmlRelaxNGFreeInclude:
381 * @incl: a include structure
382 *
383 * Deallocate a RelaxNG include structure.
384 */
385static void
386xmlRelaxNGFreeInclude(xmlRelaxNGIncludePtr incl)
387{
388 if (incl == NULL)
389 return;
390
391 if (incl->href != NULL)
392 xmlFree(incl->href);
393 if (incl->doc != NULL)
394 xmlFreeDoc(incl->doc);
395 if (incl->schema != NULL)
396 xmlRelaxNGFree(incl->schema);
397 xmlFree(incl);
398}
399
400/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000401 * xmlRelaxNGNewRelaxNG:
402 * @ctxt: a Relax-NG validation context (optional)
403 *
404 * Allocate a new RelaxNG structure.
405 *
406 * Returns the newly allocated structure or NULL in case or error
407 */
408static xmlRelaxNGPtr
409xmlRelaxNGNewRelaxNG(xmlRelaxNGParserCtxtPtr ctxt)
410{
411 xmlRelaxNGPtr ret;
412
413 ret = (xmlRelaxNGPtr) xmlMalloc(sizeof(xmlRelaxNG));
414 if (ret == NULL) {
415 if ((ctxt != NULL) && (ctxt->error != NULL))
416 ctxt->error(ctxt->userData, "Out of memory\n");
417 ctxt->nbErrors++;
418 return (NULL);
419 }
420 memset(ret, 0, sizeof(xmlRelaxNG));
421
422 return (ret);
423}
424
425/**
426 * xmlRelaxNGFree:
427 * @schema: a schema structure
428 *
429 * Deallocate a RelaxNG structure.
430 */
431void
432xmlRelaxNGFree(xmlRelaxNGPtr schema)
433{
434 if (schema == NULL)
435 return;
436
Daniel Veillard6eadf632003-01-23 18:29:16 +0000437 if (schema->topgrammar != NULL)
438 xmlRelaxNGFreeGrammar(schema->topgrammar);
439 if (schema->doc != NULL)
440 xmlFreeDoc(schema->doc);
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000441 if (schema->documents != NULL)
442 xmlHashFree(schema->documents, (xmlHashDeallocator)
443 xmlRelaxNGFreeDocument);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000444 if (schema->includes != NULL)
445 xmlHashFree(schema->includes, (xmlHashDeallocator)
446 xmlRelaxNGFreeInclude);
Daniel Veillard419a7682003-02-03 23:22:49 +0000447 if (schema->defTab != NULL) {
448 int i;
449
450 for (i = 0;i < schema->defNr;i++)
451 xmlRelaxNGFreeDefine(schema->defTab[i]);
452 xmlFree(schema->defTab);
453 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000454
455 xmlFree(schema);
456}
457
458/**
459 * xmlRelaxNGNewGrammar:
460 * @ctxt: a Relax-NG validation context (optional)
461 *
462 * Allocate a new RelaxNG grammar.
463 *
464 * Returns the newly allocated structure or NULL in case or error
465 */
466static xmlRelaxNGGrammarPtr
467xmlRelaxNGNewGrammar(xmlRelaxNGParserCtxtPtr ctxt)
468{
469 xmlRelaxNGGrammarPtr ret;
470
471 ret = (xmlRelaxNGGrammarPtr) xmlMalloc(sizeof(xmlRelaxNGGrammar));
472 if (ret == NULL) {
473 if ((ctxt != NULL) && (ctxt->error != NULL))
474 ctxt->error(ctxt->userData, "Out of memory\n");
475 ctxt->nbErrors++;
476 return (NULL);
477 }
478 memset(ret, 0, sizeof(xmlRelaxNGGrammar));
479
480 return (ret);
481}
482
483/**
484 * xmlRelaxNGFreeGrammar:
485 * @grammar: a grammar structure
486 *
487 * Deallocate a RelaxNG grammar structure.
488 */
489static void
490xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar)
491{
492 if (grammar == NULL)
493 return;
494
Daniel Veillard419a7682003-02-03 23:22:49 +0000495 if (grammar->next != NULL) {
496 xmlRelaxNGFreeGrammar(grammar->next);
497 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000498 if (grammar->refs != NULL) {
499 xmlHashFree(grammar->refs, NULL);
500 }
501 if (grammar->defs != NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000502 xmlHashFree(grammar->defs, NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000503 }
504
505 xmlFree(grammar);
506}
507
508/**
509 * xmlRelaxNGNewDefine:
510 * @ctxt: a Relax-NG validation context
511 * @node: the node in the input document.
512 *
513 * Allocate a new RelaxNG define.
514 *
515 * Returns the newly allocated structure or NULL in case or error
516 */
517static xmlRelaxNGDefinePtr
518xmlRelaxNGNewDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
519{
520 xmlRelaxNGDefinePtr ret;
521
Daniel Veillard419a7682003-02-03 23:22:49 +0000522 if (ctxt->defMax == 0) {
523 ctxt->defMax = 16;
524 ctxt->defNr = 0;
525 ctxt->defTab = (xmlRelaxNGDefinePtr *)
526 xmlMalloc(ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
527 if (ctxt->defTab == NULL) {
528 if ((ctxt != NULL) && (ctxt->error != NULL))
529 ctxt->error(ctxt->userData, "Out of memory\n");
530 ctxt->nbErrors++;
531 return (NULL);
532 }
533 } else if (ctxt->defMax <= ctxt->defNr) {
534 xmlRelaxNGDefinePtr *tmp;
535 ctxt->defMax *= 2;
536 tmp = (xmlRelaxNGDefinePtr *) xmlRealloc(ctxt->defTab,
537 ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
538 if (tmp == NULL) {
539 if ((ctxt != NULL) && (ctxt->error != NULL))
540 ctxt->error(ctxt->userData, "Out of memory\n");
541 ctxt->nbErrors++;
542 return (NULL);
543 }
544 ctxt->defTab = tmp;
545 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000546 ret = (xmlRelaxNGDefinePtr) xmlMalloc(sizeof(xmlRelaxNGDefine));
547 if (ret == NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000548 if ((ctxt != NULL) && (ctxt->error != NULL))
549 ctxt->error(ctxt->userData, "Out of memory\n");
Daniel Veillard6eadf632003-01-23 18:29:16 +0000550 ctxt->nbErrors++;
Daniel Veillard419a7682003-02-03 23:22:49 +0000551 return(NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000552 }
553 memset(ret, 0, sizeof(xmlRelaxNGDefine));
Daniel Veillard419a7682003-02-03 23:22:49 +0000554 ctxt->defTab[ctxt->defNr++] = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000555 ret->node = node;
Daniel Veillardd4310742003-02-18 21:12:46 +0000556 ret->depth = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000557 return (ret);
558}
559
560/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000561 * xmlRelaxNGFreePartition:
562 * @partitions: a partition set structure
563 *
564 * Deallocate RelaxNG partition set structures.
565 */
566static void
567xmlRelaxNGFreePartition(xmlRelaxNGPartitionPtr partitions) {
568 xmlRelaxNGInterleaveGroupPtr group;
569 int j;
570
571 if (partitions != NULL) {
572 if (partitions->groups != NULL) {
573 for (j = 0;j < partitions->nbgroups;j++) {
574 group = partitions->groups[j];
575 if (group != NULL) {
576 if (group->defs != NULL)
577 xmlFree(group->defs);
578 xmlFree(group);
579 }
580 }
581 xmlFree(partitions->groups);
582 }
583 xmlFree(partitions);
584 }
585}
586/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000587 * xmlRelaxNGFreeDefine:
588 * @define: a define structure
589 *
590 * Deallocate a RelaxNG define structure.
591 */
592static void
593xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define)
594{
595 if (define == NULL)
596 return;
597
Daniel Veillard419a7682003-02-03 23:22:49 +0000598 if ((define->data != NULL) &&
599 (define->type == XML_RELAXNG_INTERLEAVE))
600 xmlRelaxNGFreePartition((xmlRelaxNGPartitionPtr) define->data);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000601 if (define->name != NULL)
602 xmlFree(define->name);
603 if (define->ns != NULL)
604 xmlFree(define->ns);
Daniel Veillardedc91922003-01-26 00:52:04 +0000605 if (define->value != NULL)
606 xmlFree(define->value);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000607 xmlFree(define);
608}
609
610/**
611 * xmlRelaxNGNewValidState:
612 * @ctxt: a Relax-NG validation context
613 * @node: the current node or NULL for the document
614 *
615 * Allocate a new RelaxNG validation state
616 *
617 * Returns the newly allocated structure or NULL in case or error
618 */
619static xmlRelaxNGValidStatePtr
620xmlRelaxNGNewValidState(xmlRelaxNGValidCtxtPtr ctxt, xmlNodePtr node)
621{
622 xmlRelaxNGValidStatePtr ret;
623 xmlAttrPtr attr;
624 xmlAttrPtr attrs[MAX_ATTR];
625 int nbAttrs = 0;
626 xmlNodePtr root = NULL;
627
628 if (node == NULL) {
629 root = xmlDocGetRootElement(ctxt->doc);
630 if (root == NULL)
631 return(NULL);
632 } else {
633 attr = node->properties;
634 while (attr != NULL) {
635 if (nbAttrs < MAX_ATTR)
636 attrs[nbAttrs++] = attr;
637 else
638 nbAttrs++;
639 attr = attr->next;
640 }
641 }
642
643 if (nbAttrs < MAX_ATTR)
644 attrs[nbAttrs] = NULL;
645 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(sizeof(xmlRelaxNGValidState) +
646 nbAttrs * sizeof(xmlAttrPtr));
647 if (ret == NULL) {
648 if ((ctxt != NULL) && (ctxt->error != NULL))
649 ctxt->error(ctxt->userData, "Out of memory\n");
650 return (NULL);
651 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +0000652 ret->value = NULL;
653 ret->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000654 if (node == NULL) {
655 ret->node = (xmlNodePtr) ctxt->doc;
656 ret->seq = root;
657 ret->nbAttrs = 0;
658 } else {
659 ret->node = node;
660 ret->seq = node->children;
661 ret->nbAttrs = nbAttrs;
662 if (nbAttrs > 0) {
663 if (nbAttrs < MAX_ATTR) {
664 memcpy(&(ret->attrs[0]), attrs,
665 sizeof(xmlAttrPtr) * (nbAttrs + 1));
666 } else {
667 attr = node->properties;
668 nbAttrs = 0;
669 while (attr != NULL) {
670 ret->attrs[nbAttrs++] = attr;
671 attr = attr->next;
672 }
673 ret->attrs[nbAttrs] = NULL;
674 }
675 }
676 }
Daniel Veillard1ed7f362003-02-03 10:57:45 +0000677 ret->nbAttrLeft = ret->nbAttrs;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000678 return (ret);
679}
680
681/**
682 * xmlRelaxNGCopyValidState:
683 * @ctxt: a Relax-NG validation context
684 * @state: a validation state
685 *
686 * Copy the validation state
687 *
688 * Returns the newly allocated structure or NULL in case or error
689 */
690static xmlRelaxNGValidStatePtr
691xmlRelaxNGCopyValidState(xmlRelaxNGValidCtxtPtr ctxt,
692 xmlRelaxNGValidStatePtr state)
693{
694 xmlRelaxNGValidStatePtr ret;
695 unsigned int size;
696
697 if (state == NULL)
698 return(NULL);
699
700 size = sizeof(xmlRelaxNGValidState) +
701 state->nbAttrs * sizeof(xmlAttrPtr);
702 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(size);
703 if (ret == NULL) {
704 if ((ctxt != NULL) && (ctxt->error != NULL))
705 ctxt->error(ctxt->userData, "Out of memory\n");
706 return (NULL);
707 }
708 memcpy(ret, state, size);
709 return(ret);
710}
711
712/**
713 * xmlRelaxNGFreeValidState:
714 * @state: a validation state structure
715 *
716 * Deallocate a RelaxNG validation state structure.
717 */
718static void
719xmlRelaxNGFreeValidState(xmlRelaxNGValidStatePtr state)
720{
721 if (state == NULL)
722 return;
723
724 xmlFree(state);
725}
726
727/************************************************************************
728 * *
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000729 * Document functions *
730 * *
731 ************************************************************************/
732static xmlDocPtr xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt,
733 xmlDocPtr doc);
734
735/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000736 * xmlRelaxNGIncludePush:
737 * @ctxt: the parser context
738 * @value: the element doc
739 *
740 * Pushes a new include on top of the include stack
741 *
742 * Returns 0 in case of error, the index in the stack otherwise
743 */
744static int
745xmlRelaxNGIncludePush(xmlRelaxNGParserCtxtPtr ctxt,
746 xmlRelaxNGIncludePtr value)
747{
748 if (ctxt->incTab == NULL) {
749 ctxt->incMax = 4;
750 ctxt->incNr = 0;
751 ctxt->incTab = (xmlRelaxNGIncludePtr *) xmlMalloc(
752 ctxt->incMax * sizeof(ctxt->incTab[0]));
753 if (ctxt->incTab == NULL) {
754 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
755 return (0);
756 }
757 }
758 if (ctxt->incNr >= ctxt->incMax) {
759 ctxt->incMax *= 2;
760 ctxt->incTab =
761 (xmlRelaxNGIncludePtr *) xmlRealloc(ctxt->incTab,
762 ctxt->incMax *
763 sizeof(ctxt->incTab[0]));
764 if (ctxt->incTab == NULL) {
765 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
766 return (0);
767 }
768 }
769 ctxt->incTab[ctxt->incNr] = value;
770 ctxt->inc = value;
771 return (ctxt->incNr++);
772}
773
774/**
775 * xmlRelaxNGIncludePop:
776 * @ctxt: the parser context
777 *
778 * Pops the top include from the include stack
779 *
780 * Returns the include just removed
781 */
782static xmlRelaxNGIncludePtr
783xmlRelaxNGIncludePop(xmlRelaxNGParserCtxtPtr ctxt)
784{
785 xmlRelaxNGIncludePtr ret;
786
787 if (ctxt->incNr <= 0)
788 return (0);
789 ctxt->incNr--;
790 if (ctxt->incNr > 0)
791 ctxt->inc = ctxt->incTab[ctxt->incNr - 1];
792 else
793 ctxt->inc = NULL;
794 ret = ctxt->incTab[ctxt->incNr];
795 ctxt->incTab[ctxt->incNr] = 0;
796 return (ret);
797}
798
799/**
800 * xmlRelaxNGLoadInclude:
801 * @ctxt: the parser context
802 * @URL: the normalized URL
803 * @node: the include node.
Daniel Veillard416589a2003-02-17 17:25:42 +0000804 * @ns: the namespace passed from the context.
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000805 *
806 * First lookup if the document is already loaded into the parser context,
807 * check against recursion. If not found the resource is loaded and
808 * the content is preprocessed before being returned back to the caller.
809 *
810 * Returns the xmlRelaxNGIncludePtr or NULL in case of error
811 */
812static xmlRelaxNGIncludePtr
813xmlRelaxNGLoadInclude(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillard416589a2003-02-17 17:25:42 +0000814 xmlNodePtr node, const xmlChar *ns) {
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000815 xmlRelaxNGIncludePtr ret = NULL;
816 xmlDocPtr doc;
817 int i;
818 xmlNodePtr root, tmp, tmp2, cur;
819
820 /*
821 * check against recursion in the stack
822 */
823 for (i = 0;i < ctxt->incNr;i++) {
824 if (xmlStrEqual(ctxt->incTab[i]->href, URL)) {
825 if (ctxt->error != NULL)
826 ctxt->error(ctxt->userData,
827 "Detected an externalRef recursion for %s\n",
828 URL);
829 ctxt->nbErrors++;
830 return(NULL);
831 }
832 }
833
834 /*
835 * Lookup in the hash table
836 */
837 if (ctxt->includes == NULL) {
838 ctxt->includes = xmlHashCreate(10);
839 if (ctxt->includes == NULL) {
840 if (ctxt->error != NULL)
841 ctxt->error(ctxt->userData,
842 "Failed to allocate hash table for document\n");
843 ctxt->nbErrors++;
844 return(NULL);
845 }
846 } else {
Daniel Veillard416589a2003-02-17 17:25:42 +0000847 if (ns == NULL)
848 ret = xmlHashLookup2(ctxt->includes, BAD_CAST "", URL);
849 else
850 ret = xmlHashLookup2(ctxt->includes, ns, URL);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000851 if (ret != NULL)
852 return(ret);
853 }
854
855
856 /*
857 * load the document
858 */
859 doc = xmlParseFile((const char *) URL);
860 if (doc == NULL) {
861 if (ctxt->error != NULL)
862 ctxt->error(ctxt->userData,
863 "xmlRelaxNG: could not load %s\n", URL);
864 ctxt->nbErrors++;
865 return (NULL);
866 }
867
868 /*
869 * Allocate the document structures and register it first.
870 */
871 ret = (xmlRelaxNGIncludePtr) xmlMalloc(sizeof(xmlRelaxNGInclude));
872 if (ret == NULL) {
873 if (ctxt->error != NULL)
874 ctxt->error(ctxt->userData,
875 "xmlRelaxNG: allocate memory for doc %s\n", URL);
876 ctxt->nbErrors++;
877 xmlFreeDoc(doc);
878 return (NULL);
879 }
880 memset(ret, 0, sizeof(xmlRelaxNGInclude));
881 ret->doc = doc;
882 ret->href = xmlStrdup(URL);
883
884 /*
Daniel Veillard416589a2003-02-17 17:25:42 +0000885 * transmit the ns if needed
886 */
887 if (ns != NULL) {
888 root = xmlDocGetRootElement(doc);
889 if (root != NULL) {
890 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
891 xmlSetProp(root, BAD_CAST"ns", ns);
892 }
893 }
894 }
895
896 /*
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000897 * push it on the stack and register it in the hash table
898 */
Daniel Veillard416589a2003-02-17 17:25:42 +0000899 if (ns == NULL)
900 xmlHashAddEntry2(ctxt->includes, BAD_CAST "", URL, ret);
901 else
902 xmlHashAddEntry2(ctxt->includes, ns, URL, ret);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000903 xmlRelaxNGIncludePush(ctxt, ret);
904
905 /*
906 * Some preprocessing of the document content, this include recursing
907 * in the include stack.
908 */
909 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
910 if (doc == NULL) {
911 /* xmlFreeDoc(ctxt->include); */
912 ctxt->inc = NULL;
913 return(NULL);
914 }
915
916 /*
917 * Pop up the include from the stack
918 */
919 xmlRelaxNGIncludePop(ctxt);
920
921 /*
922 * Check that the top element is a grammar
923 */
924 root = xmlDocGetRootElement(doc);
925 if (root == NULL) {
926 if (ctxt->error != NULL)
927 ctxt->error(ctxt->userData,
928 "xmlRelaxNG: included document is empty %s\n", URL);
929 ctxt->nbErrors++;
930 xmlFreeDoc(doc);
931 return (NULL);
932 }
933 if (!IS_RELAXNG(root, "grammar")) {
934 if (ctxt->error != NULL)
935 ctxt->error(ctxt->userData,
936 "xmlRelaxNG: included document %s root is not a grammar\n",
937 URL);
938 ctxt->nbErrors++;
939 xmlFreeDoc(doc);
940 return (NULL);
941 }
942
943 /*
944 * Elimination of redefined rules in the include.
945 */
946 cur = node->children;
947 while (cur != NULL) {
948 if (IS_RELAXNG(cur, "start")) {
949 int found = 0;
950
951 tmp = root->children;
952 while (tmp != NULL) {
953 tmp2 = tmp->next;
954 if (IS_RELAXNG(tmp, "start")) {
955 found = 1;
956 xmlUnlinkNode(tmp);
957 xmlFreeNode(tmp);
958 }
959 tmp = tmp2;
960 }
961 if (!found) {
962 if (ctxt->error != NULL)
963 ctxt->error(ctxt->userData,
964 "xmlRelaxNG: include %s has a start but not the included grammar\n",
965 URL);
966 ctxt->nbErrors++;
967 }
968 } else if (IS_RELAXNG(cur, "define")) {
969 xmlChar *name, *name2;
970
971 name = xmlGetProp(cur, BAD_CAST "name");
972 if (name == NULL) {
973 if (ctxt->error != NULL)
974 ctxt->error(ctxt->userData,
975 "xmlRelaxNG: include %s has define without name\n",
976 URL);
977 ctxt->nbErrors++;
978 } else {
979 int found = 0;
980
Daniel Veillardd2298792003-02-14 16:54:11 +0000981 xmlRelaxNGNormExtSpace(name);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000982 tmp = root->children;
983 while (tmp != NULL) {
984 tmp2 = tmp->next;
985 if (IS_RELAXNG(tmp, "define")) {
986 name2 = xmlGetProp(tmp, BAD_CAST "name");
Daniel Veillardd2298792003-02-14 16:54:11 +0000987 xmlRelaxNGNormExtSpace(name2);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000988 if (name2 != NULL) {
989 if (xmlStrEqual(name, name2)) {
990 found = 1;
991 xmlUnlinkNode(tmp);
992 xmlFreeNode(tmp);
993 }
994 xmlFree(name2);
995 }
996 }
997 tmp = tmp2;
998 }
999 if (!found) {
1000 if (ctxt->error != NULL)
1001 ctxt->error(ctxt->userData,
1002 "xmlRelaxNG: include %s has a define %s but not the included grammar\n",
1003 URL, name);
1004 ctxt->nbErrors++;
1005 }
1006 xmlFree(name);
1007 }
1008 }
1009 cur = cur->next;
1010 }
1011
1012
1013 return(ret);
1014}
1015
1016/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001017 * xmlRelaxNGDocumentPush:
1018 * @ctxt: the parser context
1019 * @value: the element doc
1020 *
1021 * Pushes a new doc on top of the doc stack
1022 *
1023 * Returns 0 in case of error, the index in the stack otherwise
1024 */
1025static int
1026xmlRelaxNGDocumentPush(xmlRelaxNGParserCtxtPtr ctxt,
1027 xmlRelaxNGDocumentPtr value)
1028{
1029 if (ctxt->docTab == NULL) {
1030 ctxt->docMax = 4;
1031 ctxt->docNr = 0;
1032 ctxt->docTab = (xmlRelaxNGDocumentPtr *) xmlMalloc(
1033 ctxt->docMax * sizeof(ctxt->docTab[0]));
1034 if (ctxt->docTab == NULL) {
1035 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
1036 return (0);
1037 }
1038 }
1039 if (ctxt->docNr >= ctxt->docMax) {
1040 ctxt->docMax *= 2;
1041 ctxt->docTab =
1042 (xmlRelaxNGDocumentPtr *) xmlRealloc(ctxt->docTab,
1043 ctxt->docMax *
1044 sizeof(ctxt->docTab[0]));
1045 if (ctxt->docTab == NULL) {
1046 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
1047 return (0);
1048 }
1049 }
1050 ctxt->docTab[ctxt->docNr] = value;
1051 ctxt->doc = value;
1052 return (ctxt->docNr++);
1053}
1054
1055/**
1056 * xmlRelaxNGDocumentPop:
1057 * @ctxt: the parser context
1058 *
1059 * Pops the top doc from the doc stack
1060 *
1061 * Returns the doc just removed
1062 */
1063static xmlRelaxNGDocumentPtr
1064xmlRelaxNGDocumentPop(xmlRelaxNGParserCtxtPtr ctxt)
1065{
1066 xmlRelaxNGDocumentPtr ret;
1067
1068 if (ctxt->docNr <= 0)
1069 return (0);
1070 ctxt->docNr--;
1071 if (ctxt->docNr > 0)
1072 ctxt->doc = ctxt->docTab[ctxt->docNr - 1];
1073 else
1074 ctxt->doc = NULL;
1075 ret = ctxt->docTab[ctxt->docNr];
1076 ctxt->docTab[ctxt->docNr] = 0;
1077 return (ret);
1078}
1079
1080/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001081 * xmlRelaxNGLoadExternalRef:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001082 * @ctxt: the parser context
1083 * @URL: the normalized URL
1084 * @ns: the inherited ns if any
1085 *
1086 * First lookup if the document is already loaded into the parser context,
1087 * check against recursion. If not found the resource is loaded and
1088 * the content is preprocessed before being returned back to the caller.
1089 *
1090 * Returns the xmlRelaxNGDocumentPtr or NULL in case of error
1091 */
1092static xmlRelaxNGDocumentPtr
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001093xmlRelaxNGLoadExternalRef(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001094 const xmlChar *ns) {
1095 xmlRelaxNGDocumentPtr ret = NULL;
1096 xmlDocPtr doc;
1097 xmlNodePtr root;
1098 int i;
1099
1100 /*
1101 * check against recursion in the stack
1102 */
1103 for (i = 0;i < ctxt->docNr;i++) {
1104 if (xmlStrEqual(ctxt->docTab[i]->href, URL)) {
1105 if (ctxt->error != NULL)
1106 ctxt->error(ctxt->userData,
1107 "Detected an externalRef recursion for %s\n",
1108 URL);
1109 ctxt->nbErrors++;
1110 return(NULL);
1111 }
1112 }
1113
1114 /*
1115 * Lookup in the hash table
1116 */
1117 if (ctxt->documents == NULL) {
1118 ctxt->documents = xmlHashCreate(10);
1119 if (ctxt->documents == NULL) {
1120 if (ctxt->error != NULL)
1121 ctxt->error(ctxt->userData,
1122 "Failed to allocate hash table for document\n");
1123 ctxt->nbErrors++;
1124 return(NULL);
1125 }
1126 } else {
1127 if (ns == NULL)
1128 ret = xmlHashLookup2(ctxt->documents, BAD_CAST "", URL);
1129 else
1130 ret = xmlHashLookup2(ctxt->documents, ns, URL);
1131 if (ret != NULL)
1132 return(ret);
1133 }
1134
1135
1136 /*
1137 * load the document
1138 */
1139 doc = xmlParseFile((const char *) URL);
1140 if (doc == NULL) {
1141 if (ctxt->error != NULL)
1142 ctxt->error(ctxt->userData,
1143 "xmlRelaxNG: could not load %s\n", URL);
1144 ctxt->nbErrors++;
1145 return (NULL);
1146 }
1147
1148 /*
1149 * Allocate the document structures and register it first.
1150 */
1151 ret = (xmlRelaxNGDocumentPtr) xmlMalloc(sizeof(xmlRelaxNGDocument));
1152 if (ret == NULL) {
1153 if (ctxt->error != NULL)
1154 ctxt->error(ctxt->userData,
1155 "xmlRelaxNG: allocate memory for doc %s\n", URL);
1156 ctxt->nbErrors++;
1157 xmlFreeDoc(doc);
1158 return (NULL);
1159 }
1160 memset(ret, 0, sizeof(xmlRelaxNGDocument));
1161 ret->doc = doc;
1162 ret->href = xmlStrdup(URL);
1163
1164 /*
1165 * transmit the ns if needed
1166 */
1167 if (ns != NULL) {
1168 root = xmlDocGetRootElement(doc);
1169 if (root != NULL) {
1170 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
1171 xmlSetProp(root, BAD_CAST"ns", ns);
1172 }
1173 }
1174 }
1175
1176 /*
1177 * push it on the stack and register it in the hash table
1178 */
1179 if (ns == NULL)
1180 xmlHashAddEntry2(ctxt->documents, BAD_CAST "", URL, ret);
1181 else
1182 xmlHashAddEntry2(ctxt->documents, ns, URL, ret);
1183 xmlRelaxNGDocumentPush(ctxt, ret);
1184
1185 /*
1186 * Some preprocessing of the document content
1187 */
1188 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
1189 if (doc == NULL) {
1190 xmlFreeDoc(ctxt->document);
1191 ctxt->doc = NULL;
1192 return(NULL);
1193 }
1194
1195 xmlRelaxNGDocumentPop(ctxt);
1196
1197 return(ret);
1198}
1199
1200/************************************************************************
1201 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +00001202 * Error functions *
1203 * *
1204 ************************************************************************/
1205
1206#define VALID_CTXT() \
Daniel Veillard231d7912003-02-09 14:22:17 +00001207 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1208 xmlGenericError(xmlGenericErrorContext, \
Daniel Veillard6eadf632003-01-23 18:29:16 +00001209 "error detected at %s:%d\n", \
1210 __FILE__, __LINE__);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001211
1212#define VALID_ERROR(a) \
Daniel Veillard231d7912003-02-09 14:22:17 +00001213 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001214 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a)
1215#define VALID_ERROR2(a, b) \
1216 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1217 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a, b)
1218#define VALID_ERROR3(a, b, c) \
1219 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1220 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a, b, c)
Daniel Veillard6eadf632003-01-23 18:29:16 +00001221
Daniel Veillardd2298792003-02-14 16:54:11 +00001222#ifdef DEBUG
Daniel Veillard231d7912003-02-09 14:22:17 +00001223static const char *
1224xmlRelaxNGDefName(xmlRelaxNGDefinePtr def) {
1225 if (def == NULL)
1226 return("none");
1227 switch(def->type) {
1228 case XML_RELAXNG_EMPTY: return("empty");
1229 case XML_RELAXNG_NOT_ALLOWED: return("notAllowed");
1230 case XML_RELAXNG_EXCEPT: return("except");
1231 case XML_RELAXNG_TEXT: return("text");
1232 case XML_RELAXNG_ELEMENT: return("element");
1233 case XML_RELAXNG_DATATYPE: return("datatype");
1234 case XML_RELAXNG_VALUE: return("value");
1235 case XML_RELAXNG_LIST: return("list");
1236 case XML_RELAXNG_ATTRIBUTE: return("attribute");
1237 case XML_RELAXNG_DEF: return("def");
1238 case XML_RELAXNG_REF: return("ref");
1239 case XML_RELAXNG_EXTERNALREF: return("externalRef");
1240 case XML_RELAXNG_PARENTREF: return("parentRef");
1241 case XML_RELAXNG_OPTIONAL: return("optional");
1242 case XML_RELAXNG_ZEROORMORE: return("zeroOrMore");
1243 case XML_RELAXNG_ONEORMORE: return("oneOrMore");
1244 case XML_RELAXNG_CHOICE: return("choice");
1245 case XML_RELAXNG_GROUP: return("group");
1246 case XML_RELAXNG_INTERLEAVE: return("interleave");
1247 case XML_RELAXNG_START: return("start");
1248 }
1249 return("unknown");
1250}
Daniel Veillardd2298792003-02-14 16:54:11 +00001251#endif
1252
Daniel Veillard6eadf632003-01-23 18:29:16 +00001253#if 0
1254/**
1255 * xmlRelaxNGErrorContext:
1256 * @ctxt: the parsing context
1257 * @schema: the schema being built
1258 * @node: the node being processed
1259 * @child: the child being processed
1260 *
1261 * Dump a RelaxNGType structure
1262 */
1263static void
1264xmlRelaxNGErrorContext(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGPtr schema,
1265 xmlNodePtr node, xmlNodePtr child)
1266{
1267 int line = 0;
1268 const xmlChar *file = NULL;
1269 const xmlChar *name = NULL;
1270 const char *type = "error";
1271
1272 if ((ctxt == NULL) || (ctxt->error == NULL))
1273 return;
1274
1275 if (child != NULL)
1276 node = child;
1277
1278 if (node != NULL) {
1279 if ((node->type == XML_DOCUMENT_NODE) ||
1280 (node->type == XML_HTML_DOCUMENT_NODE)) {
1281 xmlDocPtr doc = (xmlDocPtr) node;
1282
1283 file = doc->URL;
1284 } else {
1285 /*
1286 * Try to find contextual informations to report
1287 */
1288 if (node->type == XML_ELEMENT_NODE) {
1289 line = (int) node->content;
1290 } else if ((node->prev != NULL) &&
1291 (node->prev->type == XML_ELEMENT_NODE)) {
1292 line = (int) node->prev->content;
1293 } else if ((node->parent != NULL) &&
1294 (node->parent->type == XML_ELEMENT_NODE)) {
1295 line = (int) node->parent->content;
1296 }
1297 if ((node->doc != NULL) && (node->doc->URL != NULL))
1298 file = node->doc->URL;
1299 if (node->name != NULL)
1300 name = node->name;
1301 }
1302 }
1303
1304 if (ctxt != NULL)
1305 type = "compilation error";
1306 else if (schema != NULL)
1307 type = "runtime error";
1308
1309 if ((file != NULL) && (line != 0) && (name != NULL))
1310 ctxt->error(ctxt->userData, "%s: file %s line %d element %s\n",
1311 type, file, line, name);
1312 else if ((file != NULL) && (name != NULL))
1313 ctxt->error(ctxt->userData, "%s: file %s element %s\n",
1314 type, file, name);
1315 else if ((file != NULL) && (line != 0))
1316 ctxt->error(ctxt->userData, "%s: file %s line %d\n", type, file, line);
1317 else if (file != NULL)
1318 ctxt->error(ctxt->userData, "%s: file %s\n", type, file);
1319 else if (name != NULL)
1320 ctxt->error(ctxt->userData, "%s: element %s\n", type, name);
1321 else
1322 ctxt->error(ctxt->userData, "%s\n", type);
1323}
1324#endif
1325
1326/************************************************************************
1327 * *
1328 * Type library hooks *
1329 * *
1330 ************************************************************************/
Daniel Veillardea3f3982003-01-26 19:45:18 +00001331static xmlChar *xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt,
1332 const xmlChar *str);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001333
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001334/**
1335 * xmlRelaxNGSchemaTypeHave:
1336 * @data: data needed for the library
1337 * @type: the type name
1338 *
1339 * Check if the given type is provided by
1340 * the W3C XMLSchema Datatype library.
1341 *
1342 * Returns 1 if yes, 0 if no and -1 in case of error.
1343 */
1344static int
1345xmlRelaxNGSchemaTypeHave(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001346 const xmlChar *type) {
1347 xmlSchemaTypePtr typ;
1348
1349 if (type == NULL)
1350 return(-1);
1351 typ = xmlSchemaGetPredefinedType(type,
1352 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1353 if (typ == NULL)
1354 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001355 return(1);
1356}
1357
1358/**
1359 * xmlRelaxNGSchemaTypeCheck:
1360 * @data: data needed for the library
1361 * @type: the type name
1362 * @value: the value to check
1363 *
1364 * Check if the given type and value are validated by
1365 * the W3C XMLSchema Datatype library.
1366 *
1367 * Returns 1 if yes, 0 if no and -1 in case of error.
1368 */
1369static int
1370xmlRelaxNGSchemaTypeCheck(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001371 const xmlChar *type,
1372 const xmlChar *value) {
1373 xmlSchemaTypePtr typ;
1374 int ret;
1375
1376 /*
1377 * TODO: the type should be cached ab provided back, interface subject
1378 * to changes.
1379 * TODO: handle facets, may require an additional interface and keep
1380 * the value returned from the validation.
1381 */
1382 if ((type == NULL) || (value == NULL))
1383 return(-1);
1384 typ = xmlSchemaGetPredefinedType(type,
1385 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1386 if (typ == NULL)
1387 return(-1);
1388 ret = xmlSchemaValidatePredefinedType(typ, value, NULL);
1389 if (ret == 0)
1390 return(1);
1391 if (ret > 0)
1392 return(0);
1393 return(-1);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001394}
1395
1396/**
1397 * xmlRelaxNGSchemaTypeCompare:
1398 * @data: data needed for the library
1399 * @type: the type name
1400 * @value1: the first value
1401 * @value2: the second value
1402 *
1403 * Compare two values accordingly a type from the W3C XMLSchema
1404 * Datatype library.
1405 *
1406 * Returns 1 if yes, 0 if no and -1 in case of error.
1407 */
1408static int
1409xmlRelaxNGSchemaTypeCompare(void *data ATTRIBUTE_UNUSED,
1410 const xmlChar *type ATTRIBUTE_UNUSED,
1411 const xmlChar *value1 ATTRIBUTE_UNUSED,
1412 const xmlChar *value2 ATTRIBUTE_UNUSED) {
1413 TODO
1414 return(1);
1415}
1416
1417/**
1418 * xmlRelaxNGDefaultTypeHave:
1419 * @data: data needed for the library
1420 * @type: the type name
1421 *
1422 * Check if the given type is provided by
1423 * the default datatype library.
1424 *
1425 * Returns 1 if yes, 0 if no and -1 in case of error.
1426 */
1427static int
1428xmlRelaxNGDefaultTypeHave(void *data ATTRIBUTE_UNUSED, const xmlChar *type) {
1429 if (type == NULL)
1430 return(-1);
1431 if (xmlStrEqual(type, BAD_CAST "string"))
1432 return(1);
1433 if (xmlStrEqual(type, BAD_CAST "token"))
1434 return(1);
1435 return(0);
1436}
1437
1438/**
1439 * xmlRelaxNGDefaultTypeCheck:
1440 * @data: data needed for the library
1441 * @type: the type name
1442 * @value: the value to check
1443 *
1444 * Check if the given type and value are validated by
1445 * the default datatype library.
1446 *
1447 * Returns 1 if yes, 0 if no and -1 in case of error.
1448 */
1449static int
1450xmlRelaxNGDefaultTypeCheck(void *data ATTRIBUTE_UNUSED,
1451 const xmlChar *type ATTRIBUTE_UNUSED,
1452 const xmlChar *value ATTRIBUTE_UNUSED) {
Daniel Veillardd4310742003-02-18 21:12:46 +00001453 if (value == NULL)
1454 return(-1);
1455 if (xmlStrEqual(type, BAD_CAST "string"))
1456 return(1);
1457 if (xmlStrEqual(type, BAD_CAST "token")) {
1458#if 0
1459 const xmlChar *cur = value;
1460
1461 while (*cur != 0) {
1462 if (!IS_BLANK(*cur))
1463 return(1);
1464 cur++;
1465 }
1466#endif
1467 return(1);
1468 }
1469
1470 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001471}
1472
1473/**
1474 * xmlRelaxNGDefaultTypeCompare:
1475 * @data: data needed for the library
1476 * @type: the type name
1477 * @value1: the first value
1478 * @value2: the second value
1479 *
1480 * Compare two values accordingly a type from the default
1481 * datatype library.
1482 *
1483 * Returns 1 if yes, 0 if no and -1 in case of error.
1484 */
1485static int
1486xmlRelaxNGDefaultTypeCompare(void *data ATTRIBUTE_UNUSED,
1487 const xmlChar *type ATTRIBUTE_UNUSED,
1488 const xmlChar *value1 ATTRIBUTE_UNUSED,
1489 const xmlChar *value2 ATTRIBUTE_UNUSED) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00001490 int ret = -1;
1491
1492 if (xmlStrEqual(type, BAD_CAST "string")) {
1493 ret = xmlStrEqual(value1, value2);
1494 } else if (xmlStrEqual(type, BAD_CAST "token")) {
1495 if (!xmlStrEqual(value1, value2)) {
1496 xmlChar *nval, *nvalue;
1497
1498 /*
1499 * TODO: trivial optimizations are possible by
1500 * computing at compile-time
1501 */
1502 nval = xmlRelaxNGNormalize(NULL, value1);
1503 nvalue = xmlRelaxNGNormalize(NULL, value2);
1504
Daniel Veillardd4310742003-02-18 21:12:46 +00001505 if ((nval == NULL) || (nvalue == NULL))
Daniel Veillardea3f3982003-01-26 19:45:18 +00001506 ret = -1;
Daniel Veillardd4310742003-02-18 21:12:46 +00001507 else if (xmlStrEqual(nval, nvalue))
1508 ret = 1;
1509 else
1510 ret = 0;
Daniel Veillardea3f3982003-01-26 19:45:18 +00001511 if (nval != NULL)
1512 xmlFree(nval);
1513 if (nvalue != NULL)
1514 xmlFree(nvalue);
Daniel Veillardd4310742003-02-18 21:12:46 +00001515 } else
1516 ret = 1;
Daniel Veillardea3f3982003-01-26 19:45:18 +00001517 }
1518 return(ret);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001519}
1520
1521static int xmlRelaxNGTypeInitialized = 0;
1522static xmlHashTablePtr xmlRelaxNGRegisteredTypes = NULL;
1523
1524/**
1525 * xmlRelaxNGFreeTypeLibrary:
1526 * @lib: the type library structure
1527 * @namespace: the URI bound to the library
1528 *
1529 * Free the structure associated to the type library
1530 */
Daniel Veillard6eadf632003-01-23 18:29:16 +00001531static void
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001532xmlRelaxNGFreeTypeLibrary(xmlRelaxNGTypeLibraryPtr lib,
1533 const xmlChar *namespace ATTRIBUTE_UNUSED) {
1534 if (lib == NULL)
1535 return;
1536 if (lib->namespace != NULL)
1537 xmlFree((xmlChar *)lib->namespace);
1538 xmlFree(lib);
1539}
1540
1541/**
1542 * xmlRelaxNGRegisterTypeLibrary:
1543 * @namespace: the URI bound to the library
1544 * @data: data associated to the library
1545 * @have: the provide function
1546 * @check: the checking function
1547 * @comp: the comparison function
1548 *
1549 * Register a new type library
1550 *
1551 * Returns 0 in case of success and -1 in case of error.
1552 */
1553static int
1554xmlRelaxNGRegisterTypeLibrary(const xmlChar *namespace, void *data,
1555 xmlRelaxNGTypeHave have, xmlRelaxNGTypeCheck check,
1556 xmlRelaxNGTypeCompare comp) {
1557 xmlRelaxNGTypeLibraryPtr lib;
1558 int ret;
1559
1560 if ((xmlRelaxNGRegisteredTypes == NULL) || (namespace == NULL) ||
1561 (check == NULL) || (comp == NULL))
1562 return(-1);
1563 if (xmlHashLookup(xmlRelaxNGRegisteredTypes, namespace) != NULL) {
1564 xmlGenericError(xmlGenericErrorContext,
1565 "Relax-NG types library '%s' already registered\n",
1566 namespace);
1567 return(-1);
1568 }
1569 lib = (xmlRelaxNGTypeLibraryPtr) xmlMalloc(sizeof(xmlRelaxNGTypeLibrary));
1570 if (lib == NULL) {
1571 xmlGenericError(xmlGenericErrorContext,
1572 "Relax-NG types library '%s' malloc() failed\n",
1573 namespace);
1574 return (-1);
1575 }
1576 memset(lib, 0, sizeof(xmlRelaxNGTypeLibrary));
1577 lib->namespace = xmlStrdup(namespace);
1578 lib->data = data;
1579 lib->have = have;
1580 lib->comp = comp;
1581 lib->check = check;
1582 ret = xmlHashAddEntry(xmlRelaxNGRegisteredTypes, namespace, lib);
1583 if (ret < 0) {
1584 xmlGenericError(xmlGenericErrorContext,
1585 "Relax-NG types library failed to register '%s'\n",
1586 namespace);
1587 xmlRelaxNGFreeTypeLibrary(lib, namespace);
1588 return(-1);
1589 }
1590 return(0);
1591}
1592
1593/**
1594 * xmlRelaxNGInitTypes:
1595 *
1596 * Initilize the default type libraries.
1597 *
1598 * Returns 0 in case of success and -1 in case of error.
1599 */
1600static int
Daniel Veillard6eadf632003-01-23 18:29:16 +00001601xmlRelaxNGInitTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001602 if (xmlRelaxNGTypeInitialized != 0)
1603 return(0);
1604 xmlRelaxNGRegisteredTypes = xmlHashCreate(10);
1605 if (xmlRelaxNGRegisteredTypes == NULL) {
1606 xmlGenericError(xmlGenericErrorContext,
1607 "Failed to allocate sh table for Relax-NG types\n");
1608 return(-1);
1609 }
1610 xmlRelaxNGRegisterTypeLibrary(
1611 BAD_CAST "http://www.w3.org/2001/XMLSchema-datatypes",
1612 NULL,
1613 xmlRelaxNGSchemaTypeHave,
1614 xmlRelaxNGSchemaTypeCheck,
1615 xmlRelaxNGSchemaTypeCompare);
1616 xmlRelaxNGRegisterTypeLibrary(
1617 xmlRelaxNGNs,
1618 NULL,
1619 xmlRelaxNGDefaultTypeHave,
1620 xmlRelaxNGDefaultTypeCheck,
1621 xmlRelaxNGDefaultTypeCompare);
1622 xmlRelaxNGTypeInitialized = 1;
1623 return(0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001624}
1625
1626/**
1627 * xmlRelaxNGCleanupTypes:
1628 *
1629 * Cleanup the default Schemas type library associated to RelaxNG
1630 */
1631void
1632xmlRelaxNGCleanupTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001633 if (xmlRelaxNGTypeInitialized == 0)
1634 return;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001635 xmlSchemaCleanupTypes();
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001636 xmlHashFree(xmlRelaxNGRegisteredTypes, (xmlHashDeallocator)
1637 xmlRelaxNGFreeTypeLibrary);
1638 xmlRelaxNGTypeInitialized = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001639}
1640
1641/************************************************************************
1642 * *
1643 * Parsing functions *
1644 * *
1645 ************************************************************************/
1646
1647static xmlRelaxNGDefinePtr xmlRelaxNGParseAttribute(
1648 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1649static xmlRelaxNGDefinePtr xmlRelaxNGParseElement(
1650 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1651static xmlRelaxNGDefinePtr xmlRelaxNGParsePatterns(
Daniel Veillard154877e2003-01-30 12:17:05 +00001652 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes, int group);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001653static xmlRelaxNGDefinePtr xmlRelaxNGParsePattern(
1654 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001655static xmlRelaxNGPtr xmlRelaxNGParseDocument(
1656 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00001657static int xmlRelaxNGParseGrammarContent(
1658 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00001659static xmlRelaxNGDefinePtr xmlRelaxNGParseNameClass(
1660 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
1661 xmlRelaxNGDefinePtr def);
Daniel Veillard419a7682003-02-03 23:22:49 +00001662static xmlRelaxNGGrammarPtr xmlRelaxNGParseGrammar(
1663 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001664
1665
1666#define IS_BLANK_NODE(n) \
1667 (((n)->type == XML_TEXT_NODE) && (xmlRelaxNGIsBlank((n)->content)))
1668
1669/**
1670 * xmlRelaxNGIsBlank:
1671 * @str: a string
1672 *
1673 * Check if a string is ignorable c.f. 4.2. Whitespace
1674 *
1675 * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
1676 */
1677static int
1678xmlRelaxNGIsBlank(xmlChar *str) {
1679 if (str == NULL)
1680 return(1);
1681 while (*str != 0) {
1682 if (!(IS_BLANK(*str))) return(0);
1683 str++;
1684 }
1685 return(1);
1686}
1687
Daniel Veillard6eadf632003-01-23 18:29:16 +00001688/**
1689 * xmlRelaxNGGetDataTypeLibrary:
1690 * @ctxt: a Relax-NG parser context
1691 * @node: the current data or value element
1692 *
1693 * Applies algorithm from 4.3. datatypeLibrary attribute
1694 *
1695 * Returns the datatypeLibary value or NULL if not found
1696 */
1697static xmlChar *
1698xmlRelaxNGGetDataTypeLibrary(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1699 xmlNodePtr node) {
1700 xmlChar *ret, *escape;
1701
Daniel Veillard6eadf632003-01-23 18:29:16 +00001702 if ((IS_RELAXNG(node, "data")) || (IS_RELAXNG(node, "value"))) {
1703 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1704 if (ret != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001705 if (ret[0] == 0) {
1706 xmlFree(ret);
1707 return(NULL);
1708 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001709 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +00001710 if (escape == NULL) {
1711 return(ret);
1712 }
1713 xmlFree(ret);
1714 return(escape);
1715 }
1716 }
1717 node = node->parent;
1718 while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) {
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001719 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1720 if (ret != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001721 if (ret[0] == 0) {
1722 xmlFree(ret);
1723 return(NULL);
1724 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001725 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
1726 if (escape == NULL) {
1727 return(ret);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001728 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001729 xmlFree(ret);
1730 return(escape);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001731 }
1732 node = node->parent;
1733 }
1734 return(NULL);
1735}
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001736
1737/**
Daniel Veillardedc91922003-01-26 00:52:04 +00001738 * xmlRelaxNGParseValue:
1739 * @ctxt: a Relax-NG parser context
1740 * @node: the data node.
1741 *
1742 * parse the content of a RelaxNG value node.
1743 *
1744 * Returns the definition pointer or NULL in case of error
1745 */
1746static xmlRelaxNGDefinePtr
1747xmlRelaxNGParseValue(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1748 xmlRelaxNGDefinePtr def = NULL;
1749 xmlRelaxNGTypeLibraryPtr lib;
1750 xmlChar *type;
1751 xmlChar *library;
1752 int tmp;
1753
1754 def = xmlRelaxNGNewDefine(ctxt, node);
1755 if (def == NULL)
1756 return(NULL);
1757 def->type = XML_RELAXNG_VALUE;
1758
1759 type = xmlGetProp(node, BAD_CAST "type");
1760 if (type != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001761 xmlRelaxNGNormExtSpace(type);
1762 if (xmlValidateNCName(type, 0)) {
1763 if (ctxt->error != NULL)
1764 ctxt->error(ctxt->userData,
1765 "value type '%s' is not an NCName\n",
1766 type);
1767 ctxt->nbErrors++;
1768 }
Daniel Veillardedc91922003-01-26 00:52:04 +00001769 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1770 if (library == NULL)
1771 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1772
1773 def->name = type;
1774 def->ns = library;
1775
1776 lib = (xmlRelaxNGTypeLibraryPtr)
1777 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1778 if (lib == NULL) {
1779 if (ctxt->error != NULL)
1780 ctxt->error(ctxt->userData,
1781 "Use of unregistered type library '%s'\n",
1782 library);
1783 ctxt->nbErrors++;
1784 def->data = NULL;
1785 } else {
1786 def->data = lib;
1787 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001788 if (ctxt->error != NULL)
1789 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00001790 "Internal error with type library '%s': no 'have'\n",
1791 library);
1792 ctxt->nbErrors++;
1793 } else {
1794 tmp = lib->have(lib->data, def->name);
1795 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001796 if (ctxt->error != NULL)
1797 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00001798 "Error type '%s' is not exported by type library '%s'\n",
1799 def->name, library);
1800 ctxt->nbErrors++;
1801 }
1802 }
1803 }
1804 }
1805 if (node->children == NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00001806 def->value = xmlStrdup(BAD_CAST "");
Daniel Veillardedc91922003-01-26 00:52:04 +00001807 } else if ((node->children->type != XML_TEXT_NODE) ||
1808 (node->children->next != NULL)) {
1809 if (ctxt->error != NULL)
1810 ctxt->error(ctxt->userData,
1811 "Expecting a single text value for <value>content\n");
1812 ctxt->nbErrors++;
1813 } else {
1814 def->value = xmlNodeGetContent(node);
1815 if (def->value == NULL) {
1816 if (ctxt->error != NULL)
1817 ctxt->error(ctxt->userData,
1818 "Element <value> has no content\n");
1819 ctxt->nbErrors++;
1820 }
1821 }
1822 /* TODO check ahead of time that the value is okay per the type */
1823 return(def);
1824}
1825
1826/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001827 * xmlRelaxNGParseData:
1828 * @ctxt: a Relax-NG parser context
1829 * @node: the data node.
1830 *
1831 * parse the content of a RelaxNG data node.
1832 *
1833 * Returns the definition pointer or NULL in case of error
1834 */
1835static xmlRelaxNGDefinePtr
1836xmlRelaxNGParseData(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
Daniel Veillard416589a2003-02-17 17:25:42 +00001837 xmlRelaxNGDefinePtr def = NULL, except, last = NULL;
Daniel Veillard8fe98712003-02-19 00:19:14 +00001838 xmlRelaxNGDefinePtr param, lastparam = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001839 xmlRelaxNGTypeLibraryPtr lib;
1840 xmlChar *type;
1841 xmlChar *library;
1842 xmlNodePtr content;
1843 int tmp;
1844
1845 type = xmlGetProp(node, BAD_CAST "type");
1846 if (type == NULL) {
1847 if (ctxt->error != NULL)
1848 ctxt->error(ctxt->userData,
1849 "data has no type\n");
1850 ctxt->nbErrors++;
1851 return(NULL);
1852 }
Daniel Veillardd2298792003-02-14 16:54:11 +00001853 xmlRelaxNGNormExtSpace(type);
1854 if (xmlValidateNCName(type, 0)) {
1855 if (ctxt->error != NULL)
1856 ctxt->error(ctxt->userData,
1857 "data type '%s' is not an NCName\n",
1858 type);
1859 ctxt->nbErrors++;
1860 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001861 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1862 if (library == NULL)
1863 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1864
1865 def = xmlRelaxNGNewDefine(ctxt, node);
1866 if (def == NULL) {
1867 xmlFree(type);
1868 return(NULL);
1869 }
1870 def->type = XML_RELAXNG_DATATYPE;
1871 def->name = type;
1872 def->ns = library;
1873
1874 lib = (xmlRelaxNGTypeLibraryPtr)
1875 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1876 if (lib == NULL) {
1877 if (ctxt->error != NULL)
1878 ctxt->error(ctxt->userData,
1879 "Use of unregistered type library '%s'\n",
1880 library);
1881 ctxt->nbErrors++;
1882 def->data = NULL;
1883 } else {
1884 def->data = lib;
1885 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001886 if (ctxt->error != NULL)
1887 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001888 "Internal error with type library '%s': no 'have'\n",
1889 library);
1890 ctxt->nbErrors++;
1891 } else {
1892 tmp = lib->have(lib->data, def->name);
1893 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001894 if (ctxt->error != NULL)
1895 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001896 "Error type '%s' is not exported by type library '%s'\n",
1897 def->name, library);
1898 ctxt->nbErrors++;
1899 }
1900 }
1901 }
1902 content = node->children;
Daniel Veillard416589a2003-02-17 17:25:42 +00001903
1904 /*
1905 * Handle optional params
1906 */
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001907 while (content != NULL) {
Daniel Veillard416589a2003-02-17 17:25:42 +00001908 if (!xmlStrEqual(content->name, BAD_CAST "param"))
1909 break;
Daniel Veillard8fe98712003-02-19 00:19:14 +00001910 param = xmlRelaxNGNewDefine(ctxt, node);
1911 if (param != NULL) {
1912 param->type = XML_RELAXNG_PARAM;
1913 param->name = xmlGetProp(content, BAD_CAST "name");
1914 if (param->name == NULL) {
1915 if (ctxt->error != NULL)
1916 ctxt->error(ctxt->userData,
1917 "param has no name\n");
1918 ctxt->nbErrors++;
1919 }
1920 param->value = xmlNodeGetContent(content);
1921 if (lastparam == NULL) {
1922 def->attrs = lastparam = param;
1923 } else {
1924 lastparam->next = param;
1925 lastparam = param;
1926 }
1927 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001928 content = content->next;
1929 }
Daniel Veillard416589a2003-02-17 17:25:42 +00001930 /*
1931 * Handle optional except
1932 */
1933 if ((content != NULL) && (xmlStrEqual(content->name, BAD_CAST "except"))) {
1934 xmlNodePtr child;
1935 xmlRelaxNGDefinePtr tmp2, last2 = NULL;
1936
1937 except = xmlRelaxNGNewDefine(ctxt, node);
1938 if (except == NULL) {
1939 return(def);
1940 }
1941 except->type = XML_RELAXNG_EXCEPT;
1942 child = content->children;
1943 if (last == NULL) {
1944 def->content = except;
1945 } else {
1946 last->next = except;
1947 }
1948 if (child == NULL) {
1949 if (ctxt->error != NULL)
1950 ctxt->error(ctxt->userData,
1951 "except has no content\n");
1952 ctxt->nbErrors++;
1953 }
1954 while (child != NULL) {
1955 tmp2 = xmlRelaxNGParsePattern(ctxt, child);
1956 if (tmp2 != NULL) {
1957 if (last2 == NULL) {
1958 except->content = last2 = tmp2;
1959 } else {
1960 last2->next = tmp2;
1961 last2 = tmp2;
1962 }
1963 }
1964 child = child->next;
1965 }
1966 content = content->next;
1967 }
1968 /*
1969 * Check there is no unhandled data
1970 */
1971 if (content != NULL) {
1972 if (ctxt->error != NULL)
1973 ctxt->error(ctxt->userData,
1974 "Element data has unexpected content %s\n", content->name);
1975 ctxt->nbErrors++;
1976 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001977
1978 return(def);
1979}
1980
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001981/**
1982 * xmlRelaxNGCompareElemDefLists:
1983 * @ctxt: a Relax-NG parser context
1984 * @defs1: the first list of element defs
1985 * @defs2: the second list of element defs
1986 *
1987 * Compare the 2 lists of element definitions. The comparison is
1988 * that if both lists do not accept the same QNames, it returns 1
1989 * If the 2 lists can accept the same QName the comparison returns 0
1990 *
1991 * Returns 1 disttinct, 0 if equal
1992 */
1993static int
1994xmlRelaxNGCompareElemDefLists(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1995 xmlRelaxNGDefinePtr *def1,
1996 xmlRelaxNGDefinePtr *def2) {
1997 xmlRelaxNGDefinePtr *basedef2 = def2;
1998
Daniel Veillard154877e2003-01-30 12:17:05 +00001999 if ((def1 == NULL) || (def2 == NULL))
2000 return(1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002001 if ((*def1 == NULL) || (*def2 == NULL))
2002 return(1);
2003 while (*def1 != NULL) {
2004 while ((*def2) != NULL) {
2005 if ((*def1)->name == NULL) {
2006 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
2007 return(0);
2008 } else if ((*def2)->name == NULL) {
2009 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
2010 return(0);
2011 } else if (xmlStrEqual((*def1)->name, (*def2)->name)) {
2012 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
2013 return(0);
2014 }
2015 def2++;
2016 }
2017 def2 = basedef2;
2018 def1++;
2019 }
2020 return(1);
2021}
2022
2023/**
2024 * xmlRelaxNGGetElements:
2025 * @ctxt: a Relax-NG parser context
2026 * @def: the interleave definition
2027 *
2028 * Compute the list of top elements a definition can generate
2029 *
2030 * Returns a list of elements or NULL if none was found.
2031 */
2032static xmlRelaxNGDefinePtr *
2033xmlRelaxNGGetElements(xmlRelaxNGParserCtxtPtr ctxt,
2034 xmlRelaxNGDefinePtr def) {
2035 xmlRelaxNGDefinePtr *ret = NULL, parent, cur, tmp;
2036 int len = 0;
2037 int max = 0;
2038
2039 parent = NULL;
2040 cur = def;
2041 while (cur != NULL) {
Daniel Veillardb08c9812003-01-28 23:09:49 +00002042 if ((cur->type == XML_RELAXNG_ELEMENT) ||
2043 (cur->type == XML_RELAXNG_TEXT)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002044 if (ret == NULL) {
2045 max = 10;
2046 ret = (xmlRelaxNGDefinePtr *)
2047 xmlMalloc((max + 1) * sizeof(xmlRelaxNGDefinePtr));
2048 if (ret == NULL) {
2049 if (ctxt->error != NULL)
2050 ctxt->error(ctxt->userData,
2051 "Out of memory in element search\n");
2052 ctxt->nbErrors++;
2053 return(NULL);
2054 }
2055 } else if (max <= len) {
2056 max *= 2;
2057 ret = xmlRealloc(ret, (max + 1) * sizeof(xmlRelaxNGDefinePtr));
2058 if (ret == NULL) {
2059 if (ctxt->error != NULL)
2060 ctxt->error(ctxt->userData,
2061 "Out of memory in element search\n");
2062 ctxt->nbErrors++;
2063 return(NULL);
2064 }
2065 }
Daniel Veillardb08c9812003-01-28 23:09:49 +00002066 ret[len++] = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002067 ret[len] = NULL;
2068 } else if ((cur->type == XML_RELAXNG_CHOICE) ||
2069 (cur->type == XML_RELAXNG_INTERLEAVE) ||
2070 (cur->type == XML_RELAXNG_GROUP) ||
2071 (cur->type == XML_RELAXNG_ONEORMORE) ||
Daniel Veillardb08c9812003-01-28 23:09:49 +00002072 (cur->type == XML_RELAXNG_ZEROORMORE) ||
2073 (cur->type == XML_RELAXNG_OPTIONAL) ||
2074 (cur->type == XML_RELAXNG_REF) ||
2075 (cur->type == XML_RELAXNG_DEF)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002076 /*
2077 * Don't go within elements or attributes or string values.
2078 * Just gather the element top list
2079 */
2080 if (cur->content != NULL) {
2081 parent = cur;
2082 cur = cur->content;
2083 tmp = cur;
2084 while (tmp != NULL) {
2085 tmp->parent = parent;
2086 tmp = tmp->next;
2087 }
2088 continue;
2089 }
2090 }
Daniel Veillard154877e2003-01-30 12:17:05 +00002091 if (cur == def)
2092 return(ret);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002093 if (cur->next != NULL) {
2094 cur = cur->next;
2095 continue;
2096 }
2097 do {
2098 cur = cur->parent;
2099 if (cur == NULL) break;
2100 if (cur == def) return(ret);
2101 if (cur->next != NULL) {
2102 cur = cur->next;
2103 break;
2104 }
2105 } while (cur != NULL);
2106 }
2107 return(ret);
2108}
2109
2110/**
2111 * xmlRelaxNGComputeInterleaves:
2112 * @def: the interleave definition
2113 * @ctxt: a Relax-NG parser context
2114 * @node: the data node.
2115 *
2116 * A lot of work for preprocessing interleave definitions
2117 * is potentially needed to get a decent execution speed at runtime
2118 * - trying to get a total order on the element nodes generated
2119 * by the interleaves, order the list of interleave definitions
2120 * following that order.
2121 * - if <text/> is used to handle mixed content, it is better to
2122 * flag this in the define and simplify the runtime checking
2123 * algorithm
2124 */
2125static void
2126xmlRelaxNGComputeInterleaves(xmlRelaxNGDefinePtr def,
2127 xmlRelaxNGParserCtxtPtr ctxt,
2128 xmlChar *name ATTRIBUTE_UNUSED) {
2129 xmlRelaxNGDefinePtr cur;
2130
2131 xmlRelaxNGDefinePtr *list = NULL;
2132 xmlRelaxNGPartitionPtr partitions = NULL;
2133 xmlRelaxNGInterleaveGroupPtr *groups = NULL;
2134 xmlRelaxNGInterleaveGroupPtr group;
2135 int i,j,ret;
2136 int nbgroups = 0;
2137 int nbchild = 0;
2138
2139#ifdef DEBUG_INTERLEAVE
2140 xmlGenericError(xmlGenericErrorContext,
2141 "xmlRelaxNGComputeInterleaves(%s)\n",
2142 name);
2143#endif
2144 cur = def->content;
2145 while (cur != NULL) {
2146 nbchild++;
2147 cur = cur->next;
2148 }
2149
2150#ifdef DEBUG_INTERLEAVE
2151 xmlGenericError(xmlGenericErrorContext, " %d child\n", nbchild);
2152#endif
2153 groups = (xmlRelaxNGInterleaveGroupPtr *)
2154 xmlMalloc(nbchild * sizeof(xmlRelaxNGInterleaveGroupPtr));
2155 if (groups == NULL)
2156 goto error;
2157 cur = def->content;
2158 while (cur != NULL) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002159 groups[nbgroups] = (xmlRelaxNGInterleaveGroupPtr)
2160 xmlMalloc(sizeof(xmlRelaxNGInterleaveGroup));
2161 if (groups[nbgroups] == NULL)
2162 goto error;
2163 groups[nbgroups]->rule = cur;
2164 groups[nbgroups]->defs = xmlRelaxNGGetElements(ctxt, cur);
2165 nbgroups++;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002166 cur = cur->next;
2167 }
2168 list = NULL;
2169#ifdef DEBUG_INTERLEAVE
2170 xmlGenericError(xmlGenericErrorContext, " %d groups\n", nbgroups);
2171#endif
2172
2173 /*
2174 * Let's check that all rules makes a partitions according to 7.4
2175 */
2176 partitions = (xmlRelaxNGPartitionPtr)
2177 xmlMalloc(sizeof(xmlRelaxNGPartition));
2178 if (partitions == NULL)
2179 goto error;
2180 partitions->nbgroups = nbgroups;
2181 for (i = 0;i < nbgroups;i++) {
2182 group = groups[i];
2183 for (j = i+1;j < nbgroups;j++) {
2184 if (groups[j] == NULL)
2185 continue;
2186 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->defs,
2187 groups[j]->defs);
2188 if (ret == 0) {
2189 if (ctxt->error != NULL)
2190 ctxt->error(ctxt->userData,
2191 "Element or text conflicts in interleave\n");
2192 ctxt->nbErrors++;
2193 }
2194 }
2195 }
2196 partitions->groups = groups;
2197
2198 /*
2199 * Free Up the child list, and save the partition list back in the def
2200 */
2201 def->data = partitions;
2202 return;
2203
2204error:
2205 if (ctxt->error != NULL)
2206 ctxt->error(ctxt->userData,
2207 "Out of memory in interleave computation\n");
2208 ctxt->nbErrors++;
2209 if (list == NULL)
2210 xmlFree(list);
2211 if (groups != NULL) {
2212 for (i = 0;i < nbgroups;i++)
2213 if (groups[i] != NULL) {
2214 if (groups[i]->defs != NULL)
2215 xmlFree(groups[i]->defs);
2216 xmlFree(groups[i]);
2217 }
2218 xmlFree(groups);
2219 }
2220 xmlRelaxNGFreePartition(partitions);
2221}
2222
2223/**
2224 * xmlRelaxNGParseInterleave:
2225 * @ctxt: a Relax-NG parser context
2226 * @node: the data node.
2227 *
2228 * parse the content of a RelaxNG interleave node.
2229 *
2230 * Returns the definition pointer or NULL in case of error
2231 */
2232static xmlRelaxNGDefinePtr
2233xmlRelaxNGParseInterleave(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2234 xmlRelaxNGDefinePtr def = NULL;
2235 xmlRelaxNGDefinePtr last = NULL, cur;
2236 xmlNodePtr child;
2237
2238 def = xmlRelaxNGNewDefine(ctxt, node);
2239 if (def == NULL) {
2240 return(NULL);
2241 }
2242 def->type = XML_RELAXNG_INTERLEAVE;
2243
2244 if (ctxt->interleaves == NULL)
2245 ctxt->interleaves = xmlHashCreate(10);
2246 if (ctxt->interleaves == NULL) {
2247 if (ctxt->error != NULL)
2248 ctxt->error(ctxt->userData,
2249 "Failed to create interleaves hash table\n");
2250 ctxt->nbErrors++;
2251 } else {
2252 char name[32];
2253
2254 snprintf(name, 32, "interleave%d", ctxt->nbInterleaves++);
2255 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST name, def) < 0) {
2256 if (ctxt->error != NULL)
2257 ctxt->error(ctxt->userData,
2258 "Failed to add %s to hash table\n", name);
2259 ctxt->nbErrors++;
2260 }
2261 }
2262 child = node->children;
Daniel Veillardd2298792003-02-14 16:54:11 +00002263 if (child == NULL) {
2264 if (ctxt->error != NULL)
2265 ctxt->error(ctxt->userData, "Element interleave is empty\n");
2266 ctxt->nbErrors++;
2267 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002268 while (child != NULL) {
2269 if (IS_RELAXNG(child, "element")) {
2270 cur = xmlRelaxNGParseElement(ctxt, child);
2271 } else {
2272 cur = xmlRelaxNGParsePattern(ctxt, child);
2273 }
2274 if (cur != NULL) {
2275 cur->parent = def;
2276 if (last == NULL) {
2277 def->content = last = cur;
2278 } else {
2279 last->next = cur;
2280 last = cur;
2281 }
2282 }
2283 child = child->next;
2284 }
2285
2286 return(def);
2287}
Daniel Veillard6eadf632003-01-23 18:29:16 +00002288
2289/**
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002290 * xmlRelaxNGParseInclude:
2291 * @ctxt: a Relax-NG parser context
2292 * @node: the include node
2293 *
2294 * Integrate the content of an include node in the current grammar
2295 *
2296 * Returns 0 in case of success or -1 in case of error
2297 */
2298static int
2299xmlRelaxNGParseInclude(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2300 xmlRelaxNGIncludePtr incl;
2301 xmlNodePtr root;
2302 int ret = 0, tmp;
2303
2304 incl = node->_private;
2305 if (incl == NULL) {
2306 if (ctxt->error != NULL)
2307 ctxt->error(ctxt->userData,
2308 "Include node has no data\n");
2309 ctxt->nbErrors++;
2310 return(-1);
2311 }
2312 root = xmlDocGetRootElement(incl->doc);
2313 if (root == NULL) {
2314 if (ctxt->error != NULL)
2315 ctxt->error(ctxt->userData,
2316 "Include document is empty\n");
2317 ctxt->nbErrors++;
2318 return(-1);
2319 }
2320 if (!xmlStrEqual(root->name, BAD_CAST "grammar")) {
2321 if (ctxt->error != NULL)
2322 ctxt->error(ctxt->userData,
2323 "Include document root is not a grammar\n");
2324 ctxt->nbErrors++;
2325 return(-1);
2326 }
2327
2328 /*
2329 * Merge the definition from both the include and the internal list
2330 */
2331 if (root->children != NULL) {
2332 tmp = xmlRelaxNGParseGrammarContent(ctxt, root->children);
2333 if (tmp != 0)
2334 ret = -1;
2335 }
2336 if (node->children != NULL) {
2337 tmp = xmlRelaxNGParseGrammarContent(ctxt, node->children);
2338 if (tmp != 0)
2339 ret = -1;
2340 }
2341 return(ret);
2342}
2343
2344/**
Daniel Veillard276be4a2003-01-24 01:03:34 +00002345 * xmlRelaxNGParseDefine:
2346 * @ctxt: a Relax-NG parser context
2347 * @node: the define node
2348 *
2349 * parse the content of a RelaxNG define element node.
2350 *
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002351 * Returns 0 in case of success or -1 in case of error
Daniel Veillard276be4a2003-01-24 01:03:34 +00002352 */
2353static int
2354xmlRelaxNGParseDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2355 xmlChar *name;
2356 int ret = 0, tmp;
2357 xmlRelaxNGDefinePtr def;
2358 const xmlChar *olddefine;
2359
2360 name = xmlGetProp(node, BAD_CAST "name");
2361 if (name == NULL) {
2362 if (ctxt->error != NULL)
2363 ctxt->error(ctxt->userData,
2364 "define has no name\n");
2365 ctxt->nbErrors++;
2366 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00002367 xmlRelaxNGNormExtSpace(name);
2368 if (xmlValidateNCName(name, 0)) {
2369 if (ctxt->error != NULL)
2370 ctxt->error(ctxt->userData,
2371 "define name '%s' is not an NCName\n",
2372 name);
2373 ctxt->nbErrors++;
2374 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002375 def = xmlRelaxNGNewDefine(ctxt, node);
2376 if (def == NULL) {
2377 xmlFree(name);
2378 return(-1);
2379 }
2380 def->type = XML_RELAXNG_DEF;
2381 def->name = name;
2382 if (node->children == NULL) {
2383 if (ctxt->error != NULL)
2384 ctxt->error(ctxt->userData,
2385 "define has no children\n");
2386 ctxt->nbErrors++;
2387 } else {
2388 olddefine = ctxt->define;
2389 ctxt->define = name;
Daniel Veillard154877e2003-01-30 12:17:05 +00002390 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard276be4a2003-01-24 01:03:34 +00002391 ctxt->define = olddefine;
2392 }
2393 if (ctxt->grammar->defs == NULL)
2394 ctxt->grammar->defs = xmlHashCreate(10);
2395 if (ctxt->grammar->defs == NULL) {
2396 if (ctxt->error != NULL)
2397 ctxt->error(ctxt->userData,
2398 "Could not create definition hash\n");
2399 ctxt->nbErrors++;
2400 ret = -1;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002401 } else {
2402 tmp = xmlHashAddEntry(ctxt->grammar->defs, name, def);
2403 if (tmp < 0) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002404 xmlRelaxNGDefinePtr prev;
2405
2406 prev = xmlHashLookup(ctxt->grammar->defs, name);
2407 if (prev == NULL) {
2408 if (ctxt->error != NULL)
2409 ctxt->error(ctxt->userData,
2410 "Internal error on define aggregation of %s\n",
2411 name);
2412 ctxt->nbErrors++;
2413 ret = -1;
Daniel Veillard154877e2003-01-30 12:17:05 +00002414 } else {
2415 while (prev->nextHash != NULL)
2416 prev = prev->nextHash;
2417 prev->nextHash = def;
2418 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002419 }
2420 }
2421 }
2422 return(ret);
2423}
2424
2425/**
Daniel Veillardfebcca42003-02-16 15:44:18 +00002426 * xmlRelaxNGProcessExternalRef:
2427 * @ctxt: the parser context
2428 * @node: the externlRef node
2429 *
2430 * Process and compile an externlRef node
2431 *
2432 * Returns the xmlRelaxNGDefinePtr or NULL in case of error
2433 */
2434static xmlRelaxNGDefinePtr
2435xmlRelaxNGProcessExternalRef(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2436 xmlRelaxNGDocumentPtr docu;
2437 xmlNodePtr root, tmp;
2438 xmlChar *ns;
2439 int newNs = 0;
2440 xmlRelaxNGDefinePtr def;
2441
2442 docu = node->_private;
2443 if (docu != NULL) {
2444 def = xmlRelaxNGNewDefine(ctxt, node);
2445 if (def == NULL)
2446 return(NULL);
2447 def->type = XML_RELAXNG_EXTERNALREF;
2448
2449 if (docu->content == NULL) {
2450 /*
2451 * Then do the parsing for good
2452 */
2453 root = xmlDocGetRootElement(docu->doc);
2454 if (root == NULL) {
2455 if (ctxt->error != NULL)
2456 ctxt->error(ctxt->userData,
2457 "xmlRelaxNGParse: %s is empty\n",
2458 ctxt->URL);
2459 ctxt->nbErrors++;
2460 return (NULL);
2461 }
2462 /*
2463 * ns transmission rules
2464 */
2465 ns = xmlGetProp(root, BAD_CAST "ns");
2466 if (ns == NULL) {
2467 tmp = node;
2468 while ((tmp != NULL) &&
2469 (tmp->type == XML_ELEMENT_NODE)) {
2470 ns = xmlGetProp(tmp, BAD_CAST "ns");
2471 if (ns != NULL) {
2472 break;
2473 }
2474 tmp = tmp->parent;
2475 }
2476 if (ns != NULL) {
2477 xmlSetProp(root, BAD_CAST "ns", ns);
2478 newNs = 1;
2479 xmlFree(ns);
2480 }
2481 } else {
2482 xmlFree(ns);
2483 }
2484
2485 /*
2486 * Parsing to get a precompiled schemas.
2487 */
2488 docu->schema = xmlRelaxNGParseDocument(ctxt, root);
2489 if ((docu->schema != NULL) &&
2490 (docu->schema->topgrammar != NULL)) {
2491 docu->content = docu->schema->topgrammar->start;
2492 }
2493
2494 /*
2495 * the externalRef may be reused in a different ns context
2496 */
2497 if (newNs == 1) {
2498 xmlUnsetProp(root, BAD_CAST "ns");
2499 }
2500 }
2501 def->content = docu->content;
2502 } else {
2503 def = NULL;
2504 }
2505 return(def);
2506}
2507
2508/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00002509 * xmlRelaxNGParsePattern:
2510 * @ctxt: a Relax-NG parser context
2511 * @node: the pattern node.
2512 *
2513 * parse the content of a RelaxNG pattern node.
2514 *
Daniel Veillard276be4a2003-01-24 01:03:34 +00002515 * Returns the definition pointer or NULL in case of error or if no
2516 * pattern is generated.
Daniel Veillard6eadf632003-01-23 18:29:16 +00002517 */
2518static xmlRelaxNGDefinePtr
2519xmlRelaxNGParsePattern(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2520 xmlRelaxNGDefinePtr def = NULL;
2521
Daniel Veillardd2298792003-02-14 16:54:11 +00002522 if (node == NULL) {
2523 return(NULL);
2524 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002525 if (IS_RELAXNG(node, "element")) {
2526 def = xmlRelaxNGParseElement(ctxt, node);
2527 } else if (IS_RELAXNG(node, "attribute")) {
2528 def = xmlRelaxNGParseAttribute(ctxt, node);
2529 } else if (IS_RELAXNG(node, "empty")) {
2530 def = xmlRelaxNGNewDefine(ctxt, node);
2531 if (def == NULL)
2532 return(NULL);
2533 def->type = XML_RELAXNG_EMPTY;
Daniel Veillardd2298792003-02-14 16:54:11 +00002534 if (node->children != NULL) {
2535 if (ctxt->error != NULL)
2536 ctxt->error(ctxt->userData, "empty: had a child node\n");
2537 ctxt->nbErrors++;
2538 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002539 } else if (IS_RELAXNG(node, "text")) {
2540 def = xmlRelaxNGNewDefine(ctxt, node);
2541 if (def == NULL)
2542 return(NULL);
2543 def->type = XML_RELAXNG_TEXT;
2544 if (node->children != NULL) {
2545 if (ctxt->error != NULL)
2546 ctxt->error(ctxt->userData, "text: had a child node\n");
2547 ctxt->nbErrors++;
2548 }
2549 } else if (IS_RELAXNG(node, "zeroOrMore")) {
2550 def = xmlRelaxNGNewDefine(ctxt, node);
2551 if (def == NULL)
2552 return(NULL);
2553 def->type = XML_RELAXNG_ZEROORMORE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002554 if (node->children == NULL) {
2555 if (ctxt->error != NULL)
2556 ctxt->error(ctxt->userData,
2557 "Element %s is empty\n", node->name);
2558 ctxt->nbErrors++;
2559 } else {
2560 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2561 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002562 } else if (IS_RELAXNG(node, "oneOrMore")) {
2563 def = xmlRelaxNGNewDefine(ctxt, node);
2564 if (def == NULL)
2565 return(NULL);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002566 def->type = XML_RELAXNG_ONEORMORE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002567 if (node->children == NULL) {
2568 if (ctxt->error != NULL)
2569 ctxt->error(ctxt->userData,
2570 "Element %s is empty\n", node->name);
2571 ctxt->nbErrors++;
2572 } else {
2573 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2574 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002575 } else if (IS_RELAXNG(node, "optional")) {
2576 def = xmlRelaxNGNewDefine(ctxt, node);
2577 if (def == NULL)
2578 return(NULL);
2579 def->type = XML_RELAXNG_OPTIONAL;
Daniel Veillardd2298792003-02-14 16:54:11 +00002580 if (node->children == NULL) {
2581 if (ctxt->error != NULL)
2582 ctxt->error(ctxt->userData,
2583 "Element %s is empty\n", node->name);
2584 ctxt->nbErrors++;
2585 } else {
2586 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2587 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002588 } else if (IS_RELAXNG(node, "choice")) {
2589 def = xmlRelaxNGNewDefine(ctxt, node);
2590 if (def == NULL)
2591 return(NULL);
2592 def->type = XML_RELAXNG_CHOICE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002593 if (node->children == NULL) {
2594 if (ctxt->error != NULL)
2595 ctxt->error(ctxt->userData,
2596 "Element %s is empty\n", node->name);
2597 ctxt->nbErrors++;
2598 } else {
2599 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2600 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002601 } else if (IS_RELAXNG(node, "group")) {
2602 def = xmlRelaxNGNewDefine(ctxt, node);
2603 if (def == NULL)
2604 return(NULL);
2605 def->type = XML_RELAXNG_GROUP;
Daniel Veillardd2298792003-02-14 16:54:11 +00002606 if (node->children == NULL) {
2607 if (ctxt->error != NULL)
2608 ctxt->error(ctxt->userData,
2609 "Element %s is empty\n", node->name);
2610 ctxt->nbErrors++;
2611 } else {
2612 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2613 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002614 } else if (IS_RELAXNG(node, "ref")) {
2615 def = xmlRelaxNGNewDefine(ctxt, node);
2616 if (def == NULL)
2617 return(NULL);
2618 def->type = XML_RELAXNG_REF;
2619 def->name = xmlGetProp(node, BAD_CAST "name");
2620 if (def->name == NULL) {
2621 if (ctxt->error != NULL)
2622 ctxt->error(ctxt->userData,
2623 "ref has no name\n");
2624 ctxt->nbErrors++;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002625 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00002626 xmlRelaxNGNormExtSpace(def->name);
2627 if (xmlValidateNCName(def->name, 0)) {
2628 if (ctxt->error != NULL)
2629 ctxt->error(ctxt->userData,
2630 "ref name '%s' is not an NCName\n",
2631 def->name);
2632 ctxt->nbErrors++;
2633 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002634 }
2635 if (node->children != NULL) {
2636 if (ctxt->error != NULL)
2637 ctxt->error(ctxt->userData,
2638 "ref is not empty\n");
2639 ctxt->nbErrors++;
2640 }
2641 if (ctxt->grammar->refs == NULL)
2642 ctxt->grammar->refs = xmlHashCreate(10);
2643 if (ctxt->grammar->refs == NULL) {
2644 if (ctxt->error != NULL)
2645 ctxt->error(ctxt->userData,
2646 "Could not create references hash\n");
2647 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002648 def = NULL;
2649 } else {
2650 int tmp;
2651
2652 tmp = xmlHashAddEntry(ctxt->grammar->refs, def->name, def);
2653 if (tmp < 0) {
2654 xmlRelaxNGDefinePtr prev;
2655
2656 prev = (xmlRelaxNGDefinePtr)
2657 xmlHashLookup(ctxt->grammar->refs, def->name);
2658 if (prev == NULL) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00002659 if (def->name != NULL) {
2660 if (ctxt->error != NULL)
2661 ctxt->error(ctxt->userData,
2662 "Error refs definitions '%s'\n",
2663 def->name);
2664 } else {
2665 if (ctxt->error != NULL)
2666 ctxt->error(ctxt->userData,
2667 "Error refs definitions\n");
2668 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002669 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002670 def = NULL;
2671 } else {
2672 def->nextHash = prev->nextHash;
2673 prev->nextHash = def;
2674 }
2675 }
2676 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002677 } else if (IS_RELAXNG(node, "data")) {
2678 def = xmlRelaxNGParseData(ctxt, node);
Daniel Veillardd2298792003-02-14 16:54:11 +00002679#if 0
Daniel Veillard276be4a2003-01-24 01:03:34 +00002680 } else if (IS_RELAXNG(node, "define")) {
2681 xmlRelaxNGParseDefine(ctxt, node);
2682 def = NULL;
Daniel Veillardd2298792003-02-14 16:54:11 +00002683#endif
Daniel Veillardedc91922003-01-26 00:52:04 +00002684 } else if (IS_RELAXNG(node, "value")) {
2685 def = xmlRelaxNGParseValue(ctxt, node);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002686 } else if (IS_RELAXNG(node, "list")) {
2687 def = xmlRelaxNGNewDefine(ctxt, node);
2688 if (def == NULL)
2689 return(NULL);
2690 def->type = XML_RELAXNG_LIST;
Daniel Veillardd2298792003-02-14 16:54:11 +00002691 if (node->children == NULL) {
2692 if (ctxt->error != NULL)
2693 ctxt->error(ctxt->userData,
2694 "Element %s is empty\n", node->name);
2695 ctxt->nbErrors++;
2696 } else {
2697 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2698 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002699 } else if (IS_RELAXNG(node, "interleave")) {
2700 def = xmlRelaxNGParseInterleave(ctxt, node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002701 } else if (IS_RELAXNG(node, "externalRef")) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00002702 def = xmlRelaxNGProcessExternalRef(ctxt, node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002703 } else if (IS_RELAXNG(node, "notAllowed")) {
2704 def = xmlRelaxNGNewDefine(ctxt, node);
2705 if (def == NULL)
2706 return(NULL);
2707 def->type = XML_RELAXNG_NOT_ALLOWED;
2708 if (node->children != NULL) {
2709 if (ctxt->error != NULL)
2710 ctxt->error(ctxt->userData,
2711 "xmlRelaxNGParse: notAllowed element is not empty\n");
2712 ctxt->nbErrors++;
2713 }
Daniel Veillard419a7682003-02-03 23:22:49 +00002714 } else if (IS_RELAXNG(node, "grammar")) {
2715 xmlRelaxNGGrammarPtr grammar, old;
2716 xmlRelaxNGGrammarPtr oldparent;
2717
2718 oldparent = ctxt->parentgrammar;
2719 old = ctxt->grammar;
2720 ctxt->parentgrammar = old;
2721 grammar = xmlRelaxNGParseGrammar(ctxt, node->children);
2722 if (old != NULL) {
2723 ctxt->grammar = old;
2724 ctxt->parentgrammar = oldparent;
2725 if (grammar != NULL) {
2726 grammar->next = old->next;
2727 old->next = grammar;
2728 }
2729 }
2730 if (grammar != NULL)
2731 def = grammar->start;
2732 else
2733 def = NULL;
2734 } else if (IS_RELAXNG(node, "parentRef")) {
2735 if (ctxt->parentgrammar == NULL) {
2736 if (ctxt->error != NULL)
2737 ctxt->error(ctxt->userData,
2738 "Use of parentRef without a parent grammar\n");
2739 ctxt->nbErrors++;
2740 return(NULL);
2741 }
2742 def = xmlRelaxNGNewDefine(ctxt, node);
2743 if (def == NULL)
2744 return(NULL);
2745 def->type = XML_RELAXNG_PARENTREF;
2746 def->name = xmlGetProp(node, BAD_CAST "name");
2747 if (def->name == NULL) {
2748 if (ctxt->error != NULL)
2749 ctxt->error(ctxt->userData,
2750 "parentRef has no name\n");
2751 ctxt->nbErrors++;
Daniel Veillardd2298792003-02-14 16:54:11 +00002752 } else {
2753 xmlRelaxNGNormExtSpace(def->name);
2754 if (xmlValidateNCName(def->name, 0)) {
2755 if (ctxt->error != NULL)
2756 ctxt->error(ctxt->userData,
2757 "parentRef name '%s' is not an NCName\n",
2758 def->name);
2759 ctxt->nbErrors++;
2760 }
Daniel Veillard419a7682003-02-03 23:22:49 +00002761 }
2762 if (node->children != NULL) {
2763 if (ctxt->error != NULL)
2764 ctxt->error(ctxt->userData,
2765 "parentRef is not empty\n");
2766 ctxt->nbErrors++;
2767 }
2768 if (ctxt->parentgrammar->refs == NULL)
2769 ctxt->parentgrammar->refs = xmlHashCreate(10);
2770 if (ctxt->parentgrammar->refs == NULL) {
2771 if (ctxt->error != NULL)
2772 ctxt->error(ctxt->userData,
2773 "Could not create references hash\n");
2774 ctxt->nbErrors++;
2775 def = NULL;
Daniel Veillardd2298792003-02-14 16:54:11 +00002776 } else if (def->name != NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +00002777 int tmp;
2778
2779 tmp = xmlHashAddEntry(ctxt->parentgrammar->refs, def->name, def);
2780 if (tmp < 0) {
2781 xmlRelaxNGDefinePtr prev;
2782
2783 prev = (xmlRelaxNGDefinePtr)
2784 xmlHashLookup(ctxt->parentgrammar->refs, def->name);
2785 if (prev == NULL) {
2786 if (ctxt->error != NULL)
2787 ctxt->error(ctxt->userData,
2788 "Internal error parentRef definitions '%s'\n",
2789 def->name);
2790 ctxt->nbErrors++;
2791 def = NULL;
2792 } else {
2793 def->nextHash = prev->nextHash;
2794 prev->nextHash = def;
2795 }
2796 }
2797 }
Daniel Veillardd2298792003-02-14 16:54:11 +00002798 } else if (IS_RELAXNG(node, "mixed")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00002799 if (node->children == NULL) {
2800 if (ctxt->error != NULL)
2801 ctxt->error(ctxt->userData,
2802 "Mixed is empty\n");
2803 ctxt->nbErrors++;
2804 def = NULL;
Daniel Veillard416589a2003-02-17 17:25:42 +00002805 } else {
2806 def = xmlRelaxNGParseInterleave(ctxt, node);
2807 if (def != NULL) {
2808 xmlRelaxNGDefinePtr tmp;
Daniel Veillard2df2de22003-02-17 23:34:33 +00002809
2810 if ((def->content != NULL) && (def->content->next != NULL)) {
2811 tmp = xmlRelaxNGNewDefine(ctxt, node);
2812 if (tmp != NULL) {
2813 tmp->type = XML_RELAXNG_GROUP;
2814 tmp->content = def->content;
2815 def->content = tmp;
2816 }
2817 }
2818
Daniel Veillard416589a2003-02-17 17:25:42 +00002819 tmp = xmlRelaxNGNewDefine(ctxt, node);
2820 if (tmp == NULL)
2821 return(def);
2822 tmp->type = XML_RELAXNG_TEXT;
2823 tmp->next = def->content;
2824 def->content = tmp;
2825 }
2826 }
Daniel Veillardd2298792003-02-14 16:54:11 +00002827 } else {
2828 if (ctxt->error != NULL)
2829 ctxt->error(ctxt->userData,
2830 "Unexpected node %s is not a pattern\n",
2831 node->name);
2832 ctxt->nbErrors++;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002833 def = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002834 }
2835 return(def);
2836}
2837
2838/**
2839 * xmlRelaxNGParseAttribute:
2840 * @ctxt: a Relax-NG parser context
2841 * @node: the element node
2842 *
2843 * parse the content of a RelaxNG attribute node.
2844 *
2845 * Returns the definition pointer or NULL in case of error.
2846 */
2847static xmlRelaxNGDefinePtr
2848xmlRelaxNGParseAttribute(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
Daniel Veillardd2298792003-02-14 16:54:11 +00002849 xmlRelaxNGDefinePtr ret, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002850 xmlNodePtr child;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002851 int old_flags;
2852
2853 ret = xmlRelaxNGNewDefine(ctxt, node);
2854 if (ret == NULL)
2855 return(NULL);
2856 ret->type = XML_RELAXNG_ATTRIBUTE;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002857 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002858 child = node->children;
2859 if (child == NULL) {
2860 if (ctxt->error != NULL)
2861 ctxt->error(ctxt->userData,
2862 "xmlRelaxNGParseattribute: attribute has no children\n");
2863 ctxt->nbErrors++;
2864 return(ret);
2865 }
2866 old_flags = ctxt->flags;
2867 ctxt->flags |= XML_RELAXNG_IN_ATTRIBUTE;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002868 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
2869 if (cur != NULL)
2870 child = child->next;
2871
Daniel Veillardd2298792003-02-14 16:54:11 +00002872 if (child != NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00002873 cur = xmlRelaxNGParsePattern(ctxt, child);
2874 if (cur != NULL) {
2875 switch (cur->type) {
2876 case XML_RELAXNG_EMPTY:
2877 case XML_RELAXNG_NOT_ALLOWED:
2878 case XML_RELAXNG_TEXT:
2879 case XML_RELAXNG_ELEMENT:
2880 case XML_RELAXNG_DATATYPE:
2881 case XML_RELAXNG_VALUE:
2882 case XML_RELAXNG_LIST:
2883 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00002884 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002885 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00002886 case XML_RELAXNG_DEF:
2887 case XML_RELAXNG_ONEORMORE:
2888 case XML_RELAXNG_ZEROORMORE:
2889 case XML_RELAXNG_OPTIONAL:
2890 case XML_RELAXNG_CHOICE:
2891 case XML_RELAXNG_GROUP:
2892 case XML_RELAXNG_INTERLEAVE:
Daniel Veillardd2298792003-02-14 16:54:11 +00002893 ret->content = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002894 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002895 break;
2896 case XML_RELAXNG_ATTRIBUTE:
Daniel Veillardd2298792003-02-14 16:54:11 +00002897 if (ctxt->error != NULL)
2898 ctxt->error(ctxt->userData,
2899 "attribute has an attribute child\n");
2900 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002901 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002902 case XML_RELAXNG_START:
Daniel Veillard8fe98712003-02-19 00:19:14 +00002903 case XML_RELAXNG_PARAM:
Daniel Veillard144fae12003-02-03 13:17:57 +00002904 case XML_RELAXNG_EXCEPT:
Daniel Veillardd2298792003-02-14 16:54:11 +00002905 if (ctxt->error != NULL)
2906 ctxt->error(ctxt->userData,
2907 "attribute has invalid content\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002908 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002909 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002910 }
2911 }
2912 child = child->next;
2913 }
Daniel Veillardd2298792003-02-14 16:54:11 +00002914 if (child != NULL) {
2915 if (ctxt->error != NULL)
2916 ctxt->error(ctxt->userData, "attribute has multiple children\n");
2917 ctxt->nbErrors++;
2918 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002919 ctxt->flags = old_flags;
2920 return(ret);
2921}
2922
2923/**
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002924 * xmlRelaxNGParseExceptNameClass:
2925 * @ctxt: a Relax-NG parser context
2926 * @node: the except node
Daniel Veillard144fae12003-02-03 13:17:57 +00002927 * @attr: 1 if within an attribute, 0 if within an element
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002928 *
2929 * parse the content of a RelaxNG nameClass node.
2930 *
2931 * Returns the definition pointer or NULL in case of error.
2932 */
2933static xmlRelaxNGDefinePtr
Daniel Veillard144fae12003-02-03 13:17:57 +00002934xmlRelaxNGParseExceptNameClass(xmlRelaxNGParserCtxtPtr ctxt,
2935 xmlNodePtr node, int attr) {
2936 xmlRelaxNGDefinePtr ret, cur, last = NULL;
2937 xmlNodePtr child;
2938
Daniel Veillardd2298792003-02-14 16:54:11 +00002939 if (!IS_RELAXNG(node, "except")) {
2940 if (ctxt->error != NULL)
2941 ctxt->error(ctxt->userData,
2942 "Expecting an except node\n");
2943 ctxt->nbErrors++;
Daniel Veillard144fae12003-02-03 13:17:57 +00002944 return(NULL);
Daniel Veillardd2298792003-02-14 16:54:11 +00002945 }
2946 if (node->next != NULL) {
2947 if (ctxt->error != NULL)
2948 ctxt->error(ctxt->userData,
2949 "exceptNameClass allows only a single except node\n");
2950 ctxt->nbErrors++;
2951 }
Daniel Veillard144fae12003-02-03 13:17:57 +00002952 if (node->children == NULL) {
2953 if (ctxt->error != NULL)
2954 ctxt->error(ctxt->userData,
2955 "except has no content\n");
2956 ctxt->nbErrors++;
2957 return(NULL);
2958 }
2959
2960 ret = xmlRelaxNGNewDefine(ctxt, node);
2961 if (ret == NULL)
2962 return(NULL);
2963 ret->type = XML_RELAXNG_EXCEPT;
2964 child = node->children;
2965 while (child != NULL) {
2966 cur = xmlRelaxNGNewDefine(ctxt, child);
2967 if (cur == NULL)
2968 break;
2969 if (attr)
2970 cur->type = XML_RELAXNG_ATTRIBUTE;
2971 else
2972 cur->type = XML_RELAXNG_ELEMENT;
2973
Daniel Veillard419a7682003-02-03 23:22:49 +00002974 if (xmlRelaxNGParseNameClass(ctxt, child, cur) != NULL) {
Daniel Veillard144fae12003-02-03 13:17:57 +00002975 if (last == NULL) {
2976 ret->content = cur;
2977 } else {
2978 last->next = cur;
2979 }
2980 last = cur;
2981 }
2982 child = child->next;
2983 }
2984
2985 return(ret);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002986}
2987
2988/**
2989 * xmlRelaxNGParseNameClass:
2990 * @ctxt: a Relax-NG parser context
2991 * @node: the nameClass node
2992 * @def: the current definition
2993 *
2994 * parse the content of a RelaxNG nameClass node.
2995 *
2996 * Returns the definition pointer or NULL in case of error.
2997 */
2998static xmlRelaxNGDefinePtr
2999xmlRelaxNGParseNameClass(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
3000 xmlRelaxNGDefinePtr def) {
3001 xmlRelaxNGDefinePtr ret = def;
3002 xmlChar *val;
3003
3004 if (IS_RELAXNG(node, "name")) {
3005 val = xmlNodeGetContent(node);
Daniel Veillardd2298792003-02-14 16:54:11 +00003006 xmlRelaxNGNormExtSpace(val);
3007 if (xmlValidateNCName(val, 0)) {
3008 if (ctxt->error != NULL) {
3009 if (node->parent != NULL)
3010 ctxt->error(ctxt->userData,
3011 "Element %s name '%s' is not an NCName\n",
3012 node->parent->name, val);
3013 else
3014 ctxt->error(ctxt->userData,
3015 "name '%s' is not an NCName\n",
3016 val);
3017 }
3018 ctxt->nbErrors++;
3019 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003020 ret->name = val;
3021 val = xmlGetProp(node, BAD_CAST "ns");
3022 ret->ns = val;
Daniel Veillard416589a2003-02-17 17:25:42 +00003023 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3024 (val != NULL) &&
3025 (xmlStrEqual(val, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
3026 ctxt->error(ctxt->userData,
3027 "Attribute with namespace '%s' is not allowed\n",
3028 val);
3029 ctxt->nbErrors++;
3030 }
3031 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3032 (val != NULL) &&
3033 (val[0] == 0) &&
3034 (xmlStrEqual(ret->name, BAD_CAST "xmlns"))) {
3035 ctxt->error(ctxt->userData,
3036 "Attribute with QName 'xmlns' is not allowed\n",
3037 val);
3038 ctxt->nbErrors++;
3039 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003040 } else if (IS_RELAXNG(node, "anyName")) {
3041 ret->name = NULL;
3042 ret->ns = NULL;
3043 if (node->children != NULL) {
3044 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00003045 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
3046 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003047 }
3048 } else if (IS_RELAXNG(node, "nsName")) {
3049 ret->name = NULL;
3050 ret->ns = xmlGetProp(node, BAD_CAST "ns");
3051 if (ret->ns == NULL) {
3052 if (ctxt->error != NULL)
3053 ctxt->error(ctxt->userData,
3054 "nsName has no ns attribute\n");
3055 ctxt->nbErrors++;
3056 }
Daniel Veillard416589a2003-02-17 17:25:42 +00003057 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3058 (ret->ns != NULL) &&
3059 (xmlStrEqual(ret->ns, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
3060 ctxt->error(ctxt->userData,
3061 "Attribute with namespace '%s' is not allowed\n",
3062 ret->ns);
3063 ctxt->nbErrors++;
3064 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003065 if (node->children != NULL) {
3066 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00003067 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
3068 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003069 }
3070 } else if (IS_RELAXNG(node, "choice")) {
Daniel Veillardd4310742003-02-18 21:12:46 +00003071 ret = xmlRelaxNGNewDefine(ctxt, node);
3072 if (ret == NULL)
3073 return(NULL);
3074 ret->parent = def;
3075 ret->type = XML_RELAXNG_CHOICE;
3076
Daniel Veillardd2298792003-02-14 16:54:11 +00003077 if (node->children == NULL) {
3078 if (ctxt->error != NULL)
3079 ctxt->error(ctxt->userData,
3080 "Element choice is empty\n");
3081 ctxt->nbErrors++;
3082 } else {
3083 xmlNodePtr child;
3084 xmlRelaxNGDefinePtr last = NULL, tmp;
3085
3086 child = node->children;
3087 while (child != NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00003088 tmp = xmlRelaxNGParseNameClass(ctxt, child, ret);
Daniel Veillardd2298792003-02-14 16:54:11 +00003089 if (tmp != NULL) {
3090 if (last == NULL) {
3091 last = ret->nameClass = tmp;
3092 } else {
3093 last->next = tmp;
3094 last = tmp;
3095 }
3096 }
3097 child = child->next;
3098 }
3099 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003100 } else {
3101 if (ctxt->error != NULL)
3102 ctxt->error(ctxt->userData,
3103 "expecting name, anyName, nsName or choice : got %s\n",
3104 node->name);
3105 ctxt->nbErrors++;
3106 return(NULL);
3107 }
3108 return(ret);
3109}
3110
3111/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00003112 * xmlRelaxNGParseElement:
3113 * @ctxt: a Relax-NG parser context
3114 * @node: the element node
3115 *
3116 * parse the content of a RelaxNG element node.
3117 *
3118 * Returns the definition pointer or NULL in case of error.
3119 */
3120static xmlRelaxNGDefinePtr
3121xmlRelaxNGParseElement(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3122 xmlRelaxNGDefinePtr ret, cur, last;
3123 xmlNodePtr child;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003124 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003125
3126 ret = xmlRelaxNGNewDefine(ctxt, node);
3127 if (ret == NULL)
3128 return(NULL);
3129 ret->type = XML_RELAXNG_ELEMENT;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003130 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003131 child = node->children;
3132 if (child == NULL) {
3133 if (ctxt->error != NULL)
3134 ctxt->error(ctxt->userData,
3135 "xmlRelaxNGParseElement: element has no children\n");
3136 ctxt->nbErrors++;
3137 return(ret);
3138 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003139 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
3140 if (cur != NULL)
3141 child = child->next;
3142
Daniel Veillard6eadf632003-01-23 18:29:16 +00003143 if (child == NULL) {
3144 if (ctxt->error != NULL)
3145 ctxt->error(ctxt->userData,
3146 "xmlRelaxNGParseElement: element has no content\n");
3147 ctxt->nbErrors++;
3148 return(ret);
3149 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003150 olddefine = ctxt->define;
3151 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003152 last = NULL;
3153 while (child != NULL) {
3154 cur = xmlRelaxNGParsePattern(ctxt, child);
3155 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003156 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003157 switch (cur->type) {
3158 case XML_RELAXNG_EMPTY:
3159 case XML_RELAXNG_NOT_ALLOWED:
3160 case XML_RELAXNG_TEXT:
3161 case XML_RELAXNG_ELEMENT:
3162 case XML_RELAXNG_DATATYPE:
3163 case XML_RELAXNG_VALUE:
3164 case XML_RELAXNG_LIST:
3165 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00003166 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003167 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00003168 case XML_RELAXNG_DEF:
3169 case XML_RELAXNG_ZEROORMORE:
3170 case XML_RELAXNG_ONEORMORE:
3171 case XML_RELAXNG_OPTIONAL:
3172 case XML_RELAXNG_CHOICE:
3173 case XML_RELAXNG_GROUP:
3174 case XML_RELAXNG_INTERLEAVE:
3175 if (last == NULL) {
3176 ret->content = last = cur;
3177 } else {
3178 if ((last->type == XML_RELAXNG_ELEMENT) &&
3179 (ret->content == last)) {
3180 ret->content = xmlRelaxNGNewDefine(ctxt, node);
3181 if (ret->content != NULL) {
3182 ret->content->type = XML_RELAXNG_GROUP;
3183 ret->content->content = last;
3184 } else {
3185 ret->content = last;
3186 }
3187 }
3188 last->next = cur;
3189 last = cur;
3190 }
3191 break;
3192 case XML_RELAXNG_ATTRIBUTE:
3193 cur->next = ret->attrs;
3194 ret->attrs = cur;
3195 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003196 case XML_RELAXNG_START:
Daniel Veillard8fe98712003-02-19 00:19:14 +00003197 case XML_RELAXNG_PARAM:
Daniel Veillard144fae12003-02-03 13:17:57 +00003198 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003199 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003200 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003201 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003202 }
3203 }
3204 child = child->next;
3205 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003206 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003207 return(ret);
3208}
3209
3210/**
3211 * xmlRelaxNGParsePatterns:
3212 * @ctxt: a Relax-NG parser context
3213 * @nodes: list of nodes
Daniel Veillard154877e2003-01-30 12:17:05 +00003214 * @group: use an implicit <group> for elements
Daniel Veillard6eadf632003-01-23 18:29:16 +00003215 *
3216 * parse the content of a RelaxNG start node.
3217 *
3218 * Returns the definition pointer or NULL in case of error.
3219 */
3220static xmlRelaxNGDefinePtr
Daniel Veillard154877e2003-01-30 12:17:05 +00003221xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes,
3222 int group) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003223 xmlRelaxNGDefinePtr def = NULL, last = NULL, cur, parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003224
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003225 parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003226 while (nodes != NULL) {
3227 if (IS_RELAXNG(nodes, "element")) {
3228 cur = xmlRelaxNGParseElement(ctxt, nodes);
3229 if (def == NULL) {
3230 def = last = cur;
3231 } else {
Daniel Veillard154877e2003-01-30 12:17:05 +00003232 if ((group == 1) && (def->type == XML_RELAXNG_ELEMENT) &&
3233 (def == last)) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003234 def = xmlRelaxNGNewDefine(ctxt, nodes);
3235 def->type = XML_RELAXNG_GROUP;
3236 def->content = last;
3237 }
3238 last->next = cur;
3239 last = cur;
3240 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003241 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003242 } else {
3243 cur = xmlRelaxNGParsePattern(ctxt, nodes);
Daniel Veillard419a7682003-02-03 23:22:49 +00003244 if (cur != NULL) {
3245 if (def == NULL) {
3246 def = last = cur;
3247 } else {
3248 last->next = cur;
3249 last = cur;
3250 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003251 }
3252 }
3253 nodes = nodes->next;
3254 }
3255 return(def);
3256}
3257
3258/**
3259 * xmlRelaxNGParseStart:
3260 * @ctxt: a Relax-NG parser context
3261 * @nodes: start children nodes
3262 *
3263 * parse the content of a RelaxNG start node.
3264 *
3265 * Returns 0 in case of success, -1 in case of error
3266 */
3267static int
3268xmlRelaxNGParseStart(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
3269 int ret = 0;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003270 xmlRelaxNGDefinePtr def = NULL, last;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003271
Daniel Veillardd2298792003-02-14 16:54:11 +00003272 if (nodes == NULL) {
3273 if (ctxt->error != NULL)
3274 ctxt->error(ctxt->userData,
3275 "start has no children\n");
3276 ctxt->nbErrors++;
3277 return(-1);
3278 }
3279 if (IS_RELAXNG(nodes, "empty")) {
3280 def = xmlRelaxNGNewDefine(ctxt, nodes);
3281 if (def == NULL)
3282 return(-1);
3283 def->type = XML_RELAXNG_EMPTY;
3284 if (nodes->children != NULL) {
3285 if (ctxt->error != NULL)
3286 ctxt->error(ctxt->userData, "element empty is not empty\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003287 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003288 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003289 } else if (IS_RELAXNG(nodes, "notAllowed")) {
3290 def = xmlRelaxNGNewDefine(ctxt, nodes);
3291 if (def == NULL)
3292 return(-1);
3293 def->type = XML_RELAXNG_NOT_ALLOWED;
3294 if (nodes->children != NULL) {
3295 if (ctxt->error != NULL)
3296 ctxt->error(ctxt->userData,
3297 "element notAllowed is not empty\n");
3298 ctxt->nbErrors++;
3299 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003300 } else {
3301 def = xmlRelaxNGParsePatterns(ctxt, nodes, 1);
Daniel Veillard2df2de22003-02-17 23:34:33 +00003302 }
3303 if (ctxt->grammar->start != NULL) {
3304 last = ctxt->grammar->start;
3305 while (last->next != NULL)
3306 last = last->next;
3307 last->next = def;
3308 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00003309 ctxt->grammar->start = def;
3310 }
3311 nodes = nodes->next;
3312 if (nodes != NULL) {
3313 if (ctxt->error != NULL)
3314 ctxt->error(ctxt->userData,
3315 "start more than one children\n");
3316 ctxt->nbErrors++;
3317 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003318 }
3319 return(ret);
3320}
3321
3322/**
3323 * xmlRelaxNGParseGrammarContent:
3324 * @ctxt: a Relax-NG parser context
3325 * @nodes: grammar children nodes
3326 *
3327 * parse the content of a RelaxNG grammar node.
3328 *
3329 * Returns 0 in case of success, -1 in case of error
3330 */
3331static int
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003332xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003333{
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003334 int ret = 0, tmp;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003335
3336 if (nodes == NULL) {
3337 if (ctxt->error != NULL)
3338 ctxt->error(ctxt->userData,
3339 "grammar has no children\n");
3340 ctxt->nbErrors++;
3341 return(-1);
3342 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003343 while (nodes != NULL) {
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003344 if (IS_RELAXNG(nodes, "start")) {
3345 if (nodes->children == NULL) {
3346 if (ctxt->error != NULL)
3347 ctxt->error(ctxt->userData,
Daniel Veillardd2298792003-02-14 16:54:11 +00003348 "start has no children\n");
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003349 ctxt->nbErrors++;
3350 } else {
3351 tmp = xmlRelaxNGParseStart(ctxt, nodes->children);
3352 if (tmp != 0)
3353 ret = -1;
3354 }
3355 } else if (IS_RELAXNG(nodes, "define")) {
3356 tmp = xmlRelaxNGParseDefine(ctxt, nodes);
3357 if (tmp != 0)
3358 ret = -1;
3359 } else if (IS_RELAXNG(nodes, "include")) {
3360 tmp = xmlRelaxNGParseInclude(ctxt, nodes);
3361 if (tmp != 0)
3362 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003363 } else {
3364 if (ctxt->error != NULL)
3365 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003366 "grammar has unexpected child %s\n", nodes->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003367 ctxt->nbErrors++;
3368 ret = -1;
3369 }
3370 nodes = nodes->next;
3371 }
3372 return (ret);
3373}
3374
3375/**
3376 * xmlRelaxNGCheckReference:
3377 * @ref: the ref
3378 * @ctxt: a Relax-NG parser context
3379 * @name: the name associated to the defines
3380 *
3381 * Applies the 4.17. combine attribute rule for all the define
3382 * element of a given grammar using the same name.
3383 */
3384static void
3385xmlRelaxNGCheckReference(xmlRelaxNGDefinePtr ref,
3386 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
3387 xmlRelaxNGGrammarPtr grammar;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003388 xmlRelaxNGDefinePtr def, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003389
3390 grammar = ctxt->grammar;
3391 if (grammar == NULL) {
3392 if (ctxt->error != NULL)
3393 ctxt->error(ctxt->userData,
3394 "Internal error: no grammar in CheckReference %s\n",
3395 name);
3396 ctxt->nbErrors++;
3397 return;
3398 }
3399 if (ref->content != NULL) {
3400 if (ctxt->error != NULL)
3401 ctxt->error(ctxt->userData,
3402 "Internal error: reference has content in CheckReference %s\n",
3403 name);
3404 ctxt->nbErrors++;
3405 return;
3406 }
3407 if (grammar->defs != NULL) {
3408 def = xmlHashLookup(grammar->defs, name);
3409 if (def != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00003410 cur = ref;
3411 while (cur != NULL) {
3412 cur->content = def;
3413 cur = cur->nextHash;
3414 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003415 } else {
Daniel Veillardd4310742003-02-18 21:12:46 +00003416 if (ctxt->error != NULL)
3417 ctxt->error(ctxt->userData,
3418 "Reference %s has no matching definition\n",
3419 name);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003420 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003421 }
Daniel Veillardd4310742003-02-18 21:12:46 +00003422 } else {
3423 if (ctxt->error != NULL)
3424 ctxt->error(ctxt->userData,
3425 "Reference %s has no matching definition\n",
3426 name);
3427 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003428 }
3429 /*
3430 * TODO: make a closure and verify there is no loop !
3431 */
3432}
3433
3434/**
3435 * xmlRelaxNGCheckCombine:
3436 * @define: the define(s) list
3437 * @ctxt: a Relax-NG parser context
3438 * @name: the name associated to the defines
3439 *
3440 * Applies the 4.17. combine attribute rule for all the define
3441 * element of a given grammar using the same name.
3442 */
3443static void
3444xmlRelaxNGCheckCombine(xmlRelaxNGDefinePtr define,
3445 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
3446 xmlChar *combine;
3447 int choiceOrInterleave = -1;
3448 int missing = 0;
3449 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
3450
3451 if (define->nextHash == NULL)
3452 return;
3453 cur = define;
3454 while (cur != NULL) {
3455 combine = xmlGetProp(cur->node, BAD_CAST "combine");
3456 if (combine != NULL) {
3457 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3458 if (choiceOrInterleave == -1)
3459 choiceOrInterleave = 1;
3460 else if (choiceOrInterleave == 0) {
3461 if (ctxt->error != NULL)
3462 ctxt->error(ctxt->userData,
3463 "Defines for %s use both 'choice' and 'interleave'\n",
3464 name);
3465 ctxt->nbErrors++;
3466 }
Daniel Veillard154877e2003-01-30 12:17:05 +00003467 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003468 if (choiceOrInterleave == -1)
3469 choiceOrInterleave = 0;
3470 else if (choiceOrInterleave == 1) {
3471 if (ctxt->error != NULL)
3472 ctxt->error(ctxt->userData,
3473 "Defines for %s use both 'choice' and 'interleave'\n",
3474 name);
3475 ctxt->nbErrors++;
3476 }
3477 } else {
3478 if (ctxt->error != NULL)
3479 ctxt->error(ctxt->userData,
3480 "Defines for %s use unknown combine value '%s''\n",
3481 name, combine);
3482 ctxt->nbErrors++;
3483 }
3484 xmlFree(combine);
3485 } else {
3486 if (missing == 0)
3487 missing = 1;
3488 else {
3489 if (ctxt->error != NULL)
3490 ctxt->error(ctxt->userData,
3491 "Some defines for %s lacks the combine attribute\n",
3492 name);
3493 ctxt->nbErrors++;
3494 }
3495 }
3496
3497 cur = cur->nextHash;
3498 }
3499#ifdef DEBUG
3500 xmlGenericError(xmlGenericErrorContext,
3501 "xmlRelaxNGCheckCombine(): merging %s defines: %d\n",
3502 name, choiceOrInterleave);
3503#endif
3504 if (choiceOrInterleave == -1)
3505 choiceOrInterleave = 0;
3506 cur = xmlRelaxNGNewDefine(ctxt, define->node);
3507 if (cur == NULL)
3508 return;
3509 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003510 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard154877e2003-01-30 12:17:05 +00003511 else
3512 cur->type = XML_RELAXNG_CHOICE;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003513 tmp = define;
3514 last = NULL;
3515 while (tmp != NULL) {
3516 if (tmp->content != NULL) {
3517 if (tmp->content->next != NULL) {
3518 /*
3519 * we need first to create a wrapper.
3520 */
3521 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
3522 if (tmp2 == NULL)
3523 break;
3524 tmp2->type = XML_RELAXNG_GROUP;
3525 tmp2->content = tmp->content;
3526 } else {
3527 tmp2 = tmp->content;
3528 }
3529 if (last == NULL) {
3530 cur->content = tmp2;
3531 } else {
3532 last->next = tmp2;
3533 }
3534 last = tmp2;
3535 tmp->content = NULL;
3536 }
3537 tmp = tmp->nextHash;
3538 }
3539 define->content = cur;
Daniel Veillard154877e2003-01-30 12:17:05 +00003540 if (choiceOrInterleave == 0) {
3541 if (ctxt->interleaves == NULL)
3542 ctxt->interleaves = xmlHashCreate(10);
3543 if (ctxt->interleaves == NULL) {
3544 if (ctxt->error != NULL)
3545 ctxt->error(ctxt->userData,
3546 "Failed to create interleaves hash table\n");
3547 ctxt->nbErrors++;
3548 } else {
3549 char tmpname[32];
3550
3551 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
3552 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
3553 if (ctxt->error != NULL)
3554 ctxt->error(ctxt->userData,
3555 "Failed to add %s to hash table\n", tmpname);
3556 ctxt->nbErrors++;
3557 }
3558 }
3559 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003560}
3561
3562/**
3563 * xmlRelaxNGCombineStart:
3564 * @ctxt: a Relax-NG parser context
3565 * @grammar: the grammar
3566 *
3567 * Applies the 4.17. combine rule for all the start
3568 * element of a given grammar.
3569 */
3570static void
3571xmlRelaxNGCombineStart(xmlRelaxNGParserCtxtPtr ctxt,
3572 xmlRelaxNGGrammarPtr grammar) {
3573 xmlRelaxNGDefinePtr starts;
3574 xmlChar *combine;
3575 int choiceOrInterleave = -1;
3576 int missing = 0;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003577 xmlRelaxNGDefinePtr cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003578
Daniel Veillard2df2de22003-02-17 23:34:33 +00003579 starts = grammar->start;
3580 if ((starts == NULL) || (starts->next == NULL))
Daniel Veillard6eadf632003-01-23 18:29:16 +00003581 return;
3582 cur = starts;
3583 while (cur != NULL) {
Daniel Veillard2df2de22003-02-17 23:34:33 +00003584 if ((cur->node == NULL) || (cur->node->parent == NULL) ||
3585 (!xmlStrEqual(cur->node->parent->name, BAD_CAST "start"))) {
3586 combine = NULL;
3587 if (ctxt->error != NULL)
3588 ctxt->error(ctxt->userData,
3589 "Internal error: start element not found\n");
3590 ctxt->nbErrors++;
3591 } else {
3592 combine = xmlGetProp(cur->node->parent, BAD_CAST "combine");
3593 }
3594
Daniel Veillard6eadf632003-01-23 18:29:16 +00003595 if (combine != NULL) {
3596 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3597 if (choiceOrInterleave == -1)
3598 choiceOrInterleave = 1;
3599 else if (choiceOrInterleave == 0) {
3600 if (ctxt->error != NULL)
3601 ctxt->error(ctxt->userData,
3602 "<start> use both 'choice' and 'interleave'\n");
3603 ctxt->nbErrors++;
3604 }
Daniel Veillard2df2de22003-02-17 23:34:33 +00003605 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003606 if (choiceOrInterleave == -1)
3607 choiceOrInterleave = 0;
3608 else if (choiceOrInterleave == 1) {
3609 if (ctxt->error != NULL)
3610 ctxt->error(ctxt->userData,
3611 "<start> use both 'choice' and 'interleave'\n");
3612 ctxt->nbErrors++;
3613 }
3614 } else {
3615 if (ctxt->error != NULL)
3616 ctxt->error(ctxt->userData,
3617 "<start> uses unknown combine value '%s''\n", combine);
3618 ctxt->nbErrors++;
3619 }
3620 xmlFree(combine);
3621 } else {
3622 if (missing == 0)
3623 missing = 1;
3624 else {
3625 if (ctxt->error != NULL)
3626 ctxt->error(ctxt->userData,
3627 "Some <start> elements lacks the combine attribute\n");
3628 ctxt->nbErrors++;
3629 }
3630 }
3631
Daniel Veillard2df2de22003-02-17 23:34:33 +00003632 cur = cur->next;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003633 }
3634#ifdef DEBUG
3635 xmlGenericError(xmlGenericErrorContext,
3636 "xmlRelaxNGCombineStart(): merging <start>: %d\n",
3637 choiceOrInterleave);
3638#endif
3639 if (choiceOrInterleave == -1)
3640 choiceOrInterleave = 0;
3641 cur = xmlRelaxNGNewDefine(ctxt, starts->node);
3642 if (cur == NULL)
3643 return;
3644 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003645 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003646 else
3647 cur->type = XML_RELAXNG_CHOICE;
3648 cur->content = grammar->start;
3649 grammar->start = cur;
3650 if (choiceOrInterleave == 0) {
3651 if (ctxt->interleaves == NULL)
3652 ctxt->interleaves = xmlHashCreate(10);
3653 if (ctxt->interleaves == NULL) {
3654 if (ctxt->error != NULL)
3655 ctxt->error(ctxt->userData,
3656 "Failed to create interleaves hash table\n");
3657 ctxt->nbErrors++;
3658 } else {
3659 char tmpname[32];
3660
3661 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
3662 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
3663 if (ctxt->error != NULL)
3664 ctxt->error(ctxt->userData,
3665 "Failed to add %s to hash table\n", tmpname);
3666 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003667 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003668 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003669 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003670}
3671
3672/**
Daniel Veillardd4310742003-02-18 21:12:46 +00003673 * xmlRelaxNGCheckCycles:
3674 * @ctxt: a Relax-NG parser context
3675 * @nodes: grammar children nodes
3676 * @depth: the counter
3677 *
3678 * Check for cycles.
3679 *
3680 * Returns 0 if check passed, and -1 in case of error
3681 */
3682static int
3683xmlRelaxNGCheckCycles(xmlRelaxNGParserCtxtPtr ctxt,
3684 xmlRelaxNGDefinePtr cur, int depth) {
3685 int ret = 0;
3686
3687 while ((ret == 0) && (cur != NULL)) {
3688 if ((cur->type == XML_RELAXNG_REF) ||
3689 (cur->type == XML_RELAXNG_PARENTREF)) {
3690 if (cur->depth == -1) {
3691 cur->depth = depth;
3692 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth);
3693 cur->depth = -2;
3694 } else if (depth == cur->depth) {
3695 if (ctxt->error != NULL)
3696 ctxt->error(ctxt->userData,
3697 "Detected a cycle in %s references\n", cur->name);
3698 ctxt->nbErrors++;
3699 return(-1);
3700 }
3701 } else if (cur->type == XML_RELAXNG_ELEMENT) {
3702 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth + 1);
3703 } else {
3704 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth);
3705 }
3706 cur = cur->next;
3707 }
3708 return(ret);
3709}
3710
3711/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00003712 * xmlRelaxNGParseGrammar:
3713 * @ctxt: a Relax-NG parser context
3714 * @nodes: grammar children nodes
3715 *
3716 * parse a Relax-NG <grammar> node
3717 *
3718 * Returns the internal xmlRelaxNGGrammarPtr built or
3719 * NULL in case of error
3720 */
3721static xmlRelaxNGGrammarPtr
3722xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
3723 xmlRelaxNGGrammarPtr ret, tmp, old;
3724
Daniel Veillard6eadf632003-01-23 18:29:16 +00003725 ret = xmlRelaxNGNewGrammar(ctxt);
3726 if (ret == NULL)
3727 return(NULL);
3728
3729 /*
3730 * Link the new grammar in the tree
3731 */
3732 ret->parent = ctxt->grammar;
3733 if (ctxt->grammar != NULL) {
3734 tmp = ctxt->grammar->children;
3735 if (tmp == NULL) {
3736 ctxt->grammar->children = ret;
3737 } else {
3738 while (tmp->next != NULL)
3739 tmp = tmp->next;
3740 tmp->next = ret;
3741 }
3742 }
3743
3744 old = ctxt->grammar;
3745 ctxt->grammar = ret;
3746 xmlRelaxNGParseGrammarContent(ctxt, nodes);
3747 ctxt->grammar = ret;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003748 if (ctxt->grammar == NULL) {
3749 if (ctxt->error != NULL)
3750 ctxt->error(ctxt->userData,
3751 "Failed to parse <grammar> content\n");
3752 ctxt->nbErrors++;
3753 } else if (ctxt->grammar->start == NULL) {
3754 if (ctxt->error != NULL)
3755 ctxt->error(ctxt->userData,
3756 "Element <grammar> has no <start>\n");
3757 ctxt->nbErrors++;
3758 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003759
3760 /*
3761 * Apply 4.17 mergingd rules to defines and starts
3762 */
3763 xmlRelaxNGCombineStart(ctxt, ret);
3764 if (ret->defs != NULL) {
3765 xmlHashScan(ret->defs, (xmlHashScanner) xmlRelaxNGCheckCombine,
3766 ctxt);
3767 }
3768
3769 /*
3770 * link together defines and refs in this grammar
3771 */
3772 if (ret->refs != NULL) {
3773 xmlHashScan(ret->refs, (xmlHashScanner) xmlRelaxNGCheckReference,
3774 ctxt);
3775 }
3776 ctxt->grammar = old;
3777 return(ret);
3778}
3779
3780/**
3781 * xmlRelaxNGParseDocument:
3782 * @ctxt: a Relax-NG parser context
3783 * @node: the root node of the RelaxNG schema
3784 *
3785 * parse a Relax-NG definition resource and build an internal
3786 * xmlRelaxNG struture which can be used to validate instances.
3787 *
3788 * Returns the internal XML RelaxNG structure built or
3789 * NULL in case of error
3790 */
3791static xmlRelaxNGPtr
3792xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3793 xmlRelaxNGPtr schema = NULL;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003794 const xmlChar *olddefine;
Daniel Veillarde431a272003-01-29 23:02:33 +00003795 xmlRelaxNGGrammarPtr old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003796
3797 if ((ctxt == NULL) || (node == NULL))
3798 return (NULL);
3799
3800 schema = xmlRelaxNGNewRelaxNG(ctxt);
3801 if (schema == NULL)
3802 return(NULL);
3803
Daniel Veillard276be4a2003-01-24 01:03:34 +00003804 olddefine = ctxt->define;
3805 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003806 if (IS_RELAXNG(node, "grammar")) {
3807 schema->topgrammar = xmlRelaxNGParseGrammar(ctxt, node->children);
3808 } else {
3809 schema->topgrammar = xmlRelaxNGNewGrammar(ctxt);
3810 if (schema->topgrammar == NULL) {
3811 return(schema);
3812 }
3813 schema->topgrammar->parent = NULL;
Daniel Veillarde431a272003-01-29 23:02:33 +00003814 old = ctxt->grammar;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003815 ctxt->grammar = schema->topgrammar;
3816 xmlRelaxNGParseStart(ctxt, node);
Daniel Veillarde431a272003-01-29 23:02:33 +00003817 if (old != NULL)
3818 ctxt->grammar = old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003819 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003820 ctxt->define = olddefine;
Daniel Veillardd4310742003-02-18 21:12:46 +00003821 if (schema->topgrammar->start != NULL) {
3822 xmlRelaxNGCheckCycles(ctxt, schema->topgrammar->start, 0);
3823 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003824
3825#ifdef DEBUG
3826 if (schema == NULL)
3827 xmlGenericError(xmlGenericErrorContext,
3828 "xmlRelaxNGParseDocument() failed\n");
3829#endif
3830
3831 return (schema);
3832}
3833
3834/************************************************************************
3835 * *
3836 * Reading RelaxNGs *
3837 * *
3838 ************************************************************************/
3839
3840/**
3841 * xmlRelaxNGNewParserCtxt:
3842 * @URL: the location of the schema
3843 *
3844 * Create an XML RelaxNGs parse context for that file/resource expected
3845 * to contain an XML RelaxNGs file.
3846 *
3847 * Returns the parser context or NULL in case of error
3848 */
3849xmlRelaxNGParserCtxtPtr
3850xmlRelaxNGNewParserCtxt(const char *URL) {
3851 xmlRelaxNGParserCtxtPtr ret;
3852
3853 if (URL == NULL)
3854 return(NULL);
3855
3856 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
3857 if (ret == NULL) {
3858 xmlGenericError(xmlGenericErrorContext,
3859 "Failed to allocate new schama parser context for %s\n", URL);
3860 return (NULL);
3861 }
3862 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
3863 ret->URL = xmlStrdup((const xmlChar *)URL);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003864 ret->error = xmlGenericError;
3865 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003866 return (ret);
3867}
3868
3869/**
3870 * xmlRelaxNGNewMemParserCtxt:
3871 * @buffer: a pointer to a char array containing the schemas
3872 * @size: the size of the array
3873 *
3874 * Create an XML RelaxNGs parse context for that memory buffer expected
3875 * to contain an XML RelaxNGs file.
3876 *
3877 * Returns the parser context or NULL in case of error
3878 */
3879xmlRelaxNGParserCtxtPtr
3880xmlRelaxNGNewMemParserCtxt(const char *buffer, int size) {
3881 xmlRelaxNGParserCtxtPtr ret;
3882
3883 if ((buffer == NULL) || (size <= 0))
3884 return(NULL);
3885
3886 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
3887 if (ret == NULL) {
3888 xmlGenericError(xmlGenericErrorContext,
3889 "Failed to allocate new schama parser context\n");
3890 return (NULL);
3891 }
3892 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
3893 ret->buffer = buffer;
3894 ret->size = size;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003895 ret->error = xmlGenericError;
3896 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003897 return (ret);
3898}
3899
3900/**
3901 * xmlRelaxNGFreeParserCtxt:
3902 * @ctxt: the schema parser context
3903 *
3904 * Free the resources associated to the schema parser context
3905 */
3906void
3907xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxtPtr ctxt) {
3908 if (ctxt == NULL)
3909 return;
3910 if (ctxt->URL != NULL)
3911 xmlFree(ctxt->URL);
3912 if (ctxt->doc != NULL)
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003913 xmlFreeDoc(ctxt->document);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003914 if (ctxt->interleaves != NULL)
3915 xmlHashFree(ctxt->interleaves, NULL);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003916 if (ctxt->documents != NULL)
3917 xmlHashFree(ctxt->documents, (xmlHashDeallocator)
3918 xmlRelaxNGFreeDocument);
3919 if (ctxt->docTab != NULL)
3920 xmlFree(ctxt->docTab);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003921 if (ctxt->incTab != NULL)
3922 xmlFree(ctxt->incTab);
Daniel Veillard419a7682003-02-03 23:22:49 +00003923 if (ctxt->defTab != NULL) {
3924 int i;
3925
3926 for (i = 0;i < ctxt->defNr;i++)
3927 xmlRelaxNGFreeDefine(ctxt->defTab[i]);
3928 xmlFree(ctxt->defTab);
3929 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003930 xmlFree(ctxt);
3931}
3932
Daniel Veillard6eadf632003-01-23 18:29:16 +00003933/**
Daniel Veillardd2298792003-02-14 16:54:11 +00003934 * xmlRelaxNGNormExtSpace:
3935 * @value: a value
3936 *
3937 * Removes the leading and ending spaces of the value
3938 * The string is modified "in situ"
3939 */
3940static void
3941xmlRelaxNGNormExtSpace(xmlChar *value) {
3942 xmlChar *start = value;
3943 xmlChar *cur = value;
3944 if (value == NULL)
3945 return;
3946
3947 while (IS_BLANK(*cur)) cur++;
3948 if (cur == start) {
3949 do {
3950 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
3951 if (*cur == 0)
3952 return;
3953 start = cur;
3954 while (IS_BLANK(*cur)) cur++;
3955 if (*cur == 0) {
3956 *start = 0;
3957 return;
3958 }
3959 } while (1);
3960 } else {
3961 do {
3962 while ((*cur != 0) && (!IS_BLANK(*cur)))
3963 *start++ = *cur++;
3964 if (*cur == 0) {
3965 *start = 0;
3966 return;
3967 }
3968 /* don't try to normalize the inner spaces */
3969 while (IS_BLANK(*cur)) cur++;
3970 *start++ = *cur++;
3971 if (*cur == 0) {
3972 *start = 0;
3973 return;
3974 }
3975 } while (1);
3976 }
3977}
3978
3979/**
3980 * xmlRelaxNGCheckAttributes:
3981 * @ctxt: a Relax-NG parser context
3982 * @node: a Relax-NG node
3983 *
3984 * Check all the attributes on the given node
3985 */
3986static void
3987xmlRelaxNGCleanupAttributes(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3988 xmlAttrPtr cur, next;
3989
3990 cur = node->properties;
3991 while (cur != NULL) {
3992 next = cur->next;
3993 if ((cur->ns == NULL) ||
3994 (xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
3995 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
3996 if ((!xmlStrEqual(node->name, BAD_CAST "element")) &&
3997 (!xmlStrEqual(node->name, BAD_CAST "attribute")) &&
3998 (!xmlStrEqual(node->name, BAD_CAST "ref")) &&
3999 (!xmlStrEqual(node->name, BAD_CAST "parentRef")) &&
Daniel Veillard2df2de22003-02-17 23:34:33 +00004000 (!xmlStrEqual(node->name, BAD_CAST "param")) &&
Daniel Veillardd2298792003-02-14 16:54:11 +00004001 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
4002 if (ctxt->error != NULL)
4003 ctxt->error(ctxt->userData,
4004 "Attribute %s is not allowed on %s\n",
4005 cur->name, node->name);
4006 ctxt->nbErrors++;
4007 }
4008 } else if (xmlStrEqual(cur->name, BAD_CAST "type")) {
4009 if ((!xmlStrEqual(node->name, BAD_CAST "value")) &&
4010 (!xmlStrEqual(node->name, BAD_CAST "data"))) {
4011 if (ctxt->error != NULL)
4012 ctxt->error(ctxt->userData,
4013 "Attribute %s is not allowed on %s\n",
4014 cur->name, node->name);
4015 ctxt->nbErrors++;
4016 }
4017 } else if (xmlStrEqual(cur->name, BAD_CAST "href")) {
4018 if ((!xmlStrEqual(node->name, BAD_CAST "externalRef")) &&
4019 (!xmlStrEqual(node->name, BAD_CAST "include"))) {
4020 if (ctxt->error != NULL)
4021 ctxt->error(ctxt->userData,
4022 "Attribute %s is not allowed on %s\n",
4023 cur->name, node->name);
4024 ctxt->nbErrors++;
4025 }
4026 } else if (xmlStrEqual(cur->name, BAD_CAST "combine")) {
4027 if ((!xmlStrEqual(node->name, BAD_CAST "start")) &&
4028 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
4029 if (ctxt->error != NULL)
4030 ctxt->error(ctxt->userData,
4031 "Attribute %s is not allowed on %s\n",
4032 cur->name, node->name);
4033 ctxt->nbErrors++;
4034 }
4035 } else if (xmlStrEqual(cur->name, BAD_CAST "datatypeLibrary")) {
4036 xmlChar *val;
4037 xmlURIPtr uri;
4038
4039 val = xmlNodeListGetString(node->doc, cur->children, 1);
4040 if (val != NULL) {
4041 if (val[0] != 0) {
4042 uri = xmlParseURI((const char *) val);
4043 if (uri == NULL) {
4044 if (ctxt->error != NULL)
4045 ctxt->error(ctxt->userData,
4046 "Attribute %s contains invalid URI %s\n",
4047 cur->name, val);
4048 ctxt->nbErrors++;
4049 } else {
4050 if (uri->scheme == NULL) {
4051 if (ctxt->error != NULL)
4052 ctxt->error(ctxt->userData,
4053 "Attribute %s URI %s is not absolute\n",
4054 cur->name, val);
4055 ctxt->nbErrors++;
4056 }
4057 if (uri->fragment != NULL) {
4058 if (ctxt->error != NULL)
4059 ctxt->error(ctxt->userData,
4060 "Attribute %s URI %s has a fragment ID\n",
4061 cur->name, val);
4062 ctxt->nbErrors++;
4063 }
4064 xmlFreeURI(uri);
4065 }
4066 }
4067 xmlFree(val);
4068 }
4069 } else if (!xmlStrEqual(cur->name, BAD_CAST "ns")) {
4070 if (ctxt->error != NULL)
4071 ctxt->error(ctxt->userData,
4072 "Unknown attribute %s on %s\n",
4073 cur->name, node->name);
4074 ctxt->nbErrors++;
4075 }
4076 }
4077 cur = next;
4078 }
4079}
4080
4081/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004082 * xmlRelaxNGCleanupDoc:
4083 * @ctxt: a Relax-NG parser context
4084 * @doc: an xmldocPtr document pointer
Daniel Veillard6eadf632003-01-23 18:29:16 +00004085 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004086 * Cleanup the document from unwanted nodes for parsing, resolve
4087 * Include and externalRef lookups.
Daniel Veillard6eadf632003-01-23 18:29:16 +00004088 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004089 * Returns the cleaned up document or NULL in case of error
Daniel Veillard6eadf632003-01-23 18:29:16 +00004090 */
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004091static xmlDocPtr
4092xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt, xmlDocPtr doc) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00004093 xmlNodePtr root, cur, delete;
4094
Daniel Veillard6eadf632003-01-23 18:29:16 +00004095 /*
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004096 * Extract the root
Daniel Veillard6eadf632003-01-23 18:29:16 +00004097 */
4098 root = xmlDocGetRootElement(doc);
4099 if (root == NULL) {
4100 if (ctxt->error != NULL)
4101 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
4102 ctxt->URL);
4103 ctxt->nbErrors++;
4104 return (NULL);
4105 }
4106
4107 /*
4108 * Remove all the blank text nodes
4109 */
4110 delete = NULL;
4111 cur = root;
4112 while (cur != NULL) {
4113 if (delete != NULL) {
4114 xmlUnlinkNode(delete);
4115 xmlFreeNode(delete);
4116 delete = NULL;
4117 }
4118 if (cur->type == XML_ELEMENT_NODE) {
4119 /*
4120 * Simplification 4.1. Annotations
4121 */
4122 if ((cur->ns == NULL) ||
4123 (!xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
Daniel Veillardd2298792003-02-14 16:54:11 +00004124 if ((cur->parent != NULL) &&
4125 (cur->parent->type == XML_ELEMENT_NODE) &&
4126 ((xmlStrEqual(cur->parent->name, BAD_CAST "name")) ||
4127 (xmlStrEqual(cur->parent->name, BAD_CAST "value")) ||
4128 (xmlStrEqual(cur->parent->name, BAD_CAST "param")))) {
4129 if (ctxt->error != NULL)
4130 ctxt->error(ctxt->userData,
4131 "element %s doesn't allow foreign elements\n",
4132 cur->parent->name);
4133 ctxt->nbErrors++;
4134 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004135 delete = cur;
4136 goto skip_children;
4137 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00004138 xmlRelaxNGCleanupAttributes(ctxt, cur);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004139 if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004140 xmlChar *href, *ns, *base, *URL;
4141 xmlRelaxNGDocumentPtr docu;
Daniel Veillard416589a2003-02-17 17:25:42 +00004142 xmlNodePtr tmp;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004143
4144 ns = xmlGetProp(cur, BAD_CAST "ns");
Daniel Veillard416589a2003-02-17 17:25:42 +00004145 if (ns == NULL) {
4146 tmp = cur->parent;
4147 while ((tmp != NULL) &&
4148 (tmp->type == XML_ELEMENT_NODE)) {
4149 ns = xmlGetProp(tmp, BAD_CAST "ns");
4150 if (ns != NULL)
4151 break;
4152 tmp = tmp->parent;
4153 }
4154 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004155 href = xmlGetProp(cur, BAD_CAST "href");
4156 if (href == NULL) {
4157 if (ctxt->error != NULL)
4158 ctxt->error(ctxt->userData,
4159 "xmlRelaxNGParse: externalRef has no href attribute\n");
4160 ctxt->nbErrors++;
4161 delete = cur;
4162 goto skip_children;
4163 }
4164 base = xmlNodeGetBase(cur->doc, cur);
4165 URL = xmlBuildURI(href, base);
4166 if (URL == NULL) {
4167 if (ctxt->error != NULL)
4168 ctxt->error(ctxt->userData,
4169 "Failed to compute URL for externalRef %s\n", href);
4170 ctxt->nbErrors++;
4171 if (href != NULL)
4172 xmlFree(href);
4173 if (base != NULL)
4174 xmlFree(base);
4175 delete = cur;
4176 goto skip_children;
4177 }
4178 if (href != NULL)
4179 xmlFree(href);
4180 if (base != NULL)
4181 xmlFree(base);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004182 docu = xmlRelaxNGLoadExternalRef(ctxt, URL, ns);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004183 if (docu == NULL) {
4184 if (ctxt->error != NULL)
4185 ctxt->error(ctxt->userData,
4186 "Failed to load externalRef %s\n", URL);
4187 ctxt->nbErrors++;
4188 xmlFree(URL);
4189 delete = cur;
4190 goto skip_children;
4191 }
4192 xmlFree(URL);
4193 cur->_private = docu;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004194 } else if (xmlStrEqual(cur->name, BAD_CAST "include")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00004195 xmlChar *href, *ns, *base, *URL;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004196 xmlRelaxNGIncludePtr incl;
Daniel Veillard416589a2003-02-17 17:25:42 +00004197 xmlNodePtr tmp;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004198
4199 href = xmlGetProp(cur, BAD_CAST "href");
4200 if (href == NULL) {
4201 if (ctxt->error != NULL)
4202 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004203 "xmlRelaxNGParse: include has no href attribute\n");
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004204 ctxt->nbErrors++;
4205 delete = cur;
4206 goto skip_children;
4207 }
4208 base = xmlNodeGetBase(cur->doc, cur);
4209 URL = xmlBuildURI(href, base);
4210 if (URL == NULL) {
4211 if (ctxt->error != NULL)
4212 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004213 "Failed to compute URL for include %s\n", href);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004214 ctxt->nbErrors++;
4215 if (href != NULL)
4216 xmlFree(href);
4217 if (base != NULL)
4218 xmlFree(base);
4219 delete = cur;
4220 goto skip_children;
4221 }
4222 if (href != NULL)
4223 xmlFree(href);
4224 if (base != NULL)
4225 xmlFree(base);
Daniel Veillard416589a2003-02-17 17:25:42 +00004226 ns = xmlGetProp(cur, BAD_CAST "ns");
4227 if (ns == NULL) {
4228 tmp = cur->parent;
4229 while ((tmp != NULL) &&
4230 (tmp->type == XML_ELEMENT_NODE)) {
4231 ns = xmlGetProp(tmp, BAD_CAST "ns");
4232 if (ns != NULL)
4233 break;
4234 tmp = tmp->parent;
4235 }
4236 }
4237 incl = xmlRelaxNGLoadInclude(ctxt, URL, cur, ns);
4238 if (ns != NULL)
4239 xmlFree(ns);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004240 if (incl == NULL) {
4241 if (ctxt->error != NULL)
4242 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004243 "Failed to load include %s\n", URL);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004244 ctxt->nbErrors++;
4245 xmlFree(URL);
4246 delete = cur;
4247 goto skip_children;
4248 }
4249 xmlFree(URL);
4250 cur->_private = incl;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004251 } else if ((xmlStrEqual(cur->name, BAD_CAST "element")) ||
4252 (xmlStrEqual(cur->name, BAD_CAST "attribute"))) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00004253 xmlChar *name, *ns;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004254 xmlNodePtr text = NULL;
4255
4256 /*
4257 * Simplification 4.8. name attribute of element
4258 * and attribute elements
4259 */
4260 name = xmlGetProp(cur, BAD_CAST "name");
4261 if (name != NULL) {
4262 if (cur->children == NULL) {
4263 text = xmlNewChild(cur, cur->ns, BAD_CAST "name",
4264 name);
4265 } else {
4266 xmlNodePtr node;
4267 node = xmlNewNode(cur->ns, BAD_CAST "name");
4268 if (node != NULL) {
4269 xmlAddPrevSibling(cur->children, node);
4270 text = xmlNewText(name);
4271 xmlAddChild(node, text);
4272 text = node;
4273 }
4274 }
Daniel Veillardfebcca42003-02-16 15:44:18 +00004275 if (text == NULL) {
4276 if (ctxt->error != NULL)
4277 ctxt->error(ctxt->userData,
4278 "Failed to create a name %s element\n", name);
4279 ctxt->nbErrors++;
4280 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004281 xmlUnsetProp(cur, BAD_CAST "name");
4282 xmlFree(name);
Daniel Veillardfebcca42003-02-16 15:44:18 +00004283 ns = xmlGetProp(cur, BAD_CAST "ns");
4284 if (ns != NULL) {
4285 if (text != NULL) {
4286 xmlSetProp(text, BAD_CAST "ns", ns);
4287 /* xmlUnsetProp(cur, BAD_CAST "ns"); */
Daniel Veillard6eadf632003-01-23 18:29:16 +00004288 }
Daniel Veillardfebcca42003-02-16 15:44:18 +00004289 xmlFree(ns);
4290 } else if (xmlStrEqual(cur->name,
4291 BAD_CAST "attribute")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00004292 xmlSetProp(text, BAD_CAST "ns", BAD_CAST "");
4293 }
4294 }
4295 } else if ((xmlStrEqual(cur->name, BAD_CAST "name")) ||
4296 (xmlStrEqual(cur->name, BAD_CAST "nsName")) ||
4297 (xmlStrEqual(cur->name, BAD_CAST "value"))) {
4298 /*
4299 * Simplification 4.8. name attribute of element
4300 * and attribute elements
4301 */
4302 if (xmlHasProp(cur, BAD_CAST "ns") == NULL) {
4303 xmlNodePtr node;
4304 xmlChar *ns = NULL;
4305
4306 node = cur->parent;
4307 while ((node != NULL) &&
4308 (node->type == XML_ELEMENT_NODE)) {
4309 ns = xmlGetProp(node, BAD_CAST "ns");
4310 if (ns != NULL) {
4311 break;
4312 }
4313 node = node->parent;
4314 }
4315 if (ns == NULL) {
4316 xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
4317 } else {
4318 xmlSetProp(cur, BAD_CAST "ns", ns);
4319 xmlFree(ns);
4320 }
4321 }
4322 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
4323 xmlChar *name, *local, *prefix;
4324
4325 /*
4326 * Simplification: 4.10. QNames
4327 */
4328 name = xmlNodeGetContent(cur);
4329 if (name != NULL) {
4330 local = xmlSplitQName2(name, &prefix);
4331 if (local != NULL) {
4332 xmlNsPtr ns;
4333
4334 ns = xmlSearchNs(cur->doc, cur, prefix);
4335 if (ns == NULL) {
4336 if (ctxt->error != NULL)
4337 ctxt->error(ctxt->userData,
4338 "xmlRelaxNGParse: no namespace for prefix %s\n", prefix);
4339 ctxt->nbErrors++;
4340 } else {
4341 xmlSetProp(cur, BAD_CAST "ns", ns->href);
4342 xmlNodeSetContent(cur, local);
4343 }
4344 xmlFree(local);
4345 xmlFree(prefix);
4346 }
4347 xmlFree(name);
4348 }
4349 }
4350 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004351 /*
4352 * Thisd is not an else since "include" is transformed
4353 * into a div
4354 */
4355 if (xmlStrEqual(cur->name, BAD_CAST "div")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00004356 xmlChar *ns;
4357 xmlNodePtr child, ins, tmp;
4358
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004359 /*
4360 * implements rule 4.11
4361 */
Daniel Veillard416589a2003-02-17 17:25:42 +00004362
4363 ns = xmlGetProp(cur, BAD_CAST "ns");
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004364
4365 child = cur->children;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004366 ins = cur;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004367 while (child != NULL) {
Daniel Veillard416589a2003-02-17 17:25:42 +00004368 if (ns != NULL) {
4369 if (!xmlHasProp(child, BAD_CAST "ns")) {
4370 xmlSetProp(child, BAD_CAST "ns", ns);
4371 }
4372 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004373 tmp = child->next;
4374 xmlUnlinkNode(child);
4375 ins = xmlAddNextSibling(ins, child);
4376 child = tmp;
4377 }
Daniel Veillard416589a2003-02-17 17:25:42 +00004378 if (ns != NULL)
4379 xmlFree(ns);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004380 delete = cur;
4381 goto skip_children;
4382 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004383 }
4384 }
4385 /*
4386 * Simplification 4.2 whitespaces
4387 */
4388 else if (cur->type == XML_TEXT_NODE) {
4389 if (IS_BLANK_NODE(cur)) {
4390 if (cur->parent->type == XML_ELEMENT_NODE) {
4391 if ((!xmlStrEqual(cur->parent->name, BAD_CAST "value")) &&
4392 (!xmlStrEqual(cur->parent->name, BAD_CAST "param")))
4393 delete = cur;
4394 } else {
4395 delete = cur;
4396 goto skip_children;
4397 }
4398 }
4399 } else if (cur->type != XML_CDATA_SECTION_NODE) {
4400 delete = cur;
4401 goto skip_children;
4402 }
4403
4404 /*
4405 * Skip to next node
4406 */
4407 if (cur->children != NULL) {
4408 if ((cur->children->type != XML_ENTITY_DECL) &&
4409 (cur->children->type != XML_ENTITY_REF_NODE) &&
4410 (cur->children->type != XML_ENTITY_NODE)) {
4411 cur = cur->children;
4412 continue;
4413 }
4414 }
4415skip_children:
4416 if (cur->next != NULL) {
4417 cur = cur->next;
4418 continue;
4419 }
4420
4421 do {
4422 cur = cur->parent;
4423 if (cur == NULL)
4424 break;
4425 if (cur == root) {
4426 cur = NULL;
4427 break;
4428 }
4429 if (cur->next != NULL) {
4430 cur = cur->next;
4431 break;
4432 }
4433 } while (cur != NULL);
4434 }
4435 if (delete != NULL) {
4436 xmlUnlinkNode(delete);
4437 xmlFreeNode(delete);
4438 delete = NULL;
4439 }
4440
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004441 return(doc);
4442}
4443
4444/**
4445 * xmlRelaxNGParse:
4446 * @ctxt: a Relax-NG parser context
4447 *
4448 * parse a schema definition resource and build an internal
4449 * XML Shema struture which can be used to validate instances.
4450 * *WARNING* this interface is highly subject to change
4451 *
4452 * Returns the internal XML RelaxNG structure built from the resource or
4453 * NULL in case of error
4454 */
4455xmlRelaxNGPtr
4456xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt)
4457{
4458 xmlRelaxNGPtr ret = NULL;
4459 xmlDocPtr doc;
4460 xmlNodePtr root;
4461
4462 xmlRelaxNGInitTypes();
4463
4464 if (ctxt == NULL)
4465 return (NULL);
4466
4467 /*
4468 * First step is to parse the input document into an DOM/Infoset
4469 */
4470 if (ctxt->URL != NULL) {
4471 doc = xmlParseFile((const char *) ctxt->URL);
4472 if (doc == NULL) {
4473 if (ctxt->error != NULL)
4474 ctxt->error(ctxt->userData,
4475 "xmlRelaxNGParse: could not load %s\n", ctxt->URL);
4476 ctxt->nbErrors++;
4477 return (NULL);
4478 }
4479 } else if (ctxt->buffer != NULL) {
4480 doc = xmlParseMemory(ctxt->buffer, ctxt->size);
4481 if (doc == NULL) {
4482 if (ctxt->error != NULL)
4483 ctxt->error(ctxt->userData,
4484 "xmlRelaxNGParse: could not parse schemas\n");
4485 ctxt->nbErrors++;
4486 return (NULL);
4487 }
4488 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
4489 ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
4490 } else {
4491 if (ctxt->error != NULL)
4492 ctxt->error(ctxt->userData,
4493 "xmlRelaxNGParse: nothing to parse\n");
4494 ctxt->nbErrors++;
4495 return (NULL);
4496 }
4497 ctxt->document = doc;
4498
4499 /*
4500 * Some preprocessing of the document content
4501 */
4502 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
4503 if (doc == NULL) {
4504 xmlFreeDoc(ctxt->document);
4505 ctxt->document = NULL;
4506 return(NULL);
4507 }
4508
Daniel Veillard6eadf632003-01-23 18:29:16 +00004509 /*
4510 * Then do the parsing for good
4511 */
4512 root = xmlDocGetRootElement(doc);
4513 if (root == NULL) {
4514 if (ctxt->error != NULL)
4515 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
4516 ctxt->URL);
4517 ctxt->nbErrors++;
4518 return (NULL);
4519 }
4520 ret = xmlRelaxNGParseDocument(ctxt, root);
4521 if (ret == NULL)
4522 return(NULL);
4523
4524 /*
4525 * Check the ref/defines links
4526 */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004527 /*
4528 * try to preprocess interleaves
4529 */
4530 if (ctxt->interleaves != NULL) {
4531 xmlHashScan(ctxt->interleaves,
4532 (xmlHashScanner)xmlRelaxNGComputeInterleaves, ctxt);
4533 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004534
4535 /*
4536 * if there was a parsing error return NULL
4537 */
4538 if (ctxt->nbErrors > 0) {
4539 xmlRelaxNGFree(ret);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004540 ctxt->document = NULL;
4541 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004542 return(NULL);
4543 }
4544
4545 /*
4546 * Transfer the pointer for cleanup at the schema level.
4547 */
4548 ret->doc = doc;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004549 ctxt->document = NULL;
4550 ret->documents = ctxt->documents;
4551 ctxt->documents = NULL;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004552 ret->includes = ctxt->includes;
4553 ctxt->includes = NULL;
Daniel Veillard419a7682003-02-03 23:22:49 +00004554 ret->defNr = ctxt->defNr;
4555 ret->defTab = ctxt->defTab;
4556 ctxt->defTab = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004557
4558 return (ret);
4559}
4560
4561/**
4562 * xmlRelaxNGSetParserErrors:
4563 * @ctxt: a Relax-NG validation context
4564 * @err: the error callback
4565 * @warn: the warning callback
4566 * @ctx: contextual data for the callbacks
4567 *
4568 * Set the callback functions used to handle errors for a validation context
4569 */
4570void
4571xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
4572 xmlRelaxNGValidityErrorFunc err,
4573 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
4574 if (ctxt == NULL)
4575 return;
4576 ctxt->error = err;
4577 ctxt->warning = warn;
4578 ctxt->userData = ctx;
4579}
4580/************************************************************************
4581 * *
4582 * Dump back a compiled form *
4583 * *
4584 ************************************************************************/
4585static void xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define);
4586
4587/**
4588 * xmlRelaxNGDumpDefines:
4589 * @output: the file output
4590 * @defines: a list of define structures
4591 *
4592 * Dump a RelaxNG structure back
4593 */
4594static void
4595xmlRelaxNGDumpDefines(FILE * output, xmlRelaxNGDefinePtr defines) {
4596 while (defines != NULL) {
4597 xmlRelaxNGDumpDefine(output, defines);
4598 defines = defines->next;
4599 }
4600}
4601
4602/**
4603 * xmlRelaxNGDumpDefine:
4604 * @output: the file output
4605 * @define: a define structure
4606 *
4607 * Dump a RelaxNG structure back
4608 */
4609static void
4610xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define) {
4611 if (define == NULL)
4612 return;
4613 switch(define->type) {
4614 case XML_RELAXNG_EMPTY:
4615 fprintf(output, "<empty/>\n");
4616 break;
4617 case XML_RELAXNG_NOT_ALLOWED:
4618 fprintf(output, "<notAllowed/>\n");
4619 break;
4620 case XML_RELAXNG_TEXT:
4621 fprintf(output, "<text/>\n");
4622 break;
4623 case XML_RELAXNG_ELEMENT:
4624 fprintf(output, "<element>\n");
4625 if (define->name != NULL) {
4626 fprintf(output, "<name");
4627 if (define->ns != NULL)
4628 fprintf(output, " ns=\"%s\"", define->ns);
4629 fprintf(output, ">%s</name>\n", define->name);
4630 }
4631 xmlRelaxNGDumpDefines(output, define->attrs);
4632 xmlRelaxNGDumpDefines(output, define->content);
4633 fprintf(output, "</element>\n");
4634 break;
4635 case XML_RELAXNG_LIST:
4636 fprintf(output, "<list>\n");
4637 xmlRelaxNGDumpDefines(output, define->content);
4638 fprintf(output, "</list>\n");
4639 break;
4640 case XML_RELAXNG_ONEORMORE:
4641 fprintf(output, "<oneOrMore>\n");
4642 xmlRelaxNGDumpDefines(output, define->content);
4643 fprintf(output, "</oneOrMore>\n");
4644 break;
4645 case XML_RELAXNG_ZEROORMORE:
4646 fprintf(output, "<zeroOrMore>\n");
4647 xmlRelaxNGDumpDefines(output, define->content);
4648 fprintf(output, "</zeroOrMore>\n");
4649 break;
4650 case XML_RELAXNG_CHOICE:
4651 fprintf(output, "<choice>\n");
4652 xmlRelaxNGDumpDefines(output, define->content);
4653 fprintf(output, "</choice>\n");
4654 break;
4655 case XML_RELAXNG_GROUP:
4656 fprintf(output, "<group>\n");
4657 xmlRelaxNGDumpDefines(output, define->content);
4658 fprintf(output, "</group>\n");
4659 break;
4660 case XML_RELAXNG_INTERLEAVE:
4661 fprintf(output, "<interleave>\n");
4662 xmlRelaxNGDumpDefines(output, define->content);
4663 fprintf(output, "</interleave>\n");
4664 break;
4665 case XML_RELAXNG_OPTIONAL:
4666 fprintf(output, "<optional>\n");
4667 xmlRelaxNGDumpDefines(output, define->content);
4668 fprintf(output, "</optional>\n");
4669 break;
4670 case XML_RELAXNG_ATTRIBUTE:
4671 fprintf(output, "<attribute>\n");
4672 xmlRelaxNGDumpDefines(output, define->content);
4673 fprintf(output, "</attribute>\n");
4674 break;
4675 case XML_RELAXNG_DEF:
4676 fprintf(output, "<define");
4677 if (define->name != NULL)
4678 fprintf(output, " name=\"%s\"", define->name);
4679 fprintf(output, ">\n");
4680 xmlRelaxNGDumpDefines(output, define->content);
4681 fprintf(output, "</define>\n");
4682 break;
4683 case XML_RELAXNG_REF:
4684 fprintf(output, "<ref");
4685 if (define->name != NULL)
4686 fprintf(output, " name=\"%s\"", define->name);
4687 fprintf(output, ">\n");
4688 xmlRelaxNGDumpDefines(output, define->content);
4689 fprintf(output, "</ref>\n");
4690 break;
Daniel Veillard419a7682003-02-03 23:22:49 +00004691 case XML_RELAXNG_PARENTREF:
4692 fprintf(output, "<parentRef");
4693 if (define->name != NULL)
4694 fprintf(output, " name=\"%s\"", define->name);
4695 fprintf(output, ">\n");
4696 xmlRelaxNGDumpDefines(output, define->content);
4697 fprintf(output, "</parentRef>\n");
4698 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004699 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard416589a2003-02-17 17:25:42 +00004700 fprintf(output, "<externalRef>");
Daniel Veillarde431a272003-01-29 23:02:33 +00004701 xmlRelaxNGDumpDefines(output, define->content);
4702 fprintf(output, "</externalRef>\n");
4703 break;
4704 case XML_RELAXNG_DATATYPE:
Daniel Veillard6eadf632003-01-23 18:29:16 +00004705 case XML_RELAXNG_VALUE:
4706 TODO
4707 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004708 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00004709 case XML_RELAXNG_EXCEPT:
Daniel Veillard8fe98712003-02-19 00:19:14 +00004710 case XML_RELAXNG_PARAM:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004711 TODO
4712 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004713 }
4714}
4715
4716/**
4717 * xmlRelaxNGDumpGrammar:
4718 * @output: the file output
4719 * @grammar: a grammar structure
4720 * @top: is this a top grammar
4721 *
4722 * Dump a RelaxNG structure back
4723 */
4724static void
4725xmlRelaxNGDumpGrammar(FILE * output, xmlRelaxNGGrammarPtr grammar, int top)
4726{
4727 if (grammar == NULL)
4728 return;
4729
4730 fprintf(output, "<grammar");
4731 if (top)
4732 fprintf(output,
4733 " xmlns=\"http://relaxng.org/ns/structure/1.0\"");
4734 switch(grammar->combine) {
4735 case XML_RELAXNG_COMBINE_UNDEFINED:
4736 break;
4737 case XML_RELAXNG_COMBINE_CHOICE:
4738 fprintf(output, " combine=\"choice\"");
4739 break;
4740 case XML_RELAXNG_COMBINE_INTERLEAVE:
4741 fprintf(output, " combine=\"interleave\"");
4742 break;
4743 default:
4744 fprintf(output, " <!-- invalid combine value -->");
4745 }
4746 fprintf(output, ">\n");
4747 if (grammar->start == NULL) {
4748 fprintf(output, " <!-- grammar had no start -->");
4749 } else {
4750 fprintf(output, "<start>\n");
4751 xmlRelaxNGDumpDefine(output, grammar->start);
4752 fprintf(output, "</start>\n");
4753 }
4754 /* TODO ? Dump the defines ? */
4755 fprintf(output, "</grammar>\n");
4756}
4757
4758/**
4759 * xmlRelaxNGDump:
4760 * @output: the file output
4761 * @schema: a schema structure
4762 *
4763 * Dump a RelaxNG structure back
4764 */
4765void
4766xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema)
4767{
4768 if (schema == NULL) {
4769 fprintf(output, "RelaxNG empty or failed to compile\n");
4770 return;
4771 }
4772 fprintf(output, "RelaxNG: ");
4773 if (schema->doc == NULL) {
4774 fprintf(output, "no document\n");
4775 } else if (schema->doc->URL != NULL) {
4776 fprintf(output, "%s\n", schema->doc->URL);
4777 } else {
4778 fprintf(output, "\n");
4779 }
4780 if (schema->topgrammar == NULL) {
4781 fprintf(output, "RelaxNG has no top grammar\n");
4782 return;
4783 }
4784 xmlRelaxNGDumpGrammar(output, schema->topgrammar, 1);
4785}
4786
Daniel Veillardfebcca42003-02-16 15:44:18 +00004787/**
4788 * xmlRelaxNGDumpTree:
4789 * @output: the file output
4790 * @schema: a schema structure
4791 *
4792 * Dump the transformed RelaxNG tree.
4793 */
4794void
4795xmlRelaxNGDumpTree(FILE * output, xmlRelaxNGPtr schema)
4796{
4797 if (schema == NULL) {
4798 fprintf(output, "RelaxNG empty or failed to compile\n");
4799 return;
4800 }
4801 if (schema->doc == NULL) {
4802 fprintf(output, "no document\n");
4803 } else {
4804 xmlDocDump(output, schema->doc);
4805 }
4806}
4807
Daniel Veillard6eadf632003-01-23 18:29:16 +00004808/************************************************************************
4809 * *
4810 * Validation implementation *
4811 * *
4812 ************************************************************************/
4813static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
4814 xmlRelaxNGDefinePtr define);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004815static int xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
4816 xmlRelaxNGDefinePtr define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004817
4818/**
4819 * xmlRelaxNGSkipIgnored:
4820 * @ctxt: a schema validation context
4821 * @node: the top node.
4822 *
4823 * Skip ignorable nodes in that context
4824 *
4825 * Returns the new sibling or NULL in case of error.
4826 */
4827static xmlNodePtr
4828xmlRelaxNGSkipIgnored(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
4829 xmlNodePtr node) {
4830 /*
4831 * TODO complete and handle entities
4832 */
4833 while ((node != NULL) &&
4834 ((node->type == XML_COMMENT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004835 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00004836 ((node->type == XML_TEXT_NODE) &&
4837 (IS_BLANK_NODE(node))))) {
4838 node = node->next;
4839 }
4840 return(node);
4841}
4842
4843/**
Daniel Veillardedc91922003-01-26 00:52:04 +00004844 * xmlRelaxNGNormalize:
4845 * @ctxt: a schema validation context
4846 * @str: the string to normalize
4847 *
4848 * Implements the normalizeWhiteSpace( s ) function from
4849 * section 6.2.9 of the spec
4850 *
4851 * Returns the new string or NULL in case of error.
4852 */
4853static xmlChar *
4854xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *str) {
4855 xmlChar *ret, *p;
4856 const xmlChar *tmp;
4857 int len;
4858
4859 if (str == NULL)
4860 return(NULL);
4861 tmp = str;
4862 while (*tmp != 0) tmp++;
4863 len = tmp - str;
4864
4865 ret = (xmlChar *) xmlMalloc((len + 1) * sizeof(xmlChar));
4866 if (ret == NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00004867 if (ctxt != NULL) {
4868 VALID_CTXT();
4869 VALID_ERROR("xmlRelaxNGNormalize: out of memory\n");
4870 } else {
4871 xmlGenericError(xmlGenericErrorContext,
4872 "xmlRelaxNGNormalize: out of memory\n");
4873 }
Daniel Veillardedc91922003-01-26 00:52:04 +00004874 return(NULL);
4875 }
4876 p = ret;
4877 while (IS_BLANK(*str)) str++;
4878 while (*str != 0) {
4879 if (IS_BLANK(*str)) {
4880 while (IS_BLANK(*str)) str++;
4881 if (*str == 0)
4882 break;
4883 *p++ = ' ';
4884 } else
4885 *p++ = *str++;
4886 }
4887 *p = 0;
4888 return(ret);
4889}
4890
4891/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004892 * xmlRelaxNGValidateDatatype:
4893 * @ctxt: a Relax-NG validation context
4894 * @value: the string value
4895 * @type: the datatype definition
4896 *
4897 * Validate the given value against the dataype
4898 *
4899 * Returns 0 if the validation succeeded or an error code.
4900 */
4901static int
4902xmlRelaxNGValidateDatatype(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *value,
4903 xmlRelaxNGDefinePtr define) {
4904 int ret;
4905 xmlRelaxNGTypeLibraryPtr lib;
4906
4907 if ((define == NULL) || (define->data == NULL)) {
4908 return(-1);
4909 }
4910 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
4911 if (lib->check != NULL)
4912 ret = lib->check(lib->data, define->name, value);
4913 else
4914 ret = -1;
4915 if (ret < 0) {
4916 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004917 VALID_ERROR2("Internal: failed to validate type %s\n", define->name);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004918 return(-1);
4919 } else if (ret == 1) {
4920 ret = 0;
4921 } else {
4922 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004923 VALID_ERROR3("Type %s doesn't allow value %s\n", define->name, value);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004924 return(-1);
4925 ret = -1;
4926 }
Daniel Veillard416589a2003-02-17 17:25:42 +00004927 if ((ret == 0) && (define->content != NULL)) {
4928 const xmlChar *oldvalue, *oldendvalue;
4929
4930 oldvalue = ctxt->state->value;
4931 oldendvalue = ctxt->state->endvalue;
4932 ctxt->state->value = (xmlChar *) value;
4933 ctxt->state->endvalue = NULL;
4934 ret = xmlRelaxNGValidateValue(ctxt, define->content);
4935 ctxt->state->value = (xmlChar *) oldvalue;
4936 ctxt->state->endvalue = (xmlChar *) oldendvalue;
4937 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004938 return(ret);
4939}
4940
4941/**
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004942 * xmlRelaxNGNextValue:
4943 * @ctxt: a Relax-NG validation context
4944 *
4945 * Skip to the next value when validating within a list
4946 *
4947 * Returns 0 if the operation succeeded or an error code.
4948 */
4949static int
4950xmlRelaxNGNextValue(xmlRelaxNGValidCtxtPtr ctxt) {
4951 xmlChar *cur;
4952
4953 cur = ctxt->state->value;
4954 if ((cur == NULL) || (ctxt->state->endvalue == NULL)) {
4955 ctxt->state->value = NULL;
Daniel Veillarde5b110b2003-02-04 14:43:39 +00004956 ctxt->state->endvalue = NULL;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004957 return(0);
4958 }
4959 while (*cur != 0) cur++;
4960 while ((cur != ctxt->state->endvalue) && (*cur == 0)) cur++;
4961 if (cur == ctxt->state->endvalue)
4962 ctxt->state->value = NULL;
4963 else
4964 ctxt->state->value = cur;
4965 return(0);
4966}
4967
4968/**
4969 * xmlRelaxNGValidateValueList:
4970 * @ctxt: a Relax-NG validation context
4971 * @defines: the list of definitions to verify
4972 *
4973 * Validate the given set of definitions for the current value
4974 *
4975 * Returns 0 if the validation succeeded or an error code.
4976 */
4977static int
4978xmlRelaxNGValidateValueList(xmlRelaxNGValidCtxtPtr ctxt,
4979 xmlRelaxNGDefinePtr defines) {
4980 int ret = 0;
4981
4982 while (defines != NULL) {
4983 ret = xmlRelaxNGValidateValue(ctxt, defines);
4984 if (ret != 0)
4985 break;
4986 defines = defines->next;
4987 }
4988 return(ret);
4989}
4990
4991/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004992 * xmlRelaxNGValidateValue:
4993 * @ctxt: a Relax-NG validation context
4994 * @define: the definition to verify
4995 *
4996 * Validate the given definition for the current value
4997 *
4998 * Returns 0 if the validation succeeded or an error code.
4999 */
5000static int
5001xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
5002 xmlRelaxNGDefinePtr define) {
Daniel Veillardedc91922003-01-26 00:52:04 +00005003 int ret = 0, oldflags;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005004 xmlChar *value;
5005
5006 value = ctxt->state->value;
5007 switch (define->type) {
Daniel Veillardd4310742003-02-18 21:12:46 +00005008 case XML_RELAXNG_EMPTY: {
5009 if ((value != NULL) && (value[0] != 0)) {
5010 int idx = 0;
5011
5012 while (IS_BLANK(value[idx]))
5013 idx++;
5014 if (value[idx] != 0)
5015 ret = -1;
5016 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005017 break;
Daniel Veillardd4310742003-02-18 21:12:46 +00005018 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005019 case XML_RELAXNG_TEXT:
5020 break;
Daniel Veillardedc91922003-01-26 00:52:04 +00005021 case XML_RELAXNG_VALUE: {
5022 if (!xmlStrEqual(value, define->value)) {
5023 if (define->name != NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00005024 xmlRelaxNGTypeLibraryPtr lib;
5025
5026 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
5027 if ((lib != NULL) && (lib->comp != NULL))
5028 ret = lib->comp(lib->data, define->name, value,
5029 define->value);
5030 else
5031 ret = -1;
5032 if (ret < 0) {
5033 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005034 VALID_ERROR2("Internal: failed to compare type %s\n",
Daniel Veillardea3f3982003-01-26 19:45:18 +00005035 define->name);
5036 return(-1);
5037 } else if (ret == 1) {
5038 ret = 0;
5039 } else {
5040 ret = -1;
5041 }
Daniel Veillardedc91922003-01-26 00:52:04 +00005042 } else {
5043 xmlChar *nval, *nvalue;
5044
5045 /*
5046 * TODO: trivial optimizations are possible by
5047 * computing at compile-time
5048 */
5049 nval = xmlRelaxNGNormalize(ctxt, define->value);
5050 nvalue = xmlRelaxNGNormalize(ctxt, value);
5051
Daniel Veillardea3f3982003-01-26 19:45:18 +00005052 if ((nval == NULL) || (nvalue == NULL) ||
5053 (!xmlStrEqual(nval, nvalue)))
Daniel Veillardedc91922003-01-26 00:52:04 +00005054 ret = -1;
5055 if (nval != NULL)
5056 xmlFree(nval);
5057 if (nvalue != NULL)
5058 xmlFree(nvalue);
5059 }
5060 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005061 if (ret == 0)
5062 xmlRelaxNGNextValue(ctxt);
Daniel Veillardedc91922003-01-26 00:52:04 +00005063 break;
5064 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005065 case XML_RELAXNG_DATATYPE: {
5066 ret = xmlRelaxNGValidateDatatype(ctxt, value, define);
5067 if (ret == 0)
5068 xmlRelaxNGNextValue(ctxt);
5069
5070 break;
5071 }
5072 case XML_RELAXNG_CHOICE: {
5073 xmlRelaxNGDefinePtr list = define->content;
5074 xmlChar *oldvalue;
5075
5076 oldflags = ctxt->flags;
5077 ctxt->flags |= FLAGS_IGNORABLE;
5078
5079 oldvalue = ctxt->state->value;
5080 while (list != NULL) {
5081 ret = xmlRelaxNGValidateValue(ctxt, list);
5082 if (ret == 0) {
5083 break;
5084 }
5085 ctxt->state->value = oldvalue;
5086 list = list->next;
5087 }
5088 ctxt->flags = oldflags;
Daniel Veillard416589a2003-02-17 17:25:42 +00005089 if (ret == 0)
5090 xmlRelaxNGNextValue(ctxt);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005091 break;
5092 }
5093 case XML_RELAXNG_LIST: {
5094 xmlRelaxNGDefinePtr list = define->content;
5095 xmlChar *oldvalue, *oldend, *val, *cur;
Daniel Veillard416589a2003-02-17 17:25:42 +00005096#ifdef DEBUG_LIST
5097 int nb_values = 0;
5098#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005099
5100 oldvalue = ctxt->state->value;
5101 oldend = ctxt->state->endvalue;
5102
5103 val = xmlStrdup(oldvalue);
5104 if (val == NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00005105 val = xmlStrdup(BAD_CAST "");
5106 }
5107 if (val == NULL) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005108 VALID_CTXT();
5109 VALID_ERROR("Internal: no state\n");
5110 return(-1);
5111 }
5112 cur = val;
5113 while (*cur != 0) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005114 if (IS_BLANK(*cur)) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005115 *cur = 0;
Daniel Veillard416589a2003-02-17 17:25:42 +00005116 cur++;
5117#ifdef DEBUG_LIST
5118 nb_values++;
5119#endif
5120 while (IS_BLANK(*cur))
5121 *cur++ = 0;
5122 } else
5123 cur++;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005124 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005125#ifdef DEBUG_LIST
5126 xmlGenericError(xmlGenericErrorContext,
5127 "list value: '%s' found %d items\n", oldvalue, nb_values);
5128 nb_values = 0;
5129#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005130 ctxt->state->endvalue = cur;
5131 cur = val;
5132 while ((*cur == 0) && (cur != ctxt->state->endvalue)) cur++;
5133
5134 ctxt->state->value = cur;
5135
5136 while (list != NULL) {
5137 ret = xmlRelaxNGValidateValue(ctxt, list);
5138 if (ret != 0) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005139#ifdef DEBUG_LIST
5140 xmlGenericError(xmlGenericErrorContext,
5141 "Failed to validate value: '%s' with %d rule\n",
5142 ctxt->state->value, nb_values);
5143#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005144 break;
5145 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005146#ifdef DEBUG_LIST
5147 nb_values++;
5148#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005149 list = list->next;
5150 }
5151 if ((ret == 0) && (ctxt->state->value != NULL) &&
5152 (ctxt->state->value != ctxt->state->endvalue)) {
5153 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005154 VALID_ERROR2("Extra data in list: %s\n", ctxt->state->value);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005155 ret = -1;
5156 }
5157 xmlFree(val);
5158 ctxt->state->value = oldvalue;
5159 ctxt->state->endvalue = oldend;
5160 break;
5161 }
5162 case XML_RELAXNG_ONEORMORE:
5163 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
5164 if (ret != 0) {
5165 break;
5166 }
5167 /* no break on purpose */
5168 case XML_RELAXNG_ZEROORMORE: {
5169 xmlChar *cur, *temp;
5170
5171 oldflags = ctxt->flags;
5172 ctxt->flags |= FLAGS_IGNORABLE;
5173 cur = ctxt->state->value;
5174 temp = NULL;
5175 while ((cur != NULL) && (cur != ctxt->state->endvalue) &&
5176 (temp != cur)) {
5177 temp = cur;
5178 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
5179 if (ret != 0) {
5180 ctxt->state->value = temp;
5181 ret = 0;
5182 break;
5183 }
5184 cur = ctxt->state->value;
5185 }
5186 ctxt->flags = oldflags;
5187 break;
5188 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005189 case XML_RELAXNG_EXCEPT: {
5190 xmlRelaxNGDefinePtr list;
5191
5192 list = define->content;
5193 while (list != NULL) {
5194 ret = xmlRelaxNGValidateValue(ctxt, list);
5195 if (ret == 0) {
5196 ret = -1;
5197 break;
5198 } else
5199 ret = 0;
5200 list = list->next;
5201 }
5202 break;
5203 }
Daniel Veillardd4310742003-02-18 21:12:46 +00005204 case XML_RELAXNG_GROUP: {
5205 xmlRelaxNGDefinePtr list;
5206
5207 list = define->content;
5208 while (list != NULL) {
5209 ret = xmlRelaxNGValidateValue(ctxt, list);
5210 if (ret != 0) {
5211 ret = -1;
5212 break;
5213 } else
5214 ret = 0;
5215 list = list->next;
5216 }
5217 break;
5218 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005219 default:
5220 TODO
5221 ret = -1;
5222 }
5223 return(ret);
5224}
5225
5226/**
5227 * xmlRelaxNGValidateValueContent:
5228 * @ctxt: a Relax-NG validation context
5229 * @defines: the list of definitions to verify
5230 *
5231 * Validate the given definitions for the current value
5232 *
5233 * Returns 0 if the validation succeeded or an error code.
5234 */
5235static int
5236xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt,
5237 xmlRelaxNGDefinePtr defines) {
5238 int ret = 0;
5239
5240 while (defines != NULL) {
5241 ret = xmlRelaxNGValidateValue(ctxt, defines);
5242 if (ret != 0)
5243 break;
5244 defines = defines->next;
5245 }
5246 return(ret);
5247}
5248
5249/**
Daniel Veillard144fae12003-02-03 13:17:57 +00005250 * xmlRelaxNGAttributeMatch:
5251 * @ctxt: a Relax-NG validation context
5252 * @define: the definition to check
5253 * @prop: the attribute
5254 *
5255 * Check if the attribute matches the definition nameClass
5256 *
5257 * Returns 1 if the attribute matches, 0 if no, or -1 in case of error
5258 */
5259static int
5260xmlRelaxNGAttributeMatch(xmlRelaxNGValidCtxtPtr ctxt,
5261 xmlRelaxNGDefinePtr define,
5262 xmlAttrPtr prop) {
5263 int ret;
5264
5265 if (define->name != NULL) {
5266 if (!xmlStrEqual(define->name, prop->name))
5267 return(0);
5268 }
5269 if (define->ns != NULL) {
5270 if (define->ns[0] == 0) {
5271 if (prop->ns != NULL)
5272 return(0);
5273 } else {
5274 if ((prop->ns == NULL) ||
5275 (!xmlStrEqual(define->ns, prop->ns->href)))
5276 return(0);
5277 }
5278 }
5279 if (define->nameClass == NULL)
5280 return(1);
5281 define = define->nameClass;
5282 if (define->type == XML_RELAXNG_EXCEPT) {
5283 xmlRelaxNGDefinePtr list;
5284
5285 list = define->content;
5286 while (list != NULL) {
5287 ret = xmlRelaxNGAttributeMatch(ctxt, list, prop);
5288 if (ret == 1)
5289 return(0);
5290 if (ret < 0)
5291 return(ret);
5292 list = list->next;
5293 }
5294 } else {
5295 TODO
5296 }
5297 return(1);
5298}
5299
5300/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00005301 * xmlRelaxNGValidateAttribute:
5302 * @ctxt: a Relax-NG validation context
5303 * @define: the definition to verify
5304 *
5305 * Validate the given attribute definition for that node
5306 *
5307 * Returns 0 if the validation succeeded or an error code.
5308 */
5309static int
5310xmlRelaxNGValidateAttribute(xmlRelaxNGValidCtxtPtr ctxt,
5311 xmlRelaxNGDefinePtr define) {
5312 int ret = 0, i;
5313 xmlChar *value, *oldvalue;
5314 xmlAttrPtr prop = NULL, tmp;
5315
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005316 if (ctxt->state->nbAttrLeft <= 0)
5317 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005318 if (define->name != NULL) {
5319 for (i = 0;i < ctxt->state->nbAttrs;i++) {
5320 tmp = ctxt->state->attrs[i];
5321 if ((tmp != NULL) && (xmlStrEqual(define->name, tmp->name))) {
5322 if ((((define->ns == NULL) || (define->ns[0] == 0)) &&
5323 (tmp->ns == NULL)) ||
5324 ((tmp->ns != NULL) &&
5325 (xmlStrEqual(define->ns, tmp->ns->href)))) {
5326 prop = tmp;
5327 break;
5328 }
5329 }
5330 }
5331 if (prop != NULL) {
5332 value = xmlNodeListGetString(prop->doc, prop->children, 1);
5333 oldvalue = ctxt->state->value;
5334 ctxt->state->value = value;
Daniel Veillard231d7912003-02-09 14:22:17 +00005335 ctxt->state->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005336 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00005337 if (ctxt->state->value != NULL)
5338 value = ctxt->state->value;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005339 if (value != NULL)
5340 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00005341 ctxt->state->value = oldvalue;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005342 if (ret == 0) {
5343 /*
5344 * flag the attribute as processed
5345 */
5346 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005347 ctxt->state->nbAttrLeft--;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005348 }
5349 } else {
5350 ret = -1;
5351 }
5352#ifdef DEBUG
5353 xmlGenericError(xmlGenericErrorContext,
5354 "xmlRelaxNGValidateAttribute(%s): %d\n", define->name, ret);
5355#endif
5356 } else {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005357 for (i = 0;i < ctxt->state->nbAttrs;i++) {
5358 tmp = ctxt->state->attrs[i];
Daniel Veillard144fae12003-02-03 13:17:57 +00005359 if ((tmp != NULL) &&
5360 (xmlRelaxNGAttributeMatch(ctxt, define, tmp) == 1)) {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005361 prop = tmp;
5362 break;
5363 }
5364 }
5365 if (prop != NULL) {
5366 value = xmlNodeListGetString(prop->doc, prop->children, 1);
5367 oldvalue = ctxt->state->value;
5368 ctxt->state->value = value;
5369 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00005370 if (ctxt->state->value != NULL)
5371 value = ctxt->state->value;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005372 if (value != NULL)
5373 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00005374 ctxt->state->value = oldvalue;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005375 if (ret == 0) {
5376 /*
5377 * flag the attribute as processed
5378 */
5379 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005380 ctxt->state->nbAttrLeft--;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005381 }
5382 } else {
5383 ret = -1;
5384 }
5385#ifdef DEBUG
Daniel Veillard144fae12003-02-03 13:17:57 +00005386 if (define->ns != NULL) {
5387 xmlGenericError(xmlGenericErrorContext,
5388 "xmlRelaxNGValidateAttribute(nsName ns = %s): %d\n",
5389 define->ns, ret);
5390 } else {
5391 xmlGenericError(xmlGenericErrorContext,
5392 "xmlRelaxNGValidateAttribute(anyName): %d\n",
5393 ret);
5394 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005395#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00005396 }
5397
5398 return(ret);
5399}
5400
5401/**
5402 * xmlRelaxNGValidateAttributeList:
5403 * @ctxt: a Relax-NG validation context
5404 * @define: the list of definition to verify
5405 *
5406 * Validate the given node against the list of attribute definitions
5407 *
5408 * Returns 0 if the validation succeeded or an error code.
5409 */
5410static int
5411xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt,
5412 xmlRelaxNGDefinePtr defines) {
5413 int ret = 0;
5414 while (defines != NULL) {
5415 if (xmlRelaxNGValidateAttribute(ctxt, defines) != 0)
5416 ret = -1;
5417 defines = defines->next;
5418 }
5419 return(ret);
5420}
5421
5422/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005423 * xmlRelaxNGValidateTryPermutation:
5424 * @ctxt: a Relax-NG validation context
5425 * @groups: the array of groups
5426 * @nbgroups: the number of groups in the array
5427 * @array: the permutation to try
5428 * @len: the size of the set
5429 *
5430 * Try to validate a permutation for the group of definitions.
5431 *
5432 * Returns 0 if the validation succeeded or an error code.
5433 */
5434static int
5435xmlRelaxNGValidateTryPermutation(xmlRelaxNGValidCtxtPtr ctxt,
5436 xmlRelaxNGDefinePtr rule,
5437 xmlNodePtr *array, int len) {
5438 int i, ret;
5439
5440 if (len > 0) {
5441 /*
5442 * One only need the next pointer set-up to do the validation
5443 */
5444 for (i = 0;i < (len - 1);i++)
5445 array[i]->next = array[i + 1];
5446 array[i]->next = NULL;
5447
5448 /*
5449 * Now try to validate the sequence
5450 */
5451 ctxt->state->seq = array[0];
5452 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
5453 } else {
5454 ctxt->state->seq = NULL;
5455 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
5456 }
5457
5458 /*
5459 * the sequence must be fully consumed
5460 */
5461 if (ctxt->state->seq != NULL)
5462 return(-1);
5463
5464 return(ret);
5465}
5466
5467/**
5468 * xmlRelaxNGValidateWalkPermutations:
5469 * @ctxt: a Relax-NG validation context
5470 * @groups: the array of groups
5471 * @nbgroups: the number of groups in the array
5472 * @nodes: the set of nodes
5473 * @array: the current state of the parmutation
5474 * @len: the size of the set
5475 * @level: a pointer to the level variable
5476 * @k: the index in the array to fill
5477 *
5478 * Validate a set of nodes for a groups of definitions, will try the
5479 * full set of permutations
5480 *
5481 * Returns 0 if the validation succeeded or an error code.
5482 */
5483static int
5484xmlRelaxNGValidateWalkPermutations(xmlRelaxNGValidCtxtPtr ctxt,
5485 xmlRelaxNGDefinePtr rule, xmlNodePtr *nodes,
5486 xmlNodePtr *array, int len,
5487 int *level, int k) {
5488 int i, ret;
5489
5490 if ((k >= 0) && (k < len))
5491 array[k] = nodes[*level];
5492 *level = *level + 1;
5493 if (*level == len) {
5494 ret = xmlRelaxNGValidateTryPermutation(ctxt, rule, array, len);
5495 if (ret == 0)
5496 return(0);
5497 } else {
5498 for (i = 0;i < len;i++) {
5499 if (array[i] == NULL) {
5500 ret = xmlRelaxNGValidateWalkPermutations(ctxt, rule,
5501 nodes, array, len, level, i);
5502 if (ret == 0)
5503 return(0);
5504 }
5505 }
5506 }
5507 *level = *level - 1;
5508 array[k] = NULL;
5509 return(-1);
5510}
5511
5512/**
5513 * xmlRelaxNGNodeMatchesList:
5514 * @node: the node
5515 * @list: a NULL terminated array of definitions
5516 *
5517 * Check if a node can be matched by one of the definitions
5518 *
5519 * Returns 1 if matches 0 otherwise
5520 */
5521static int
5522xmlRelaxNGNodeMatchesList(xmlNodePtr node, xmlRelaxNGDefinePtr *list) {
5523 xmlRelaxNGDefinePtr cur;
5524 int i = 0;
5525
5526 if ((node == NULL) || (list == NULL))
5527 return(0);
5528
5529 cur = list[i++];
5530 while (cur != NULL) {
5531 if ((node->type == XML_ELEMENT_NODE) &&
5532 (cur->type == XML_RELAXNG_ELEMENT)) {
5533 if (cur->name == NULL) {
5534 if ((node->ns != NULL) &&
5535 (xmlStrEqual(node->ns->href, cur->ns)))
5536 return(1);
5537 } else if (xmlStrEqual(cur->name, node->name)) {
5538 if ((cur->ns == NULL) || (cur->ns[0] == 0)) {
5539 if (node->ns == NULL)
5540 return(1);
5541 } else {
5542 if ((node->ns != NULL) &&
5543 (xmlStrEqual(node->ns->href, cur->ns)))
5544 return(1);
5545 }
5546 }
5547 } else if ((node->type == XML_TEXT_NODE) &&
5548 (cur->type == XML_RELAXNG_TEXT)) {
5549 return(1);
5550 }
5551 cur = list[i++];
5552 }
5553 return(0);
5554}
5555
5556/**
5557 * xmlRelaxNGValidatePartGroup:
5558 * @ctxt: a Relax-NG validation context
5559 * @groups: the array of groups
5560 * @nbgroups: the number of groups in the array
5561 * @nodes: the set of nodes
5562 * @len: the size of the set of nodes
5563 *
5564 * Validate a set of nodes for a groups of definitions
5565 *
5566 * Returns 0 if the validation succeeded or an error code.
5567 */
5568static int
5569xmlRelaxNGValidatePartGroup(xmlRelaxNGValidCtxtPtr ctxt,
5570 xmlRelaxNGInterleaveGroupPtr *groups,
5571 int nbgroups, xmlNodePtr *nodes, int len) {
Daniel Veillard231d7912003-02-09 14:22:17 +00005572 int level, ret = -1, i, j, k, top_j, max_j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005573 xmlNodePtr *array = NULL, *list, oldseq;
5574 xmlRelaxNGInterleaveGroupPtr group;
5575
5576 list = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
5577 if (list == NULL) {
5578 return(-1);
5579 }
5580 array = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
5581 if (array == NULL) {
5582 xmlFree(list);
5583 return(-1);
5584 }
5585 memset(array, 0, len * sizeof(xmlNodePtr));
5586
5587 /*
5588 * Partition the elements and validate the subsets.
5589 */
5590 oldseq = ctxt->state->seq;
Daniel Veillard231d7912003-02-09 14:22:17 +00005591 max_j = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005592 for (i = 0;i < nbgroups;i++) {
5593 group = groups[i];
5594 if (group == NULL)
5595 continue;
5596 k = 0;
Daniel Veillard231d7912003-02-09 14:22:17 +00005597 top_j = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005598 for (j = 0;j < len;j++) {
5599 if (nodes[j] == NULL)
5600 continue;
5601 if (xmlRelaxNGNodeMatchesList(nodes[j], group->defs)) {
5602 list[k++] = nodes[j];
5603 nodes[j] = NULL;
Daniel Veillard231d7912003-02-09 14:22:17 +00005604 top_j = j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005605 }
5606 }
Daniel Veillard231d7912003-02-09 14:22:17 +00005607 if (top_j > max_j)
5608 max_j = top_j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005609 ctxt->state->seq = oldseq;
5610 if (k > 1) {
5611 memset(array, 0, k * sizeof(xmlNodePtr));
Daniel Veillardb08c9812003-01-28 23:09:49 +00005612 level = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005613 ret = xmlRelaxNGValidateWalkPermutations(ctxt, group->rule,
5614 list, array, k, &level, -1);
5615 } else {
5616 ret = xmlRelaxNGValidateTryPermutation(ctxt, group->rule, list, k);
5617 }
5618 if (ret != 0) {
5619 ctxt->state->seq = oldseq;
5620 break;
5621 }
5622 }
5623
Daniel Veillard231d7912003-02-09 14:22:17 +00005624 for (j = 0;j < max_j;j++) {
5625 if (nodes[j] != NULL) {
5626 TODO /* problem, one of the nodes didn't got a match */
5627 }
5628 }
5629 if (ret == 0) {
5630 if (max_j + 1 < len)
5631 ctxt->state->seq = nodes[max_j + 1];
5632 else
5633 ctxt->state->seq = NULL;
5634 }
5635
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005636 xmlFree(list);
5637 xmlFree(array);
5638 return(ret);
5639}
5640
5641/**
5642 * xmlRelaxNGValidateInterleave:
5643 * @ctxt: a Relax-NG validation context
5644 * @define: the definition to verify
5645 *
5646 * Validate an interleave definition for a node.
5647 *
5648 * Returns 0 if the validation succeeded or an error code.
5649 */
5650static int
5651xmlRelaxNGValidateInterleave(xmlRelaxNGValidCtxtPtr ctxt,
5652 xmlRelaxNGDefinePtr define) {
5653 int ret = 0, nbchildren, nbtot, i, j;
5654 xmlRelaxNGPartitionPtr partitions;
5655 xmlNodePtr *children = NULL;
5656 xmlNodePtr *order = NULL;
Daniel Veillard231d7912003-02-09 14:22:17 +00005657 xmlNodePtr cur, oldseq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005658
5659 if (define->data != NULL) {
5660 partitions = (xmlRelaxNGPartitionPtr) define->data;
5661 } else {
5662 VALID_CTXT();
5663 VALID_ERROR("Internal: interleave block has no data\n");
5664 return(-1);
5665 }
5666
5667 /*
5668 * Build the sequence of child and an array preserving the children
5669 * initial order.
5670 */
5671 cur = ctxt->state->seq;
Daniel Veillard231d7912003-02-09 14:22:17 +00005672 oldseq = ctxt->state->seq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005673 nbchildren = 0;
5674 nbtot = 0;
5675 while (cur != NULL) {
5676 if ((cur->type == XML_COMMENT_NODE) ||
5677 (cur->type == XML_PI_NODE) ||
5678 ((cur->type == XML_TEXT_NODE) &&
5679 (IS_BLANK_NODE(cur)))) {
5680 nbtot++;
5681 } else {
5682 nbchildren++;
5683 nbtot++;
5684 }
5685 cur = cur->next;
5686 }
5687 children = (xmlNodePtr *) xmlMalloc(nbchildren * sizeof(xmlNodePtr));
5688 if (children == NULL)
5689 goto error;
5690 order = (xmlNodePtr *) xmlMalloc(nbtot * sizeof(xmlNodePtr));
5691 if (order == NULL)
5692 goto error;
5693 cur = ctxt->state->seq;
5694 i = 0;
5695 j = 0;
5696 while (cur != NULL) {
5697 if ((cur->type == XML_COMMENT_NODE) ||
5698 (cur->type == XML_PI_NODE) ||
5699 ((cur->type == XML_TEXT_NODE) &&
5700 (IS_BLANK_NODE(cur)))) {
5701 order[j++] = cur;
5702 } else {
5703 order[j++] = cur;
5704 children[i++] = cur;
5705 }
5706 cur = cur->next;
5707 }
5708
5709 /* TODO: retry with a maller set of child if there is a next... */
5710 ret = xmlRelaxNGValidatePartGroup(ctxt, partitions->groups,
5711 partitions->nbgroups, children, nbchildren);
Daniel Veillard231d7912003-02-09 14:22:17 +00005712 if (ret != 0)
5713 ctxt->state->seq = oldseq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005714
5715 /*
5716 * Cleanup: rebuid the child sequence and free the structure
5717 */
5718 if (order != NULL) {
5719 for (i = 0;i < nbtot;i++) {
5720 if (i == 0)
5721 order[i]->prev = NULL;
5722 else
5723 order[i]->prev = order[i - 1];
5724 if (i == nbtot - 1)
5725 order[i]->next = NULL;
5726 else
5727 order[i]->next = order[i + 1];
5728 }
5729 xmlFree(order);
5730 }
5731 if (children != NULL)
5732 xmlFree(children);
5733
5734 return(ret);
5735
5736error:
5737 if (order != NULL) {
5738 for (i = 0;i < nbtot;i++) {
5739 if (i == 0)
5740 order[i]->prev = NULL;
5741 else
5742 order[i]->prev = order[i - 1];
5743 if (i == nbtot - 1)
5744 order[i]->next = NULL;
5745 else
5746 order[i]->next = order[i + 1];
5747 }
5748 xmlFree(order);
5749 }
5750 if (children != NULL)
5751 xmlFree(children);
5752 return(-1);
5753}
5754
5755/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00005756 * xmlRelaxNGValidateElementContent:
5757 * @ctxt: a Relax-NG validation context
5758 * @define: the list of definition to verify
5759 *
5760 * Validate the given node content against the (list) of definitions
5761 *
5762 * Returns 0 if the validation succeeded or an error code.
5763 */
5764static int
5765xmlRelaxNGValidateElementContent(xmlRelaxNGValidCtxtPtr ctxt,
5766 xmlRelaxNGDefinePtr defines) {
5767 int ret = 0, res;
5768
5769 if (ctxt->state == NULL) {
5770 VALID_CTXT();
5771 VALID_ERROR("Internal: no state\n");
5772 return(-1);
5773 }
5774 while (defines != NULL) {
5775 res = xmlRelaxNGValidateDefinition(ctxt, defines);
5776 if (res < 0)
5777 ret = -1;
5778 defines = defines->next;
5779 }
5780
5781 return(ret);
5782}
5783
5784/**
Daniel Veillard416589a2003-02-17 17:25:42 +00005785 * xmlRelaxNGElementMatch:
5786 * @ctxt: a Relax-NG validation context
5787 * @define: the definition to check
5788 * @elem: the element
5789 *
5790 * Check if the element matches the definition nameClass
5791 *
5792 * Returns 1 if the element matches, 0 if no, or -1 in case of error
5793 */
5794static int
5795xmlRelaxNGElementMatch(xmlRelaxNGValidCtxtPtr ctxt,
5796 xmlRelaxNGDefinePtr define,
5797 xmlNodePtr elem) {
5798 int ret, oldflags;
5799
5800 if (define->name != NULL) {
5801 if (!xmlStrEqual(elem->name, define->name)) {
5802 VALID_CTXT();
5803 VALID_ERROR3("Expecting element %s, got %s\n",
5804 define->name, elem->name);
5805 return(0);
5806 }
5807 }
5808 if ((define->ns != NULL) && (define->ns[0] != 0)) {
5809 if (elem->ns == NULL) {
5810 VALID_CTXT();
5811 VALID_ERROR2("Expecting a namespace for element %s\n",
5812 elem->name);
5813 return(0);
5814 } else if (!xmlStrEqual(elem->ns->href, define->ns)) {
5815 VALID_CTXT();
5816 VALID_ERROR3("Expecting element %s has wrong namespace: expecting %s\n",
5817 elem->name, define->ns);
5818 return(0);
5819 }
Daniel Veillard8fe98712003-02-19 00:19:14 +00005820 } else if ((elem->ns != NULL) && (define->ns != NULL) &&
5821 (define->name == NULL)) {
5822 VALID_CTXT();
5823 VALID_ERROR2("Expecting no namespace for element %s\n",
5824 define->name);
5825 return(0);
5826 } else if ((elem->ns != NULL) && (define->name != NULL)) {
5827 VALID_CTXT();
5828 VALID_ERROR2("Expecting no namespace for element %s\n",
5829 define->name);
5830 return(0);
Daniel Veillard416589a2003-02-17 17:25:42 +00005831 }
5832
5833 if (define->nameClass == NULL)
5834 return(1);
5835
5836 define = define->nameClass;
5837 if (define->type == XML_RELAXNG_EXCEPT) {
5838 xmlRelaxNGDefinePtr list;
5839 oldflags = ctxt->flags;
5840 ctxt->flags |= FLAGS_IGNORABLE;
5841
5842 list = define->content;
5843 while (list != NULL) {
5844 ret = xmlRelaxNGElementMatch(ctxt, list, elem);
5845 if (ret == 1) {
5846 ctxt->flags = oldflags;
5847 return(0);
5848 }
5849 if (ret < 0) {
5850 ctxt->flags = oldflags;
5851 return(ret);
5852 }
5853 list = list->next;
5854 }
5855 ctxt->flags = oldflags;
5856 } else {
5857 TODO
5858 }
5859 return(1);
5860}
5861
5862/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00005863 * xmlRelaxNGValidateDefinition:
5864 * @ctxt: a Relax-NG validation context
5865 * @define: the definition to verify
5866 *
5867 * Validate the current node against the definition
5868 *
5869 * Returns 0 if the validation succeeded or an error code.
5870 */
5871static int
5872xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
5873 xmlRelaxNGDefinePtr define) {
5874 xmlNodePtr node;
5875 int ret = 0, i, tmp, oldflags;
5876 xmlRelaxNGValidStatePtr oldstate, state;
5877
5878 if (define == NULL) {
5879 VALID_CTXT();
5880 VALID_ERROR("internal error: define == NULL\n");
5881 return(-1);
5882 }
5883 if (ctxt->state != NULL) {
5884 node = ctxt->state->seq;
5885 } else {
5886 node = NULL;
5887 }
Daniel Veillard231d7912003-02-09 14:22:17 +00005888#ifdef DEBUG
5889 for (i = 0;i < ctxt->depth;i++)
5890 xmlGenericError(xmlGenericErrorContext, " ");
5891 xmlGenericError(xmlGenericErrorContext,
5892 "Start validating %s ", xmlRelaxNGDefName(define));
5893 if (define->name != NULL)
5894 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
5895 if ((node != NULL) && (node->name != NULL))
5896 xmlGenericError(xmlGenericErrorContext, "on %s\n", node->name);
5897 else
5898 xmlGenericError(xmlGenericErrorContext, "\n");
5899#endif
5900 ctxt->depth++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005901 switch (define->type) {
5902 case XML_RELAXNG_EMPTY:
Daniel Veillardd4310742003-02-18 21:12:46 +00005903 node = xmlRelaxNGSkipIgnored(ctxt, node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005904 if (node != NULL) {
5905 VALID_CTXT();
5906 VALID_ERROR("Expecting an empty element\n");
Daniel Veillard231d7912003-02-09 14:22:17 +00005907 ret = -1;
5908 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005909 }
Daniel Veillard231d7912003-02-09 14:22:17 +00005910 ret = 0;
5911 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005912 case XML_RELAXNG_NOT_ALLOWED:
Daniel Veillard231d7912003-02-09 14:22:17 +00005913 ret = -1;
5914 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005915 case XML_RELAXNG_TEXT:
Daniel Veillard231d7912003-02-09 14:22:17 +00005916 if (node == NULL) {
5917 ret = 0;
5918 break;
5919 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005920 while ((node != NULL) &&
5921 ((node->type == XML_TEXT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005922 (node->type == XML_COMMENT_NODE) ||
5923 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00005924 (node->type == XML_CDATA_SECTION_NODE)))
5925 node = node->next;
Daniel Veillard276be4a2003-01-24 01:03:34 +00005926 if (node == ctxt->state->seq) {
5927 VALID_CTXT();
5928 VALID_ERROR("Expecting text content\n");
5929 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005930 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00005931 ctxt->state->seq = node;
5932 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005933 case XML_RELAXNG_ELEMENT:
5934 node = xmlRelaxNGSkipIgnored(ctxt, node);
Daniel Veillard231d7912003-02-09 14:22:17 +00005935 if (node == NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005936 VALID_CTXT();
Daniel Veillard231d7912003-02-09 14:22:17 +00005937 VALID_ERROR("Expecting an element, got empty\n");
5938 ret = -1;
5939 break;
5940 }
5941 if (node->type != XML_ELEMENT_NODE) {
5942 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005943 VALID_ERROR2("Expecting an element got %d type\n", node->type);
Daniel Veillard231d7912003-02-09 14:22:17 +00005944 ret = -1;
5945 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005946 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00005947 /*
5948 * This node was already validated successfully against
5949 * this definition.
5950 */
5951 if (node->_private == define)
5952 break;
Daniel Veillard416589a2003-02-17 17:25:42 +00005953
5954 ret = xmlRelaxNGElementMatch(ctxt, define, node);
5955 if (ret <= 0) {
5956 ret = -1;
5957 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005958 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005959 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005960
5961 state = xmlRelaxNGNewValidState(ctxt, node);
5962 if (state == NULL) {
Daniel Veillard231d7912003-02-09 14:22:17 +00005963 ret = -1;
5964 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005965 }
5966
5967 oldstate = ctxt->state;
5968 ctxt->state = state;
5969 if (define->attrs != NULL) {
5970 tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
Daniel Veillard231d7912003-02-09 14:22:17 +00005971 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005972 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00005973#ifdef DEBUG
5974 xmlGenericError(xmlGenericErrorContext,
5975 "E: Element %s failed to validate attributes\n",
5976 node->name);
5977#endif
5978 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005979 }
5980 if (define->content != NULL) {
5981 tmp = xmlRelaxNGValidateElementContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00005982 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005983 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00005984#ifdef DEBUG
5985 xmlGenericError(xmlGenericErrorContext,
5986 "E: Element %s failed to validate element content\n",
5987 node->name);
5988#endif
5989 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005990 }
5991 state = ctxt->state;
5992 if (state->seq != NULL) {
5993 state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq);
5994 if (state->seq != NULL) {
5995 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005996 VALID_ERROR3("Extra content for element %s: %s\n",
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005997 node->name, state->seq->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005998 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00005999#ifdef DEBUG
6000 xmlGenericError(xmlGenericErrorContext,
6001 "E: Element %s has extra content: %s\n",
6002 node->name, state->seq->name);
6003#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00006004 }
6005 }
6006 for (i = 0;i < state->nbAttrs;i++) {
6007 if (state->attrs[i] != NULL) {
6008 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006009 VALID_ERROR3("Invalid attribute %s for element %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00006010 state->attrs[i]->name, node->name);
6011 ret = -1;
6012 }
6013 }
6014 ctxt->state = oldstate;
6015 xmlRelaxNGFreeValidState(state);
6016 if (oldstate != NULL)
Daniel Veillarde5b110b2003-02-04 14:43:39 +00006017 oldstate->seq = xmlRelaxNGSkipIgnored(ctxt, node->next);
6018 if (ret == 0)
6019 node->_private = define;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006020
6021
6022#ifdef DEBUG
6023 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard231d7912003-02-09 14:22:17 +00006024 "xmlRelaxNGValidateDefinition(): validated %s : %d",
Daniel Veillard6eadf632003-01-23 18:29:16 +00006025 node->name, ret);
Daniel Veillard231d7912003-02-09 14:22:17 +00006026 if (oldstate == NULL)
6027 xmlGenericError(xmlGenericErrorContext, ": no state\n");
6028 else if (oldstate->seq == NULL)
6029 xmlGenericError(xmlGenericErrorContext, ": done\n");
6030 else if (oldstate->seq->type == XML_ELEMENT_NODE)
6031 xmlGenericError(xmlGenericErrorContext, ": next elem %s\n",
6032 oldstate->seq->name);
6033 else
6034 xmlGenericError(xmlGenericErrorContext, ": next %s %d\n",
6035 oldstate->seq->name, oldstate->seq->type);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006036#endif
6037 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006038 case XML_RELAXNG_OPTIONAL:
6039 oldflags = ctxt->flags;
6040 ctxt->flags |= FLAGS_IGNORABLE;
6041 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6042 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6043 if (ret != 0) {
6044 xmlRelaxNGFreeValidState(ctxt->state);
6045 ctxt->state = oldstate;
6046 ret = 0;
6047 break;
6048 }
6049 xmlRelaxNGFreeValidState(oldstate);
6050 ctxt->flags = oldflags;
6051 ret = 0;
6052 break;
6053 case XML_RELAXNG_ONEORMORE:
6054 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6055 if (ret != 0) {
6056 break;
6057 }
6058 /* no break on purpose */
Daniel Veillard276be4a2003-01-24 01:03:34 +00006059 case XML_RELAXNG_ZEROORMORE: {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006060 oldflags = ctxt->flags;
6061 ctxt->flags |= FLAGS_IGNORABLE;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006062 while (ctxt->state->nbAttrLeft != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006063 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6064 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6065 if (ret != 0) {
6066 xmlRelaxNGFreeValidState(ctxt->state);
6067 ctxt->state = oldstate;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006068 break;
6069 }
6070 xmlRelaxNGFreeValidState(oldstate);
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006071 }
6072 if (ret == 0) {
6073 /*
6074 * There is no attribute left to be consumed,
6075 * we can check the closure by looking at ctxt->state->seq
6076 */
6077 xmlNodePtr cur, temp;
6078
Daniel Veillard276be4a2003-01-24 01:03:34 +00006079 cur = ctxt->state->seq;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006080 temp = NULL;
6081 while ((cur != NULL) && (temp != cur)) {
6082 temp = cur;
6083 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6084 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6085 if (ret != 0) {
6086 xmlRelaxNGFreeValidState(ctxt->state);
6087 ctxt->state = oldstate;
6088 break;
6089 }
6090 xmlRelaxNGFreeValidState(oldstate);
6091 cur = ctxt->state->seq;
6092 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006093 }
6094 ctxt->flags = oldflags;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006095 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006096 break;
Daniel Veillard276be4a2003-01-24 01:03:34 +00006097 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006098 case XML_RELAXNG_CHOICE: {
6099 xmlRelaxNGDefinePtr list = define->content;
6100
6101 oldflags = ctxt->flags;
6102 ctxt->flags |= FLAGS_IGNORABLE;
6103
6104 while (list != NULL) {
6105 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6106 ret = xmlRelaxNGValidateDefinition(ctxt, list);
6107 if (ret == 0) {
6108 xmlRelaxNGFreeValidState(oldstate);
6109 break;
6110 }
6111 xmlRelaxNGFreeValidState(ctxt->state);
6112 ctxt->state = oldstate;
6113 list = list->next;
6114 }
6115 ctxt->flags = oldflags;
6116 break;
6117 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00006118 case XML_RELAXNG_DEF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00006119 case XML_RELAXNG_GROUP: {
6120 xmlRelaxNGDefinePtr list = define->content;
6121
6122 while (list != NULL) {
6123 ret = xmlRelaxNGValidateDefinition(ctxt, list);
6124 if (ret != 0)
6125 break;
6126 list = list->next;
6127 }
6128 break;
6129 }
6130 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006131 ret = xmlRelaxNGValidateInterleave(ctxt, define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006132 break;
6133 case XML_RELAXNG_ATTRIBUTE:
6134 ret = xmlRelaxNGValidateAttribute(ctxt, define);
6135 break;
6136 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00006137 case XML_RELAXNG_PARENTREF:
6138 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00006139 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6140 break;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006141 case XML_RELAXNG_DATATYPE: {
Daniel Veillardd4310742003-02-18 21:12:46 +00006142 xmlNodePtr child;
6143 xmlChar *content = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006144
Daniel Veillardd4310742003-02-18 21:12:46 +00006145 child = node;
6146 while (child != NULL) {
6147 if (child->type == XML_ELEMENT_NODE) {
6148 VALID_CTXT();
6149 VALID_ERROR2("Element %s has child elements\n",
6150 node->parent->name);
6151 ret = -1;
6152 break;
6153 } else if ((child->type == XML_TEXT_NODE) ||
6154 (child->type == XML_CDATA_SECTION_NODE)) {
6155 content = xmlStrcat(content, child->content);
6156 }
6157 /* TODO: handle entities ... */
6158 child = child->next;
6159 }
6160 if (ret == -1) {
6161 if (content != NULL)
6162 xmlFree(content);
6163 break;
6164 }
6165 if (content == NULL) {
6166 content = xmlStrdup(BAD_CAST "");
6167 if (content == NULL) {
6168 VALID_CTXT();
6169 VALID_ERROR("Allocation failure\n");
6170 ret = -1;
6171 break;
6172 }
6173 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006174 ret = xmlRelaxNGValidateDatatype(ctxt, content, define);
6175 if (ret == -1) {
6176 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006177 VALID_ERROR2("internal error validating %s\n", define->name);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006178 } else if (ret == 0) {
Daniel Veillardd4310742003-02-18 21:12:46 +00006179 ctxt->state->seq = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006180 }
6181 if (content != NULL)
6182 xmlFree(content);
6183 break;
6184 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00006185 case XML_RELAXNG_VALUE: {
Daniel Veillardd4310742003-02-18 21:12:46 +00006186 xmlChar *content = NULL;
Daniel Veillardea3f3982003-01-26 19:45:18 +00006187 xmlChar *oldvalue;
Daniel Veillardd4310742003-02-18 21:12:46 +00006188 xmlNodePtr child;
Daniel Veillardea3f3982003-01-26 19:45:18 +00006189
Daniel Veillardd4310742003-02-18 21:12:46 +00006190 child = node;
6191 while (child != NULL) {
6192 if (child->type == XML_ELEMENT_NODE) {
6193 VALID_CTXT();
6194 VALID_ERROR2("Element %s has child elements\n",
6195 node->parent->name);
6196 ret = -1;
6197 break;
6198 } else if ((child->type == XML_TEXT_NODE) ||
6199 (child->type == XML_CDATA_SECTION_NODE)) {
6200 content = xmlStrcat(content, child->content);
6201 }
6202 /* TODO: handle entities ... */
6203 child = child->next;
6204 }
6205 if (ret == -1) {
6206 if (content != NULL)
6207 xmlFree(content);
6208 break;
6209 }
6210 if (content == NULL) {
6211 content = xmlStrdup(BAD_CAST "");
6212 if (content == NULL) {
6213 VALID_CTXT();
6214 VALID_ERROR("Allocation failure\n");
6215 ret = -1;
6216 break;
6217 }
6218 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00006219 oldvalue = ctxt->state->value;
6220 ctxt->state->value = content;
6221 ret = xmlRelaxNGValidateValue(ctxt, define);
6222 ctxt->state->value = oldvalue;
6223 if (ret == -1) {
6224 VALID_CTXT();
Daniel Veillardd2298792003-02-14 16:54:11 +00006225 if (define->name != NULL) {
6226 VALID_ERROR2("error validating value %s\n", define->name);
6227 } else {
6228 VALID_ERROR("error validating value\n");
6229 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00006230 } else if (ret == 0) {
Daniel Veillardd4310742003-02-18 21:12:46 +00006231 ctxt->state->seq = NULL;
Daniel Veillardea3f3982003-01-26 19:45:18 +00006232 }
6233 if (content != NULL)
6234 xmlFree(content);
6235 break;
6236 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006237 case XML_RELAXNG_LIST: {
6238 xmlChar *content;
Daniel Veillardd4310742003-02-18 21:12:46 +00006239 xmlNodePtr child;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006240 xmlChar *oldvalue, *oldendvalue;
6241 int len;
Daniel Veillardea3f3982003-01-26 19:45:18 +00006242
Daniel Veillardd4310742003-02-18 21:12:46 +00006243 /*
6244 * Make sure it's only text nodes
6245 */
6246
6247 content = NULL;
6248 child = node;
6249 while (child != NULL) {
6250 if (child->type == XML_ELEMENT_NODE) {
6251 VALID_CTXT();
6252 VALID_ERROR2("Element %s has child elements\n",
6253 node->parent->name);
6254 ret = -1;
6255 break;
6256 } else if ((child->type == XML_TEXT_NODE) ||
6257 (child->type == XML_CDATA_SECTION_NODE)) {
6258 content = xmlStrcat(content, child->content);
6259 }
6260 /* TODO: handle entities ... */
6261 child = child->next;
6262 }
6263 if (ret == -1) {
6264 if (content != NULL)
6265 xmlFree(content);
6266 break;
6267 }
6268 if (content == NULL) {
6269 content = xmlStrdup(BAD_CAST "");
6270 if (content == NULL) {
6271 VALID_CTXT();
6272 VALID_ERROR("Allocation failure\n");
6273 ret = -1;
6274 break;
6275 }
6276 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006277 len = xmlStrlen(content);
6278 oldvalue = ctxt->state->value;
6279 oldendvalue = ctxt->state->endvalue;
6280 ctxt->state->value = content;
6281 ctxt->state->endvalue = content + len;
6282 ret = xmlRelaxNGValidateValue(ctxt, define);
6283 ctxt->state->value = oldvalue;
6284 ctxt->state->endvalue = oldendvalue;
6285 if (ret == -1) {
6286 VALID_CTXT();
6287 VALID_ERROR("internal error validating list\n");
Daniel Veillardd4310742003-02-18 21:12:46 +00006288 } else if ((ret == 0) && (node != NULL)) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006289 ctxt->state->seq = node->next;
6290 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006291 if (content != NULL)
6292 xmlFree(content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006293 break;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006294 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006295 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00006296 case XML_RELAXNG_EXCEPT:
Daniel Veillard8fe98712003-02-19 00:19:14 +00006297 case XML_RELAXNG_PARAM:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006298 TODO
Daniel Veillard416589a2003-02-17 17:25:42 +00006299 ret = -1;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006300 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006301 }
Daniel Veillard231d7912003-02-09 14:22:17 +00006302 ctxt->depth--;
6303#ifdef DEBUG
6304 for (i = 0;i < ctxt->depth;i++)
6305 xmlGenericError(xmlGenericErrorContext, " ");
6306 xmlGenericError(xmlGenericErrorContext,
6307 "Validating %s ", xmlRelaxNGDefName(define));
6308 if (define->name != NULL)
6309 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
6310 if (ret == 0)
6311 xmlGenericError(xmlGenericErrorContext, "suceeded\n");
6312 else
6313 xmlGenericError(xmlGenericErrorContext, "failed\n");
6314#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00006315 return(ret);
6316}
6317
6318/**
6319 * xmlRelaxNGValidateDocument:
6320 * @ctxt: a Relax-NG validation context
6321 * @doc: the document
6322 *
6323 * Validate the given document
6324 *
6325 * Returns 0 if the validation succeeded or an error code.
6326 */
6327static int
6328xmlRelaxNGValidateDocument(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
6329 int ret;
6330 xmlRelaxNGPtr schema;
6331 xmlRelaxNGGrammarPtr grammar;
6332 xmlRelaxNGValidStatePtr state;
6333
6334 if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL))
6335 return(-1);
6336
6337 schema = ctxt->schema;
6338 grammar = schema->topgrammar;
6339 if (grammar == NULL) {
6340 VALID_CTXT();
6341 VALID_ERROR("No top grammar defined\n");
6342 return(-1);
6343 }
6344 state = xmlRelaxNGNewValidState(ctxt, NULL);
6345 ctxt->state = state;
6346 ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start);
6347 state = ctxt->state;
6348 if ((state != NULL) && (state->seq != NULL)) {
6349 xmlNodePtr node;
6350
6351 node = state->seq;
6352 node = xmlRelaxNGSkipIgnored(ctxt, node);
6353 if (node != NULL) {
6354 VALID_CTXT();
6355 VALID_ERROR("extra data on the document\n");
6356 ret = -1;
6357 }
6358 }
6359 xmlRelaxNGFreeValidState(state);
6360
6361 return(ret);
6362}
6363
6364/************************************************************************
6365 * *
6366 * Validation interfaces *
6367 * *
6368 ************************************************************************/
6369/**
6370 * xmlRelaxNGNewValidCtxt:
6371 * @schema: a precompiled XML RelaxNGs
6372 *
6373 * Create an XML RelaxNGs validation context based on the given schema
6374 *
6375 * Returns the validation context or NULL in case of error
6376 */
6377xmlRelaxNGValidCtxtPtr
6378xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema) {
6379 xmlRelaxNGValidCtxtPtr ret;
6380
6381 ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt));
6382 if (ret == NULL) {
6383 xmlGenericError(xmlGenericErrorContext,
6384 "Failed to allocate new schama validation context\n");
6385 return (NULL);
6386 }
6387 memset(ret, 0, sizeof(xmlRelaxNGValidCtxt));
6388 ret->schema = schema;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006389 ret->error = xmlGenericError;
6390 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006391 return (ret);
6392}
6393
6394/**
6395 * xmlRelaxNGFreeValidCtxt:
6396 * @ctxt: the schema validation context
6397 *
6398 * Free the resources associated to the schema validation context
6399 */
6400void
6401xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt) {
6402 if (ctxt == NULL)
6403 return;
6404 xmlFree(ctxt);
6405}
6406
6407/**
6408 * xmlRelaxNGSetValidErrors:
6409 * @ctxt: a Relax-NG validation context
6410 * @err: the error function
6411 * @warn: the warning function
6412 * @ctx: the functions context
6413 *
6414 * Set the error and warning callback informations
6415 */
6416void
6417xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
6418 xmlRelaxNGValidityErrorFunc err,
6419 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
6420 if (ctxt == NULL)
6421 return;
6422 ctxt->error = err;
6423 ctxt->warning = warn;
6424 ctxt->userData = ctx;
6425}
6426
6427/**
6428 * xmlRelaxNGValidateDoc:
6429 * @ctxt: a Relax-NG validation context
6430 * @doc: a parsed document tree
6431 *
6432 * Validate a document tree in memory.
6433 *
6434 * Returns 0 if the document is valid, a positive error code
6435 * number otherwise and -1 in case of internal or API error.
6436 */
6437int
6438xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
6439 int ret;
6440
6441 if ((ctxt == NULL) || (doc == NULL))
6442 return(-1);
6443
6444 ctxt->doc = doc;
6445
6446 ret = xmlRelaxNGValidateDocument(ctxt, doc);
Daniel Veillard71531f32003-02-05 13:19:53 +00006447 /*
6448 * TODO: build error codes
6449 */
6450 if (ret == -1)
6451 return(1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006452 return(ret);
6453}
6454
6455#endif /* LIBXML_SCHEMAS_ENABLED */
6456