blob: 13beb42979cb0d5cb0a51faa947821c0586640cd [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) {
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003001 xmlRelaxNGDefinePtr ret, tmp;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003002 xmlChar *val;
3003
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003004 ret = def;
3005 if ((IS_RELAXNG(node, "name")) || (IS_RELAXNG(node, "anyName")) ||
3006 (IS_RELAXNG(node, "nsName"))) {
3007 if ((def->type != XML_RELAXNG_ELEMENT) &&
3008 (def->type != XML_RELAXNG_ATTRIBUTE)) {
3009 ret = xmlRelaxNGNewDefine(ctxt, node);
3010 if (ret == NULL)
3011 return(NULL);
3012 ret->parent = def;
3013 if (ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE)
3014 ret->type = XML_RELAXNG_ATTRIBUTE;
3015 else
3016 ret->type = XML_RELAXNG_ELEMENT;
3017 }
3018 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003019 if (IS_RELAXNG(node, "name")) {
3020 val = xmlNodeGetContent(node);
Daniel Veillardd2298792003-02-14 16:54:11 +00003021 xmlRelaxNGNormExtSpace(val);
3022 if (xmlValidateNCName(val, 0)) {
3023 if (ctxt->error != NULL) {
3024 if (node->parent != NULL)
3025 ctxt->error(ctxt->userData,
3026 "Element %s name '%s' is not an NCName\n",
3027 node->parent->name, val);
3028 else
3029 ctxt->error(ctxt->userData,
3030 "name '%s' is not an NCName\n",
3031 val);
3032 }
3033 ctxt->nbErrors++;
3034 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003035 ret->name = val;
3036 val = xmlGetProp(node, BAD_CAST "ns");
3037 ret->ns = val;
Daniel Veillard416589a2003-02-17 17:25:42 +00003038 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3039 (val != NULL) &&
3040 (xmlStrEqual(val, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
3041 ctxt->error(ctxt->userData,
3042 "Attribute with namespace '%s' is not allowed\n",
3043 val);
3044 ctxt->nbErrors++;
3045 }
3046 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3047 (val != NULL) &&
3048 (val[0] == 0) &&
3049 (xmlStrEqual(ret->name, BAD_CAST "xmlns"))) {
3050 ctxt->error(ctxt->userData,
3051 "Attribute with QName 'xmlns' is not allowed\n",
3052 val);
3053 ctxt->nbErrors++;
3054 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003055 } else if (IS_RELAXNG(node, "anyName")) {
3056 ret->name = NULL;
3057 ret->ns = NULL;
3058 if (node->children != NULL) {
3059 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00003060 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
3061 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003062 }
3063 } else if (IS_RELAXNG(node, "nsName")) {
3064 ret->name = NULL;
3065 ret->ns = xmlGetProp(node, BAD_CAST "ns");
3066 if (ret->ns == NULL) {
3067 if (ctxt->error != NULL)
3068 ctxt->error(ctxt->userData,
3069 "nsName has no ns attribute\n");
3070 ctxt->nbErrors++;
3071 }
Daniel Veillard416589a2003-02-17 17:25:42 +00003072 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3073 (ret->ns != NULL) &&
3074 (xmlStrEqual(ret->ns, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
3075 ctxt->error(ctxt->userData,
3076 "Attribute with namespace '%s' is not allowed\n",
3077 ret->ns);
3078 ctxt->nbErrors++;
3079 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003080 if (node->children != NULL) {
3081 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00003082 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
3083 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003084 }
3085 } else if (IS_RELAXNG(node, "choice")) {
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003086 xmlNodePtr child;
3087 xmlRelaxNGDefinePtr last = NULL;
3088
Daniel Veillardd4310742003-02-18 21:12:46 +00003089 ret = xmlRelaxNGNewDefine(ctxt, node);
3090 if (ret == NULL)
3091 return(NULL);
3092 ret->parent = def;
3093 ret->type = XML_RELAXNG_CHOICE;
3094
Daniel Veillardd2298792003-02-14 16:54:11 +00003095 if (node->children == NULL) {
3096 if (ctxt->error != NULL)
3097 ctxt->error(ctxt->userData,
3098 "Element choice is empty\n");
3099 ctxt->nbErrors++;
3100 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00003101
3102 child = node->children;
3103 while (child != NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00003104 tmp = xmlRelaxNGParseNameClass(ctxt, child, ret);
Daniel Veillardd2298792003-02-14 16:54:11 +00003105 if (tmp != NULL) {
3106 if (last == NULL) {
3107 last = ret->nameClass = tmp;
3108 } else {
3109 last->next = tmp;
3110 last = tmp;
3111 }
3112 }
3113 child = child->next;
3114 }
3115 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003116 } else {
3117 if (ctxt->error != NULL)
3118 ctxt->error(ctxt->userData,
3119 "expecting name, anyName, nsName or choice : got %s\n",
3120 node->name);
3121 ctxt->nbErrors++;
3122 return(NULL);
3123 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003124 if (ret != def) {
3125 if (def->nameClass == NULL) {
3126 def->nameClass = ret;
3127 } else {
3128 tmp = def->nameClass;
3129 while (tmp->next != NULL) {
3130 tmp = tmp->next;
3131 }
3132 tmp->next = ret;
3133 }
3134 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003135 return(ret);
3136}
3137
3138/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00003139 * xmlRelaxNGParseElement:
3140 * @ctxt: a Relax-NG parser context
3141 * @node: the element node
3142 *
3143 * parse the content of a RelaxNG element node.
3144 *
3145 * Returns the definition pointer or NULL in case of error.
3146 */
3147static xmlRelaxNGDefinePtr
3148xmlRelaxNGParseElement(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3149 xmlRelaxNGDefinePtr ret, cur, last;
3150 xmlNodePtr child;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003151 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003152
3153 ret = xmlRelaxNGNewDefine(ctxt, node);
3154 if (ret == NULL)
3155 return(NULL);
3156 ret->type = XML_RELAXNG_ELEMENT;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003157 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003158 child = node->children;
3159 if (child == NULL) {
3160 if (ctxt->error != NULL)
3161 ctxt->error(ctxt->userData,
3162 "xmlRelaxNGParseElement: element has no children\n");
3163 ctxt->nbErrors++;
3164 return(ret);
3165 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003166 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
3167 if (cur != NULL)
3168 child = child->next;
3169
Daniel Veillard6eadf632003-01-23 18:29:16 +00003170 if (child == NULL) {
3171 if (ctxt->error != NULL)
3172 ctxt->error(ctxt->userData,
3173 "xmlRelaxNGParseElement: element has no content\n");
3174 ctxt->nbErrors++;
3175 return(ret);
3176 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003177 olddefine = ctxt->define;
3178 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003179 last = NULL;
3180 while (child != NULL) {
3181 cur = xmlRelaxNGParsePattern(ctxt, child);
3182 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003183 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003184 switch (cur->type) {
3185 case XML_RELAXNG_EMPTY:
3186 case XML_RELAXNG_NOT_ALLOWED:
3187 case XML_RELAXNG_TEXT:
3188 case XML_RELAXNG_ELEMENT:
3189 case XML_RELAXNG_DATATYPE:
3190 case XML_RELAXNG_VALUE:
3191 case XML_RELAXNG_LIST:
3192 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00003193 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003194 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00003195 case XML_RELAXNG_DEF:
3196 case XML_RELAXNG_ZEROORMORE:
3197 case XML_RELAXNG_ONEORMORE:
3198 case XML_RELAXNG_OPTIONAL:
3199 case XML_RELAXNG_CHOICE:
3200 case XML_RELAXNG_GROUP:
3201 case XML_RELAXNG_INTERLEAVE:
3202 if (last == NULL) {
3203 ret->content = last = cur;
3204 } else {
3205 if ((last->type == XML_RELAXNG_ELEMENT) &&
3206 (ret->content == last)) {
3207 ret->content = xmlRelaxNGNewDefine(ctxt, node);
3208 if (ret->content != NULL) {
3209 ret->content->type = XML_RELAXNG_GROUP;
3210 ret->content->content = last;
3211 } else {
3212 ret->content = last;
3213 }
3214 }
3215 last->next = cur;
3216 last = cur;
3217 }
3218 break;
3219 case XML_RELAXNG_ATTRIBUTE:
3220 cur->next = ret->attrs;
3221 ret->attrs = cur;
3222 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003223 case XML_RELAXNG_START:
Daniel Veillard8fe98712003-02-19 00:19:14 +00003224 case XML_RELAXNG_PARAM:
Daniel Veillard144fae12003-02-03 13:17:57 +00003225 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003226 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003227 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003228 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003229 }
3230 }
3231 child = child->next;
3232 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003233 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003234 return(ret);
3235}
3236
3237/**
3238 * xmlRelaxNGParsePatterns:
3239 * @ctxt: a Relax-NG parser context
3240 * @nodes: list of nodes
Daniel Veillard154877e2003-01-30 12:17:05 +00003241 * @group: use an implicit <group> for elements
Daniel Veillard6eadf632003-01-23 18:29:16 +00003242 *
3243 * parse the content of a RelaxNG start node.
3244 *
3245 * Returns the definition pointer or NULL in case of error.
3246 */
3247static xmlRelaxNGDefinePtr
Daniel Veillard154877e2003-01-30 12:17:05 +00003248xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes,
3249 int group) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003250 xmlRelaxNGDefinePtr def = NULL, last = NULL, cur, parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003251
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003252 parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003253 while (nodes != NULL) {
3254 if (IS_RELAXNG(nodes, "element")) {
3255 cur = xmlRelaxNGParseElement(ctxt, nodes);
3256 if (def == NULL) {
3257 def = last = cur;
3258 } else {
Daniel Veillard154877e2003-01-30 12:17:05 +00003259 if ((group == 1) && (def->type == XML_RELAXNG_ELEMENT) &&
3260 (def == last)) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003261 def = xmlRelaxNGNewDefine(ctxt, nodes);
3262 def->type = XML_RELAXNG_GROUP;
3263 def->content = last;
3264 }
3265 last->next = cur;
3266 last = cur;
3267 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003268 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003269 } else {
3270 cur = xmlRelaxNGParsePattern(ctxt, nodes);
Daniel Veillard419a7682003-02-03 23:22:49 +00003271 if (cur != NULL) {
3272 if (def == NULL) {
3273 def = last = cur;
3274 } else {
3275 last->next = cur;
3276 last = cur;
3277 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003278 }
3279 }
3280 nodes = nodes->next;
3281 }
3282 return(def);
3283}
3284
3285/**
3286 * xmlRelaxNGParseStart:
3287 * @ctxt: a Relax-NG parser context
3288 * @nodes: start children nodes
3289 *
3290 * parse the content of a RelaxNG start node.
3291 *
3292 * Returns 0 in case of success, -1 in case of error
3293 */
3294static int
3295xmlRelaxNGParseStart(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
3296 int ret = 0;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003297 xmlRelaxNGDefinePtr def = NULL, last;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003298
Daniel Veillardd2298792003-02-14 16:54:11 +00003299 if (nodes == NULL) {
3300 if (ctxt->error != NULL)
3301 ctxt->error(ctxt->userData,
3302 "start has no children\n");
3303 ctxt->nbErrors++;
3304 return(-1);
3305 }
3306 if (IS_RELAXNG(nodes, "empty")) {
3307 def = xmlRelaxNGNewDefine(ctxt, nodes);
3308 if (def == NULL)
3309 return(-1);
3310 def->type = XML_RELAXNG_EMPTY;
3311 if (nodes->children != NULL) {
3312 if (ctxt->error != NULL)
3313 ctxt->error(ctxt->userData, "element empty is not empty\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003314 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003315 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003316 } else if (IS_RELAXNG(nodes, "notAllowed")) {
3317 def = xmlRelaxNGNewDefine(ctxt, nodes);
3318 if (def == NULL)
3319 return(-1);
3320 def->type = XML_RELAXNG_NOT_ALLOWED;
3321 if (nodes->children != NULL) {
3322 if (ctxt->error != NULL)
3323 ctxt->error(ctxt->userData,
3324 "element notAllowed is not empty\n");
3325 ctxt->nbErrors++;
3326 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003327 } else {
3328 def = xmlRelaxNGParsePatterns(ctxt, nodes, 1);
Daniel Veillard2df2de22003-02-17 23:34:33 +00003329 }
3330 if (ctxt->grammar->start != NULL) {
3331 last = ctxt->grammar->start;
3332 while (last->next != NULL)
3333 last = last->next;
3334 last->next = def;
3335 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00003336 ctxt->grammar->start = def;
3337 }
3338 nodes = nodes->next;
3339 if (nodes != NULL) {
3340 if (ctxt->error != NULL)
3341 ctxt->error(ctxt->userData,
3342 "start more than one children\n");
3343 ctxt->nbErrors++;
3344 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003345 }
3346 return(ret);
3347}
3348
3349/**
3350 * xmlRelaxNGParseGrammarContent:
3351 * @ctxt: a Relax-NG parser context
3352 * @nodes: grammar children nodes
3353 *
3354 * parse the content of a RelaxNG grammar node.
3355 *
3356 * Returns 0 in case of success, -1 in case of error
3357 */
3358static int
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003359xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003360{
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003361 int ret = 0, tmp;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003362
3363 if (nodes == NULL) {
3364 if (ctxt->error != NULL)
3365 ctxt->error(ctxt->userData,
3366 "grammar has no children\n");
3367 ctxt->nbErrors++;
3368 return(-1);
3369 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003370 while (nodes != NULL) {
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003371 if (IS_RELAXNG(nodes, "start")) {
3372 if (nodes->children == NULL) {
3373 if (ctxt->error != NULL)
3374 ctxt->error(ctxt->userData,
Daniel Veillardd2298792003-02-14 16:54:11 +00003375 "start has no children\n");
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003376 ctxt->nbErrors++;
3377 } else {
3378 tmp = xmlRelaxNGParseStart(ctxt, nodes->children);
3379 if (tmp != 0)
3380 ret = -1;
3381 }
3382 } else if (IS_RELAXNG(nodes, "define")) {
3383 tmp = xmlRelaxNGParseDefine(ctxt, nodes);
3384 if (tmp != 0)
3385 ret = -1;
3386 } else if (IS_RELAXNG(nodes, "include")) {
3387 tmp = xmlRelaxNGParseInclude(ctxt, nodes);
3388 if (tmp != 0)
3389 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003390 } else {
3391 if (ctxt->error != NULL)
3392 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003393 "grammar has unexpected child %s\n", nodes->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003394 ctxt->nbErrors++;
3395 ret = -1;
3396 }
3397 nodes = nodes->next;
3398 }
3399 return (ret);
3400}
3401
3402/**
3403 * xmlRelaxNGCheckReference:
3404 * @ref: the ref
3405 * @ctxt: a Relax-NG parser context
3406 * @name: the name associated to the defines
3407 *
3408 * Applies the 4.17. combine attribute rule for all the define
3409 * element of a given grammar using the same name.
3410 */
3411static void
3412xmlRelaxNGCheckReference(xmlRelaxNGDefinePtr ref,
3413 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
3414 xmlRelaxNGGrammarPtr grammar;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003415 xmlRelaxNGDefinePtr def, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003416
3417 grammar = ctxt->grammar;
3418 if (grammar == NULL) {
3419 if (ctxt->error != NULL)
3420 ctxt->error(ctxt->userData,
3421 "Internal error: no grammar in CheckReference %s\n",
3422 name);
3423 ctxt->nbErrors++;
3424 return;
3425 }
3426 if (ref->content != NULL) {
3427 if (ctxt->error != NULL)
3428 ctxt->error(ctxt->userData,
3429 "Internal error: reference has content in CheckReference %s\n",
3430 name);
3431 ctxt->nbErrors++;
3432 return;
3433 }
3434 if (grammar->defs != NULL) {
3435 def = xmlHashLookup(grammar->defs, name);
3436 if (def != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00003437 cur = ref;
3438 while (cur != NULL) {
3439 cur->content = def;
3440 cur = cur->nextHash;
3441 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003442 } else {
Daniel Veillardd4310742003-02-18 21:12:46 +00003443 if (ctxt->error != NULL)
3444 ctxt->error(ctxt->userData,
3445 "Reference %s has no matching definition\n",
3446 name);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003447 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003448 }
Daniel Veillardd4310742003-02-18 21:12:46 +00003449 } else {
3450 if (ctxt->error != NULL)
3451 ctxt->error(ctxt->userData,
3452 "Reference %s has no matching definition\n",
3453 name);
3454 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003455 }
3456 /*
3457 * TODO: make a closure and verify there is no loop !
3458 */
3459}
3460
3461/**
3462 * xmlRelaxNGCheckCombine:
3463 * @define: the define(s) list
3464 * @ctxt: a Relax-NG parser context
3465 * @name: the name associated to the defines
3466 *
3467 * Applies the 4.17. combine attribute rule for all the define
3468 * element of a given grammar using the same name.
3469 */
3470static void
3471xmlRelaxNGCheckCombine(xmlRelaxNGDefinePtr define,
3472 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
3473 xmlChar *combine;
3474 int choiceOrInterleave = -1;
3475 int missing = 0;
3476 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
3477
3478 if (define->nextHash == NULL)
3479 return;
3480 cur = define;
3481 while (cur != NULL) {
3482 combine = xmlGetProp(cur->node, BAD_CAST "combine");
3483 if (combine != NULL) {
3484 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3485 if (choiceOrInterleave == -1)
3486 choiceOrInterleave = 1;
3487 else if (choiceOrInterleave == 0) {
3488 if (ctxt->error != NULL)
3489 ctxt->error(ctxt->userData,
3490 "Defines for %s use both 'choice' and 'interleave'\n",
3491 name);
3492 ctxt->nbErrors++;
3493 }
Daniel Veillard154877e2003-01-30 12:17:05 +00003494 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003495 if (choiceOrInterleave == -1)
3496 choiceOrInterleave = 0;
3497 else if (choiceOrInterleave == 1) {
3498 if (ctxt->error != NULL)
3499 ctxt->error(ctxt->userData,
3500 "Defines for %s use both 'choice' and 'interleave'\n",
3501 name);
3502 ctxt->nbErrors++;
3503 }
3504 } else {
3505 if (ctxt->error != NULL)
3506 ctxt->error(ctxt->userData,
3507 "Defines for %s use unknown combine value '%s''\n",
3508 name, combine);
3509 ctxt->nbErrors++;
3510 }
3511 xmlFree(combine);
3512 } else {
3513 if (missing == 0)
3514 missing = 1;
3515 else {
3516 if (ctxt->error != NULL)
3517 ctxt->error(ctxt->userData,
3518 "Some defines for %s lacks the combine attribute\n",
3519 name);
3520 ctxt->nbErrors++;
3521 }
3522 }
3523
3524 cur = cur->nextHash;
3525 }
3526#ifdef DEBUG
3527 xmlGenericError(xmlGenericErrorContext,
3528 "xmlRelaxNGCheckCombine(): merging %s defines: %d\n",
3529 name, choiceOrInterleave);
3530#endif
3531 if (choiceOrInterleave == -1)
3532 choiceOrInterleave = 0;
3533 cur = xmlRelaxNGNewDefine(ctxt, define->node);
3534 if (cur == NULL)
3535 return;
3536 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003537 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard154877e2003-01-30 12:17:05 +00003538 else
3539 cur->type = XML_RELAXNG_CHOICE;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003540 tmp = define;
3541 last = NULL;
3542 while (tmp != NULL) {
3543 if (tmp->content != NULL) {
3544 if (tmp->content->next != NULL) {
3545 /*
3546 * we need first to create a wrapper.
3547 */
3548 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
3549 if (tmp2 == NULL)
3550 break;
3551 tmp2->type = XML_RELAXNG_GROUP;
3552 tmp2->content = tmp->content;
3553 } else {
3554 tmp2 = tmp->content;
3555 }
3556 if (last == NULL) {
3557 cur->content = tmp2;
3558 } else {
3559 last->next = tmp2;
3560 }
3561 last = tmp2;
3562 tmp->content = NULL;
3563 }
3564 tmp = tmp->nextHash;
3565 }
3566 define->content = cur;
Daniel Veillard154877e2003-01-30 12:17:05 +00003567 if (choiceOrInterleave == 0) {
3568 if (ctxt->interleaves == NULL)
3569 ctxt->interleaves = xmlHashCreate(10);
3570 if (ctxt->interleaves == NULL) {
3571 if (ctxt->error != NULL)
3572 ctxt->error(ctxt->userData,
3573 "Failed to create interleaves hash table\n");
3574 ctxt->nbErrors++;
3575 } else {
3576 char tmpname[32];
3577
3578 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
3579 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
3580 if (ctxt->error != NULL)
3581 ctxt->error(ctxt->userData,
3582 "Failed to add %s to hash table\n", tmpname);
3583 ctxt->nbErrors++;
3584 }
3585 }
3586 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003587}
3588
3589/**
3590 * xmlRelaxNGCombineStart:
3591 * @ctxt: a Relax-NG parser context
3592 * @grammar: the grammar
3593 *
3594 * Applies the 4.17. combine rule for all the start
3595 * element of a given grammar.
3596 */
3597static void
3598xmlRelaxNGCombineStart(xmlRelaxNGParserCtxtPtr ctxt,
3599 xmlRelaxNGGrammarPtr grammar) {
3600 xmlRelaxNGDefinePtr starts;
3601 xmlChar *combine;
3602 int choiceOrInterleave = -1;
3603 int missing = 0;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003604 xmlRelaxNGDefinePtr cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003605
Daniel Veillard2df2de22003-02-17 23:34:33 +00003606 starts = grammar->start;
3607 if ((starts == NULL) || (starts->next == NULL))
Daniel Veillard6eadf632003-01-23 18:29:16 +00003608 return;
3609 cur = starts;
3610 while (cur != NULL) {
Daniel Veillard2df2de22003-02-17 23:34:33 +00003611 if ((cur->node == NULL) || (cur->node->parent == NULL) ||
3612 (!xmlStrEqual(cur->node->parent->name, BAD_CAST "start"))) {
3613 combine = NULL;
3614 if (ctxt->error != NULL)
3615 ctxt->error(ctxt->userData,
3616 "Internal error: start element not found\n");
3617 ctxt->nbErrors++;
3618 } else {
3619 combine = xmlGetProp(cur->node->parent, BAD_CAST "combine");
3620 }
3621
Daniel Veillard6eadf632003-01-23 18:29:16 +00003622 if (combine != NULL) {
3623 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3624 if (choiceOrInterleave == -1)
3625 choiceOrInterleave = 1;
3626 else if (choiceOrInterleave == 0) {
3627 if (ctxt->error != NULL)
3628 ctxt->error(ctxt->userData,
3629 "<start> use both 'choice' and 'interleave'\n");
3630 ctxt->nbErrors++;
3631 }
Daniel Veillard2df2de22003-02-17 23:34:33 +00003632 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003633 if (choiceOrInterleave == -1)
3634 choiceOrInterleave = 0;
3635 else if (choiceOrInterleave == 1) {
3636 if (ctxt->error != NULL)
3637 ctxt->error(ctxt->userData,
3638 "<start> use both 'choice' and 'interleave'\n");
3639 ctxt->nbErrors++;
3640 }
3641 } else {
3642 if (ctxt->error != NULL)
3643 ctxt->error(ctxt->userData,
3644 "<start> uses unknown combine value '%s''\n", combine);
3645 ctxt->nbErrors++;
3646 }
3647 xmlFree(combine);
3648 } else {
3649 if (missing == 0)
3650 missing = 1;
3651 else {
3652 if (ctxt->error != NULL)
3653 ctxt->error(ctxt->userData,
3654 "Some <start> elements lacks the combine attribute\n");
3655 ctxt->nbErrors++;
3656 }
3657 }
3658
Daniel Veillard2df2de22003-02-17 23:34:33 +00003659 cur = cur->next;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003660 }
3661#ifdef DEBUG
3662 xmlGenericError(xmlGenericErrorContext,
3663 "xmlRelaxNGCombineStart(): merging <start>: %d\n",
3664 choiceOrInterleave);
3665#endif
3666 if (choiceOrInterleave == -1)
3667 choiceOrInterleave = 0;
3668 cur = xmlRelaxNGNewDefine(ctxt, starts->node);
3669 if (cur == NULL)
3670 return;
3671 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003672 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003673 else
3674 cur->type = XML_RELAXNG_CHOICE;
3675 cur->content = grammar->start;
3676 grammar->start = cur;
3677 if (choiceOrInterleave == 0) {
3678 if (ctxt->interleaves == NULL)
3679 ctxt->interleaves = xmlHashCreate(10);
3680 if (ctxt->interleaves == NULL) {
3681 if (ctxt->error != NULL)
3682 ctxt->error(ctxt->userData,
3683 "Failed to create interleaves hash table\n");
3684 ctxt->nbErrors++;
3685 } else {
3686 char tmpname[32];
3687
3688 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
3689 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
3690 if (ctxt->error != NULL)
3691 ctxt->error(ctxt->userData,
3692 "Failed to add %s to hash table\n", tmpname);
3693 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003694 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003695 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003696 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003697}
3698
3699/**
Daniel Veillardd4310742003-02-18 21:12:46 +00003700 * xmlRelaxNGCheckCycles:
3701 * @ctxt: a Relax-NG parser context
3702 * @nodes: grammar children nodes
3703 * @depth: the counter
3704 *
3705 * Check for cycles.
3706 *
3707 * Returns 0 if check passed, and -1 in case of error
3708 */
3709static int
3710xmlRelaxNGCheckCycles(xmlRelaxNGParserCtxtPtr ctxt,
3711 xmlRelaxNGDefinePtr cur, int depth) {
3712 int ret = 0;
3713
3714 while ((ret == 0) && (cur != NULL)) {
3715 if ((cur->type == XML_RELAXNG_REF) ||
3716 (cur->type == XML_RELAXNG_PARENTREF)) {
3717 if (cur->depth == -1) {
3718 cur->depth = depth;
3719 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth);
3720 cur->depth = -2;
3721 } else if (depth == cur->depth) {
3722 if (ctxt->error != NULL)
3723 ctxt->error(ctxt->userData,
3724 "Detected a cycle in %s references\n", cur->name);
3725 ctxt->nbErrors++;
3726 return(-1);
3727 }
3728 } else if (cur->type == XML_RELAXNG_ELEMENT) {
3729 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth + 1);
3730 } else {
3731 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth);
3732 }
3733 cur = cur->next;
3734 }
3735 return(ret);
3736}
3737
3738/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00003739 * xmlRelaxNGParseGrammar:
3740 * @ctxt: a Relax-NG parser context
3741 * @nodes: grammar children nodes
3742 *
3743 * parse a Relax-NG <grammar> node
3744 *
3745 * Returns the internal xmlRelaxNGGrammarPtr built or
3746 * NULL in case of error
3747 */
3748static xmlRelaxNGGrammarPtr
3749xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
3750 xmlRelaxNGGrammarPtr ret, tmp, old;
3751
Daniel Veillard6eadf632003-01-23 18:29:16 +00003752 ret = xmlRelaxNGNewGrammar(ctxt);
3753 if (ret == NULL)
3754 return(NULL);
3755
3756 /*
3757 * Link the new grammar in the tree
3758 */
3759 ret->parent = ctxt->grammar;
3760 if (ctxt->grammar != NULL) {
3761 tmp = ctxt->grammar->children;
3762 if (tmp == NULL) {
3763 ctxt->grammar->children = ret;
3764 } else {
3765 while (tmp->next != NULL)
3766 tmp = tmp->next;
3767 tmp->next = ret;
3768 }
3769 }
3770
3771 old = ctxt->grammar;
3772 ctxt->grammar = ret;
3773 xmlRelaxNGParseGrammarContent(ctxt, nodes);
3774 ctxt->grammar = ret;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003775 if (ctxt->grammar == NULL) {
3776 if (ctxt->error != NULL)
3777 ctxt->error(ctxt->userData,
3778 "Failed to parse <grammar> content\n");
3779 ctxt->nbErrors++;
3780 } else if (ctxt->grammar->start == NULL) {
3781 if (ctxt->error != NULL)
3782 ctxt->error(ctxt->userData,
3783 "Element <grammar> has no <start>\n");
3784 ctxt->nbErrors++;
3785 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003786
3787 /*
3788 * Apply 4.17 mergingd rules to defines and starts
3789 */
3790 xmlRelaxNGCombineStart(ctxt, ret);
3791 if (ret->defs != NULL) {
3792 xmlHashScan(ret->defs, (xmlHashScanner) xmlRelaxNGCheckCombine,
3793 ctxt);
3794 }
3795
3796 /*
3797 * link together defines and refs in this grammar
3798 */
3799 if (ret->refs != NULL) {
3800 xmlHashScan(ret->refs, (xmlHashScanner) xmlRelaxNGCheckReference,
3801 ctxt);
3802 }
3803 ctxt->grammar = old;
3804 return(ret);
3805}
3806
3807/**
3808 * xmlRelaxNGParseDocument:
3809 * @ctxt: a Relax-NG parser context
3810 * @node: the root node of the RelaxNG schema
3811 *
3812 * parse a Relax-NG definition resource and build an internal
3813 * xmlRelaxNG struture which can be used to validate instances.
3814 *
3815 * Returns the internal XML RelaxNG structure built or
3816 * NULL in case of error
3817 */
3818static xmlRelaxNGPtr
3819xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3820 xmlRelaxNGPtr schema = NULL;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003821 const xmlChar *olddefine;
Daniel Veillarde431a272003-01-29 23:02:33 +00003822 xmlRelaxNGGrammarPtr old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003823
3824 if ((ctxt == NULL) || (node == NULL))
3825 return (NULL);
3826
3827 schema = xmlRelaxNGNewRelaxNG(ctxt);
3828 if (schema == NULL)
3829 return(NULL);
3830
Daniel Veillard276be4a2003-01-24 01:03:34 +00003831 olddefine = ctxt->define;
3832 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003833 if (IS_RELAXNG(node, "grammar")) {
3834 schema->topgrammar = xmlRelaxNGParseGrammar(ctxt, node->children);
3835 } else {
3836 schema->topgrammar = xmlRelaxNGNewGrammar(ctxt);
3837 if (schema->topgrammar == NULL) {
3838 return(schema);
3839 }
3840 schema->topgrammar->parent = NULL;
Daniel Veillarde431a272003-01-29 23:02:33 +00003841 old = ctxt->grammar;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003842 ctxt->grammar = schema->topgrammar;
3843 xmlRelaxNGParseStart(ctxt, node);
Daniel Veillarde431a272003-01-29 23:02:33 +00003844 if (old != NULL)
3845 ctxt->grammar = old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003846 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003847 ctxt->define = olddefine;
Daniel Veillardd4310742003-02-18 21:12:46 +00003848 if (schema->topgrammar->start != NULL) {
3849 xmlRelaxNGCheckCycles(ctxt, schema->topgrammar->start, 0);
3850 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003851
3852#ifdef DEBUG
3853 if (schema == NULL)
3854 xmlGenericError(xmlGenericErrorContext,
3855 "xmlRelaxNGParseDocument() failed\n");
3856#endif
3857
3858 return (schema);
3859}
3860
3861/************************************************************************
3862 * *
3863 * Reading RelaxNGs *
3864 * *
3865 ************************************************************************/
3866
3867/**
3868 * xmlRelaxNGNewParserCtxt:
3869 * @URL: the location of the schema
3870 *
3871 * Create an XML RelaxNGs parse context for that file/resource expected
3872 * to contain an XML RelaxNGs file.
3873 *
3874 * Returns the parser context or NULL in case of error
3875 */
3876xmlRelaxNGParserCtxtPtr
3877xmlRelaxNGNewParserCtxt(const char *URL) {
3878 xmlRelaxNGParserCtxtPtr ret;
3879
3880 if (URL == NULL)
3881 return(NULL);
3882
3883 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
3884 if (ret == NULL) {
3885 xmlGenericError(xmlGenericErrorContext,
3886 "Failed to allocate new schama parser context for %s\n", URL);
3887 return (NULL);
3888 }
3889 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
3890 ret->URL = xmlStrdup((const xmlChar *)URL);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003891 ret->error = xmlGenericError;
3892 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003893 return (ret);
3894}
3895
3896/**
3897 * xmlRelaxNGNewMemParserCtxt:
3898 * @buffer: a pointer to a char array containing the schemas
3899 * @size: the size of the array
3900 *
3901 * Create an XML RelaxNGs parse context for that memory buffer expected
3902 * to contain an XML RelaxNGs file.
3903 *
3904 * Returns the parser context or NULL in case of error
3905 */
3906xmlRelaxNGParserCtxtPtr
3907xmlRelaxNGNewMemParserCtxt(const char *buffer, int size) {
3908 xmlRelaxNGParserCtxtPtr ret;
3909
3910 if ((buffer == NULL) || (size <= 0))
3911 return(NULL);
3912
3913 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
3914 if (ret == NULL) {
3915 xmlGenericError(xmlGenericErrorContext,
3916 "Failed to allocate new schama parser context\n");
3917 return (NULL);
3918 }
3919 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
3920 ret->buffer = buffer;
3921 ret->size = size;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003922 ret->error = xmlGenericError;
3923 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003924 return (ret);
3925}
3926
3927/**
3928 * xmlRelaxNGFreeParserCtxt:
3929 * @ctxt: the schema parser context
3930 *
3931 * Free the resources associated to the schema parser context
3932 */
3933void
3934xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxtPtr ctxt) {
3935 if (ctxt == NULL)
3936 return;
3937 if (ctxt->URL != NULL)
3938 xmlFree(ctxt->URL);
3939 if (ctxt->doc != NULL)
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003940 xmlFreeDoc(ctxt->document);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003941 if (ctxt->interleaves != NULL)
3942 xmlHashFree(ctxt->interleaves, NULL);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003943 if (ctxt->documents != NULL)
3944 xmlHashFree(ctxt->documents, (xmlHashDeallocator)
3945 xmlRelaxNGFreeDocument);
3946 if (ctxt->docTab != NULL)
3947 xmlFree(ctxt->docTab);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003948 if (ctxt->incTab != NULL)
3949 xmlFree(ctxt->incTab);
Daniel Veillard419a7682003-02-03 23:22:49 +00003950 if (ctxt->defTab != NULL) {
3951 int i;
3952
3953 for (i = 0;i < ctxt->defNr;i++)
3954 xmlRelaxNGFreeDefine(ctxt->defTab[i]);
3955 xmlFree(ctxt->defTab);
3956 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003957 xmlFree(ctxt);
3958}
3959
Daniel Veillard6eadf632003-01-23 18:29:16 +00003960/**
Daniel Veillardd2298792003-02-14 16:54:11 +00003961 * xmlRelaxNGNormExtSpace:
3962 * @value: a value
3963 *
3964 * Removes the leading and ending spaces of the value
3965 * The string is modified "in situ"
3966 */
3967static void
3968xmlRelaxNGNormExtSpace(xmlChar *value) {
3969 xmlChar *start = value;
3970 xmlChar *cur = value;
3971 if (value == NULL)
3972 return;
3973
3974 while (IS_BLANK(*cur)) cur++;
3975 if (cur == start) {
3976 do {
3977 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
3978 if (*cur == 0)
3979 return;
3980 start = cur;
3981 while (IS_BLANK(*cur)) cur++;
3982 if (*cur == 0) {
3983 *start = 0;
3984 return;
3985 }
3986 } while (1);
3987 } else {
3988 do {
3989 while ((*cur != 0) && (!IS_BLANK(*cur)))
3990 *start++ = *cur++;
3991 if (*cur == 0) {
3992 *start = 0;
3993 return;
3994 }
3995 /* don't try to normalize the inner spaces */
3996 while (IS_BLANK(*cur)) cur++;
3997 *start++ = *cur++;
3998 if (*cur == 0) {
3999 *start = 0;
4000 return;
4001 }
4002 } while (1);
4003 }
4004}
4005
4006/**
4007 * xmlRelaxNGCheckAttributes:
4008 * @ctxt: a Relax-NG parser context
4009 * @node: a Relax-NG node
4010 *
4011 * Check all the attributes on the given node
4012 */
4013static void
4014xmlRelaxNGCleanupAttributes(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
4015 xmlAttrPtr cur, next;
4016
4017 cur = node->properties;
4018 while (cur != NULL) {
4019 next = cur->next;
4020 if ((cur->ns == NULL) ||
4021 (xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
4022 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
4023 if ((!xmlStrEqual(node->name, BAD_CAST "element")) &&
4024 (!xmlStrEqual(node->name, BAD_CAST "attribute")) &&
4025 (!xmlStrEqual(node->name, BAD_CAST "ref")) &&
4026 (!xmlStrEqual(node->name, BAD_CAST "parentRef")) &&
Daniel Veillard2df2de22003-02-17 23:34:33 +00004027 (!xmlStrEqual(node->name, BAD_CAST "param")) &&
Daniel Veillardd2298792003-02-14 16:54:11 +00004028 (!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 "type")) {
4036 if ((!xmlStrEqual(node->name, BAD_CAST "value")) &&
4037 (!xmlStrEqual(node->name, BAD_CAST "data"))) {
4038 if (ctxt->error != NULL)
4039 ctxt->error(ctxt->userData,
4040 "Attribute %s is not allowed on %s\n",
4041 cur->name, node->name);
4042 ctxt->nbErrors++;
4043 }
4044 } else if (xmlStrEqual(cur->name, BAD_CAST "href")) {
4045 if ((!xmlStrEqual(node->name, BAD_CAST "externalRef")) &&
4046 (!xmlStrEqual(node->name, BAD_CAST "include"))) {
4047 if (ctxt->error != NULL)
4048 ctxt->error(ctxt->userData,
4049 "Attribute %s is not allowed on %s\n",
4050 cur->name, node->name);
4051 ctxt->nbErrors++;
4052 }
4053 } else if (xmlStrEqual(cur->name, BAD_CAST "combine")) {
4054 if ((!xmlStrEqual(node->name, BAD_CAST "start")) &&
4055 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
4056 if (ctxt->error != NULL)
4057 ctxt->error(ctxt->userData,
4058 "Attribute %s is not allowed on %s\n",
4059 cur->name, node->name);
4060 ctxt->nbErrors++;
4061 }
4062 } else if (xmlStrEqual(cur->name, BAD_CAST "datatypeLibrary")) {
4063 xmlChar *val;
4064 xmlURIPtr uri;
4065
4066 val = xmlNodeListGetString(node->doc, cur->children, 1);
4067 if (val != NULL) {
4068 if (val[0] != 0) {
4069 uri = xmlParseURI((const char *) val);
4070 if (uri == NULL) {
4071 if (ctxt->error != NULL)
4072 ctxt->error(ctxt->userData,
4073 "Attribute %s contains invalid URI %s\n",
4074 cur->name, val);
4075 ctxt->nbErrors++;
4076 } else {
4077 if (uri->scheme == NULL) {
4078 if (ctxt->error != NULL)
4079 ctxt->error(ctxt->userData,
4080 "Attribute %s URI %s is not absolute\n",
4081 cur->name, val);
4082 ctxt->nbErrors++;
4083 }
4084 if (uri->fragment != NULL) {
4085 if (ctxt->error != NULL)
4086 ctxt->error(ctxt->userData,
4087 "Attribute %s URI %s has a fragment ID\n",
4088 cur->name, val);
4089 ctxt->nbErrors++;
4090 }
4091 xmlFreeURI(uri);
4092 }
4093 }
4094 xmlFree(val);
4095 }
4096 } else if (!xmlStrEqual(cur->name, BAD_CAST "ns")) {
4097 if (ctxt->error != NULL)
4098 ctxt->error(ctxt->userData,
4099 "Unknown attribute %s on %s\n",
4100 cur->name, node->name);
4101 ctxt->nbErrors++;
4102 }
4103 }
4104 cur = next;
4105 }
4106}
4107
4108/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004109 * xmlRelaxNGCleanupDoc:
4110 * @ctxt: a Relax-NG parser context
4111 * @doc: an xmldocPtr document pointer
Daniel Veillard6eadf632003-01-23 18:29:16 +00004112 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004113 * Cleanup the document from unwanted nodes for parsing, resolve
4114 * Include and externalRef lookups.
Daniel Veillard6eadf632003-01-23 18:29:16 +00004115 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004116 * Returns the cleaned up document or NULL in case of error
Daniel Veillard6eadf632003-01-23 18:29:16 +00004117 */
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004118static xmlDocPtr
4119xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt, xmlDocPtr doc) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00004120 xmlNodePtr root, cur, delete;
4121
Daniel Veillard6eadf632003-01-23 18:29:16 +00004122 /*
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004123 * Extract the root
Daniel Veillard6eadf632003-01-23 18:29:16 +00004124 */
4125 root = xmlDocGetRootElement(doc);
4126 if (root == NULL) {
4127 if (ctxt->error != NULL)
4128 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
4129 ctxt->URL);
4130 ctxt->nbErrors++;
4131 return (NULL);
4132 }
4133
4134 /*
4135 * Remove all the blank text nodes
4136 */
4137 delete = NULL;
4138 cur = root;
4139 while (cur != NULL) {
4140 if (delete != NULL) {
4141 xmlUnlinkNode(delete);
4142 xmlFreeNode(delete);
4143 delete = NULL;
4144 }
4145 if (cur->type == XML_ELEMENT_NODE) {
4146 /*
4147 * Simplification 4.1. Annotations
4148 */
4149 if ((cur->ns == NULL) ||
4150 (!xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
Daniel Veillardd2298792003-02-14 16:54:11 +00004151 if ((cur->parent != NULL) &&
4152 (cur->parent->type == XML_ELEMENT_NODE) &&
4153 ((xmlStrEqual(cur->parent->name, BAD_CAST "name")) ||
4154 (xmlStrEqual(cur->parent->name, BAD_CAST "value")) ||
4155 (xmlStrEqual(cur->parent->name, BAD_CAST "param")))) {
4156 if (ctxt->error != NULL)
4157 ctxt->error(ctxt->userData,
4158 "element %s doesn't allow foreign elements\n",
4159 cur->parent->name);
4160 ctxt->nbErrors++;
4161 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004162 delete = cur;
4163 goto skip_children;
4164 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00004165 xmlRelaxNGCleanupAttributes(ctxt, cur);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004166 if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004167 xmlChar *href, *ns, *base, *URL;
4168 xmlRelaxNGDocumentPtr docu;
Daniel Veillard416589a2003-02-17 17:25:42 +00004169 xmlNodePtr tmp;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004170
4171 ns = xmlGetProp(cur, BAD_CAST "ns");
Daniel Veillard416589a2003-02-17 17:25:42 +00004172 if (ns == NULL) {
4173 tmp = cur->parent;
4174 while ((tmp != NULL) &&
4175 (tmp->type == XML_ELEMENT_NODE)) {
4176 ns = xmlGetProp(tmp, BAD_CAST "ns");
4177 if (ns != NULL)
4178 break;
4179 tmp = tmp->parent;
4180 }
4181 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004182 href = xmlGetProp(cur, BAD_CAST "href");
4183 if (href == NULL) {
4184 if (ctxt->error != NULL)
4185 ctxt->error(ctxt->userData,
4186 "xmlRelaxNGParse: externalRef has no href attribute\n");
4187 ctxt->nbErrors++;
4188 delete = cur;
4189 goto skip_children;
4190 }
4191 base = xmlNodeGetBase(cur->doc, cur);
4192 URL = xmlBuildURI(href, base);
4193 if (URL == NULL) {
4194 if (ctxt->error != NULL)
4195 ctxt->error(ctxt->userData,
4196 "Failed to compute URL for externalRef %s\n", href);
4197 ctxt->nbErrors++;
4198 if (href != NULL)
4199 xmlFree(href);
4200 if (base != NULL)
4201 xmlFree(base);
4202 delete = cur;
4203 goto skip_children;
4204 }
4205 if (href != NULL)
4206 xmlFree(href);
4207 if (base != NULL)
4208 xmlFree(base);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004209 docu = xmlRelaxNGLoadExternalRef(ctxt, URL, ns);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004210 if (docu == NULL) {
4211 if (ctxt->error != NULL)
4212 ctxt->error(ctxt->userData,
4213 "Failed to load externalRef %s\n", URL);
4214 ctxt->nbErrors++;
4215 xmlFree(URL);
4216 delete = cur;
4217 goto skip_children;
4218 }
4219 xmlFree(URL);
4220 cur->_private = docu;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004221 } else if (xmlStrEqual(cur->name, BAD_CAST "include")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00004222 xmlChar *href, *ns, *base, *URL;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004223 xmlRelaxNGIncludePtr incl;
Daniel Veillard416589a2003-02-17 17:25:42 +00004224 xmlNodePtr tmp;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004225
4226 href = xmlGetProp(cur, BAD_CAST "href");
4227 if (href == NULL) {
4228 if (ctxt->error != NULL)
4229 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004230 "xmlRelaxNGParse: include has no href attribute\n");
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004231 ctxt->nbErrors++;
4232 delete = cur;
4233 goto skip_children;
4234 }
4235 base = xmlNodeGetBase(cur->doc, cur);
4236 URL = xmlBuildURI(href, base);
4237 if (URL == NULL) {
4238 if (ctxt->error != NULL)
4239 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004240 "Failed to compute URL for include %s\n", href);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004241 ctxt->nbErrors++;
4242 if (href != NULL)
4243 xmlFree(href);
4244 if (base != NULL)
4245 xmlFree(base);
4246 delete = cur;
4247 goto skip_children;
4248 }
4249 if (href != NULL)
4250 xmlFree(href);
4251 if (base != NULL)
4252 xmlFree(base);
Daniel Veillard416589a2003-02-17 17:25:42 +00004253 ns = xmlGetProp(cur, BAD_CAST "ns");
4254 if (ns == NULL) {
4255 tmp = cur->parent;
4256 while ((tmp != NULL) &&
4257 (tmp->type == XML_ELEMENT_NODE)) {
4258 ns = xmlGetProp(tmp, BAD_CAST "ns");
4259 if (ns != NULL)
4260 break;
4261 tmp = tmp->parent;
4262 }
4263 }
4264 incl = xmlRelaxNGLoadInclude(ctxt, URL, cur, ns);
4265 if (ns != NULL)
4266 xmlFree(ns);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004267 if (incl == NULL) {
4268 if (ctxt->error != NULL)
4269 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004270 "Failed to load include %s\n", URL);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004271 ctxt->nbErrors++;
4272 xmlFree(URL);
4273 delete = cur;
4274 goto skip_children;
4275 }
4276 xmlFree(URL);
4277 cur->_private = incl;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004278 } else if ((xmlStrEqual(cur->name, BAD_CAST "element")) ||
4279 (xmlStrEqual(cur->name, BAD_CAST "attribute"))) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00004280 xmlChar *name, *ns;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004281 xmlNodePtr text = NULL;
4282
4283 /*
4284 * Simplification 4.8. name attribute of element
4285 * and attribute elements
4286 */
4287 name = xmlGetProp(cur, BAD_CAST "name");
4288 if (name != NULL) {
4289 if (cur->children == NULL) {
4290 text = xmlNewChild(cur, cur->ns, BAD_CAST "name",
4291 name);
4292 } else {
4293 xmlNodePtr node;
4294 node = xmlNewNode(cur->ns, BAD_CAST "name");
4295 if (node != NULL) {
4296 xmlAddPrevSibling(cur->children, node);
4297 text = xmlNewText(name);
4298 xmlAddChild(node, text);
4299 text = node;
4300 }
4301 }
Daniel Veillardfebcca42003-02-16 15:44:18 +00004302 if (text == NULL) {
4303 if (ctxt->error != NULL)
4304 ctxt->error(ctxt->userData,
4305 "Failed to create a name %s element\n", name);
4306 ctxt->nbErrors++;
4307 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004308 xmlUnsetProp(cur, BAD_CAST "name");
4309 xmlFree(name);
Daniel Veillardfebcca42003-02-16 15:44:18 +00004310 ns = xmlGetProp(cur, BAD_CAST "ns");
4311 if (ns != NULL) {
4312 if (text != NULL) {
4313 xmlSetProp(text, BAD_CAST "ns", ns);
4314 /* xmlUnsetProp(cur, BAD_CAST "ns"); */
Daniel Veillard6eadf632003-01-23 18:29:16 +00004315 }
Daniel Veillardfebcca42003-02-16 15:44:18 +00004316 xmlFree(ns);
4317 } else if (xmlStrEqual(cur->name,
4318 BAD_CAST "attribute")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00004319 xmlSetProp(text, BAD_CAST "ns", BAD_CAST "");
4320 }
4321 }
4322 } else if ((xmlStrEqual(cur->name, BAD_CAST "name")) ||
4323 (xmlStrEqual(cur->name, BAD_CAST "nsName")) ||
4324 (xmlStrEqual(cur->name, BAD_CAST "value"))) {
4325 /*
4326 * Simplification 4.8. name attribute of element
4327 * and attribute elements
4328 */
4329 if (xmlHasProp(cur, BAD_CAST "ns") == NULL) {
4330 xmlNodePtr node;
4331 xmlChar *ns = NULL;
4332
4333 node = cur->parent;
4334 while ((node != NULL) &&
4335 (node->type == XML_ELEMENT_NODE)) {
4336 ns = xmlGetProp(node, BAD_CAST "ns");
4337 if (ns != NULL) {
4338 break;
4339 }
4340 node = node->parent;
4341 }
4342 if (ns == NULL) {
4343 xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
4344 } else {
4345 xmlSetProp(cur, BAD_CAST "ns", ns);
4346 xmlFree(ns);
4347 }
4348 }
4349 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
4350 xmlChar *name, *local, *prefix;
4351
4352 /*
4353 * Simplification: 4.10. QNames
4354 */
4355 name = xmlNodeGetContent(cur);
4356 if (name != NULL) {
4357 local = xmlSplitQName2(name, &prefix);
4358 if (local != NULL) {
4359 xmlNsPtr ns;
4360
4361 ns = xmlSearchNs(cur->doc, cur, prefix);
4362 if (ns == NULL) {
4363 if (ctxt->error != NULL)
4364 ctxt->error(ctxt->userData,
4365 "xmlRelaxNGParse: no namespace for prefix %s\n", prefix);
4366 ctxt->nbErrors++;
4367 } else {
4368 xmlSetProp(cur, BAD_CAST "ns", ns->href);
4369 xmlNodeSetContent(cur, local);
4370 }
4371 xmlFree(local);
4372 xmlFree(prefix);
4373 }
4374 xmlFree(name);
4375 }
4376 }
4377 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004378 /*
4379 * Thisd is not an else since "include" is transformed
4380 * into a div
4381 */
4382 if (xmlStrEqual(cur->name, BAD_CAST "div")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00004383 xmlChar *ns;
4384 xmlNodePtr child, ins, tmp;
4385
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004386 /*
4387 * implements rule 4.11
4388 */
Daniel Veillard416589a2003-02-17 17:25:42 +00004389
4390 ns = xmlGetProp(cur, BAD_CAST "ns");
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004391
4392 child = cur->children;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004393 ins = cur;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004394 while (child != NULL) {
Daniel Veillard416589a2003-02-17 17:25:42 +00004395 if (ns != NULL) {
4396 if (!xmlHasProp(child, BAD_CAST "ns")) {
4397 xmlSetProp(child, BAD_CAST "ns", ns);
4398 }
4399 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004400 tmp = child->next;
4401 xmlUnlinkNode(child);
4402 ins = xmlAddNextSibling(ins, child);
4403 child = tmp;
4404 }
Daniel Veillard416589a2003-02-17 17:25:42 +00004405 if (ns != NULL)
4406 xmlFree(ns);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004407 delete = cur;
4408 goto skip_children;
4409 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004410 }
4411 }
4412 /*
4413 * Simplification 4.2 whitespaces
4414 */
4415 else if (cur->type == XML_TEXT_NODE) {
4416 if (IS_BLANK_NODE(cur)) {
4417 if (cur->parent->type == XML_ELEMENT_NODE) {
4418 if ((!xmlStrEqual(cur->parent->name, BAD_CAST "value")) &&
4419 (!xmlStrEqual(cur->parent->name, BAD_CAST "param")))
4420 delete = cur;
4421 } else {
4422 delete = cur;
4423 goto skip_children;
4424 }
4425 }
4426 } else if (cur->type != XML_CDATA_SECTION_NODE) {
4427 delete = cur;
4428 goto skip_children;
4429 }
4430
4431 /*
4432 * Skip to next node
4433 */
4434 if (cur->children != NULL) {
4435 if ((cur->children->type != XML_ENTITY_DECL) &&
4436 (cur->children->type != XML_ENTITY_REF_NODE) &&
4437 (cur->children->type != XML_ENTITY_NODE)) {
4438 cur = cur->children;
4439 continue;
4440 }
4441 }
4442skip_children:
4443 if (cur->next != NULL) {
4444 cur = cur->next;
4445 continue;
4446 }
4447
4448 do {
4449 cur = cur->parent;
4450 if (cur == NULL)
4451 break;
4452 if (cur == root) {
4453 cur = NULL;
4454 break;
4455 }
4456 if (cur->next != NULL) {
4457 cur = cur->next;
4458 break;
4459 }
4460 } while (cur != NULL);
4461 }
4462 if (delete != NULL) {
4463 xmlUnlinkNode(delete);
4464 xmlFreeNode(delete);
4465 delete = NULL;
4466 }
4467
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004468 return(doc);
4469}
4470
4471/**
4472 * xmlRelaxNGParse:
4473 * @ctxt: a Relax-NG parser context
4474 *
4475 * parse a schema definition resource and build an internal
4476 * XML Shema struture which can be used to validate instances.
4477 * *WARNING* this interface is highly subject to change
4478 *
4479 * Returns the internal XML RelaxNG structure built from the resource or
4480 * NULL in case of error
4481 */
4482xmlRelaxNGPtr
4483xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt)
4484{
4485 xmlRelaxNGPtr ret = NULL;
4486 xmlDocPtr doc;
4487 xmlNodePtr root;
4488
4489 xmlRelaxNGInitTypes();
4490
4491 if (ctxt == NULL)
4492 return (NULL);
4493
4494 /*
4495 * First step is to parse the input document into an DOM/Infoset
4496 */
4497 if (ctxt->URL != NULL) {
4498 doc = xmlParseFile((const char *) ctxt->URL);
4499 if (doc == NULL) {
4500 if (ctxt->error != NULL)
4501 ctxt->error(ctxt->userData,
4502 "xmlRelaxNGParse: could not load %s\n", ctxt->URL);
4503 ctxt->nbErrors++;
4504 return (NULL);
4505 }
4506 } else if (ctxt->buffer != NULL) {
4507 doc = xmlParseMemory(ctxt->buffer, ctxt->size);
4508 if (doc == NULL) {
4509 if (ctxt->error != NULL)
4510 ctxt->error(ctxt->userData,
4511 "xmlRelaxNGParse: could not parse schemas\n");
4512 ctxt->nbErrors++;
4513 return (NULL);
4514 }
4515 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
4516 ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
4517 } else {
4518 if (ctxt->error != NULL)
4519 ctxt->error(ctxt->userData,
4520 "xmlRelaxNGParse: nothing to parse\n");
4521 ctxt->nbErrors++;
4522 return (NULL);
4523 }
4524 ctxt->document = doc;
4525
4526 /*
4527 * Some preprocessing of the document content
4528 */
4529 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
4530 if (doc == NULL) {
4531 xmlFreeDoc(ctxt->document);
4532 ctxt->document = NULL;
4533 return(NULL);
4534 }
4535
Daniel Veillard6eadf632003-01-23 18:29:16 +00004536 /*
4537 * Then do the parsing for good
4538 */
4539 root = xmlDocGetRootElement(doc);
4540 if (root == NULL) {
4541 if (ctxt->error != NULL)
4542 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
4543 ctxt->URL);
4544 ctxt->nbErrors++;
4545 return (NULL);
4546 }
4547 ret = xmlRelaxNGParseDocument(ctxt, root);
4548 if (ret == NULL)
4549 return(NULL);
4550
4551 /*
4552 * Check the ref/defines links
4553 */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004554 /*
4555 * try to preprocess interleaves
4556 */
4557 if (ctxt->interleaves != NULL) {
4558 xmlHashScan(ctxt->interleaves,
4559 (xmlHashScanner)xmlRelaxNGComputeInterleaves, ctxt);
4560 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004561
4562 /*
4563 * if there was a parsing error return NULL
4564 */
4565 if (ctxt->nbErrors > 0) {
4566 xmlRelaxNGFree(ret);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004567 ctxt->document = NULL;
4568 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004569 return(NULL);
4570 }
4571
4572 /*
4573 * Transfer the pointer for cleanup at the schema level.
4574 */
4575 ret->doc = doc;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004576 ctxt->document = NULL;
4577 ret->documents = ctxt->documents;
4578 ctxt->documents = NULL;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004579 ret->includes = ctxt->includes;
4580 ctxt->includes = NULL;
Daniel Veillard419a7682003-02-03 23:22:49 +00004581 ret->defNr = ctxt->defNr;
4582 ret->defTab = ctxt->defTab;
4583 ctxt->defTab = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004584
4585 return (ret);
4586}
4587
4588/**
4589 * xmlRelaxNGSetParserErrors:
4590 * @ctxt: a Relax-NG validation context
4591 * @err: the error callback
4592 * @warn: the warning callback
4593 * @ctx: contextual data for the callbacks
4594 *
4595 * Set the callback functions used to handle errors for a validation context
4596 */
4597void
4598xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
4599 xmlRelaxNGValidityErrorFunc err,
4600 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
4601 if (ctxt == NULL)
4602 return;
4603 ctxt->error = err;
4604 ctxt->warning = warn;
4605 ctxt->userData = ctx;
4606}
4607/************************************************************************
4608 * *
4609 * Dump back a compiled form *
4610 * *
4611 ************************************************************************/
4612static void xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define);
4613
4614/**
4615 * xmlRelaxNGDumpDefines:
4616 * @output: the file output
4617 * @defines: a list of define structures
4618 *
4619 * Dump a RelaxNG structure back
4620 */
4621static void
4622xmlRelaxNGDumpDefines(FILE * output, xmlRelaxNGDefinePtr defines) {
4623 while (defines != NULL) {
4624 xmlRelaxNGDumpDefine(output, defines);
4625 defines = defines->next;
4626 }
4627}
4628
4629/**
4630 * xmlRelaxNGDumpDefine:
4631 * @output: the file output
4632 * @define: a define structure
4633 *
4634 * Dump a RelaxNG structure back
4635 */
4636static void
4637xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define) {
4638 if (define == NULL)
4639 return;
4640 switch(define->type) {
4641 case XML_RELAXNG_EMPTY:
4642 fprintf(output, "<empty/>\n");
4643 break;
4644 case XML_RELAXNG_NOT_ALLOWED:
4645 fprintf(output, "<notAllowed/>\n");
4646 break;
4647 case XML_RELAXNG_TEXT:
4648 fprintf(output, "<text/>\n");
4649 break;
4650 case XML_RELAXNG_ELEMENT:
4651 fprintf(output, "<element>\n");
4652 if (define->name != NULL) {
4653 fprintf(output, "<name");
4654 if (define->ns != NULL)
4655 fprintf(output, " ns=\"%s\"", define->ns);
4656 fprintf(output, ">%s</name>\n", define->name);
4657 }
4658 xmlRelaxNGDumpDefines(output, define->attrs);
4659 xmlRelaxNGDumpDefines(output, define->content);
4660 fprintf(output, "</element>\n");
4661 break;
4662 case XML_RELAXNG_LIST:
4663 fprintf(output, "<list>\n");
4664 xmlRelaxNGDumpDefines(output, define->content);
4665 fprintf(output, "</list>\n");
4666 break;
4667 case XML_RELAXNG_ONEORMORE:
4668 fprintf(output, "<oneOrMore>\n");
4669 xmlRelaxNGDumpDefines(output, define->content);
4670 fprintf(output, "</oneOrMore>\n");
4671 break;
4672 case XML_RELAXNG_ZEROORMORE:
4673 fprintf(output, "<zeroOrMore>\n");
4674 xmlRelaxNGDumpDefines(output, define->content);
4675 fprintf(output, "</zeroOrMore>\n");
4676 break;
4677 case XML_RELAXNG_CHOICE:
4678 fprintf(output, "<choice>\n");
4679 xmlRelaxNGDumpDefines(output, define->content);
4680 fprintf(output, "</choice>\n");
4681 break;
4682 case XML_RELAXNG_GROUP:
4683 fprintf(output, "<group>\n");
4684 xmlRelaxNGDumpDefines(output, define->content);
4685 fprintf(output, "</group>\n");
4686 break;
4687 case XML_RELAXNG_INTERLEAVE:
4688 fprintf(output, "<interleave>\n");
4689 xmlRelaxNGDumpDefines(output, define->content);
4690 fprintf(output, "</interleave>\n");
4691 break;
4692 case XML_RELAXNG_OPTIONAL:
4693 fprintf(output, "<optional>\n");
4694 xmlRelaxNGDumpDefines(output, define->content);
4695 fprintf(output, "</optional>\n");
4696 break;
4697 case XML_RELAXNG_ATTRIBUTE:
4698 fprintf(output, "<attribute>\n");
4699 xmlRelaxNGDumpDefines(output, define->content);
4700 fprintf(output, "</attribute>\n");
4701 break;
4702 case XML_RELAXNG_DEF:
4703 fprintf(output, "<define");
4704 if (define->name != NULL)
4705 fprintf(output, " name=\"%s\"", define->name);
4706 fprintf(output, ">\n");
4707 xmlRelaxNGDumpDefines(output, define->content);
4708 fprintf(output, "</define>\n");
4709 break;
4710 case XML_RELAXNG_REF:
4711 fprintf(output, "<ref");
4712 if (define->name != NULL)
4713 fprintf(output, " name=\"%s\"", define->name);
4714 fprintf(output, ">\n");
4715 xmlRelaxNGDumpDefines(output, define->content);
4716 fprintf(output, "</ref>\n");
4717 break;
Daniel Veillard419a7682003-02-03 23:22:49 +00004718 case XML_RELAXNG_PARENTREF:
4719 fprintf(output, "<parentRef");
4720 if (define->name != NULL)
4721 fprintf(output, " name=\"%s\"", define->name);
4722 fprintf(output, ">\n");
4723 xmlRelaxNGDumpDefines(output, define->content);
4724 fprintf(output, "</parentRef>\n");
4725 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004726 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard416589a2003-02-17 17:25:42 +00004727 fprintf(output, "<externalRef>");
Daniel Veillarde431a272003-01-29 23:02:33 +00004728 xmlRelaxNGDumpDefines(output, define->content);
4729 fprintf(output, "</externalRef>\n");
4730 break;
4731 case XML_RELAXNG_DATATYPE:
Daniel Veillard6eadf632003-01-23 18:29:16 +00004732 case XML_RELAXNG_VALUE:
4733 TODO
4734 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004735 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00004736 case XML_RELAXNG_EXCEPT:
Daniel Veillard8fe98712003-02-19 00:19:14 +00004737 case XML_RELAXNG_PARAM:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004738 TODO
4739 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004740 }
4741}
4742
4743/**
4744 * xmlRelaxNGDumpGrammar:
4745 * @output: the file output
4746 * @grammar: a grammar structure
4747 * @top: is this a top grammar
4748 *
4749 * Dump a RelaxNG structure back
4750 */
4751static void
4752xmlRelaxNGDumpGrammar(FILE * output, xmlRelaxNGGrammarPtr grammar, int top)
4753{
4754 if (grammar == NULL)
4755 return;
4756
4757 fprintf(output, "<grammar");
4758 if (top)
4759 fprintf(output,
4760 " xmlns=\"http://relaxng.org/ns/structure/1.0\"");
4761 switch(grammar->combine) {
4762 case XML_RELAXNG_COMBINE_UNDEFINED:
4763 break;
4764 case XML_RELAXNG_COMBINE_CHOICE:
4765 fprintf(output, " combine=\"choice\"");
4766 break;
4767 case XML_RELAXNG_COMBINE_INTERLEAVE:
4768 fprintf(output, " combine=\"interleave\"");
4769 break;
4770 default:
4771 fprintf(output, " <!-- invalid combine value -->");
4772 }
4773 fprintf(output, ">\n");
4774 if (grammar->start == NULL) {
4775 fprintf(output, " <!-- grammar had no start -->");
4776 } else {
4777 fprintf(output, "<start>\n");
4778 xmlRelaxNGDumpDefine(output, grammar->start);
4779 fprintf(output, "</start>\n");
4780 }
4781 /* TODO ? Dump the defines ? */
4782 fprintf(output, "</grammar>\n");
4783}
4784
4785/**
4786 * xmlRelaxNGDump:
4787 * @output: the file output
4788 * @schema: a schema structure
4789 *
4790 * Dump a RelaxNG structure back
4791 */
4792void
4793xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema)
4794{
4795 if (schema == NULL) {
4796 fprintf(output, "RelaxNG empty or failed to compile\n");
4797 return;
4798 }
4799 fprintf(output, "RelaxNG: ");
4800 if (schema->doc == NULL) {
4801 fprintf(output, "no document\n");
4802 } else if (schema->doc->URL != NULL) {
4803 fprintf(output, "%s\n", schema->doc->URL);
4804 } else {
4805 fprintf(output, "\n");
4806 }
4807 if (schema->topgrammar == NULL) {
4808 fprintf(output, "RelaxNG has no top grammar\n");
4809 return;
4810 }
4811 xmlRelaxNGDumpGrammar(output, schema->topgrammar, 1);
4812}
4813
Daniel Veillardfebcca42003-02-16 15:44:18 +00004814/**
4815 * xmlRelaxNGDumpTree:
4816 * @output: the file output
4817 * @schema: a schema structure
4818 *
4819 * Dump the transformed RelaxNG tree.
4820 */
4821void
4822xmlRelaxNGDumpTree(FILE * output, xmlRelaxNGPtr schema)
4823{
4824 if (schema == NULL) {
4825 fprintf(output, "RelaxNG empty or failed to compile\n");
4826 return;
4827 }
4828 if (schema->doc == NULL) {
4829 fprintf(output, "no document\n");
4830 } else {
4831 xmlDocDump(output, schema->doc);
4832 }
4833}
4834
Daniel Veillard6eadf632003-01-23 18:29:16 +00004835/************************************************************************
4836 * *
4837 * Validation implementation *
4838 * *
4839 ************************************************************************/
4840static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
4841 xmlRelaxNGDefinePtr define);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004842static int xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
4843 xmlRelaxNGDefinePtr define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004844
4845/**
4846 * xmlRelaxNGSkipIgnored:
4847 * @ctxt: a schema validation context
4848 * @node: the top node.
4849 *
4850 * Skip ignorable nodes in that context
4851 *
4852 * Returns the new sibling or NULL in case of error.
4853 */
4854static xmlNodePtr
4855xmlRelaxNGSkipIgnored(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
4856 xmlNodePtr node) {
4857 /*
4858 * TODO complete and handle entities
4859 */
4860 while ((node != NULL) &&
4861 ((node->type == XML_COMMENT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004862 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00004863 ((node->type == XML_TEXT_NODE) &&
4864 (IS_BLANK_NODE(node))))) {
4865 node = node->next;
4866 }
4867 return(node);
4868}
4869
4870/**
Daniel Veillardedc91922003-01-26 00:52:04 +00004871 * xmlRelaxNGNormalize:
4872 * @ctxt: a schema validation context
4873 * @str: the string to normalize
4874 *
4875 * Implements the normalizeWhiteSpace( s ) function from
4876 * section 6.2.9 of the spec
4877 *
4878 * Returns the new string or NULL in case of error.
4879 */
4880static xmlChar *
4881xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *str) {
4882 xmlChar *ret, *p;
4883 const xmlChar *tmp;
4884 int len;
4885
4886 if (str == NULL)
4887 return(NULL);
4888 tmp = str;
4889 while (*tmp != 0) tmp++;
4890 len = tmp - str;
4891
4892 ret = (xmlChar *) xmlMalloc((len + 1) * sizeof(xmlChar));
4893 if (ret == NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00004894 if (ctxt != NULL) {
4895 VALID_CTXT();
4896 VALID_ERROR("xmlRelaxNGNormalize: out of memory\n");
4897 } else {
4898 xmlGenericError(xmlGenericErrorContext,
4899 "xmlRelaxNGNormalize: out of memory\n");
4900 }
Daniel Veillardedc91922003-01-26 00:52:04 +00004901 return(NULL);
4902 }
4903 p = ret;
4904 while (IS_BLANK(*str)) str++;
4905 while (*str != 0) {
4906 if (IS_BLANK(*str)) {
4907 while (IS_BLANK(*str)) str++;
4908 if (*str == 0)
4909 break;
4910 *p++ = ' ';
4911 } else
4912 *p++ = *str++;
4913 }
4914 *p = 0;
4915 return(ret);
4916}
4917
4918/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004919 * xmlRelaxNGValidateDatatype:
4920 * @ctxt: a Relax-NG validation context
4921 * @value: the string value
4922 * @type: the datatype definition
4923 *
4924 * Validate the given value against the dataype
4925 *
4926 * Returns 0 if the validation succeeded or an error code.
4927 */
4928static int
4929xmlRelaxNGValidateDatatype(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *value,
4930 xmlRelaxNGDefinePtr define) {
4931 int ret;
4932 xmlRelaxNGTypeLibraryPtr lib;
4933
4934 if ((define == NULL) || (define->data == NULL)) {
4935 return(-1);
4936 }
4937 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
4938 if (lib->check != NULL)
4939 ret = lib->check(lib->data, define->name, value);
4940 else
4941 ret = -1;
4942 if (ret < 0) {
4943 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004944 VALID_ERROR2("Internal: failed to validate type %s\n", define->name);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004945 return(-1);
4946 } else if (ret == 1) {
4947 ret = 0;
4948 } else {
4949 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004950 VALID_ERROR3("Type %s doesn't allow value %s\n", define->name, value);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004951 return(-1);
4952 ret = -1;
4953 }
Daniel Veillard416589a2003-02-17 17:25:42 +00004954 if ((ret == 0) && (define->content != NULL)) {
4955 const xmlChar *oldvalue, *oldendvalue;
4956
4957 oldvalue = ctxt->state->value;
4958 oldendvalue = ctxt->state->endvalue;
4959 ctxt->state->value = (xmlChar *) value;
4960 ctxt->state->endvalue = NULL;
4961 ret = xmlRelaxNGValidateValue(ctxt, define->content);
4962 ctxt->state->value = (xmlChar *) oldvalue;
4963 ctxt->state->endvalue = (xmlChar *) oldendvalue;
4964 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004965 return(ret);
4966}
4967
4968/**
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004969 * xmlRelaxNGNextValue:
4970 * @ctxt: a Relax-NG validation context
4971 *
4972 * Skip to the next value when validating within a list
4973 *
4974 * Returns 0 if the operation succeeded or an error code.
4975 */
4976static int
4977xmlRelaxNGNextValue(xmlRelaxNGValidCtxtPtr ctxt) {
4978 xmlChar *cur;
4979
4980 cur = ctxt->state->value;
4981 if ((cur == NULL) || (ctxt->state->endvalue == NULL)) {
4982 ctxt->state->value = NULL;
Daniel Veillarde5b110b2003-02-04 14:43:39 +00004983 ctxt->state->endvalue = NULL;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004984 return(0);
4985 }
4986 while (*cur != 0) cur++;
4987 while ((cur != ctxt->state->endvalue) && (*cur == 0)) cur++;
4988 if (cur == ctxt->state->endvalue)
4989 ctxt->state->value = NULL;
4990 else
4991 ctxt->state->value = cur;
4992 return(0);
4993}
4994
4995/**
4996 * xmlRelaxNGValidateValueList:
4997 * @ctxt: a Relax-NG validation context
4998 * @defines: the list of definitions to verify
4999 *
5000 * Validate the given set of definitions for the current value
5001 *
5002 * Returns 0 if the validation succeeded or an error code.
5003 */
5004static int
5005xmlRelaxNGValidateValueList(xmlRelaxNGValidCtxtPtr ctxt,
5006 xmlRelaxNGDefinePtr defines) {
5007 int ret = 0;
5008
5009 while (defines != NULL) {
5010 ret = xmlRelaxNGValidateValue(ctxt, defines);
5011 if (ret != 0)
5012 break;
5013 defines = defines->next;
5014 }
5015 return(ret);
5016}
5017
5018/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00005019 * xmlRelaxNGValidateValue:
5020 * @ctxt: a Relax-NG validation context
5021 * @define: the definition to verify
5022 *
5023 * Validate the given definition for the current value
5024 *
5025 * Returns 0 if the validation succeeded or an error code.
5026 */
5027static int
5028xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
5029 xmlRelaxNGDefinePtr define) {
Daniel Veillardedc91922003-01-26 00:52:04 +00005030 int ret = 0, oldflags;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005031 xmlChar *value;
5032
5033 value = ctxt->state->value;
5034 switch (define->type) {
Daniel Veillardd4310742003-02-18 21:12:46 +00005035 case XML_RELAXNG_EMPTY: {
5036 if ((value != NULL) && (value[0] != 0)) {
5037 int idx = 0;
5038
5039 while (IS_BLANK(value[idx]))
5040 idx++;
5041 if (value[idx] != 0)
5042 ret = -1;
5043 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005044 break;
Daniel Veillardd4310742003-02-18 21:12:46 +00005045 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005046 case XML_RELAXNG_TEXT:
5047 break;
Daniel Veillardedc91922003-01-26 00:52:04 +00005048 case XML_RELAXNG_VALUE: {
5049 if (!xmlStrEqual(value, define->value)) {
5050 if (define->name != NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00005051 xmlRelaxNGTypeLibraryPtr lib;
5052
5053 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
5054 if ((lib != NULL) && (lib->comp != NULL))
5055 ret = lib->comp(lib->data, define->name, value,
5056 define->value);
5057 else
5058 ret = -1;
5059 if (ret < 0) {
5060 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005061 VALID_ERROR2("Internal: failed to compare type %s\n",
Daniel Veillardea3f3982003-01-26 19:45:18 +00005062 define->name);
5063 return(-1);
5064 } else if (ret == 1) {
5065 ret = 0;
5066 } else {
5067 ret = -1;
5068 }
Daniel Veillardedc91922003-01-26 00:52:04 +00005069 } else {
5070 xmlChar *nval, *nvalue;
5071
5072 /*
5073 * TODO: trivial optimizations are possible by
5074 * computing at compile-time
5075 */
5076 nval = xmlRelaxNGNormalize(ctxt, define->value);
5077 nvalue = xmlRelaxNGNormalize(ctxt, value);
5078
Daniel Veillardea3f3982003-01-26 19:45:18 +00005079 if ((nval == NULL) || (nvalue == NULL) ||
5080 (!xmlStrEqual(nval, nvalue)))
Daniel Veillardedc91922003-01-26 00:52:04 +00005081 ret = -1;
5082 if (nval != NULL)
5083 xmlFree(nval);
5084 if (nvalue != NULL)
5085 xmlFree(nvalue);
5086 }
5087 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005088 if (ret == 0)
5089 xmlRelaxNGNextValue(ctxt);
Daniel Veillardedc91922003-01-26 00:52:04 +00005090 break;
5091 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005092 case XML_RELAXNG_DATATYPE: {
5093 ret = xmlRelaxNGValidateDatatype(ctxt, value, define);
5094 if (ret == 0)
5095 xmlRelaxNGNextValue(ctxt);
5096
5097 break;
5098 }
5099 case XML_RELAXNG_CHOICE: {
5100 xmlRelaxNGDefinePtr list = define->content;
5101 xmlChar *oldvalue;
5102
5103 oldflags = ctxt->flags;
5104 ctxt->flags |= FLAGS_IGNORABLE;
5105
5106 oldvalue = ctxt->state->value;
5107 while (list != NULL) {
5108 ret = xmlRelaxNGValidateValue(ctxt, list);
5109 if (ret == 0) {
5110 break;
5111 }
5112 ctxt->state->value = oldvalue;
5113 list = list->next;
5114 }
5115 ctxt->flags = oldflags;
Daniel Veillard416589a2003-02-17 17:25:42 +00005116 if (ret == 0)
5117 xmlRelaxNGNextValue(ctxt);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005118 break;
5119 }
5120 case XML_RELAXNG_LIST: {
5121 xmlRelaxNGDefinePtr list = define->content;
5122 xmlChar *oldvalue, *oldend, *val, *cur;
Daniel Veillard416589a2003-02-17 17:25:42 +00005123#ifdef DEBUG_LIST
5124 int nb_values = 0;
5125#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005126
5127 oldvalue = ctxt->state->value;
5128 oldend = ctxt->state->endvalue;
5129
5130 val = xmlStrdup(oldvalue);
5131 if (val == NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00005132 val = xmlStrdup(BAD_CAST "");
5133 }
5134 if (val == NULL) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005135 VALID_CTXT();
5136 VALID_ERROR("Internal: no state\n");
5137 return(-1);
5138 }
5139 cur = val;
5140 while (*cur != 0) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005141 if (IS_BLANK(*cur)) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005142 *cur = 0;
Daniel Veillard416589a2003-02-17 17:25:42 +00005143 cur++;
5144#ifdef DEBUG_LIST
5145 nb_values++;
5146#endif
5147 while (IS_BLANK(*cur))
5148 *cur++ = 0;
5149 } else
5150 cur++;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005151 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005152#ifdef DEBUG_LIST
5153 xmlGenericError(xmlGenericErrorContext,
5154 "list value: '%s' found %d items\n", oldvalue, nb_values);
5155 nb_values = 0;
5156#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005157 ctxt->state->endvalue = cur;
5158 cur = val;
5159 while ((*cur == 0) && (cur != ctxt->state->endvalue)) cur++;
5160
5161 ctxt->state->value = cur;
5162
5163 while (list != NULL) {
Daniel Veillard2e9b1652003-02-19 13:29:45 +00005164 if (ctxt->state->value == ctxt->state->endvalue)
5165 ctxt->state->value = NULL;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005166 ret = xmlRelaxNGValidateValue(ctxt, list);
5167 if (ret != 0) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005168#ifdef DEBUG_LIST
5169 xmlGenericError(xmlGenericErrorContext,
5170 "Failed to validate value: '%s' with %d rule\n",
5171 ctxt->state->value, nb_values);
5172#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005173 break;
5174 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005175#ifdef DEBUG_LIST
5176 nb_values++;
5177#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005178 list = list->next;
5179 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00005180
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005181 if ((ret == 0) && (ctxt->state->value != NULL) &&
5182 (ctxt->state->value != ctxt->state->endvalue)) {
5183 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005184 VALID_ERROR2("Extra data in list: %s\n", ctxt->state->value);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005185 ret = -1;
5186 }
5187 xmlFree(val);
5188 ctxt->state->value = oldvalue;
5189 ctxt->state->endvalue = oldend;
5190 break;
5191 }
5192 case XML_RELAXNG_ONEORMORE:
5193 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
5194 if (ret != 0) {
5195 break;
5196 }
5197 /* no break on purpose */
5198 case XML_RELAXNG_ZEROORMORE: {
5199 xmlChar *cur, *temp;
5200
5201 oldflags = ctxt->flags;
5202 ctxt->flags |= FLAGS_IGNORABLE;
5203 cur = ctxt->state->value;
5204 temp = NULL;
5205 while ((cur != NULL) && (cur != ctxt->state->endvalue) &&
5206 (temp != cur)) {
5207 temp = cur;
5208 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
5209 if (ret != 0) {
5210 ctxt->state->value = temp;
5211 ret = 0;
5212 break;
5213 }
5214 cur = ctxt->state->value;
5215 }
5216 ctxt->flags = oldflags;
5217 break;
5218 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005219 case XML_RELAXNG_EXCEPT: {
5220 xmlRelaxNGDefinePtr list;
5221
5222 list = define->content;
5223 while (list != NULL) {
5224 ret = xmlRelaxNGValidateValue(ctxt, list);
5225 if (ret == 0) {
5226 ret = -1;
5227 break;
5228 } else
5229 ret = 0;
5230 list = list->next;
5231 }
5232 break;
5233 }
Daniel Veillardd4310742003-02-18 21:12:46 +00005234 case XML_RELAXNG_GROUP: {
5235 xmlRelaxNGDefinePtr list;
5236
5237 list = define->content;
5238 while (list != NULL) {
5239 ret = xmlRelaxNGValidateValue(ctxt, list);
5240 if (ret != 0) {
5241 ret = -1;
5242 break;
5243 } else
5244 ret = 0;
5245 list = list->next;
5246 }
5247 break;
5248 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005249 default:
5250 TODO
5251 ret = -1;
5252 }
5253 return(ret);
5254}
5255
5256/**
5257 * xmlRelaxNGValidateValueContent:
5258 * @ctxt: a Relax-NG validation context
5259 * @defines: the list of definitions to verify
5260 *
5261 * Validate the given definitions for the current value
5262 *
5263 * Returns 0 if the validation succeeded or an error code.
5264 */
5265static int
5266xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt,
5267 xmlRelaxNGDefinePtr defines) {
5268 int ret = 0;
5269
5270 while (defines != NULL) {
5271 ret = xmlRelaxNGValidateValue(ctxt, defines);
5272 if (ret != 0)
5273 break;
5274 defines = defines->next;
5275 }
5276 return(ret);
5277}
5278
5279/**
Daniel Veillard144fae12003-02-03 13:17:57 +00005280 * xmlRelaxNGAttributeMatch:
5281 * @ctxt: a Relax-NG validation context
5282 * @define: the definition to check
5283 * @prop: the attribute
5284 *
5285 * Check if the attribute matches the definition nameClass
5286 *
5287 * Returns 1 if the attribute matches, 0 if no, or -1 in case of error
5288 */
5289static int
5290xmlRelaxNGAttributeMatch(xmlRelaxNGValidCtxtPtr ctxt,
5291 xmlRelaxNGDefinePtr define,
5292 xmlAttrPtr prop) {
5293 int ret;
5294
5295 if (define->name != NULL) {
5296 if (!xmlStrEqual(define->name, prop->name))
5297 return(0);
5298 }
5299 if (define->ns != NULL) {
5300 if (define->ns[0] == 0) {
5301 if (prop->ns != NULL)
5302 return(0);
5303 } else {
5304 if ((prop->ns == NULL) ||
5305 (!xmlStrEqual(define->ns, prop->ns->href)))
5306 return(0);
5307 }
5308 }
5309 if (define->nameClass == NULL)
5310 return(1);
5311 define = define->nameClass;
5312 if (define->type == XML_RELAXNG_EXCEPT) {
5313 xmlRelaxNGDefinePtr list;
5314
5315 list = define->content;
5316 while (list != NULL) {
5317 ret = xmlRelaxNGAttributeMatch(ctxt, list, prop);
5318 if (ret == 1)
5319 return(0);
5320 if (ret < 0)
5321 return(ret);
5322 list = list->next;
5323 }
5324 } else {
5325 TODO
5326 }
5327 return(1);
5328}
5329
5330/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00005331 * xmlRelaxNGValidateAttribute:
5332 * @ctxt: a Relax-NG validation context
5333 * @define: the definition to verify
5334 *
5335 * Validate the given attribute definition for that node
5336 *
5337 * Returns 0 if the validation succeeded or an error code.
5338 */
5339static int
5340xmlRelaxNGValidateAttribute(xmlRelaxNGValidCtxtPtr ctxt,
5341 xmlRelaxNGDefinePtr define) {
5342 int ret = 0, i;
5343 xmlChar *value, *oldvalue;
5344 xmlAttrPtr prop = NULL, tmp;
5345
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005346 if (ctxt->state->nbAttrLeft <= 0)
5347 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005348 if (define->name != NULL) {
5349 for (i = 0;i < ctxt->state->nbAttrs;i++) {
5350 tmp = ctxt->state->attrs[i];
5351 if ((tmp != NULL) && (xmlStrEqual(define->name, tmp->name))) {
5352 if ((((define->ns == NULL) || (define->ns[0] == 0)) &&
5353 (tmp->ns == NULL)) ||
5354 ((tmp->ns != NULL) &&
5355 (xmlStrEqual(define->ns, tmp->ns->href)))) {
5356 prop = tmp;
5357 break;
5358 }
5359 }
5360 }
5361 if (prop != NULL) {
5362 value = xmlNodeListGetString(prop->doc, prop->children, 1);
5363 oldvalue = ctxt->state->value;
5364 ctxt->state->value = value;
Daniel Veillard231d7912003-02-09 14:22:17 +00005365 ctxt->state->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005366 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00005367 if (ctxt->state->value != NULL)
5368 value = ctxt->state->value;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005369 if (value != NULL)
5370 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00005371 ctxt->state->value = oldvalue;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005372 if (ret == 0) {
5373 /*
5374 * flag the attribute as processed
5375 */
5376 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005377 ctxt->state->nbAttrLeft--;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005378 }
5379 } else {
5380 ret = -1;
5381 }
5382#ifdef DEBUG
5383 xmlGenericError(xmlGenericErrorContext,
5384 "xmlRelaxNGValidateAttribute(%s): %d\n", define->name, ret);
5385#endif
5386 } else {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005387 for (i = 0;i < ctxt->state->nbAttrs;i++) {
5388 tmp = ctxt->state->attrs[i];
Daniel Veillard144fae12003-02-03 13:17:57 +00005389 if ((tmp != NULL) &&
5390 (xmlRelaxNGAttributeMatch(ctxt, define, tmp) == 1)) {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005391 prop = tmp;
5392 break;
5393 }
5394 }
5395 if (prop != NULL) {
5396 value = xmlNodeListGetString(prop->doc, prop->children, 1);
5397 oldvalue = ctxt->state->value;
5398 ctxt->state->value = value;
5399 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00005400 if (ctxt->state->value != NULL)
5401 value = ctxt->state->value;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005402 if (value != NULL)
5403 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00005404 ctxt->state->value = oldvalue;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005405 if (ret == 0) {
5406 /*
5407 * flag the attribute as processed
5408 */
5409 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005410 ctxt->state->nbAttrLeft--;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005411 }
5412 } else {
5413 ret = -1;
5414 }
5415#ifdef DEBUG
Daniel Veillard144fae12003-02-03 13:17:57 +00005416 if (define->ns != NULL) {
5417 xmlGenericError(xmlGenericErrorContext,
5418 "xmlRelaxNGValidateAttribute(nsName ns = %s): %d\n",
5419 define->ns, ret);
5420 } else {
5421 xmlGenericError(xmlGenericErrorContext,
5422 "xmlRelaxNGValidateAttribute(anyName): %d\n",
5423 ret);
5424 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005425#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00005426 }
5427
5428 return(ret);
5429}
5430
5431/**
5432 * xmlRelaxNGValidateAttributeList:
5433 * @ctxt: a Relax-NG validation context
5434 * @define: the list of definition to verify
5435 *
5436 * Validate the given node against the list of attribute definitions
5437 *
5438 * Returns 0 if the validation succeeded or an error code.
5439 */
5440static int
5441xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt,
5442 xmlRelaxNGDefinePtr defines) {
5443 int ret = 0;
5444 while (defines != NULL) {
5445 if (xmlRelaxNGValidateAttribute(ctxt, defines) != 0)
5446 ret = -1;
5447 defines = defines->next;
5448 }
5449 return(ret);
5450}
5451
5452/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005453 * xmlRelaxNGValidateTryPermutation:
5454 * @ctxt: a Relax-NG validation context
5455 * @groups: the array of groups
5456 * @nbgroups: the number of groups in the array
5457 * @array: the permutation to try
5458 * @len: the size of the set
5459 *
5460 * Try to validate a permutation for the group of definitions.
5461 *
5462 * Returns 0 if the validation succeeded or an error code.
5463 */
5464static int
5465xmlRelaxNGValidateTryPermutation(xmlRelaxNGValidCtxtPtr ctxt,
5466 xmlRelaxNGDefinePtr rule,
5467 xmlNodePtr *array, int len) {
5468 int i, ret;
5469
5470 if (len > 0) {
5471 /*
5472 * One only need the next pointer set-up to do the validation
5473 */
5474 for (i = 0;i < (len - 1);i++)
5475 array[i]->next = array[i + 1];
5476 array[i]->next = NULL;
5477
5478 /*
5479 * Now try to validate the sequence
5480 */
5481 ctxt->state->seq = array[0];
5482 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
5483 } else {
5484 ctxt->state->seq = NULL;
5485 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
5486 }
5487
5488 /*
5489 * the sequence must be fully consumed
5490 */
5491 if (ctxt->state->seq != NULL)
5492 return(-1);
5493
5494 return(ret);
5495}
5496
5497/**
5498 * xmlRelaxNGValidateWalkPermutations:
5499 * @ctxt: a Relax-NG validation context
5500 * @groups: the array of groups
5501 * @nbgroups: the number of groups in the array
5502 * @nodes: the set of nodes
5503 * @array: the current state of the parmutation
5504 * @len: the size of the set
5505 * @level: a pointer to the level variable
5506 * @k: the index in the array to fill
5507 *
5508 * Validate a set of nodes for a groups of definitions, will try the
5509 * full set of permutations
5510 *
5511 * Returns 0 if the validation succeeded or an error code.
5512 */
5513static int
5514xmlRelaxNGValidateWalkPermutations(xmlRelaxNGValidCtxtPtr ctxt,
5515 xmlRelaxNGDefinePtr rule, xmlNodePtr *nodes,
5516 xmlNodePtr *array, int len,
5517 int *level, int k) {
5518 int i, ret;
5519
5520 if ((k >= 0) && (k < len))
5521 array[k] = nodes[*level];
5522 *level = *level + 1;
5523 if (*level == len) {
5524 ret = xmlRelaxNGValidateTryPermutation(ctxt, rule, array, len);
5525 if (ret == 0)
5526 return(0);
5527 } else {
5528 for (i = 0;i < len;i++) {
5529 if (array[i] == NULL) {
5530 ret = xmlRelaxNGValidateWalkPermutations(ctxt, rule,
5531 nodes, array, len, level, i);
5532 if (ret == 0)
5533 return(0);
5534 }
5535 }
5536 }
5537 *level = *level - 1;
5538 array[k] = NULL;
5539 return(-1);
5540}
5541
5542/**
5543 * xmlRelaxNGNodeMatchesList:
5544 * @node: the node
5545 * @list: a NULL terminated array of definitions
5546 *
5547 * Check if a node can be matched by one of the definitions
5548 *
5549 * Returns 1 if matches 0 otherwise
5550 */
5551static int
5552xmlRelaxNGNodeMatchesList(xmlNodePtr node, xmlRelaxNGDefinePtr *list) {
5553 xmlRelaxNGDefinePtr cur;
5554 int i = 0;
5555
5556 if ((node == NULL) || (list == NULL))
5557 return(0);
5558
5559 cur = list[i++];
5560 while (cur != NULL) {
5561 if ((node->type == XML_ELEMENT_NODE) &&
5562 (cur->type == XML_RELAXNG_ELEMENT)) {
5563 if (cur->name == NULL) {
5564 if ((node->ns != NULL) &&
5565 (xmlStrEqual(node->ns->href, cur->ns)))
5566 return(1);
5567 } else if (xmlStrEqual(cur->name, node->name)) {
5568 if ((cur->ns == NULL) || (cur->ns[0] == 0)) {
5569 if (node->ns == NULL)
5570 return(1);
5571 } else {
5572 if ((node->ns != NULL) &&
5573 (xmlStrEqual(node->ns->href, cur->ns)))
5574 return(1);
5575 }
5576 }
5577 } else if ((node->type == XML_TEXT_NODE) &&
5578 (cur->type == XML_RELAXNG_TEXT)) {
5579 return(1);
5580 }
5581 cur = list[i++];
5582 }
5583 return(0);
5584}
5585
5586/**
5587 * xmlRelaxNGValidatePartGroup:
5588 * @ctxt: a Relax-NG validation context
5589 * @groups: the array of groups
5590 * @nbgroups: the number of groups in the array
5591 * @nodes: the set of nodes
5592 * @len: the size of the set of nodes
5593 *
5594 * Validate a set of nodes for a groups of definitions
5595 *
5596 * Returns 0 if the validation succeeded or an error code.
5597 */
5598static int
5599xmlRelaxNGValidatePartGroup(xmlRelaxNGValidCtxtPtr ctxt,
5600 xmlRelaxNGInterleaveGroupPtr *groups,
5601 int nbgroups, xmlNodePtr *nodes, int len) {
Daniel Veillard231d7912003-02-09 14:22:17 +00005602 int level, ret = -1, i, j, k, top_j, max_j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005603 xmlNodePtr *array = NULL, *list, oldseq;
5604 xmlRelaxNGInterleaveGroupPtr group;
5605
5606 list = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
5607 if (list == NULL) {
5608 return(-1);
5609 }
5610 array = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
5611 if (array == NULL) {
5612 xmlFree(list);
5613 return(-1);
5614 }
5615 memset(array, 0, len * sizeof(xmlNodePtr));
5616
5617 /*
5618 * Partition the elements and validate the subsets.
5619 */
5620 oldseq = ctxt->state->seq;
Daniel Veillard231d7912003-02-09 14:22:17 +00005621 max_j = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005622 for (i = 0;i < nbgroups;i++) {
5623 group = groups[i];
5624 if (group == NULL)
5625 continue;
5626 k = 0;
Daniel Veillard231d7912003-02-09 14:22:17 +00005627 top_j = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005628 for (j = 0;j < len;j++) {
5629 if (nodes[j] == NULL)
5630 continue;
5631 if (xmlRelaxNGNodeMatchesList(nodes[j], group->defs)) {
5632 list[k++] = nodes[j];
5633 nodes[j] = NULL;
Daniel Veillard231d7912003-02-09 14:22:17 +00005634 top_j = j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005635 }
5636 }
Daniel Veillard231d7912003-02-09 14:22:17 +00005637 if (top_j > max_j)
5638 max_j = top_j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005639 ctxt->state->seq = oldseq;
5640 if (k > 1) {
5641 memset(array, 0, k * sizeof(xmlNodePtr));
Daniel Veillardb08c9812003-01-28 23:09:49 +00005642 level = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005643 ret = xmlRelaxNGValidateWalkPermutations(ctxt, group->rule,
5644 list, array, k, &level, -1);
5645 } else {
5646 ret = xmlRelaxNGValidateTryPermutation(ctxt, group->rule, list, k);
5647 }
5648 if (ret != 0) {
5649 ctxt->state->seq = oldseq;
5650 break;
5651 }
5652 }
5653
Daniel Veillard231d7912003-02-09 14:22:17 +00005654 for (j = 0;j < max_j;j++) {
5655 if (nodes[j] != NULL) {
5656 TODO /* problem, one of the nodes didn't got a match */
5657 }
5658 }
5659 if (ret == 0) {
5660 if (max_j + 1 < len)
5661 ctxt->state->seq = nodes[max_j + 1];
5662 else
5663 ctxt->state->seq = NULL;
5664 }
5665
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005666 xmlFree(list);
5667 xmlFree(array);
5668 return(ret);
5669}
5670
5671/**
5672 * xmlRelaxNGValidateInterleave:
5673 * @ctxt: a Relax-NG validation context
5674 * @define: the definition to verify
5675 *
5676 * Validate an interleave definition for a node.
5677 *
5678 * Returns 0 if the validation succeeded or an error code.
5679 */
5680static int
5681xmlRelaxNGValidateInterleave(xmlRelaxNGValidCtxtPtr ctxt,
5682 xmlRelaxNGDefinePtr define) {
5683 int ret = 0, nbchildren, nbtot, i, j;
5684 xmlRelaxNGPartitionPtr partitions;
5685 xmlNodePtr *children = NULL;
5686 xmlNodePtr *order = NULL;
Daniel Veillard231d7912003-02-09 14:22:17 +00005687 xmlNodePtr cur, oldseq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005688
5689 if (define->data != NULL) {
5690 partitions = (xmlRelaxNGPartitionPtr) define->data;
5691 } else {
5692 VALID_CTXT();
5693 VALID_ERROR("Internal: interleave block has no data\n");
5694 return(-1);
5695 }
5696
5697 /*
5698 * Build the sequence of child and an array preserving the children
5699 * initial order.
5700 */
5701 cur = ctxt->state->seq;
Daniel Veillard231d7912003-02-09 14:22:17 +00005702 oldseq = ctxt->state->seq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005703 nbchildren = 0;
5704 nbtot = 0;
5705 while (cur != NULL) {
5706 if ((cur->type == XML_COMMENT_NODE) ||
5707 (cur->type == XML_PI_NODE) ||
5708 ((cur->type == XML_TEXT_NODE) &&
5709 (IS_BLANK_NODE(cur)))) {
5710 nbtot++;
5711 } else {
5712 nbchildren++;
5713 nbtot++;
5714 }
5715 cur = cur->next;
5716 }
5717 children = (xmlNodePtr *) xmlMalloc(nbchildren * sizeof(xmlNodePtr));
5718 if (children == NULL)
5719 goto error;
5720 order = (xmlNodePtr *) xmlMalloc(nbtot * sizeof(xmlNodePtr));
5721 if (order == NULL)
5722 goto error;
5723 cur = ctxt->state->seq;
5724 i = 0;
5725 j = 0;
5726 while (cur != NULL) {
5727 if ((cur->type == XML_COMMENT_NODE) ||
5728 (cur->type == XML_PI_NODE) ||
5729 ((cur->type == XML_TEXT_NODE) &&
5730 (IS_BLANK_NODE(cur)))) {
5731 order[j++] = cur;
5732 } else {
5733 order[j++] = cur;
5734 children[i++] = cur;
5735 }
5736 cur = cur->next;
5737 }
5738
5739 /* TODO: retry with a maller set of child if there is a next... */
5740 ret = xmlRelaxNGValidatePartGroup(ctxt, partitions->groups,
5741 partitions->nbgroups, children, nbchildren);
Daniel Veillard231d7912003-02-09 14:22:17 +00005742 if (ret != 0)
5743 ctxt->state->seq = oldseq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005744
5745 /*
5746 * Cleanup: rebuid the child sequence and free the structure
5747 */
5748 if (order != NULL) {
5749 for (i = 0;i < nbtot;i++) {
5750 if (i == 0)
5751 order[i]->prev = NULL;
5752 else
5753 order[i]->prev = order[i - 1];
5754 if (i == nbtot - 1)
5755 order[i]->next = NULL;
5756 else
5757 order[i]->next = order[i + 1];
5758 }
5759 xmlFree(order);
5760 }
5761 if (children != NULL)
5762 xmlFree(children);
5763
5764 return(ret);
5765
5766error:
5767 if (order != NULL) {
5768 for (i = 0;i < nbtot;i++) {
5769 if (i == 0)
5770 order[i]->prev = NULL;
5771 else
5772 order[i]->prev = order[i - 1];
5773 if (i == nbtot - 1)
5774 order[i]->next = NULL;
5775 else
5776 order[i]->next = order[i + 1];
5777 }
5778 xmlFree(order);
5779 }
5780 if (children != NULL)
5781 xmlFree(children);
5782 return(-1);
5783}
5784
5785/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00005786 * xmlRelaxNGValidateElementContent:
5787 * @ctxt: a Relax-NG validation context
5788 * @define: the list of definition to verify
5789 *
5790 * Validate the given node content against the (list) of definitions
5791 *
5792 * Returns 0 if the validation succeeded or an error code.
5793 */
5794static int
5795xmlRelaxNGValidateElementContent(xmlRelaxNGValidCtxtPtr ctxt,
5796 xmlRelaxNGDefinePtr defines) {
5797 int ret = 0, res;
5798
5799 if (ctxt->state == NULL) {
5800 VALID_CTXT();
5801 VALID_ERROR("Internal: no state\n");
5802 return(-1);
5803 }
5804 while (defines != NULL) {
5805 res = xmlRelaxNGValidateDefinition(ctxt, defines);
5806 if (res < 0)
5807 ret = -1;
5808 defines = defines->next;
5809 }
5810
5811 return(ret);
5812}
5813
5814/**
Daniel Veillard416589a2003-02-17 17:25:42 +00005815 * xmlRelaxNGElementMatch:
5816 * @ctxt: a Relax-NG validation context
5817 * @define: the definition to check
5818 * @elem: the element
5819 *
5820 * Check if the element matches the definition nameClass
5821 *
5822 * Returns 1 if the element matches, 0 if no, or -1 in case of error
5823 */
5824static int
5825xmlRelaxNGElementMatch(xmlRelaxNGValidCtxtPtr ctxt,
5826 xmlRelaxNGDefinePtr define,
5827 xmlNodePtr elem) {
5828 int ret, oldflags;
5829
5830 if (define->name != NULL) {
5831 if (!xmlStrEqual(elem->name, define->name)) {
5832 VALID_CTXT();
5833 VALID_ERROR3("Expecting element %s, got %s\n",
5834 define->name, elem->name);
5835 return(0);
5836 }
5837 }
5838 if ((define->ns != NULL) && (define->ns[0] != 0)) {
5839 if (elem->ns == NULL) {
5840 VALID_CTXT();
5841 VALID_ERROR2("Expecting a namespace for element %s\n",
5842 elem->name);
5843 return(0);
5844 } else if (!xmlStrEqual(elem->ns->href, define->ns)) {
5845 VALID_CTXT();
5846 VALID_ERROR3("Expecting element %s has wrong namespace: expecting %s\n",
5847 elem->name, define->ns);
5848 return(0);
5849 }
Daniel Veillard8fe98712003-02-19 00:19:14 +00005850 } else if ((elem->ns != NULL) && (define->ns != NULL) &&
5851 (define->name == NULL)) {
5852 VALID_CTXT();
5853 VALID_ERROR2("Expecting no namespace for element %s\n",
5854 define->name);
5855 return(0);
5856 } else if ((elem->ns != NULL) && (define->name != NULL)) {
5857 VALID_CTXT();
5858 VALID_ERROR2("Expecting no namespace for element %s\n",
5859 define->name);
5860 return(0);
Daniel Veillard416589a2003-02-17 17:25:42 +00005861 }
5862
5863 if (define->nameClass == NULL)
5864 return(1);
5865
5866 define = define->nameClass;
5867 if (define->type == XML_RELAXNG_EXCEPT) {
5868 xmlRelaxNGDefinePtr list;
5869 oldflags = ctxt->flags;
5870 ctxt->flags |= FLAGS_IGNORABLE;
5871
5872 list = define->content;
5873 while (list != NULL) {
5874 ret = xmlRelaxNGElementMatch(ctxt, list, elem);
5875 if (ret == 1) {
5876 ctxt->flags = oldflags;
5877 return(0);
5878 }
5879 if (ret < 0) {
5880 ctxt->flags = oldflags;
5881 return(ret);
5882 }
5883 list = list->next;
5884 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00005885 ret = 1;
5886 ctxt->flags = oldflags;
5887 } else if (define->type == XML_RELAXNG_CHOICE) {
5888 xmlRelaxNGDefinePtr list;
5889 oldflags = ctxt->flags;
5890 ctxt->flags |= FLAGS_IGNORABLE;
5891
5892 list = define->nameClass;
5893 while (list != NULL) {
5894 ret = xmlRelaxNGElementMatch(ctxt, list, elem);
5895 if (ret == 1) {
5896 ctxt->flags = oldflags;
5897 return(1);
5898 }
5899 if (ret < 0) {
5900 ctxt->flags = oldflags;
5901 return(ret);
5902 }
5903 list = list->next;
5904 }
5905 ret = 0;
Daniel Veillard416589a2003-02-17 17:25:42 +00005906 ctxt->flags = oldflags;
5907 } else {
5908 TODO
Daniel Veillard2e9b1652003-02-19 13:29:45 +00005909 ret = -1;
Daniel Veillard416589a2003-02-17 17:25:42 +00005910 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00005911 return(ret);
Daniel Veillard416589a2003-02-17 17:25:42 +00005912}
5913
5914/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00005915 * xmlRelaxNGValidateDefinition:
5916 * @ctxt: a Relax-NG validation context
5917 * @define: the definition to verify
5918 *
5919 * Validate the current node against the definition
5920 *
5921 * Returns 0 if the validation succeeded or an error code.
5922 */
5923static int
5924xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
5925 xmlRelaxNGDefinePtr define) {
5926 xmlNodePtr node;
5927 int ret = 0, i, tmp, oldflags;
5928 xmlRelaxNGValidStatePtr oldstate, state;
5929
5930 if (define == NULL) {
5931 VALID_CTXT();
5932 VALID_ERROR("internal error: define == NULL\n");
5933 return(-1);
5934 }
5935 if (ctxt->state != NULL) {
5936 node = ctxt->state->seq;
5937 } else {
5938 node = NULL;
5939 }
Daniel Veillard231d7912003-02-09 14:22:17 +00005940#ifdef DEBUG
5941 for (i = 0;i < ctxt->depth;i++)
5942 xmlGenericError(xmlGenericErrorContext, " ");
5943 xmlGenericError(xmlGenericErrorContext,
5944 "Start validating %s ", xmlRelaxNGDefName(define));
5945 if (define->name != NULL)
5946 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
5947 if ((node != NULL) && (node->name != NULL))
5948 xmlGenericError(xmlGenericErrorContext, "on %s\n", node->name);
5949 else
5950 xmlGenericError(xmlGenericErrorContext, "\n");
5951#endif
5952 ctxt->depth++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005953 switch (define->type) {
5954 case XML_RELAXNG_EMPTY:
Daniel Veillardd4310742003-02-18 21:12:46 +00005955 node = xmlRelaxNGSkipIgnored(ctxt, node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005956 if (node != NULL) {
5957 VALID_CTXT();
5958 VALID_ERROR("Expecting an empty element\n");
Daniel Veillard231d7912003-02-09 14:22:17 +00005959 ret = -1;
5960 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005961 }
Daniel Veillard231d7912003-02-09 14:22:17 +00005962 ret = 0;
5963 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005964 case XML_RELAXNG_NOT_ALLOWED:
Daniel Veillard231d7912003-02-09 14:22:17 +00005965 ret = -1;
5966 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005967 case XML_RELAXNG_TEXT:
Daniel Veillard231d7912003-02-09 14:22:17 +00005968 if (node == NULL) {
5969 ret = 0;
5970 break;
5971 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005972 while ((node != NULL) &&
5973 ((node->type == XML_TEXT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005974 (node->type == XML_COMMENT_NODE) ||
5975 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00005976 (node->type == XML_CDATA_SECTION_NODE)))
5977 node = node->next;
Daniel Veillard276be4a2003-01-24 01:03:34 +00005978 if (node == ctxt->state->seq) {
5979 VALID_CTXT();
5980 VALID_ERROR("Expecting text content\n");
5981 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005982 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00005983 ctxt->state->seq = node;
5984 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005985 case XML_RELAXNG_ELEMENT:
5986 node = xmlRelaxNGSkipIgnored(ctxt, node);
Daniel Veillard231d7912003-02-09 14:22:17 +00005987 if (node == NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005988 VALID_CTXT();
Daniel Veillard231d7912003-02-09 14:22:17 +00005989 VALID_ERROR("Expecting an element, got empty\n");
5990 ret = -1;
5991 break;
5992 }
5993 if (node->type != XML_ELEMENT_NODE) {
5994 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005995 VALID_ERROR2("Expecting an element got %d type\n", node->type);
Daniel Veillard231d7912003-02-09 14:22:17 +00005996 ret = -1;
5997 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005998 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00005999 /*
6000 * This node was already validated successfully against
6001 * this definition.
6002 */
6003 if (node->_private == define)
6004 break;
Daniel Veillard416589a2003-02-17 17:25:42 +00006005
6006 ret = xmlRelaxNGElementMatch(ctxt, define, node);
6007 if (ret <= 0) {
6008 ret = -1;
6009 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006010 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006011 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006012
6013 state = xmlRelaxNGNewValidState(ctxt, node);
6014 if (state == NULL) {
Daniel Veillard231d7912003-02-09 14:22:17 +00006015 ret = -1;
6016 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006017 }
6018
6019 oldstate = ctxt->state;
6020 ctxt->state = state;
6021 if (define->attrs != NULL) {
6022 tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
Daniel Veillard231d7912003-02-09 14:22:17 +00006023 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006024 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00006025#ifdef DEBUG
6026 xmlGenericError(xmlGenericErrorContext,
6027 "E: Element %s failed to validate attributes\n",
6028 node->name);
6029#endif
6030 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006031 }
6032 if (define->content != NULL) {
6033 tmp = xmlRelaxNGValidateElementContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00006034 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006035 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00006036#ifdef DEBUG
6037 xmlGenericError(xmlGenericErrorContext,
6038 "E: Element %s failed to validate element content\n",
6039 node->name);
6040#endif
6041 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006042 }
6043 state = ctxt->state;
6044 if (state->seq != NULL) {
6045 state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq);
6046 if (state->seq != NULL) {
6047 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006048 VALID_ERROR3("Extra content for element %s: %s\n",
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006049 node->name, state->seq->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006050 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00006051#ifdef DEBUG
6052 xmlGenericError(xmlGenericErrorContext,
6053 "E: Element %s has extra content: %s\n",
6054 node->name, state->seq->name);
6055#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00006056 }
6057 }
6058 for (i = 0;i < state->nbAttrs;i++) {
6059 if (state->attrs[i] != NULL) {
6060 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006061 VALID_ERROR3("Invalid attribute %s for element %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00006062 state->attrs[i]->name, node->name);
6063 ret = -1;
6064 }
6065 }
6066 ctxt->state = oldstate;
6067 xmlRelaxNGFreeValidState(state);
6068 if (oldstate != NULL)
Daniel Veillarde5b110b2003-02-04 14:43:39 +00006069 oldstate->seq = xmlRelaxNGSkipIgnored(ctxt, node->next);
6070 if (ret == 0)
6071 node->_private = define;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006072
6073
6074#ifdef DEBUG
6075 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard231d7912003-02-09 14:22:17 +00006076 "xmlRelaxNGValidateDefinition(): validated %s : %d",
Daniel Veillard6eadf632003-01-23 18:29:16 +00006077 node->name, ret);
Daniel Veillard231d7912003-02-09 14:22:17 +00006078 if (oldstate == NULL)
6079 xmlGenericError(xmlGenericErrorContext, ": no state\n");
6080 else if (oldstate->seq == NULL)
6081 xmlGenericError(xmlGenericErrorContext, ": done\n");
6082 else if (oldstate->seq->type == XML_ELEMENT_NODE)
6083 xmlGenericError(xmlGenericErrorContext, ": next elem %s\n",
6084 oldstate->seq->name);
6085 else
6086 xmlGenericError(xmlGenericErrorContext, ": next %s %d\n",
6087 oldstate->seq->name, oldstate->seq->type);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006088#endif
6089 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006090 case XML_RELAXNG_OPTIONAL:
6091 oldflags = ctxt->flags;
6092 ctxt->flags |= FLAGS_IGNORABLE;
6093 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6094 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6095 if (ret != 0) {
6096 xmlRelaxNGFreeValidState(ctxt->state);
6097 ctxt->state = oldstate;
6098 ret = 0;
6099 break;
6100 }
6101 xmlRelaxNGFreeValidState(oldstate);
6102 ctxt->flags = oldflags;
6103 ret = 0;
6104 break;
6105 case XML_RELAXNG_ONEORMORE:
6106 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6107 if (ret != 0) {
6108 break;
6109 }
6110 /* no break on purpose */
Daniel Veillard276be4a2003-01-24 01:03:34 +00006111 case XML_RELAXNG_ZEROORMORE: {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006112 oldflags = ctxt->flags;
6113 ctxt->flags |= FLAGS_IGNORABLE;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006114 while (ctxt->state->nbAttrLeft != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006115 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6116 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6117 if (ret != 0) {
6118 xmlRelaxNGFreeValidState(ctxt->state);
6119 ctxt->state = oldstate;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006120 break;
6121 }
6122 xmlRelaxNGFreeValidState(oldstate);
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006123 }
6124 if (ret == 0) {
6125 /*
6126 * There is no attribute left to be consumed,
6127 * we can check the closure by looking at ctxt->state->seq
6128 */
6129 xmlNodePtr cur, temp;
6130
Daniel Veillard276be4a2003-01-24 01:03:34 +00006131 cur = ctxt->state->seq;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006132 temp = NULL;
6133 while ((cur != NULL) && (temp != cur)) {
6134 temp = cur;
6135 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6136 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6137 if (ret != 0) {
6138 xmlRelaxNGFreeValidState(ctxt->state);
6139 ctxt->state = oldstate;
6140 break;
6141 }
6142 xmlRelaxNGFreeValidState(oldstate);
6143 cur = ctxt->state->seq;
6144 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006145 }
6146 ctxt->flags = oldflags;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006147 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006148 break;
Daniel Veillard276be4a2003-01-24 01:03:34 +00006149 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006150 case XML_RELAXNG_CHOICE: {
6151 xmlRelaxNGDefinePtr list = define->content;
6152
6153 oldflags = ctxt->flags;
6154 ctxt->flags |= FLAGS_IGNORABLE;
6155
6156 while (list != NULL) {
6157 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6158 ret = xmlRelaxNGValidateDefinition(ctxt, list);
6159 if (ret == 0) {
6160 xmlRelaxNGFreeValidState(oldstate);
6161 break;
6162 }
6163 xmlRelaxNGFreeValidState(ctxt->state);
6164 ctxt->state = oldstate;
6165 list = list->next;
6166 }
6167 ctxt->flags = oldflags;
6168 break;
6169 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00006170 case XML_RELAXNG_DEF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00006171 case XML_RELAXNG_GROUP: {
6172 xmlRelaxNGDefinePtr list = define->content;
6173
6174 while (list != NULL) {
6175 ret = xmlRelaxNGValidateDefinition(ctxt, list);
6176 if (ret != 0)
6177 break;
6178 list = list->next;
6179 }
6180 break;
6181 }
6182 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006183 ret = xmlRelaxNGValidateInterleave(ctxt, define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006184 break;
6185 case XML_RELAXNG_ATTRIBUTE:
6186 ret = xmlRelaxNGValidateAttribute(ctxt, define);
6187 break;
6188 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00006189 case XML_RELAXNG_PARENTREF:
6190 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00006191 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6192 break;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006193 case XML_RELAXNG_DATATYPE: {
Daniel Veillardd4310742003-02-18 21:12:46 +00006194 xmlNodePtr child;
6195 xmlChar *content = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006196
Daniel Veillardd4310742003-02-18 21:12:46 +00006197 child = node;
6198 while (child != NULL) {
6199 if (child->type == XML_ELEMENT_NODE) {
6200 VALID_CTXT();
6201 VALID_ERROR2("Element %s has child elements\n",
6202 node->parent->name);
6203 ret = -1;
6204 break;
6205 } else if ((child->type == XML_TEXT_NODE) ||
6206 (child->type == XML_CDATA_SECTION_NODE)) {
6207 content = xmlStrcat(content, child->content);
6208 }
6209 /* TODO: handle entities ... */
6210 child = child->next;
6211 }
6212 if (ret == -1) {
6213 if (content != NULL)
6214 xmlFree(content);
6215 break;
6216 }
6217 if (content == NULL) {
6218 content = xmlStrdup(BAD_CAST "");
6219 if (content == NULL) {
6220 VALID_CTXT();
6221 VALID_ERROR("Allocation failure\n");
6222 ret = -1;
6223 break;
6224 }
6225 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006226 ret = xmlRelaxNGValidateDatatype(ctxt, content, define);
6227 if (ret == -1) {
6228 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006229 VALID_ERROR2("internal error validating %s\n", define->name);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006230 } else if (ret == 0) {
Daniel Veillardd4310742003-02-18 21:12:46 +00006231 ctxt->state->seq = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006232 }
6233 if (content != NULL)
6234 xmlFree(content);
6235 break;
6236 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00006237 case XML_RELAXNG_VALUE: {
Daniel Veillardd4310742003-02-18 21:12:46 +00006238 xmlChar *content = NULL;
Daniel Veillardea3f3982003-01-26 19:45:18 +00006239 xmlChar *oldvalue;
Daniel Veillardd4310742003-02-18 21:12:46 +00006240 xmlNodePtr child;
Daniel Veillardea3f3982003-01-26 19:45:18 +00006241
Daniel Veillardd4310742003-02-18 21:12:46 +00006242 child = node;
6243 while (child != NULL) {
6244 if (child->type == XML_ELEMENT_NODE) {
6245 VALID_CTXT();
6246 VALID_ERROR2("Element %s has child elements\n",
6247 node->parent->name);
6248 ret = -1;
6249 break;
6250 } else if ((child->type == XML_TEXT_NODE) ||
6251 (child->type == XML_CDATA_SECTION_NODE)) {
6252 content = xmlStrcat(content, child->content);
6253 }
6254 /* TODO: handle entities ... */
6255 child = child->next;
6256 }
6257 if (ret == -1) {
6258 if (content != NULL)
6259 xmlFree(content);
6260 break;
6261 }
6262 if (content == NULL) {
6263 content = xmlStrdup(BAD_CAST "");
6264 if (content == NULL) {
6265 VALID_CTXT();
6266 VALID_ERROR("Allocation failure\n");
6267 ret = -1;
6268 break;
6269 }
6270 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00006271 oldvalue = ctxt->state->value;
6272 ctxt->state->value = content;
6273 ret = xmlRelaxNGValidateValue(ctxt, define);
6274 ctxt->state->value = oldvalue;
6275 if (ret == -1) {
6276 VALID_CTXT();
Daniel Veillardd2298792003-02-14 16:54:11 +00006277 if (define->name != NULL) {
6278 VALID_ERROR2("error validating value %s\n", define->name);
6279 } else {
6280 VALID_ERROR("error validating value\n");
6281 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00006282 } else if (ret == 0) {
Daniel Veillardd4310742003-02-18 21:12:46 +00006283 ctxt->state->seq = NULL;
Daniel Veillardea3f3982003-01-26 19:45:18 +00006284 }
6285 if (content != NULL)
6286 xmlFree(content);
6287 break;
6288 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006289 case XML_RELAXNG_LIST: {
6290 xmlChar *content;
Daniel Veillardd4310742003-02-18 21:12:46 +00006291 xmlNodePtr child;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006292 xmlChar *oldvalue, *oldendvalue;
6293 int len;
Daniel Veillardea3f3982003-01-26 19:45:18 +00006294
Daniel Veillardd4310742003-02-18 21:12:46 +00006295 /*
6296 * Make sure it's only text nodes
6297 */
6298
6299 content = NULL;
6300 child = node;
6301 while (child != NULL) {
6302 if (child->type == XML_ELEMENT_NODE) {
6303 VALID_CTXT();
6304 VALID_ERROR2("Element %s has child elements\n",
6305 node->parent->name);
6306 ret = -1;
6307 break;
6308 } else if ((child->type == XML_TEXT_NODE) ||
6309 (child->type == XML_CDATA_SECTION_NODE)) {
6310 content = xmlStrcat(content, child->content);
6311 }
6312 /* TODO: handle entities ... */
6313 child = child->next;
6314 }
6315 if (ret == -1) {
6316 if (content != NULL)
6317 xmlFree(content);
6318 break;
6319 }
6320 if (content == NULL) {
6321 content = xmlStrdup(BAD_CAST "");
6322 if (content == NULL) {
6323 VALID_CTXT();
6324 VALID_ERROR("Allocation failure\n");
6325 ret = -1;
6326 break;
6327 }
6328 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006329 len = xmlStrlen(content);
6330 oldvalue = ctxt->state->value;
6331 oldendvalue = ctxt->state->endvalue;
6332 ctxt->state->value = content;
6333 ctxt->state->endvalue = content + len;
6334 ret = xmlRelaxNGValidateValue(ctxt, define);
6335 ctxt->state->value = oldvalue;
6336 ctxt->state->endvalue = oldendvalue;
6337 if (ret == -1) {
6338 VALID_CTXT();
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006339 VALID_ERROR("error validating list\n");
Daniel Veillardd4310742003-02-18 21:12:46 +00006340 } else if ((ret == 0) && (node != NULL)) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006341 ctxt->state->seq = node->next;
6342 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006343 if (content != NULL)
6344 xmlFree(content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006345 break;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006346 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006347 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00006348 case XML_RELAXNG_EXCEPT:
Daniel Veillard8fe98712003-02-19 00:19:14 +00006349 case XML_RELAXNG_PARAM:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006350 TODO
Daniel Veillard416589a2003-02-17 17:25:42 +00006351 ret = -1;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006352 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006353 }
Daniel Veillard231d7912003-02-09 14:22:17 +00006354 ctxt->depth--;
6355#ifdef DEBUG
6356 for (i = 0;i < ctxt->depth;i++)
6357 xmlGenericError(xmlGenericErrorContext, " ");
6358 xmlGenericError(xmlGenericErrorContext,
6359 "Validating %s ", xmlRelaxNGDefName(define));
6360 if (define->name != NULL)
6361 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
6362 if (ret == 0)
6363 xmlGenericError(xmlGenericErrorContext, "suceeded\n");
6364 else
6365 xmlGenericError(xmlGenericErrorContext, "failed\n");
6366#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00006367 return(ret);
6368}
6369
6370/**
6371 * xmlRelaxNGValidateDocument:
6372 * @ctxt: a Relax-NG validation context
6373 * @doc: the document
6374 *
6375 * Validate the given document
6376 *
6377 * Returns 0 if the validation succeeded or an error code.
6378 */
6379static int
6380xmlRelaxNGValidateDocument(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
6381 int ret;
6382 xmlRelaxNGPtr schema;
6383 xmlRelaxNGGrammarPtr grammar;
6384 xmlRelaxNGValidStatePtr state;
6385
6386 if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL))
6387 return(-1);
6388
6389 schema = ctxt->schema;
6390 grammar = schema->topgrammar;
6391 if (grammar == NULL) {
6392 VALID_CTXT();
6393 VALID_ERROR("No top grammar defined\n");
6394 return(-1);
6395 }
6396 state = xmlRelaxNGNewValidState(ctxt, NULL);
6397 ctxt->state = state;
6398 ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start);
6399 state = ctxt->state;
6400 if ((state != NULL) && (state->seq != NULL)) {
6401 xmlNodePtr node;
6402
6403 node = state->seq;
6404 node = xmlRelaxNGSkipIgnored(ctxt, node);
6405 if (node != NULL) {
6406 VALID_CTXT();
6407 VALID_ERROR("extra data on the document\n");
6408 ret = -1;
6409 }
6410 }
6411 xmlRelaxNGFreeValidState(state);
6412
6413 return(ret);
6414}
6415
6416/************************************************************************
6417 * *
6418 * Validation interfaces *
6419 * *
6420 ************************************************************************/
6421/**
6422 * xmlRelaxNGNewValidCtxt:
6423 * @schema: a precompiled XML RelaxNGs
6424 *
6425 * Create an XML RelaxNGs validation context based on the given schema
6426 *
6427 * Returns the validation context or NULL in case of error
6428 */
6429xmlRelaxNGValidCtxtPtr
6430xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema) {
6431 xmlRelaxNGValidCtxtPtr ret;
6432
6433 ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt));
6434 if (ret == NULL) {
6435 xmlGenericError(xmlGenericErrorContext,
6436 "Failed to allocate new schama validation context\n");
6437 return (NULL);
6438 }
6439 memset(ret, 0, sizeof(xmlRelaxNGValidCtxt));
6440 ret->schema = schema;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006441 ret->error = xmlGenericError;
6442 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006443 return (ret);
6444}
6445
6446/**
6447 * xmlRelaxNGFreeValidCtxt:
6448 * @ctxt: the schema validation context
6449 *
6450 * Free the resources associated to the schema validation context
6451 */
6452void
6453xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt) {
6454 if (ctxt == NULL)
6455 return;
6456 xmlFree(ctxt);
6457}
6458
6459/**
6460 * xmlRelaxNGSetValidErrors:
6461 * @ctxt: a Relax-NG validation context
6462 * @err: the error function
6463 * @warn: the warning function
6464 * @ctx: the functions context
6465 *
6466 * Set the error and warning callback informations
6467 */
6468void
6469xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
6470 xmlRelaxNGValidityErrorFunc err,
6471 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
6472 if (ctxt == NULL)
6473 return;
6474 ctxt->error = err;
6475 ctxt->warning = warn;
6476 ctxt->userData = ctx;
6477}
6478
6479/**
6480 * xmlRelaxNGValidateDoc:
6481 * @ctxt: a Relax-NG validation context
6482 * @doc: a parsed document tree
6483 *
6484 * Validate a document tree in memory.
6485 *
6486 * Returns 0 if the document is valid, a positive error code
6487 * number otherwise and -1 in case of internal or API error.
6488 */
6489int
6490xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
6491 int ret;
6492
6493 if ((ctxt == NULL) || (doc == NULL))
6494 return(-1);
6495
6496 ctxt->doc = doc;
6497
6498 ret = xmlRelaxNGValidateDocument(ctxt, doc);
Daniel Veillard71531f32003-02-05 13:19:53 +00006499 /*
6500 * TODO: build error codes
6501 */
6502 if (ret == -1)
6503 return(1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006504 return(ret);
6505}
6506
6507#endif /* LIBXML_SCHEMAS_ENABLED */
6508