blob: a2bdf53cba2753b2df10ebcce0a33c9391919c60 [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/**
Daniel Veillardce14fa52003-02-19 17:32:48 +0000713 * xmlRelaxNGEqualValidState:
714 * @ctxt: a Relax-NG validation context
715 * @state1: a validation state
716 * @state2: a validation state
717 *
718 * Compare the validation states for equality
719 *
720 * Returns 1 if equald, 0 otherwise
721 */
722static int
723xmlRelaxNGEqualValidState(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
724 xmlRelaxNGValidStatePtr state1,
725 xmlRelaxNGValidStatePtr state2)
726{
727 int i;
728
729 if ((state1 == NULL) || (state2 == NULL))
730 return(0);
731 if (state1 == state2)
732 return(1);
733 if (state1->node != state2->node)
734 return(0);
735 if (state1->seq != state2->seq)
736 return(0);
737 if (state1->nbAttrLeft != state2->nbAttrLeft)
738 return(0);
739 if (state1->nbAttrs != state2->nbAttrs)
740 return(0);
741 if (state1->endvalue != state2->endvalue)
742 return(0);
743 if ((state1->value != state2->value) &&
744 (!xmlStrEqual(state1->value, state2->value)))
745 return(0);
746 for (i = 0;i < state1->nbAttrs;i++) {
747 if (state1->attrs[i] != state2->attrs[i])
748 return(0);
749 }
750 return(1);
751}
752
753/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000754 * xmlRelaxNGFreeValidState:
755 * @state: a validation state structure
756 *
757 * Deallocate a RelaxNG validation state structure.
758 */
759static void
760xmlRelaxNGFreeValidState(xmlRelaxNGValidStatePtr state)
761{
762 if (state == NULL)
763 return;
764
765 xmlFree(state);
766}
767
768/************************************************************************
769 * *
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000770 * Document functions *
771 * *
772 ************************************************************************/
773static xmlDocPtr xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt,
774 xmlDocPtr doc);
775
776/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000777 * xmlRelaxNGIncludePush:
778 * @ctxt: the parser context
779 * @value: the element doc
780 *
781 * Pushes a new include on top of the include stack
782 *
783 * Returns 0 in case of error, the index in the stack otherwise
784 */
785static int
786xmlRelaxNGIncludePush(xmlRelaxNGParserCtxtPtr ctxt,
787 xmlRelaxNGIncludePtr value)
788{
789 if (ctxt->incTab == NULL) {
790 ctxt->incMax = 4;
791 ctxt->incNr = 0;
792 ctxt->incTab = (xmlRelaxNGIncludePtr *) xmlMalloc(
793 ctxt->incMax * sizeof(ctxt->incTab[0]));
794 if (ctxt->incTab == NULL) {
795 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
796 return (0);
797 }
798 }
799 if (ctxt->incNr >= ctxt->incMax) {
800 ctxt->incMax *= 2;
801 ctxt->incTab =
802 (xmlRelaxNGIncludePtr *) xmlRealloc(ctxt->incTab,
803 ctxt->incMax *
804 sizeof(ctxt->incTab[0]));
805 if (ctxt->incTab == NULL) {
806 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
807 return (0);
808 }
809 }
810 ctxt->incTab[ctxt->incNr] = value;
811 ctxt->inc = value;
812 return (ctxt->incNr++);
813}
814
815/**
816 * xmlRelaxNGIncludePop:
817 * @ctxt: the parser context
818 *
819 * Pops the top include from the include stack
820 *
821 * Returns the include just removed
822 */
823static xmlRelaxNGIncludePtr
824xmlRelaxNGIncludePop(xmlRelaxNGParserCtxtPtr ctxt)
825{
826 xmlRelaxNGIncludePtr ret;
827
828 if (ctxt->incNr <= 0)
829 return (0);
830 ctxt->incNr--;
831 if (ctxt->incNr > 0)
832 ctxt->inc = ctxt->incTab[ctxt->incNr - 1];
833 else
834 ctxt->inc = NULL;
835 ret = ctxt->incTab[ctxt->incNr];
836 ctxt->incTab[ctxt->incNr] = 0;
837 return (ret);
838}
839
840/**
841 * xmlRelaxNGLoadInclude:
842 * @ctxt: the parser context
843 * @URL: the normalized URL
844 * @node: the include node.
Daniel Veillard416589a2003-02-17 17:25:42 +0000845 * @ns: the namespace passed from the context.
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000846 *
847 * First lookup if the document is already loaded into the parser context,
848 * check against recursion. If not found the resource is loaded and
849 * the content is preprocessed before being returned back to the caller.
850 *
851 * Returns the xmlRelaxNGIncludePtr or NULL in case of error
852 */
853static xmlRelaxNGIncludePtr
854xmlRelaxNGLoadInclude(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillard416589a2003-02-17 17:25:42 +0000855 xmlNodePtr node, const xmlChar *ns) {
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000856 xmlRelaxNGIncludePtr ret = NULL;
857 xmlDocPtr doc;
858 int i;
859 xmlNodePtr root, tmp, tmp2, cur;
860
861 /*
862 * check against recursion in the stack
863 */
864 for (i = 0;i < ctxt->incNr;i++) {
865 if (xmlStrEqual(ctxt->incTab[i]->href, URL)) {
866 if (ctxt->error != NULL)
867 ctxt->error(ctxt->userData,
868 "Detected an externalRef recursion for %s\n",
869 URL);
870 ctxt->nbErrors++;
871 return(NULL);
872 }
873 }
874
875 /*
876 * Lookup in the hash table
877 */
878 if (ctxt->includes == NULL) {
879 ctxt->includes = xmlHashCreate(10);
880 if (ctxt->includes == NULL) {
881 if (ctxt->error != NULL)
882 ctxt->error(ctxt->userData,
883 "Failed to allocate hash table for document\n");
884 ctxt->nbErrors++;
885 return(NULL);
886 }
887 } else {
Daniel Veillard416589a2003-02-17 17:25:42 +0000888 if (ns == NULL)
889 ret = xmlHashLookup2(ctxt->includes, BAD_CAST "", URL);
890 else
891 ret = xmlHashLookup2(ctxt->includes, ns, URL);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000892 if (ret != NULL)
893 return(ret);
894 }
895
896
897 /*
898 * load the document
899 */
900 doc = xmlParseFile((const char *) URL);
901 if (doc == NULL) {
902 if (ctxt->error != NULL)
903 ctxt->error(ctxt->userData,
904 "xmlRelaxNG: could not load %s\n", URL);
905 ctxt->nbErrors++;
906 return (NULL);
907 }
908
909 /*
910 * Allocate the document structures and register it first.
911 */
912 ret = (xmlRelaxNGIncludePtr) xmlMalloc(sizeof(xmlRelaxNGInclude));
913 if (ret == NULL) {
914 if (ctxt->error != NULL)
915 ctxt->error(ctxt->userData,
916 "xmlRelaxNG: allocate memory for doc %s\n", URL);
917 ctxt->nbErrors++;
918 xmlFreeDoc(doc);
919 return (NULL);
920 }
921 memset(ret, 0, sizeof(xmlRelaxNGInclude));
922 ret->doc = doc;
923 ret->href = xmlStrdup(URL);
924
925 /*
Daniel Veillard416589a2003-02-17 17:25:42 +0000926 * transmit the ns if needed
927 */
928 if (ns != NULL) {
929 root = xmlDocGetRootElement(doc);
930 if (root != NULL) {
931 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
932 xmlSetProp(root, BAD_CAST"ns", ns);
933 }
934 }
935 }
936
937 /*
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000938 * push it on the stack and register it in the hash table
939 */
Daniel Veillard416589a2003-02-17 17:25:42 +0000940 if (ns == NULL)
941 xmlHashAddEntry2(ctxt->includes, BAD_CAST "", URL, ret);
942 else
943 xmlHashAddEntry2(ctxt->includes, ns, URL, ret);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000944 xmlRelaxNGIncludePush(ctxt, ret);
945
946 /*
947 * Some preprocessing of the document content, this include recursing
948 * in the include stack.
949 */
950 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
951 if (doc == NULL) {
952 /* xmlFreeDoc(ctxt->include); */
953 ctxt->inc = NULL;
954 return(NULL);
955 }
956
957 /*
958 * Pop up the include from the stack
959 */
960 xmlRelaxNGIncludePop(ctxt);
961
962 /*
963 * Check that the top element is a grammar
964 */
965 root = xmlDocGetRootElement(doc);
966 if (root == NULL) {
967 if (ctxt->error != NULL)
968 ctxt->error(ctxt->userData,
969 "xmlRelaxNG: included document is empty %s\n", URL);
970 ctxt->nbErrors++;
971 xmlFreeDoc(doc);
972 return (NULL);
973 }
974 if (!IS_RELAXNG(root, "grammar")) {
975 if (ctxt->error != NULL)
976 ctxt->error(ctxt->userData,
977 "xmlRelaxNG: included document %s root is not a grammar\n",
978 URL);
979 ctxt->nbErrors++;
980 xmlFreeDoc(doc);
981 return (NULL);
982 }
983
984 /*
985 * Elimination of redefined rules in the include.
986 */
987 cur = node->children;
988 while (cur != NULL) {
989 if (IS_RELAXNG(cur, "start")) {
990 int found = 0;
991
992 tmp = root->children;
993 while (tmp != NULL) {
994 tmp2 = tmp->next;
995 if (IS_RELAXNG(tmp, "start")) {
996 found = 1;
997 xmlUnlinkNode(tmp);
998 xmlFreeNode(tmp);
999 }
1000 tmp = tmp2;
1001 }
1002 if (!found) {
1003 if (ctxt->error != NULL)
1004 ctxt->error(ctxt->userData,
1005 "xmlRelaxNG: include %s has a start but not the included grammar\n",
1006 URL);
1007 ctxt->nbErrors++;
1008 }
1009 } else if (IS_RELAXNG(cur, "define")) {
1010 xmlChar *name, *name2;
1011
1012 name = xmlGetProp(cur, BAD_CAST "name");
1013 if (name == NULL) {
1014 if (ctxt->error != NULL)
1015 ctxt->error(ctxt->userData,
1016 "xmlRelaxNG: include %s has define without name\n",
1017 URL);
1018 ctxt->nbErrors++;
1019 } else {
1020 int found = 0;
1021
Daniel Veillardd2298792003-02-14 16:54:11 +00001022 xmlRelaxNGNormExtSpace(name);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001023 tmp = root->children;
1024 while (tmp != NULL) {
1025 tmp2 = tmp->next;
1026 if (IS_RELAXNG(tmp, "define")) {
1027 name2 = xmlGetProp(tmp, BAD_CAST "name");
Daniel Veillardd2298792003-02-14 16:54:11 +00001028 xmlRelaxNGNormExtSpace(name2);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001029 if (name2 != NULL) {
1030 if (xmlStrEqual(name, name2)) {
1031 found = 1;
1032 xmlUnlinkNode(tmp);
1033 xmlFreeNode(tmp);
1034 }
1035 xmlFree(name2);
1036 }
1037 }
1038 tmp = tmp2;
1039 }
1040 if (!found) {
1041 if (ctxt->error != NULL)
1042 ctxt->error(ctxt->userData,
1043 "xmlRelaxNG: include %s has a define %s but not the included grammar\n",
1044 URL, name);
1045 ctxt->nbErrors++;
1046 }
1047 xmlFree(name);
1048 }
1049 }
1050 cur = cur->next;
1051 }
1052
1053
1054 return(ret);
1055}
1056
1057/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001058 * xmlRelaxNGDocumentPush:
1059 * @ctxt: the parser context
1060 * @value: the element doc
1061 *
1062 * Pushes a new doc on top of the doc stack
1063 *
1064 * Returns 0 in case of error, the index in the stack otherwise
1065 */
1066static int
1067xmlRelaxNGDocumentPush(xmlRelaxNGParserCtxtPtr ctxt,
1068 xmlRelaxNGDocumentPtr value)
1069{
1070 if (ctxt->docTab == NULL) {
1071 ctxt->docMax = 4;
1072 ctxt->docNr = 0;
1073 ctxt->docTab = (xmlRelaxNGDocumentPtr *) xmlMalloc(
1074 ctxt->docMax * sizeof(ctxt->docTab[0]));
1075 if (ctxt->docTab == NULL) {
1076 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
1077 return (0);
1078 }
1079 }
1080 if (ctxt->docNr >= ctxt->docMax) {
1081 ctxt->docMax *= 2;
1082 ctxt->docTab =
1083 (xmlRelaxNGDocumentPtr *) xmlRealloc(ctxt->docTab,
1084 ctxt->docMax *
1085 sizeof(ctxt->docTab[0]));
1086 if (ctxt->docTab == NULL) {
1087 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
1088 return (0);
1089 }
1090 }
1091 ctxt->docTab[ctxt->docNr] = value;
1092 ctxt->doc = value;
1093 return (ctxt->docNr++);
1094}
1095
1096/**
1097 * xmlRelaxNGDocumentPop:
1098 * @ctxt: the parser context
1099 *
1100 * Pops the top doc from the doc stack
1101 *
1102 * Returns the doc just removed
1103 */
1104static xmlRelaxNGDocumentPtr
1105xmlRelaxNGDocumentPop(xmlRelaxNGParserCtxtPtr ctxt)
1106{
1107 xmlRelaxNGDocumentPtr ret;
1108
1109 if (ctxt->docNr <= 0)
1110 return (0);
1111 ctxt->docNr--;
1112 if (ctxt->docNr > 0)
1113 ctxt->doc = ctxt->docTab[ctxt->docNr - 1];
1114 else
1115 ctxt->doc = NULL;
1116 ret = ctxt->docTab[ctxt->docNr];
1117 ctxt->docTab[ctxt->docNr] = 0;
1118 return (ret);
1119}
1120
1121/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001122 * xmlRelaxNGLoadExternalRef:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001123 * @ctxt: the parser context
1124 * @URL: the normalized URL
1125 * @ns: the inherited ns if any
1126 *
1127 * First lookup if the document is already loaded into the parser context,
1128 * check against recursion. If not found the resource is loaded and
1129 * the content is preprocessed before being returned back to the caller.
1130 *
1131 * Returns the xmlRelaxNGDocumentPtr or NULL in case of error
1132 */
1133static xmlRelaxNGDocumentPtr
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001134xmlRelaxNGLoadExternalRef(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001135 const xmlChar *ns) {
1136 xmlRelaxNGDocumentPtr ret = NULL;
1137 xmlDocPtr doc;
1138 xmlNodePtr root;
1139 int i;
1140
1141 /*
1142 * check against recursion in the stack
1143 */
1144 for (i = 0;i < ctxt->docNr;i++) {
1145 if (xmlStrEqual(ctxt->docTab[i]->href, URL)) {
1146 if (ctxt->error != NULL)
1147 ctxt->error(ctxt->userData,
1148 "Detected an externalRef recursion for %s\n",
1149 URL);
1150 ctxt->nbErrors++;
1151 return(NULL);
1152 }
1153 }
1154
1155 /*
1156 * Lookup in the hash table
1157 */
1158 if (ctxt->documents == NULL) {
1159 ctxt->documents = xmlHashCreate(10);
1160 if (ctxt->documents == NULL) {
1161 if (ctxt->error != NULL)
1162 ctxt->error(ctxt->userData,
1163 "Failed to allocate hash table for document\n");
1164 ctxt->nbErrors++;
1165 return(NULL);
1166 }
1167 } else {
1168 if (ns == NULL)
1169 ret = xmlHashLookup2(ctxt->documents, BAD_CAST "", URL);
1170 else
1171 ret = xmlHashLookup2(ctxt->documents, ns, URL);
1172 if (ret != NULL)
1173 return(ret);
1174 }
1175
1176
1177 /*
1178 * load the document
1179 */
1180 doc = xmlParseFile((const char *) URL);
1181 if (doc == NULL) {
1182 if (ctxt->error != NULL)
1183 ctxt->error(ctxt->userData,
1184 "xmlRelaxNG: could not load %s\n", URL);
1185 ctxt->nbErrors++;
1186 return (NULL);
1187 }
1188
1189 /*
1190 * Allocate the document structures and register it first.
1191 */
1192 ret = (xmlRelaxNGDocumentPtr) xmlMalloc(sizeof(xmlRelaxNGDocument));
1193 if (ret == NULL) {
1194 if (ctxt->error != NULL)
1195 ctxt->error(ctxt->userData,
1196 "xmlRelaxNG: allocate memory for doc %s\n", URL);
1197 ctxt->nbErrors++;
1198 xmlFreeDoc(doc);
1199 return (NULL);
1200 }
1201 memset(ret, 0, sizeof(xmlRelaxNGDocument));
1202 ret->doc = doc;
1203 ret->href = xmlStrdup(URL);
1204
1205 /*
1206 * transmit the ns if needed
1207 */
1208 if (ns != NULL) {
1209 root = xmlDocGetRootElement(doc);
1210 if (root != NULL) {
1211 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
1212 xmlSetProp(root, BAD_CAST"ns", ns);
1213 }
1214 }
1215 }
1216
1217 /*
1218 * push it on the stack and register it in the hash table
1219 */
1220 if (ns == NULL)
1221 xmlHashAddEntry2(ctxt->documents, BAD_CAST "", URL, ret);
1222 else
1223 xmlHashAddEntry2(ctxt->documents, ns, URL, ret);
1224 xmlRelaxNGDocumentPush(ctxt, ret);
1225
1226 /*
1227 * Some preprocessing of the document content
1228 */
1229 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
1230 if (doc == NULL) {
1231 xmlFreeDoc(ctxt->document);
1232 ctxt->doc = NULL;
1233 return(NULL);
1234 }
1235
1236 xmlRelaxNGDocumentPop(ctxt);
1237
1238 return(ret);
1239}
1240
1241/************************************************************************
1242 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +00001243 * Error functions *
1244 * *
1245 ************************************************************************/
1246
1247#define VALID_CTXT() \
Daniel Veillard231d7912003-02-09 14:22:17 +00001248 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1249 xmlGenericError(xmlGenericErrorContext, \
Daniel Veillard6eadf632003-01-23 18:29:16 +00001250 "error detected at %s:%d\n", \
1251 __FILE__, __LINE__);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001252
1253#define VALID_ERROR(a) \
Daniel Veillard231d7912003-02-09 14:22:17 +00001254 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001255 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a)
1256#define VALID_ERROR2(a, b) \
1257 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1258 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a, b)
1259#define VALID_ERROR3(a, b, c) \
1260 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1261 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a, b, c)
Daniel Veillard6eadf632003-01-23 18:29:16 +00001262
Daniel Veillardd2298792003-02-14 16:54:11 +00001263#ifdef DEBUG
Daniel Veillard231d7912003-02-09 14:22:17 +00001264static const char *
1265xmlRelaxNGDefName(xmlRelaxNGDefinePtr def) {
1266 if (def == NULL)
1267 return("none");
1268 switch(def->type) {
1269 case XML_RELAXNG_EMPTY: return("empty");
1270 case XML_RELAXNG_NOT_ALLOWED: return("notAllowed");
1271 case XML_RELAXNG_EXCEPT: return("except");
1272 case XML_RELAXNG_TEXT: return("text");
1273 case XML_RELAXNG_ELEMENT: return("element");
1274 case XML_RELAXNG_DATATYPE: return("datatype");
1275 case XML_RELAXNG_VALUE: return("value");
1276 case XML_RELAXNG_LIST: return("list");
1277 case XML_RELAXNG_ATTRIBUTE: return("attribute");
1278 case XML_RELAXNG_DEF: return("def");
1279 case XML_RELAXNG_REF: return("ref");
1280 case XML_RELAXNG_EXTERNALREF: return("externalRef");
1281 case XML_RELAXNG_PARENTREF: return("parentRef");
1282 case XML_RELAXNG_OPTIONAL: return("optional");
1283 case XML_RELAXNG_ZEROORMORE: return("zeroOrMore");
1284 case XML_RELAXNG_ONEORMORE: return("oneOrMore");
1285 case XML_RELAXNG_CHOICE: return("choice");
1286 case XML_RELAXNG_GROUP: return("group");
1287 case XML_RELAXNG_INTERLEAVE: return("interleave");
1288 case XML_RELAXNG_START: return("start");
1289 }
1290 return("unknown");
1291}
Daniel Veillardd2298792003-02-14 16:54:11 +00001292#endif
1293
Daniel Veillard6eadf632003-01-23 18:29:16 +00001294#if 0
1295/**
1296 * xmlRelaxNGErrorContext:
1297 * @ctxt: the parsing context
1298 * @schema: the schema being built
1299 * @node: the node being processed
1300 * @child: the child being processed
1301 *
1302 * Dump a RelaxNGType structure
1303 */
1304static void
1305xmlRelaxNGErrorContext(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGPtr schema,
1306 xmlNodePtr node, xmlNodePtr child)
1307{
1308 int line = 0;
1309 const xmlChar *file = NULL;
1310 const xmlChar *name = NULL;
1311 const char *type = "error";
1312
1313 if ((ctxt == NULL) || (ctxt->error == NULL))
1314 return;
1315
1316 if (child != NULL)
1317 node = child;
1318
1319 if (node != NULL) {
1320 if ((node->type == XML_DOCUMENT_NODE) ||
1321 (node->type == XML_HTML_DOCUMENT_NODE)) {
1322 xmlDocPtr doc = (xmlDocPtr) node;
1323
1324 file = doc->URL;
1325 } else {
1326 /*
1327 * Try to find contextual informations to report
1328 */
1329 if (node->type == XML_ELEMENT_NODE) {
1330 line = (int) node->content;
1331 } else if ((node->prev != NULL) &&
1332 (node->prev->type == XML_ELEMENT_NODE)) {
1333 line = (int) node->prev->content;
1334 } else if ((node->parent != NULL) &&
1335 (node->parent->type == XML_ELEMENT_NODE)) {
1336 line = (int) node->parent->content;
1337 }
1338 if ((node->doc != NULL) && (node->doc->URL != NULL))
1339 file = node->doc->URL;
1340 if (node->name != NULL)
1341 name = node->name;
1342 }
1343 }
1344
1345 if (ctxt != NULL)
1346 type = "compilation error";
1347 else if (schema != NULL)
1348 type = "runtime error";
1349
1350 if ((file != NULL) && (line != 0) && (name != NULL))
1351 ctxt->error(ctxt->userData, "%s: file %s line %d element %s\n",
1352 type, file, line, name);
1353 else if ((file != NULL) && (name != NULL))
1354 ctxt->error(ctxt->userData, "%s: file %s element %s\n",
1355 type, file, name);
1356 else if ((file != NULL) && (line != 0))
1357 ctxt->error(ctxt->userData, "%s: file %s line %d\n", type, file, line);
1358 else if (file != NULL)
1359 ctxt->error(ctxt->userData, "%s: file %s\n", type, file);
1360 else if (name != NULL)
1361 ctxt->error(ctxt->userData, "%s: element %s\n", type, name);
1362 else
1363 ctxt->error(ctxt->userData, "%s\n", type);
1364}
1365#endif
1366
1367/************************************************************************
1368 * *
1369 * Type library hooks *
1370 * *
1371 ************************************************************************/
Daniel Veillardea3f3982003-01-26 19:45:18 +00001372static xmlChar *xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt,
1373 const xmlChar *str);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001374
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001375/**
1376 * xmlRelaxNGSchemaTypeHave:
1377 * @data: data needed for the library
1378 * @type: the type name
1379 *
1380 * Check if the given type is provided by
1381 * the W3C XMLSchema Datatype library.
1382 *
1383 * Returns 1 if yes, 0 if no and -1 in case of error.
1384 */
1385static int
1386xmlRelaxNGSchemaTypeHave(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001387 const xmlChar *type) {
1388 xmlSchemaTypePtr typ;
1389
1390 if (type == NULL)
1391 return(-1);
1392 typ = xmlSchemaGetPredefinedType(type,
1393 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1394 if (typ == NULL)
1395 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001396 return(1);
1397}
1398
1399/**
1400 * xmlRelaxNGSchemaTypeCheck:
1401 * @data: data needed for the library
1402 * @type: the type name
1403 * @value: the value to check
1404 *
1405 * Check if the given type and value are validated by
1406 * the W3C XMLSchema Datatype library.
1407 *
1408 * Returns 1 if yes, 0 if no and -1 in case of error.
1409 */
1410static int
1411xmlRelaxNGSchemaTypeCheck(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001412 const xmlChar *type,
1413 const xmlChar *value) {
1414 xmlSchemaTypePtr typ;
1415 int ret;
1416
1417 /*
1418 * TODO: the type should be cached ab provided back, interface subject
1419 * to changes.
1420 * TODO: handle facets, may require an additional interface and keep
1421 * the value returned from the validation.
1422 */
1423 if ((type == NULL) || (value == NULL))
1424 return(-1);
1425 typ = xmlSchemaGetPredefinedType(type,
1426 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1427 if (typ == NULL)
1428 return(-1);
1429 ret = xmlSchemaValidatePredefinedType(typ, value, NULL);
1430 if (ret == 0)
1431 return(1);
1432 if (ret > 0)
1433 return(0);
1434 return(-1);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001435}
1436
1437/**
1438 * xmlRelaxNGSchemaTypeCompare:
1439 * @data: data needed for the library
1440 * @type: the type name
1441 * @value1: the first value
1442 * @value2: the second value
1443 *
1444 * Compare two values accordingly a type from the W3C XMLSchema
1445 * Datatype library.
1446 *
1447 * Returns 1 if yes, 0 if no and -1 in case of error.
1448 */
1449static int
1450xmlRelaxNGSchemaTypeCompare(void *data ATTRIBUTE_UNUSED,
1451 const xmlChar *type ATTRIBUTE_UNUSED,
1452 const xmlChar *value1 ATTRIBUTE_UNUSED,
1453 const xmlChar *value2 ATTRIBUTE_UNUSED) {
1454 TODO
1455 return(1);
1456}
1457
1458/**
1459 * xmlRelaxNGDefaultTypeHave:
1460 * @data: data needed for the library
1461 * @type: the type name
1462 *
1463 * Check if the given type is provided by
1464 * the default datatype library.
1465 *
1466 * Returns 1 if yes, 0 if no and -1 in case of error.
1467 */
1468static int
1469xmlRelaxNGDefaultTypeHave(void *data ATTRIBUTE_UNUSED, const xmlChar *type) {
1470 if (type == NULL)
1471 return(-1);
1472 if (xmlStrEqual(type, BAD_CAST "string"))
1473 return(1);
1474 if (xmlStrEqual(type, BAD_CAST "token"))
1475 return(1);
1476 return(0);
1477}
1478
1479/**
1480 * xmlRelaxNGDefaultTypeCheck:
1481 * @data: data needed for the library
1482 * @type: the type name
1483 * @value: the value to check
1484 *
1485 * Check if the given type and value are validated by
1486 * the default datatype library.
1487 *
1488 * Returns 1 if yes, 0 if no and -1 in case of error.
1489 */
1490static int
1491xmlRelaxNGDefaultTypeCheck(void *data ATTRIBUTE_UNUSED,
1492 const xmlChar *type ATTRIBUTE_UNUSED,
1493 const xmlChar *value ATTRIBUTE_UNUSED) {
Daniel Veillardd4310742003-02-18 21:12:46 +00001494 if (value == NULL)
1495 return(-1);
1496 if (xmlStrEqual(type, BAD_CAST "string"))
1497 return(1);
1498 if (xmlStrEqual(type, BAD_CAST "token")) {
1499#if 0
1500 const xmlChar *cur = value;
1501
1502 while (*cur != 0) {
1503 if (!IS_BLANK(*cur))
1504 return(1);
1505 cur++;
1506 }
1507#endif
1508 return(1);
1509 }
1510
1511 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001512}
1513
1514/**
1515 * xmlRelaxNGDefaultTypeCompare:
1516 * @data: data needed for the library
1517 * @type: the type name
1518 * @value1: the first value
1519 * @value2: the second value
1520 *
1521 * Compare two values accordingly a type from the default
1522 * datatype library.
1523 *
1524 * Returns 1 if yes, 0 if no and -1 in case of error.
1525 */
1526static int
1527xmlRelaxNGDefaultTypeCompare(void *data ATTRIBUTE_UNUSED,
1528 const xmlChar *type ATTRIBUTE_UNUSED,
1529 const xmlChar *value1 ATTRIBUTE_UNUSED,
1530 const xmlChar *value2 ATTRIBUTE_UNUSED) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00001531 int ret = -1;
1532
1533 if (xmlStrEqual(type, BAD_CAST "string")) {
1534 ret = xmlStrEqual(value1, value2);
1535 } else if (xmlStrEqual(type, BAD_CAST "token")) {
1536 if (!xmlStrEqual(value1, value2)) {
1537 xmlChar *nval, *nvalue;
1538
1539 /*
1540 * TODO: trivial optimizations are possible by
1541 * computing at compile-time
1542 */
1543 nval = xmlRelaxNGNormalize(NULL, value1);
1544 nvalue = xmlRelaxNGNormalize(NULL, value2);
1545
Daniel Veillardd4310742003-02-18 21:12:46 +00001546 if ((nval == NULL) || (nvalue == NULL))
Daniel Veillardea3f3982003-01-26 19:45:18 +00001547 ret = -1;
Daniel Veillardd4310742003-02-18 21:12:46 +00001548 else if (xmlStrEqual(nval, nvalue))
1549 ret = 1;
1550 else
1551 ret = 0;
Daniel Veillardea3f3982003-01-26 19:45:18 +00001552 if (nval != NULL)
1553 xmlFree(nval);
1554 if (nvalue != NULL)
1555 xmlFree(nvalue);
Daniel Veillardd4310742003-02-18 21:12:46 +00001556 } else
1557 ret = 1;
Daniel Veillardea3f3982003-01-26 19:45:18 +00001558 }
1559 return(ret);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001560}
1561
1562static int xmlRelaxNGTypeInitialized = 0;
1563static xmlHashTablePtr xmlRelaxNGRegisteredTypes = NULL;
1564
1565/**
1566 * xmlRelaxNGFreeTypeLibrary:
1567 * @lib: the type library structure
1568 * @namespace: the URI bound to the library
1569 *
1570 * Free the structure associated to the type library
1571 */
Daniel Veillard6eadf632003-01-23 18:29:16 +00001572static void
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001573xmlRelaxNGFreeTypeLibrary(xmlRelaxNGTypeLibraryPtr lib,
1574 const xmlChar *namespace ATTRIBUTE_UNUSED) {
1575 if (lib == NULL)
1576 return;
1577 if (lib->namespace != NULL)
1578 xmlFree((xmlChar *)lib->namespace);
1579 xmlFree(lib);
1580}
1581
1582/**
1583 * xmlRelaxNGRegisterTypeLibrary:
1584 * @namespace: the URI bound to the library
1585 * @data: data associated to the library
1586 * @have: the provide function
1587 * @check: the checking function
1588 * @comp: the comparison function
1589 *
1590 * Register a new type library
1591 *
1592 * Returns 0 in case of success and -1 in case of error.
1593 */
1594static int
1595xmlRelaxNGRegisterTypeLibrary(const xmlChar *namespace, void *data,
1596 xmlRelaxNGTypeHave have, xmlRelaxNGTypeCheck check,
1597 xmlRelaxNGTypeCompare comp) {
1598 xmlRelaxNGTypeLibraryPtr lib;
1599 int ret;
1600
1601 if ((xmlRelaxNGRegisteredTypes == NULL) || (namespace == NULL) ||
1602 (check == NULL) || (comp == NULL))
1603 return(-1);
1604 if (xmlHashLookup(xmlRelaxNGRegisteredTypes, namespace) != NULL) {
1605 xmlGenericError(xmlGenericErrorContext,
1606 "Relax-NG types library '%s' already registered\n",
1607 namespace);
1608 return(-1);
1609 }
1610 lib = (xmlRelaxNGTypeLibraryPtr) xmlMalloc(sizeof(xmlRelaxNGTypeLibrary));
1611 if (lib == NULL) {
1612 xmlGenericError(xmlGenericErrorContext,
1613 "Relax-NG types library '%s' malloc() failed\n",
1614 namespace);
1615 return (-1);
1616 }
1617 memset(lib, 0, sizeof(xmlRelaxNGTypeLibrary));
1618 lib->namespace = xmlStrdup(namespace);
1619 lib->data = data;
1620 lib->have = have;
1621 lib->comp = comp;
1622 lib->check = check;
1623 ret = xmlHashAddEntry(xmlRelaxNGRegisteredTypes, namespace, lib);
1624 if (ret < 0) {
1625 xmlGenericError(xmlGenericErrorContext,
1626 "Relax-NG types library failed to register '%s'\n",
1627 namespace);
1628 xmlRelaxNGFreeTypeLibrary(lib, namespace);
1629 return(-1);
1630 }
1631 return(0);
1632}
1633
1634/**
1635 * xmlRelaxNGInitTypes:
1636 *
1637 * Initilize the default type libraries.
1638 *
1639 * Returns 0 in case of success and -1 in case of error.
1640 */
1641static int
Daniel Veillard6eadf632003-01-23 18:29:16 +00001642xmlRelaxNGInitTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001643 if (xmlRelaxNGTypeInitialized != 0)
1644 return(0);
1645 xmlRelaxNGRegisteredTypes = xmlHashCreate(10);
1646 if (xmlRelaxNGRegisteredTypes == NULL) {
1647 xmlGenericError(xmlGenericErrorContext,
1648 "Failed to allocate sh table for Relax-NG types\n");
1649 return(-1);
1650 }
1651 xmlRelaxNGRegisterTypeLibrary(
1652 BAD_CAST "http://www.w3.org/2001/XMLSchema-datatypes",
1653 NULL,
1654 xmlRelaxNGSchemaTypeHave,
1655 xmlRelaxNGSchemaTypeCheck,
1656 xmlRelaxNGSchemaTypeCompare);
1657 xmlRelaxNGRegisterTypeLibrary(
1658 xmlRelaxNGNs,
1659 NULL,
1660 xmlRelaxNGDefaultTypeHave,
1661 xmlRelaxNGDefaultTypeCheck,
1662 xmlRelaxNGDefaultTypeCompare);
1663 xmlRelaxNGTypeInitialized = 1;
1664 return(0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001665}
1666
1667/**
1668 * xmlRelaxNGCleanupTypes:
1669 *
1670 * Cleanup the default Schemas type library associated to RelaxNG
1671 */
1672void
1673xmlRelaxNGCleanupTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001674 if (xmlRelaxNGTypeInitialized == 0)
1675 return;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001676 xmlSchemaCleanupTypes();
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001677 xmlHashFree(xmlRelaxNGRegisteredTypes, (xmlHashDeallocator)
1678 xmlRelaxNGFreeTypeLibrary);
1679 xmlRelaxNGTypeInitialized = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001680}
1681
1682/************************************************************************
1683 * *
1684 * Parsing functions *
1685 * *
1686 ************************************************************************/
1687
1688static xmlRelaxNGDefinePtr xmlRelaxNGParseAttribute(
1689 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1690static xmlRelaxNGDefinePtr xmlRelaxNGParseElement(
1691 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1692static xmlRelaxNGDefinePtr xmlRelaxNGParsePatterns(
Daniel Veillard154877e2003-01-30 12:17:05 +00001693 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes, int group);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001694static xmlRelaxNGDefinePtr xmlRelaxNGParsePattern(
1695 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001696static xmlRelaxNGPtr xmlRelaxNGParseDocument(
1697 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00001698static int xmlRelaxNGParseGrammarContent(
1699 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00001700static xmlRelaxNGDefinePtr xmlRelaxNGParseNameClass(
1701 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
1702 xmlRelaxNGDefinePtr def);
Daniel Veillard419a7682003-02-03 23:22:49 +00001703static xmlRelaxNGGrammarPtr xmlRelaxNGParseGrammar(
1704 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001705
1706
1707#define IS_BLANK_NODE(n) \
1708 (((n)->type == XML_TEXT_NODE) && (xmlRelaxNGIsBlank((n)->content)))
1709
1710/**
1711 * xmlRelaxNGIsBlank:
1712 * @str: a string
1713 *
1714 * Check if a string is ignorable c.f. 4.2. Whitespace
1715 *
1716 * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
1717 */
1718static int
1719xmlRelaxNGIsBlank(xmlChar *str) {
1720 if (str == NULL)
1721 return(1);
1722 while (*str != 0) {
1723 if (!(IS_BLANK(*str))) return(0);
1724 str++;
1725 }
1726 return(1);
1727}
1728
Daniel Veillard6eadf632003-01-23 18:29:16 +00001729/**
1730 * xmlRelaxNGGetDataTypeLibrary:
1731 * @ctxt: a Relax-NG parser context
1732 * @node: the current data or value element
1733 *
1734 * Applies algorithm from 4.3. datatypeLibrary attribute
1735 *
1736 * Returns the datatypeLibary value or NULL if not found
1737 */
1738static xmlChar *
1739xmlRelaxNGGetDataTypeLibrary(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1740 xmlNodePtr node) {
1741 xmlChar *ret, *escape;
1742
Daniel Veillard6eadf632003-01-23 18:29:16 +00001743 if ((IS_RELAXNG(node, "data")) || (IS_RELAXNG(node, "value"))) {
1744 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1745 if (ret != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001746 if (ret[0] == 0) {
1747 xmlFree(ret);
1748 return(NULL);
1749 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001750 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +00001751 if (escape == NULL) {
1752 return(ret);
1753 }
1754 xmlFree(ret);
1755 return(escape);
1756 }
1757 }
1758 node = node->parent;
1759 while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) {
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001760 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1761 if (ret != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001762 if (ret[0] == 0) {
1763 xmlFree(ret);
1764 return(NULL);
1765 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001766 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
1767 if (escape == NULL) {
1768 return(ret);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001769 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001770 xmlFree(ret);
1771 return(escape);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001772 }
1773 node = node->parent;
1774 }
1775 return(NULL);
1776}
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001777
1778/**
Daniel Veillardedc91922003-01-26 00:52:04 +00001779 * xmlRelaxNGParseValue:
1780 * @ctxt: a Relax-NG parser context
1781 * @node: the data node.
1782 *
1783 * parse the content of a RelaxNG value node.
1784 *
1785 * Returns the definition pointer or NULL in case of error
1786 */
1787static xmlRelaxNGDefinePtr
1788xmlRelaxNGParseValue(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1789 xmlRelaxNGDefinePtr def = NULL;
1790 xmlRelaxNGTypeLibraryPtr lib;
1791 xmlChar *type;
1792 xmlChar *library;
1793 int tmp;
1794
1795 def = xmlRelaxNGNewDefine(ctxt, node);
1796 if (def == NULL)
1797 return(NULL);
1798 def->type = XML_RELAXNG_VALUE;
1799
1800 type = xmlGetProp(node, BAD_CAST "type");
1801 if (type != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001802 xmlRelaxNGNormExtSpace(type);
1803 if (xmlValidateNCName(type, 0)) {
1804 if (ctxt->error != NULL)
1805 ctxt->error(ctxt->userData,
1806 "value type '%s' is not an NCName\n",
1807 type);
1808 ctxt->nbErrors++;
1809 }
Daniel Veillardedc91922003-01-26 00:52:04 +00001810 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1811 if (library == NULL)
1812 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1813
1814 def->name = type;
1815 def->ns = library;
1816
1817 lib = (xmlRelaxNGTypeLibraryPtr)
1818 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1819 if (lib == NULL) {
1820 if (ctxt->error != NULL)
1821 ctxt->error(ctxt->userData,
1822 "Use of unregistered type library '%s'\n",
1823 library);
1824 ctxt->nbErrors++;
1825 def->data = NULL;
1826 } else {
1827 def->data = lib;
1828 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001829 if (ctxt->error != NULL)
1830 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00001831 "Internal error with type library '%s': no 'have'\n",
1832 library);
1833 ctxt->nbErrors++;
1834 } else {
1835 tmp = lib->have(lib->data, def->name);
1836 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001837 if (ctxt->error != NULL)
1838 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00001839 "Error type '%s' is not exported by type library '%s'\n",
1840 def->name, library);
1841 ctxt->nbErrors++;
1842 }
1843 }
1844 }
1845 }
1846 if (node->children == NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00001847 def->value = xmlStrdup(BAD_CAST "");
Daniel Veillardedc91922003-01-26 00:52:04 +00001848 } else if ((node->children->type != XML_TEXT_NODE) ||
1849 (node->children->next != NULL)) {
1850 if (ctxt->error != NULL)
1851 ctxt->error(ctxt->userData,
1852 "Expecting a single text value for <value>content\n");
1853 ctxt->nbErrors++;
1854 } else {
1855 def->value = xmlNodeGetContent(node);
1856 if (def->value == NULL) {
1857 if (ctxt->error != NULL)
1858 ctxt->error(ctxt->userData,
1859 "Element <value> has no content\n");
1860 ctxt->nbErrors++;
1861 }
1862 }
1863 /* TODO check ahead of time that the value is okay per the type */
1864 return(def);
1865}
1866
1867/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001868 * xmlRelaxNGParseData:
1869 * @ctxt: a Relax-NG parser context
1870 * @node: the data node.
1871 *
1872 * parse the content of a RelaxNG data node.
1873 *
1874 * Returns the definition pointer or NULL in case of error
1875 */
1876static xmlRelaxNGDefinePtr
1877xmlRelaxNGParseData(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
Daniel Veillard416589a2003-02-17 17:25:42 +00001878 xmlRelaxNGDefinePtr def = NULL, except, last = NULL;
Daniel Veillard8fe98712003-02-19 00:19:14 +00001879 xmlRelaxNGDefinePtr param, lastparam = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001880 xmlRelaxNGTypeLibraryPtr lib;
1881 xmlChar *type;
1882 xmlChar *library;
1883 xmlNodePtr content;
1884 int tmp;
1885
1886 type = xmlGetProp(node, BAD_CAST "type");
1887 if (type == NULL) {
1888 if (ctxt->error != NULL)
1889 ctxt->error(ctxt->userData,
1890 "data has no type\n");
1891 ctxt->nbErrors++;
1892 return(NULL);
1893 }
Daniel Veillardd2298792003-02-14 16:54:11 +00001894 xmlRelaxNGNormExtSpace(type);
1895 if (xmlValidateNCName(type, 0)) {
1896 if (ctxt->error != NULL)
1897 ctxt->error(ctxt->userData,
1898 "data type '%s' is not an NCName\n",
1899 type);
1900 ctxt->nbErrors++;
1901 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001902 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1903 if (library == NULL)
1904 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1905
1906 def = xmlRelaxNGNewDefine(ctxt, node);
1907 if (def == NULL) {
1908 xmlFree(type);
1909 return(NULL);
1910 }
1911 def->type = XML_RELAXNG_DATATYPE;
1912 def->name = type;
1913 def->ns = library;
1914
1915 lib = (xmlRelaxNGTypeLibraryPtr)
1916 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1917 if (lib == NULL) {
1918 if (ctxt->error != NULL)
1919 ctxt->error(ctxt->userData,
1920 "Use of unregistered type library '%s'\n",
1921 library);
1922 ctxt->nbErrors++;
1923 def->data = NULL;
1924 } else {
1925 def->data = lib;
1926 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001927 if (ctxt->error != NULL)
1928 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001929 "Internal error with type library '%s': no 'have'\n",
1930 library);
1931 ctxt->nbErrors++;
1932 } else {
1933 tmp = lib->have(lib->data, def->name);
1934 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001935 if (ctxt->error != NULL)
1936 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001937 "Error type '%s' is not exported by type library '%s'\n",
1938 def->name, library);
1939 ctxt->nbErrors++;
1940 }
1941 }
1942 }
1943 content = node->children;
Daniel Veillard416589a2003-02-17 17:25:42 +00001944
1945 /*
1946 * Handle optional params
1947 */
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001948 while (content != NULL) {
Daniel Veillard416589a2003-02-17 17:25:42 +00001949 if (!xmlStrEqual(content->name, BAD_CAST "param"))
1950 break;
Daniel Veillard8fe98712003-02-19 00:19:14 +00001951 param = xmlRelaxNGNewDefine(ctxt, node);
1952 if (param != NULL) {
1953 param->type = XML_RELAXNG_PARAM;
1954 param->name = xmlGetProp(content, BAD_CAST "name");
1955 if (param->name == NULL) {
1956 if (ctxt->error != NULL)
1957 ctxt->error(ctxt->userData,
1958 "param has no name\n");
1959 ctxt->nbErrors++;
1960 }
1961 param->value = xmlNodeGetContent(content);
1962 if (lastparam == NULL) {
1963 def->attrs = lastparam = param;
1964 } else {
1965 lastparam->next = param;
1966 lastparam = param;
1967 }
1968 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001969 content = content->next;
1970 }
Daniel Veillard416589a2003-02-17 17:25:42 +00001971 /*
1972 * Handle optional except
1973 */
1974 if ((content != NULL) && (xmlStrEqual(content->name, BAD_CAST "except"))) {
1975 xmlNodePtr child;
1976 xmlRelaxNGDefinePtr tmp2, last2 = NULL;
1977
1978 except = xmlRelaxNGNewDefine(ctxt, node);
1979 if (except == NULL) {
1980 return(def);
1981 }
1982 except->type = XML_RELAXNG_EXCEPT;
1983 child = content->children;
1984 if (last == NULL) {
1985 def->content = except;
1986 } else {
1987 last->next = except;
1988 }
1989 if (child == NULL) {
1990 if (ctxt->error != NULL)
1991 ctxt->error(ctxt->userData,
1992 "except has no content\n");
1993 ctxt->nbErrors++;
1994 }
1995 while (child != NULL) {
1996 tmp2 = xmlRelaxNGParsePattern(ctxt, child);
1997 if (tmp2 != NULL) {
1998 if (last2 == NULL) {
1999 except->content = last2 = tmp2;
2000 } else {
2001 last2->next = tmp2;
2002 last2 = tmp2;
2003 }
2004 }
2005 child = child->next;
2006 }
2007 content = content->next;
2008 }
2009 /*
2010 * Check there is no unhandled data
2011 */
2012 if (content != NULL) {
2013 if (ctxt->error != NULL)
2014 ctxt->error(ctxt->userData,
2015 "Element data has unexpected content %s\n", content->name);
2016 ctxt->nbErrors++;
2017 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002018
2019 return(def);
2020}
2021
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002022/**
2023 * xmlRelaxNGCompareElemDefLists:
2024 * @ctxt: a Relax-NG parser context
2025 * @defs1: the first list of element defs
2026 * @defs2: the second list of element defs
2027 *
2028 * Compare the 2 lists of element definitions. The comparison is
2029 * that if both lists do not accept the same QNames, it returns 1
2030 * If the 2 lists can accept the same QName the comparison returns 0
2031 *
2032 * Returns 1 disttinct, 0 if equal
2033 */
2034static int
2035xmlRelaxNGCompareElemDefLists(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
2036 xmlRelaxNGDefinePtr *def1,
2037 xmlRelaxNGDefinePtr *def2) {
2038 xmlRelaxNGDefinePtr *basedef2 = def2;
2039
Daniel Veillard154877e2003-01-30 12:17:05 +00002040 if ((def1 == NULL) || (def2 == NULL))
2041 return(1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002042 if ((*def1 == NULL) || (*def2 == NULL))
2043 return(1);
2044 while (*def1 != NULL) {
2045 while ((*def2) != NULL) {
2046 if ((*def1)->name == NULL) {
2047 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
2048 return(0);
2049 } else if ((*def2)->name == NULL) {
2050 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
2051 return(0);
2052 } else if (xmlStrEqual((*def1)->name, (*def2)->name)) {
2053 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
2054 return(0);
2055 }
2056 def2++;
2057 }
2058 def2 = basedef2;
2059 def1++;
2060 }
2061 return(1);
2062}
2063
2064/**
2065 * xmlRelaxNGGetElements:
2066 * @ctxt: a Relax-NG parser context
2067 * @def: the interleave definition
2068 *
2069 * Compute the list of top elements a definition can generate
2070 *
2071 * Returns a list of elements or NULL if none was found.
2072 */
2073static xmlRelaxNGDefinePtr *
2074xmlRelaxNGGetElements(xmlRelaxNGParserCtxtPtr ctxt,
2075 xmlRelaxNGDefinePtr def) {
2076 xmlRelaxNGDefinePtr *ret = NULL, parent, cur, tmp;
2077 int len = 0;
2078 int max = 0;
2079
2080 parent = NULL;
2081 cur = def;
2082 while (cur != NULL) {
Daniel Veillardb08c9812003-01-28 23:09:49 +00002083 if ((cur->type == XML_RELAXNG_ELEMENT) ||
2084 (cur->type == XML_RELAXNG_TEXT)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002085 if (ret == NULL) {
2086 max = 10;
2087 ret = (xmlRelaxNGDefinePtr *)
2088 xmlMalloc((max + 1) * sizeof(xmlRelaxNGDefinePtr));
2089 if (ret == NULL) {
2090 if (ctxt->error != NULL)
2091 ctxt->error(ctxt->userData,
2092 "Out of memory in element search\n");
2093 ctxt->nbErrors++;
2094 return(NULL);
2095 }
2096 } else if (max <= len) {
2097 max *= 2;
2098 ret = xmlRealloc(ret, (max + 1) * sizeof(xmlRelaxNGDefinePtr));
2099 if (ret == NULL) {
2100 if (ctxt->error != NULL)
2101 ctxt->error(ctxt->userData,
2102 "Out of memory in element search\n");
2103 ctxt->nbErrors++;
2104 return(NULL);
2105 }
2106 }
Daniel Veillardb08c9812003-01-28 23:09:49 +00002107 ret[len++] = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002108 ret[len] = NULL;
2109 } else if ((cur->type == XML_RELAXNG_CHOICE) ||
2110 (cur->type == XML_RELAXNG_INTERLEAVE) ||
2111 (cur->type == XML_RELAXNG_GROUP) ||
2112 (cur->type == XML_RELAXNG_ONEORMORE) ||
Daniel Veillardb08c9812003-01-28 23:09:49 +00002113 (cur->type == XML_RELAXNG_ZEROORMORE) ||
2114 (cur->type == XML_RELAXNG_OPTIONAL) ||
2115 (cur->type == XML_RELAXNG_REF) ||
2116 (cur->type == XML_RELAXNG_DEF)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002117 /*
2118 * Don't go within elements or attributes or string values.
2119 * Just gather the element top list
2120 */
2121 if (cur->content != NULL) {
2122 parent = cur;
2123 cur = cur->content;
2124 tmp = cur;
2125 while (tmp != NULL) {
2126 tmp->parent = parent;
2127 tmp = tmp->next;
2128 }
2129 continue;
2130 }
2131 }
Daniel Veillard154877e2003-01-30 12:17:05 +00002132 if (cur == def)
2133 return(ret);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002134 if (cur->next != NULL) {
2135 cur = cur->next;
2136 continue;
2137 }
2138 do {
2139 cur = cur->parent;
2140 if (cur == NULL) break;
2141 if (cur == def) return(ret);
2142 if (cur->next != NULL) {
2143 cur = cur->next;
2144 break;
2145 }
2146 } while (cur != NULL);
2147 }
2148 return(ret);
2149}
2150
2151/**
2152 * xmlRelaxNGComputeInterleaves:
2153 * @def: the interleave definition
2154 * @ctxt: a Relax-NG parser context
2155 * @node: the data node.
2156 *
2157 * A lot of work for preprocessing interleave definitions
2158 * is potentially needed to get a decent execution speed at runtime
2159 * - trying to get a total order on the element nodes generated
2160 * by the interleaves, order the list of interleave definitions
2161 * following that order.
2162 * - if <text/> is used to handle mixed content, it is better to
2163 * flag this in the define and simplify the runtime checking
2164 * algorithm
2165 */
2166static void
2167xmlRelaxNGComputeInterleaves(xmlRelaxNGDefinePtr def,
2168 xmlRelaxNGParserCtxtPtr ctxt,
2169 xmlChar *name ATTRIBUTE_UNUSED) {
2170 xmlRelaxNGDefinePtr cur;
2171
2172 xmlRelaxNGDefinePtr *list = NULL;
2173 xmlRelaxNGPartitionPtr partitions = NULL;
2174 xmlRelaxNGInterleaveGroupPtr *groups = NULL;
2175 xmlRelaxNGInterleaveGroupPtr group;
2176 int i,j,ret;
2177 int nbgroups = 0;
2178 int nbchild = 0;
2179
2180#ifdef DEBUG_INTERLEAVE
2181 xmlGenericError(xmlGenericErrorContext,
2182 "xmlRelaxNGComputeInterleaves(%s)\n",
2183 name);
2184#endif
2185 cur = def->content;
2186 while (cur != NULL) {
2187 nbchild++;
2188 cur = cur->next;
2189 }
2190
2191#ifdef DEBUG_INTERLEAVE
2192 xmlGenericError(xmlGenericErrorContext, " %d child\n", nbchild);
2193#endif
2194 groups = (xmlRelaxNGInterleaveGroupPtr *)
2195 xmlMalloc(nbchild * sizeof(xmlRelaxNGInterleaveGroupPtr));
2196 if (groups == NULL)
2197 goto error;
2198 cur = def->content;
2199 while (cur != NULL) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002200 groups[nbgroups] = (xmlRelaxNGInterleaveGroupPtr)
2201 xmlMalloc(sizeof(xmlRelaxNGInterleaveGroup));
2202 if (groups[nbgroups] == NULL)
2203 goto error;
2204 groups[nbgroups]->rule = cur;
2205 groups[nbgroups]->defs = xmlRelaxNGGetElements(ctxt, cur);
2206 nbgroups++;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002207 cur = cur->next;
2208 }
2209 list = NULL;
2210#ifdef DEBUG_INTERLEAVE
2211 xmlGenericError(xmlGenericErrorContext, " %d groups\n", nbgroups);
2212#endif
2213
2214 /*
2215 * Let's check that all rules makes a partitions according to 7.4
2216 */
2217 partitions = (xmlRelaxNGPartitionPtr)
2218 xmlMalloc(sizeof(xmlRelaxNGPartition));
2219 if (partitions == NULL)
2220 goto error;
2221 partitions->nbgroups = nbgroups;
2222 for (i = 0;i < nbgroups;i++) {
2223 group = groups[i];
2224 for (j = i+1;j < nbgroups;j++) {
2225 if (groups[j] == NULL)
2226 continue;
2227 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->defs,
2228 groups[j]->defs);
2229 if (ret == 0) {
2230 if (ctxt->error != NULL)
2231 ctxt->error(ctxt->userData,
2232 "Element or text conflicts in interleave\n");
2233 ctxt->nbErrors++;
2234 }
2235 }
2236 }
2237 partitions->groups = groups;
2238
2239 /*
2240 * Free Up the child list, and save the partition list back in the def
2241 */
2242 def->data = partitions;
2243 return;
2244
2245error:
2246 if (ctxt->error != NULL)
2247 ctxt->error(ctxt->userData,
2248 "Out of memory in interleave computation\n");
2249 ctxt->nbErrors++;
2250 if (list == NULL)
2251 xmlFree(list);
2252 if (groups != NULL) {
2253 for (i = 0;i < nbgroups;i++)
2254 if (groups[i] != NULL) {
2255 if (groups[i]->defs != NULL)
2256 xmlFree(groups[i]->defs);
2257 xmlFree(groups[i]);
2258 }
2259 xmlFree(groups);
2260 }
2261 xmlRelaxNGFreePartition(partitions);
2262}
2263
2264/**
2265 * xmlRelaxNGParseInterleave:
2266 * @ctxt: a Relax-NG parser context
2267 * @node: the data node.
2268 *
2269 * parse the content of a RelaxNG interleave node.
2270 *
2271 * Returns the definition pointer or NULL in case of error
2272 */
2273static xmlRelaxNGDefinePtr
2274xmlRelaxNGParseInterleave(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2275 xmlRelaxNGDefinePtr def = NULL;
2276 xmlRelaxNGDefinePtr last = NULL, cur;
2277 xmlNodePtr child;
2278
2279 def = xmlRelaxNGNewDefine(ctxt, node);
2280 if (def == NULL) {
2281 return(NULL);
2282 }
2283 def->type = XML_RELAXNG_INTERLEAVE;
2284
2285 if (ctxt->interleaves == NULL)
2286 ctxt->interleaves = xmlHashCreate(10);
2287 if (ctxt->interleaves == NULL) {
2288 if (ctxt->error != NULL)
2289 ctxt->error(ctxt->userData,
2290 "Failed to create interleaves hash table\n");
2291 ctxt->nbErrors++;
2292 } else {
2293 char name[32];
2294
2295 snprintf(name, 32, "interleave%d", ctxt->nbInterleaves++);
2296 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST name, def) < 0) {
2297 if (ctxt->error != NULL)
2298 ctxt->error(ctxt->userData,
2299 "Failed to add %s to hash table\n", name);
2300 ctxt->nbErrors++;
2301 }
2302 }
2303 child = node->children;
Daniel Veillardd2298792003-02-14 16:54:11 +00002304 if (child == NULL) {
2305 if (ctxt->error != NULL)
2306 ctxt->error(ctxt->userData, "Element interleave is empty\n");
2307 ctxt->nbErrors++;
2308 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002309 while (child != NULL) {
2310 if (IS_RELAXNG(child, "element")) {
2311 cur = xmlRelaxNGParseElement(ctxt, child);
2312 } else {
2313 cur = xmlRelaxNGParsePattern(ctxt, child);
2314 }
2315 if (cur != NULL) {
2316 cur->parent = def;
2317 if (last == NULL) {
2318 def->content = last = cur;
2319 } else {
2320 last->next = cur;
2321 last = cur;
2322 }
2323 }
2324 child = child->next;
2325 }
2326
2327 return(def);
2328}
Daniel Veillard6eadf632003-01-23 18:29:16 +00002329
2330/**
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002331 * xmlRelaxNGParseInclude:
2332 * @ctxt: a Relax-NG parser context
2333 * @node: the include node
2334 *
2335 * Integrate the content of an include node in the current grammar
2336 *
2337 * Returns 0 in case of success or -1 in case of error
2338 */
2339static int
2340xmlRelaxNGParseInclude(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2341 xmlRelaxNGIncludePtr incl;
2342 xmlNodePtr root;
2343 int ret = 0, tmp;
2344
2345 incl = node->_private;
2346 if (incl == NULL) {
2347 if (ctxt->error != NULL)
2348 ctxt->error(ctxt->userData,
2349 "Include node has no data\n");
2350 ctxt->nbErrors++;
2351 return(-1);
2352 }
2353 root = xmlDocGetRootElement(incl->doc);
2354 if (root == NULL) {
2355 if (ctxt->error != NULL)
2356 ctxt->error(ctxt->userData,
2357 "Include document is empty\n");
2358 ctxt->nbErrors++;
2359 return(-1);
2360 }
2361 if (!xmlStrEqual(root->name, BAD_CAST "grammar")) {
2362 if (ctxt->error != NULL)
2363 ctxt->error(ctxt->userData,
2364 "Include document root is not a grammar\n");
2365 ctxt->nbErrors++;
2366 return(-1);
2367 }
2368
2369 /*
2370 * Merge the definition from both the include and the internal list
2371 */
2372 if (root->children != NULL) {
2373 tmp = xmlRelaxNGParseGrammarContent(ctxt, root->children);
2374 if (tmp != 0)
2375 ret = -1;
2376 }
2377 if (node->children != NULL) {
2378 tmp = xmlRelaxNGParseGrammarContent(ctxt, node->children);
2379 if (tmp != 0)
2380 ret = -1;
2381 }
2382 return(ret);
2383}
2384
2385/**
Daniel Veillard276be4a2003-01-24 01:03:34 +00002386 * xmlRelaxNGParseDefine:
2387 * @ctxt: a Relax-NG parser context
2388 * @node: the define node
2389 *
2390 * parse the content of a RelaxNG define element node.
2391 *
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002392 * Returns 0 in case of success or -1 in case of error
Daniel Veillard276be4a2003-01-24 01:03:34 +00002393 */
2394static int
2395xmlRelaxNGParseDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2396 xmlChar *name;
2397 int ret = 0, tmp;
2398 xmlRelaxNGDefinePtr def;
2399 const xmlChar *olddefine;
2400
2401 name = xmlGetProp(node, BAD_CAST "name");
2402 if (name == NULL) {
2403 if (ctxt->error != NULL)
2404 ctxt->error(ctxt->userData,
2405 "define has no name\n");
2406 ctxt->nbErrors++;
2407 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00002408 xmlRelaxNGNormExtSpace(name);
2409 if (xmlValidateNCName(name, 0)) {
2410 if (ctxt->error != NULL)
2411 ctxt->error(ctxt->userData,
2412 "define name '%s' is not an NCName\n",
2413 name);
2414 ctxt->nbErrors++;
2415 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002416 def = xmlRelaxNGNewDefine(ctxt, node);
2417 if (def == NULL) {
2418 xmlFree(name);
2419 return(-1);
2420 }
2421 def->type = XML_RELAXNG_DEF;
2422 def->name = name;
2423 if (node->children == NULL) {
2424 if (ctxt->error != NULL)
2425 ctxt->error(ctxt->userData,
2426 "define has no children\n");
2427 ctxt->nbErrors++;
2428 } else {
2429 olddefine = ctxt->define;
2430 ctxt->define = name;
Daniel Veillard154877e2003-01-30 12:17:05 +00002431 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard276be4a2003-01-24 01:03:34 +00002432 ctxt->define = olddefine;
2433 }
2434 if (ctxt->grammar->defs == NULL)
2435 ctxt->grammar->defs = xmlHashCreate(10);
2436 if (ctxt->grammar->defs == NULL) {
2437 if (ctxt->error != NULL)
2438 ctxt->error(ctxt->userData,
2439 "Could not create definition hash\n");
2440 ctxt->nbErrors++;
2441 ret = -1;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002442 } else {
2443 tmp = xmlHashAddEntry(ctxt->grammar->defs, name, def);
2444 if (tmp < 0) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002445 xmlRelaxNGDefinePtr prev;
2446
2447 prev = xmlHashLookup(ctxt->grammar->defs, name);
2448 if (prev == NULL) {
2449 if (ctxt->error != NULL)
2450 ctxt->error(ctxt->userData,
2451 "Internal error on define aggregation of %s\n",
2452 name);
2453 ctxt->nbErrors++;
2454 ret = -1;
Daniel Veillard154877e2003-01-30 12:17:05 +00002455 } else {
2456 while (prev->nextHash != NULL)
2457 prev = prev->nextHash;
2458 prev->nextHash = def;
2459 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002460 }
2461 }
2462 }
2463 return(ret);
2464}
2465
2466/**
Daniel Veillardfebcca42003-02-16 15:44:18 +00002467 * xmlRelaxNGProcessExternalRef:
2468 * @ctxt: the parser context
2469 * @node: the externlRef node
2470 *
2471 * Process and compile an externlRef node
2472 *
2473 * Returns the xmlRelaxNGDefinePtr or NULL in case of error
2474 */
2475static xmlRelaxNGDefinePtr
2476xmlRelaxNGProcessExternalRef(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2477 xmlRelaxNGDocumentPtr docu;
2478 xmlNodePtr root, tmp;
2479 xmlChar *ns;
2480 int newNs = 0;
2481 xmlRelaxNGDefinePtr def;
2482
2483 docu = node->_private;
2484 if (docu != NULL) {
2485 def = xmlRelaxNGNewDefine(ctxt, node);
2486 if (def == NULL)
2487 return(NULL);
2488 def->type = XML_RELAXNG_EXTERNALREF;
2489
2490 if (docu->content == NULL) {
2491 /*
2492 * Then do the parsing for good
2493 */
2494 root = xmlDocGetRootElement(docu->doc);
2495 if (root == NULL) {
2496 if (ctxt->error != NULL)
2497 ctxt->error(ctxt->userData,
2498 "xmlRelaxNGParse: %s is empty\n",
2499 ctxt->URL);
2500 ctxt->nbErrors++;
2501 return (NULL);
2502 }
2503 /*
2504 * ns transmission rules
2505 */
2506 ns = xmlGetProp(root, BAD_CAST "ns");
2507 if (ns == NULL) {
2508 tmp = node;
2509 while ((tmp != NULL) &&
2510 (tmp->type == XML_ELEMENT_NODE)) {
2511 ns = xmlGetProp(tmp, BAD_CAST "ns");
2512 if (ns != NULL) {
2513 break;
2514 }
2515 tmp = tmp->parent;
2516 }
2517 if (ns != NULL) {
2518 xmlSetProp(root, BAD_CAST "ns", ns);
2519 newNs = 1;
2520 xmlFree(ns);
2521 }
2522 } else {
2523 xmlFree(ns);
2524 }
2525
2526 /*
2527 * Parsing to get a precompiled schemas.
2528 */
2529 docu->schema = xmlRelaxNGParseDocument(ctxt, root);
2530 if ((docu->schema != NULL) &&
2531 (docu->schema->topgrammar != NULL)) {
2532 docu->content = docu->schema->topgrammar->start;
2533 }
2534
2535 /*
2536 * the externalRef may be reused in a different ns context
2537 */
2538 if (newNs == 1) {
2539 xmlUnsetProp(root, BAD_CAST "ns");
2540 }
2541 }
2542 def->content = docu->content;
2543 } else {
2544 def = NULL;
2545 }
2546 return(def);
2547}
2548
2549/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00002550 * xmlRelaxNGParsePattern:
2551 * @ctxt: a Relax-NG parser context
2552 * @node: the pattern node.
2553 *
2554 * parse the content of a RelaxNG pattern node.
2555 *
Daniel Veillard276be4a2003-01-24 01:03:34 +00002556 * Returns the definition pointer or NULL in case of error or if no
2557 * pattern is generated.
Daniel Veillard6eadf632003-01-23 18:29:16 +00002558 */
2559static xmlRelaxNGDefinePtr
2560xmlRelaxNGParsePattern(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2561 xmlRelaxNGDefinePtr def = NULL;
2562
Daniel Veillardd2298792003-02-14 16:54:11 +00002563 if (node == NULL) {
2564 return(NULL);
2565 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002566 if (IS_RELAXNG(node, "element")) {
2567 def = xmlRelaxNGParseElement(ctxt, node);
2568 } else if (IS_RELAXNG(node, "attribute")) {
2569 def = xmlRelaxNGParseAttribute(ctxt, node);
2570 } else if (IS_RELAXNG(node, "empty")) {
2571 def = xmlRelaxNGNewDefine(ctxt, node);
2572 if (def == NULL)
2573 return(NULL);
2574 def->type = XML_RELAXNG_EMPTY;
Daniel Veillardd2298792003-02-14 16:54:11 +00002575 if (node->children != NULL) {
2576 if (ctxt->error != NULL)
2577 ctxt->error(ctxt->userData, "empty: had a child node\n");
2578 ctxt->nbErrors++;
2579 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002580 } else if (IS_RELAXNG(node, "text")) {
2581 def = xmlRelaxNGNewDefine(ctxt, node);
2582 if (def == NULL)
2583 return(NULL);
2584 def->type = XML_RELAXNG_TEXT;
2585 if (node->children != NULL) {
2586 if (ctxt->error != NULL)
2587 ctxt->error(ctxt->userData, "text: had a child node\n");
2588 ctxt->nbErrors++;
2589 }
2590 } else if (IS_RELAXNG(node, "zeroOrMore")) {
2591 def = xmlRelaxNGNewDefine(ctxt, node);
2592 if (def == NULL)
2593 return(NULL);
2594 def->type = XML_RELAXNG_ZEROORMORE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002595 if (node->children == NULL) {
2596 if (ctxt->error != NULL)
2597 ctxt->error(ctxt->userData,
2598 "Element %s is empty\n", node->name);
2599 ctxt->nbErrors++;
2600 } else {
2601 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2602 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002603 } else if (IS_RELAXNG(node, "oneOrMore")) {
2604 def = xmlRelaxNGNewDefine(ctxt, node);
2605 if (def == NULL)
2606 return(NULL);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002607 def->type = XML_RELAXNG_ONEORMORE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002608 if (node->children == NULL) {
2609 if (ctxt->error != NULL)
2610 ctxt->error(ctxt->userData,
2611 "Element %s is empty\n", node->name);
2612 ctxt->nbErrors++;
2613 } else {
2614 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2615 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002616 } else if (IS_RELAXNG(node, "optional")) {
2617 def = xmlRelaxNGNewDefine(ctxt, node);
2618 if (def == NULL)
2619 return(NULL);
2620 def->type = XML_RELAXNG_OPTIONAL;
Daniel Veillardd2298792003-02-14 16:54:11 +00002621 if (node->children == NULL) {
2622 if (ctxt->error != NULL)
2623 ctxt->error(ctxt->userData,
2624 "Element %s is empty\n", node->name);
2625 ctxt->nbErrors++;
2626 } else {
2627 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2628 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002629 } else if (IS_RELAXNG(node, "choice")) {
2630 def = xmlRelaxNGNewDefine(ctxt, node);
2631 if (def == NULL)
2632 return(NULL);
2633 def->type = XML_RELAXNG_CHOICE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002634 if (node->children == NULL) {
2635 if (ctxt->error != NULL)
2636 ctxt->error(ctxt->userData,
2637 "Element %s is empty\n", node->name);
2638 ctxt->nbErrors++;
2639 } else {
2640 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2641 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002642 } else if (IS_RELAXNG(node, "group")) {
2643 def = xmlRelaxNGNewDefine(ctxt, node);
2644 if (def == NULL)
2645 return(NULL);
2646 def->type = XML_RELAXNG_GROUP;
Daniel Veillardd2298792003-02-14 16:54:11 +00002647 if (node->children == NULL) {
2648 if (ctxt->error != NULL)
2649 ctxt->error(ctxt->userData,
2650 "Element %s is empty\n", node->name);
2651 ctxt->nbErrors++;
2652 } else {
2653 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2654 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002655 } else if (IS_RELAXNG(node, "ref")) {
2656 def = xmlRelaxNGNewDefine(ctxt, node);
2657 if (def == NULL)
2658 return(NULL);
2659 def->type = XML_RELAXNG_REF;
2660 def->name = xmlGetProp(node, BAD_CAST "name");
2661 if (def->name == NULL) {
2662 if (ctxt->error != NULL)
2663 ctxt->error(ctxt->userData,
2664 "ref has no name\n");
2665 ctxt->nbErrors++;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002666 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00002667 xmlRelaxNGNormExtSpace(def->name);
2668 if (xmlValidateNCName(def->name, 0)) {
2669 if (ctxt->error != NULL)
2670 ctxt->error(ctxt->userData,
2671 "ref name '%s' is not an NCName\n",
2672 def->name);
2673 ctxt->nbErrors++;
2674 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002675 }
2676 if (node->children != NULL) {
2677 if (ctxt->error != NULL)
2678 ctxt->error(ctxt->userData,
2679 "ref is not empty\n");
2680 ctxt->nbErrors++;
2681 }
2682 if (ctxt->grammar->refs == NULL)
2683 ctxt->grammar->refs = xmlHashCreate(10);
2684 if (ctxt->grammar->refs == NULL) {
2685 if (ctxt->error != NULL)
2686 ctxt->error(ctxt->userData,
2687 "Could not create references hash\n");
2688 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002689 def = NULL;
2690 } else {
2691 int tmp;
2692
2693 tmp = xmlHashAddEntry(ctxt->grammar->refs, def->name, def);
2694 if (tmp < 0) {
2695 xmlRelaxNGDefinePtr prev;
2696
2697 prev = (xmlRelaxNGDefinePtr)
2698 xmlHashLookup(ctxt->grammar->refs, def->name);
2699 if (prev == NULL) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00002700 if (def->name != NULL) {
2701 if (ctxt->error != NULL)
2702 ctxt->error(ctxt->userData,
2703 "Error refs definitions '%s'\n",
2704 def->name);
2705 } else {
2706 if (ctxt->error != NULL)
2707 ctxt->error(ctxt->userData,
2708 "Error refs definitions\n");
2709 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002710 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002711 def = NULL;
2712 } else {
2713 def->nextHash = prev->nextHash;
2714 prev->nextHash = def;
2715 }
2716 }
2717 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002718 } else if (IS_RELAXNG(node, "data")) {
2719 def = xmlRelaxNGParseData(ctxt, node);
Daniel Veillardd2298792003-02-14 16:54:11 +00002720#if 0
Daniel Veillard276be4a2003-01-24 01:03:34 +00002721 } else if (IS_RELAXNG(node, "define")) {
2722 xmlRelaxNGParseDefine(ctxt, node);
2723 def = NULL;
Daniel Veillardd2298792003-02-14 16:54:11 +00002724#endif
Daniel Veillardedc91922003-01-26 00:52:04 +00002725 } else if (IS_RELAXNG(node, "value")) {
2726 def = xmlRelaxNGParseValue(ctxt, node);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002727 } else if (IS_RELAXNG(node, "list")) {
2728 def = xmlRelaxNGNewDefine(ctxt, node);
2729 if (def == NULL)
2730 return(NULL);
2731 def->type = XML_RELAXNG_LIST;
Daniel Veillardd2298792003-02-14 16:54:11 +00002732 if (node->children == NULL) {
2733 if (ctxt->error != NULL)
2734 ctxt->error(ctxt->userData,
2735 "Element %s is empty\n", node->name);
2736 ctxt->nbErrors++;
2737 } else {
2738 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2739 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002740 } else if (IS_RELAXNG(node, "interleave")) {
2741 def = xmlRelaxNGParseInterleave(ctxt, node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002742 } else if (IS_RELAXNG(node, "externalRef")) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00002743 def = xmlRelaxNGProcessExternalRef(ctxt, node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002744 } else if (IS_RELAXNG(node, "notAllowed")) {
2745 def = xmlRelaxNGNewDefine(ctxt, node);
2746 if (def == NULL)
2747 return(NULL);
2748 def->type = XML_RELAXNG_NOT_ALLOWED;
2749 if (node->children != NULL) {
2750 if (ctxt->error != NULL)
2751 ctxt->error(ctxt->userData,
2752 "xmlRelaxNGParse: notAllowed element is not empty\n");
2753 ctxt->nbErrors++;
2754 }
Daniel Veillard419a7682003-02-03 23:22:49 +00002755 } else if (IS_RELAXNG(node, "grammar")) {
2756 xmlRelaxNGGrammarPtr grammar, old;
2757 xmlRelaxNGGrammarPtr oldparent;
2758
2759 oldparent = ctxt->parentgrammar;
2760 old = ctxt->grammar;
2761 ctxt->parentgrammar = old;
2762 grammar = xmlRelaxNGParseGrammar(ctxt, node->children);
2763 if (old != NULL) {
2764 ctxt->grammar = old;
2765 ctxt->parentgrammar = oldparent;
2766 if (grammar != NULL) {
2767 grammar->next = old->next;
2768 old->next = grammar;
2769 }
2770 }
2771 if (grammar != NULL)
2772 def = grammar->start;
2773 else
2774 def = NULL;
2775 } else if (IS_RELAXNG(node, "parentRef")) {
2776 if (ctxt->parentgrammar == NULL) {
2777 if (ctxt->error != NULL)
2778 ctxt->error(ctxt->userData,
2779 "Use of parentRef without a parent grammar\n");
2780 ctxt->nbErrors++;
2781 return(NULL);
2782 }
2783 def = xmlRelaxNGNewDefine(ctxt, node);
2784 if (def == NULL)
2785 return(NULL);
2786 def->type = XML_RELAXNG_PARENTREF;
2787 def->name = xmlGetProp(node, BAD_CAST "name");
2788 if (def->name == NULL) {
2789 if (ctxt->error != NULL)
2790 ctxt->error(ctxt->userData,
2791 "parentRef has no name\n");
2792 ctxt->nbErrors++;
Daniel Veillardd2298792003-02-14 16:54:11 +00002793 } else {
2794 xmlRelaxNGNormExtSpace(def->name);
2795 if (xmlValidateNCName(def->name, 0)) {
2796 if (ctxt->error != NULL)
2797 ctxt->error(ctxt->userData,
2798 "parentRef name '%s' is not an NCName\n",
2799 def->name);
2800 ctxt->nbErrors++;
2801 }
Daniel Veillard419a7682003-02-03 23:22:49 +00002802 }
2803 if (node->children != NULL) {
2804 if (ctxt->error != NULL)
2805 ctxt->error(ctxt->userData,
2806 "parentRef is not empty\n");
2807 ctxt->nbErrors++;
2808 }
2809 if (ctxt->parentgrammar->refs == NULL)
2810 ctxt->parentgrammar->refs = xmlHashCreate(10);
2811 if (ctxt->parentgrammar->refs == NULL) {
2812 if (ctxt->error != NULL)
2813 ctxt->error(ctxt->userData,
2814 "Could not create references hash\n");
2815 ctxt->nbErrors++;
2816 def = NULL;
Daniel Veillardd2298792003-02-14 16:54:11 +00002817 } else if (def->name != NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +00002818 int tmp;
2819
2820 tmp = xmlHashAddEntry(ctxt->parentgrammar->refs, def->name, def);
2821 if (tmp < 0) {
2822 xmlRelaxNGDefinePtr prev;
2823
2824 prev = (xmlRelaxNGDefinePtr)
2825 xmlHashLookup(ctxt->parentgrammar->refs, def->name);
2826 if (prev == NULL) {
2827 if (ctxt->error != NULL)
2828 ctxt->error(ctxt->userData,
2829 "Internal error parentRef definitions '%s'\n",
2830 def->name);
2831 ctxt->nbErrors++;
2832 def = NULL;
2833 } else {
2834 def->nextHash = prev->nextHash;
2835 prev->nextHash = def;
2836 }
2837 }
2838 }
Daniel Veillardd2298792003-02-14 16:54:11 +00002839 } else if (IS_RELAXNG(node, "mixed")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00002840 if (node->children == NULL) {
2841 if (ctxt->error != NULL)
2842 ctxt->error(ctxt->userData,
2843 "Mixed is empty\n");
2844 ctxt->nbErrors++;
2845 def = NULL;
Daniel Veillard416589a2003-02-17 17:25:42 +00002846 } else {
2847 def = xmlRelaxNGParseInterleave(ctxt, node);
2848 if (def != NULL) {
2849 xmlRelaxNGDefinePtr tmp;
Daniel Veillard2df2de22003-02-17 23:34:33 +00002850
2851 if ((def->content != NULL) && (def->content->next != NULL)) {
2852 tmp = xmlRelaxNGNewDefine(ctxt, node);
2853 if (tmp != NULL) {
2854 tmp->type = XML_RELAXNG_GROUP;
2855 tmp->content = def->content;
2856 def->content = tmp;
2857 }
2858 }
2859
Daniel Veillard416589a2003-02-17 17:25:42 +00002860 tmp = xmlRelaxNGNewDefine(ctxt, node);
2861 if (tmp == NULL)
2862 return(def);
2863 tmp->type = XML_RELAXNG_TEXT;
2864 tmp->next = def->content;
2865 def->content = tmp;
2866 }
2867 }
Daniel Veillardd2298792003-02-14 16:54:11 +00002868 } else {
2869 if (ctxt->error != NULL)
2870 ctxt->error(ctxt->userData,
2871 "Unexpected node %s is not a pattern\n",
2872 node->name);
2873 ctxt->nbErrors++;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002874 def = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002875 }
2876 return(def);
2877}
2878
2879/**
2880 * xmlRelaxNGParseAttribute:
2881 * @ctxt: a Relax-NG parser context
2882 * @node: the element node
2883 *
2884 * parse the content of a RelaxNG attribute node.
2885 *
2886 * Returns the definition pointer or NULL in case of error.
2887 */
2888static xmlRelaxNGDefinePtr
2889xmlRelaxNGParseAttribute(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
Daniel Veillardd2298792003-02-14 16:54:11 +00002890 xmlRelaxNGDefinePtr ret, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002891 xmlNodePtr child;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002892 int old_flags;
2893
2894 ret = xmlRelaxNGNewDefine(ctxt, node);
2895 if (ret == NULL)
2896 return(NULL);
2897 ret->type = XML_RELAXNG_ATTRIBUTE;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002898 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002899 child = node->children;
2900 if (child == NULL) {
2901 if (ctxt->error != NULL)
2902 ctxt->error(ctxt->userData,
2903 "xmlRelaxNGParseattribute: attribute has no children\n");
2904 ctxt->nbErrors++;
2905 return(ret);
2906 }
2907 old_flags = ctxt->flags;
2908 ctxt->flags |= XML_RELAXNG_IN_ATTRIBUTE;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002909 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
2910 if (cur != NULL)
2911 child = child->next;
2912
Daniel Veillardd2298792003-02-14 16:54:11 +00002913 if (child != NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00002914 cur = xmlRelaxNGParsePattern(ctxt, child);
2915 if (cur != NULL) {
2916 switch (cur->type) {
2917 case XML_RELAXNG_EMPTY:
2918 case XML_RELAXNG_NOT_ALLOWED:
2919 case XML_RELAXNG_TEXT:
2920 case XML_RELAXNG_ELEMENT:
2921 case XML_RELAXNG_DATATYPE:
2922 case XML_RELAXNG_VALUE:
2923 case XML_RELAXNG_LIST:
2924 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00002925 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002926 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00002927 case XML_RELAXNG_DEF:
2928 case XML_RELAXNG_ONEORMORE:
2929 case XML_RELAXNG_ZEROORMORE:
2930 case XML_RELAXNG_OPTIONAL:
2931 case XML_RELAXNG_CHOICE:
2932 case XML_RELAXNG_GROUP:
2933 case XML_RELAXNG_INTERLEAVE:
Daniel Veillardd2298792003-02-14 16:54:11 +00002934 ret->content = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002935 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002936 break;
2937 case XML_RELAXNG_ATTRIBUTE:
Daniel Veillardd2298792003-02-14 16:54:11 +00002938 if (ctxt->error != NULL)
2939 ctxt->error(ctxt->userData,
2940 "attribute has an attribute child\n");
2941 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002942 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002943 case XML_RELAXNG_START:
Daniel Veillard8fe98712003-02-19 00:19:14 +00002944 case XML_RELAXNG_PARAM:
Daniel Veillard144fae12003-02-03 13:17:57 +00002945 case XML_RELAXNG_EXCEPT:
Daniel Veillardd2298792003-02-14 16:54:11 +00002946 if (ctxt->error != NULL)
2947 ctxt->error(ctxt->userData,
2948 "attribute has invalid content\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002949 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002950 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002951 }
2952 }
2953 child = child->next;
2954 }
Daniel Veillardd2298792003-02-14 16:54:11 +00002955 if (child != NULL) {
2956 if (ctxt->error != NULL)
2957 ctxt->error(ctxt->userData, "attribute has multiple children\n");
2958 ctxt->nbErrors++;
2959 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002960 ctxt->flags = old_flags;
2961 return(ret);
2962}
2963
2964/**
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002965 * xmlRelaxNGParseExceptNameClass:
2966 * @ctxt: a Relax-NG parser context
2967 * @node: the except node
Daniel Veillard144fae12003-02-03 13:17:57 +00002968 * @attr: 1 if within an attribute, 0 if within an element
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002969 *
2970 * parse the content of a RelaxNG nameClass node.
2971 *
2972 * Returns the definition pointer or NULL in case of error.
2973 */
2974static xmlRelaxNGDefinePtr
Daniel Veillard144fae12003-02-03 13:17:57 +00002975xmlRelaxNGParseExceptNameClass(xmlRelaxNGParserCtxtPtr ctxt,
2976 xmlNodePtr node, int attr) {
2977 xmlRelaxNGDefinePtr ret, cur, last = NULL;
2978 xmlNodePtr child;
2979
Daniel Veillardd2298792003-02-14 16:54:11 +00002980 if (!IS_RELAXNG(node, "except")) {
2981 if (ctxt->error != NULL)
2982 ctxt->error(ctxt->userData,
2983 "Expecting an except node\n");
2984 ctxt->nbErrors++;
Daniel Veillard144fae12003-02-03 13:17:57 +00002985 return(NULL);
Daniel Veillardd2298792003-02-14 16:54:11 +00002986 }
2987 if (node->next != NULL) {
2988 if (ctxt->error != NULL)
2989 ctxt->error(ctxt->userData,
2990 "exceptNameClass allows only a single except node\n");
2991 ctxt->nbErrors++;
2992 }
Daniel Veillard144fae12003-02-03 13:17:57 +00002993 if (node->children == NULL) {
2994 if (ctxt->error != NULL)
2995 ctxt->error(ctxt->userData,
2996 "except has no content\n");
2997 ctxt->nbErrors++;
2998 return(NULL);
2999 }
3000
3001 ret = xmlRelaxNGNewDefine(ctxt, node);
3002 if (ret == NULL)
3003 return(NULL);
3004 ret->type = XML_RELAXNG_EXCEPT;
3005 child = node->children;
3006 while (child != NULL) {
3007 cur = xmlRelaxNGNewDefine(ctxt, child);
3008 if (cur == NULL)
3009 break;
3010 if (attr)
3011 cur->type = XML_RELAXNG_ATTRIBUTE;
3012 else
3013 cur->type = XML_RELAXNG_ELEMENT;
3014
Daniel Veillard419a7682003-02-03 23:22:49 +00003015 if (xmlRelaxNGParseNameClass(ctxt, child, cur) != NULL) {
Daniel Veillard144fae12003-02-03 13:17:57 +00003016 if (last == NULL) {
3017 ret->content = cur;
3018 } else {
3019 last->next = cur;
3020 }
3021 last = cur;
3022 }
3023 child = child->next;
3024 }
3025
3026 return(ret);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003027}
3028
3029/**
3030 * xmlRelaxNGParseNameClass:
3031 * @ctxt: a Relax-NG parser context
3032 * @node: the nameClass node
3033 * @def: the current definition
3034 *
3035 * parse the content of a RelaxNG nameClass node.
3036 *
3037 * Returns the definition pointer or NULL in case of error.
3038 */
3039static xmlRelaxNGDefinePtr
3040xmlRelaxNGParseNameClass(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
3041 xmlRelaxNGDefinePtr def) {
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003042 xmlRelaxNGDefinePtr ret, tmp;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003043 xmlChar *val;
3044
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003045 ret = def;
3046 if ((IS_RELAXNG(node, "name")) || (IS_RELAXNG(node, "anyName")) ||
3047 (IS_RELAXNG(node, "nsName"))) {
3048 if ((def->type != XML_RELAXNG_ELEMENT) &&
3049 (def->type != XML_RELAXNG_ATTRIBUTE)) {
3050 ret = xmlRelaxNGNewDefine(ctxt, node);
3051 if (ret == NULL)
3052 return(NULL);
3053 ret->parent = def;
3054 if (ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE)
3055 ret->type = XML_RELAXNG_ATTRIBUTE;
3056 else
3057 ret->type = XML_RELAXNG_ELEMENT;
3058 }
3059 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003060 if (IS_RELAXNG(node, "name")) {
3061 val = xmlNodeGetContent(node);
Daniel Veillardd2298792003-02-14 16:54:11 +00003062 xmlRelaxNGNormExtSpace(val);
3063 if (xmlValidateNCName(val, 0)) {
3064 if (ctxt->error != NULL) {
3065 if (node->parent != NULL)
3066 ctxt->error(ctxt->userData,
3067 "Element %s name '%s' is not an NCName\n",
3068 node->parent->name, val);
3069 else
3070 ctxt->error(ctxt->userData,
3071 "name '%s' is not an NCName\n",
3072 val);
3073 }
3074 ctxt->nbErrors++;
3075 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003076 ret->name = val;
3077 val = xmlGetProp(node, BAD_CAST "ns");
3078 ret->ns = val;
Daniel Veillard416589a2003-02-17 17:25:42 +00003079 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3080 (val != NULL) &&
3081 (xmlStrEqual(val, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
3082 ctxt->error(ctxt->userData,
3083 "Attribute with namespace '%s' is not allowed\n",
3084 val);
3085 ctxt->nbErrors++;
3086 }
3087 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3088 (val != NULL) &&
3089 (val[0] == 0) &&
3090 (xmlStrEqual(ret->name, BAD_CAST "xmlns"))) {
3091 ctxt->error(ctxt->userData,
3092 "Attribute with QName 'xmlns' is not allowed\n",
3093 val);
3094 ctxt->nbErrors++;
3095 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003096 } else if (IS_RELAXNG(node, "anyName")) {
3097 ret->name = NULL;
3098 ret->ns = NULL;
3099 if (node->children != NULL) {
3100 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00003101 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
3102 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003103 }
3104 } else if (IS_RELAXNG(node, "nsName")) {
3105 ret->name = NULL;
3106 ret->ns = xmlGetProp(node, BAD_CAST "ns");
3107 if (ret->ns == NULL) {
3108 if (ctxt->error != NULL)
3109 ctxt->error(ctxt->userData,
3110 "nsName has no ns attribute\n");
3111 ctxt->nbErrors++;
3112 }
Daniel Veillard416589a2003-02-17 17:25:42 +00003113 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3114 (ret->ns != NULL) &&
3115 (xmlStrEqual(ret->ns, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
3116 ctxt->error(ctxt->userData,
3117 "Attribute with namespace '%s' is not allowed\n",
3118 ret->ns);
3119 ctxt->nbErrors++;
3120 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003121 if (node->children != NULL) {
3122 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00003123 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
3124 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003125 }
3126 } else if (IS_RELAXNG(node, "choice")) {
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003127 xmlNodePtr child;
3128 xmlRelaxNGDefinePtr last = NULL;
3129
Daniel Veillardd4310742003-02-18 21:12:46 +00003130 ret = xmlRelaxNGNewDefine(ctxt, node);
3131 if (ret == NULL)
3132 return(NULL);
3133 ret->parent = def;
3134 ret->type = XML_RELAXNG_CHOICE;
3135
Daniel Veillardd2298792003-02-14 16:54:11 +00003136 if (node->children == NULL) {
3137 if (ctxt->error != NULL)
3138 ctxt->error(ctxt->userData,
3139 "Element choice is empty\n");
3140 ctxt->nbErrors++;
3141 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00003142
3143 child = node->children;
3144 while (child != NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00003145 tmp = xmlRelaxNGParseNameClass(ctxt, child, ret);
Daniel Veillardd2298792003-02-14 16:54:11 +00003146 if (tmp != NULL) {
3147 if (last == NULL) {
3148 last = ret->nameClass = tmp;
3149 } else {
3150 last->next = tmp;
3151 last = tmp;
3152 }
3153 }
3154 child = child->next;
3155 }
3156 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003157 } else {
3158 if (ctxt->error != NULL)
3159 ctxt->error(ctxt->userData,
3160 "expecting name, anyName, nsName or choice : got %s\n",
3161 node->name);
3162 ctxt->nbErrors++;
3163 return(NULL);
3164 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003165 if (ret != def) {
3166 if (def->nameClass == NULL) {
3167 def->nameClass = ret;
3168 } else {
3169 tmp = def->nameClass;
3170 while (tmp->next != NULL) {
3171 tmp = tmp->next;
3172 }
3173 tmp->next = ret;
3174 }
3175 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003176 return(ret);
3177}
3178
3179/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00003180 * xmlRelaxNGParseElement:
3181 * @ctxt: a Relax-NG parser context
3182 * @node: the element node
3183 *
3184 * parse the content of a RelaxNG element node.
3185 *
3186 * Returns the definition pointer or NULL in case of error.
3187 */
3188static xmlRelaxNGDefinePtr
3189xmlRelaxNGParseElement(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3190 xmlRelaxNGDefinePtr ret, cur, last;
3191 xmlNodePtr child;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003192 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003193
3194 ret = xmlRelaxNGNewDefine(ctxt, node);
3195 if (ret == NULL)
3196 return(NULL);
3197 ret->type = XML_RELAXNG_ELEMENT;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003198 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003199 child = node->children;
3200 if (child == NULL) {
3201 if (ctxt->error != NULL)
3202 ctxt->error(ctxt->userData,
3203 "xmlRelaxNGParseElement: element has no children\n");
3204 ctxt->nbErrors++;
3205 return(ret);
3206 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003207 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
3208 if (cur != NULL)
3209 child = child->next;
3210
Daniel Veillard6eadf632003-01-23 18:29:16 +00003211 if (child == NULL) {
3212 if (ctxt->error != NULL)
3213 ctxt->error(ctxt->userData,
3214 "xmlRelaxNGParseElement: element has no content\n");
3215 ctxt->nbErrors++;
3216 return(ret);
3217 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003218 olddefine = ctxt->define;
3219 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003220 last = NULL;
3221 while (child != NULL) {
3222 cur = xmlRelaxNGParsePattern(ctxt, child);
3223 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003224 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003225 switch (cur->type) {
3226 case XML_RELAXNG_EMPTY:
3227 case XML_RELAXNG_NOT_ALLOWED:
3228 case XML_RELAXNG_TEXT:
3229 case XML_RELAXNG_ELEMENT:
3230 case XML_RELAXNG_DATATYPE:
3231 case XML_RELAXNG_VALUE:
3232 case XML_RELAXNG_LIST:
3233 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00003234 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003235 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00003236 case XML_RELAXNG_DEF:
3237 case XML_RELAXNG_ZEROORMORE:
3238 case XML_RELAXNG_ONEORMORE:
3239 case XML_RELAXNG_OPTIONAL:
3240 case XML_RELAXNG_CHOICE:
3241 case XML_RELAXNG_GROUP:
3242 case XML_RELAXNG_INTERLEAVE:
3243 if (last == NULL) {
3244 ret->content = last = cur;
3245 } else {
3246 if ((last->type == XML_RELAXNG_ELEMENT) &&
3247 (ret->content == last)) {
3248 ret->content = xmlRelaxNGNewDefine(ctxt, node);
3249 if (ret->content != NULL) {
3250 ret->content->type = XML_RELAXNG_GROUP;
3251 ret->content->content = last;
3252 } else {
3253 ret->content = last;
3254 }
3255 }
3256 last->next = cur;
3257 last = cur;
3258 }
3259 break;
3260 case XML_RELAXNG_ATTRIBUTE:
3261 cur->next = ret->attrs;
3262 ret->attrs = cur;
3263 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003264 case XML_RELAXNG_START:
Daniel Veillard8fe98712003-02-19 00:19:14 +00003265 case XML_RELAXNG_PARAM:
Daniel Veillard144fae12003-02-03 13:17:57 +00003266 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003267 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003268 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003269 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003270 }
3271 }
3272 child = child->next;
3273 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003274 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003275 return(ret);
3276}
3277
3278/**
3279 * xmlRelaxNGParsePatterns:
3280 * @ctxt: a Relax-NG parser context
3281 * @nodes: list of nodes
Daniel Veillard154877e2003-01-30 12:17:05 +00003282 * @group: use an implicit <group> for elements
Daniel Veillard6eadf632003-01-23 18:29:16 +00003283 *
3284 * parse the content of a RelaxNG start node.
3285 *
3286 * Returns the definition pointer or NULL in case of error.
3287 */
3288static xmlRelaxNGDefinePtr
Daniel Veillard154877e2003-01-30 12:17:05 +00003289xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes,
3290 int group) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003291 xmlRelaxNGDefinePtr def = NULL, last = NULL, cur, parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003292
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003293 parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003294 while (nodes != NULL) {
3295 if (IS_RELAXNG(nodes, "element")) {
3296 cur = xmlRelaxNGParseElement(ctxt, nodes);
3297 if (def == NULL) {
3298 def = last = cur;
3299 } else {
Daniel Veillard154877e2003-01-30 12:17:05 +00003300 if ((group == 1) && (def->type == XML_RELAXNG_ELEMENT) &&
3301 (def == last)) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003302 def = xmlRelaxNGNewDefine(ctxt, nodes);
3303 def->type = XML_RELAXNG_GROUP;
3304 def->content = last;
3305 }
3306 last->next = cur;
3307 last = cur;
3308 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003309 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003310 } else {
3311 cur = xmlRelaxNGParsePattern(ctxt, nodes);
Daniel Veillard419a7682003-02-03 23:22:49 +00003312 if (cur != NULL) {
3313 if (def == NULL) {
3314 def = last = cur;
3315 } else {
3316 last->next = cur;
3317 last = cur;
3318 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003319 }
3320 }
3321 nodes = nodes->next;
3322 }
3323 return(def);
3324}
3325
3326/**
3327 * xmlRelaxNGParseStart:
3328 * @ctxt: a Relax-NG parser context
3329 * @nodes: start children nodes
3330 *
3331 * parse the content of a RelaxNG start node.
3332 *
3333 * Returns 0 in case of success, -1 in case of error
3334 */
3335static int
3336xmlRelaxNGParseStart(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
3337 int ret = 0;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003338 xmlRelaxNGDefinePtr def = NULL, last;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003339
Daniel Veillardd2298792003-02-14 16:54:11 +00003340 if (nodes == NULL) {
3341 if (ctxt->error != NULL)
3342 ctxt->error(ctxt->userData,
3343 "start has no children\n");
3344 ctxt->nbErrors++;
3345 return(-1);
3346 }
3347 if (IS_RELAXNG(nodes, "empty")) {
3348 def = xmlRelaxNGNewDefine(ctxt, nodes);
3349 if (def == NULL)
3350 return(-1);
3351 def->type = XML_RELAXNG_EMPTY;
3352 if (nodes->children != NULL) {
3353 if (ctxt->error != NULL)
3354 ctxt->error(ctxt->userData, "element empty is not empty\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003355 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003356 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003357 } else if (IS_RELAXNG(nodes, "notAllowed")) {
3358 def = xmlRelaxNGNewDefine(ctxt, nodes);
3359 if (def == NULL)
3360 return(-1);
3361 def->type = XML_RELAXNG_NOT_ALLOWED;
3362 if (nodes->children != NULL) {
3363 if (ctxt->error != NULL)
3364 ctxt->error(ctxt->userData,
3365 "element notAllowed is not empty\n");
3366 ctxt->nbErrors++;
3367 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003368 } else {
3369 def = xmlRelaxNGParsePatterns(ctxt, nodes, 1);
Daniel Veillard2df2de22003-02-17 23:34:33 +00003370 }
3371 if (ctxt->grammar->start != NULL) {
3372 last = ctxt->grammar->start;
3373 while (last->next != NULL)
3374 last = last->next;
3375 last->next = def;
3376 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00003377 ctxt->grammar->start = def;
3378 }
3379 nodes = nodes->next;
3380 if (nodes != NULL) {
3381 if (ctxt->error != NULL)
3382 ctxt->error(ctxt->userData,
3383 "start more than one children\n");
3384 ctxt->nbErrors++;
3385 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003386 }
3387 return(ret);
3388}
3389
3390/**
3391 * xmlRelaxNGParseGrammarContent:
3392 * @ctxt: a Relax-NG parser context
3393 * @nodes: grammar children nodes
3394 *
3395 * parse the content of a RelaxNG grammar node.
3396 *
3397 * Returns 0 in case of success, -1 in case of error
3398 */
3399static int
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003400xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003401{
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003402 int ret = 0, tmp;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003403
3404 if (nodes == NULL) {
3405 if (ctxt->error != NULL)
3406 ctxt->error(ctxt->userData,
3407 "grammar has no children\n");
3408 ctxt->nbErrors++;
3409 return(-1);
3410 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003411 while (nodes != NULL) {
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003412 if (IS_RELAXNG(nodes, "start")) {
3413 if (nodes->children == NULL) {
3414 if (ctxt->error != NULL)
3415 ctxt->error(ctxt->userData,
Daniel Veillardd2298792003-02-14 16:54:11 +00003416 "start has no children\n");
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003417 ctxt->nbErrors++;
3418 } else {
3419 tmp = xmlRelaxNGParseStart(ctxt, nodes->children);
3420 if (tmp != 0)
3421 ret = -1;
3422 }
3423 } else if (IS_RELAXNG(nodes, "define")) {
3424 tmp = xmlRelaxNGParseDefine(ctxt, nodes);
3425 if (tmp != 0)
3426 ret = -1;
3427 } else if (IS_RELAXNG(nodes, "include")) {
3428 tmp = xmlRelaxNGParseInclude(ctxt, nodes);
3429 if (tmp != 0)
3430 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003431 } else {
3432 if (ctxt->error != NULL)
3433 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003434 "grammar has unexpected child %s\n", nodes->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003435 ctxt->nbErrors++;
3436 ret = -1;
3437 }
3438 nodes = nodes->next;
3439 }
3440 return (ret);
3441}
3442
3443/**
3444 * xmlRelaxNGCheckReference:
3445 * @ref: the ref
3446 * @ctxt: a Relax-NG parser context
3447 * @name: the name associated to the defines
3448 *
3449 * Applies the 4.17. combine attribute rule for all the define
3450 * element of a given grammar using the same name.
3451 */
3452static void
3453xmlRelaxNGCheckReference(xmlRelaxNGDefinePtr ref,
3454 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
3455 xmlRelaxNGGrammarPtr grammar;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003456 xmlRelaxNGDefinePtr def, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003457
3458 grammar = ctxt->grammar;
3459 if (grammar == NULL) {
3460 if (ctxt->error != NULL)
3461 ctxt->error(ctxt->userData,
3462 "Internal error: no grammar in CheckReference %s\n",
3463 name);
3464 ctxt->nbErrors++;
3465 return;
3466 }
3467 if (ref->content != NULL) {
3468 if (ctxt->error != NULL)
3469 ctxt->error(ctxt->userData,
3470 "Internal error: reference has content in CheckReference %s\n",
3471 name);
3472 ctxt->nbErrors++;
3473 return;
3474 }
3475 if (grammar->defs != NULL) {
3476 def = xmlHashLookup(grammar->defs, name);
3477 if (def != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00003478 cur = ref;
3479 while (cur != NULL) {
3480 cur->content = def;
3481 cur = cur->nextHash;
3482 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003483 } else {
Daniel Veillardd4310742003-02-18 21:12:46 +00003484 if (ctxt->error != NULL)
3485 ctxt->error(ctxt->userData,
3486 "Reference %s has no matching definition\n",
3487 name);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003488 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003489 }
Daniel Veillardd4310742003-02-18 21:12:46 +00003490 } else {
3491 if (ctxt->error != NULL)
3492 ctxt->error(ctxt->userData,
3493 "Reference %s has no matching definition\n",
3494 name);
3495 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003496 }
3497 /*
3498 * TODO: make a closure and verify there is no loop !
3499 */
3500}
3501
3502/**
3503 * xmlRelaxNGCheckCombine:
3504 * @define: the define(s) list
3505 * @ctxt: a Relax-NG parser context
3506 * @name: the name associated to the defines
3507 *
3508 * Applies the 4.17. combine attribute rule for all the define
3509 * element of a given grammar using the same name.
3510 */
3511static void
3512xmlRelaxNGCheckCombine(xmlRelaxNGDefinePtr define,
3513 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
3514 xmlChar *combine;
3515 int choiceOrInterleave = -1;
3516 int missing = 0;
3517 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
3518
3519 if (define->nextHash == NULL)
3520 return;
3521 cur = define;
3522 while (cur != NULL) {
3523 combine = xmlGetProp(cur->node, BAD_CAST "combine");
3524 if (combine != NULL) {
3525 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3526 if (choiceOrInterleave == -1)
3527 choiceOrInterleave = 1;
3528 else if (choiceOrInterleave == 0) {
3529 if (ctxt->error != NULL)
3530 ctxt->error(ctxt->userData,
3531 "Defines for %s use both 'choice' and 'interleave'\n",
3532 name);
3533 ctxt->nbErrors++;
3534 }
Daniel Veillard154877e2003-01-30 12:17:05 +00003535 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003536 if (choiceOrInterleave == -1)
3537 choiceOrInterleave = 0;
3538 else if (choiceOrInterleave == 1) {
3539 if (ctxt->error != NULL)
3540 ctxt->error(ctxt->userData,
3541 "Defines for %s use both 'choice' and 'interleave'\n",
3542 name);
3543 ctxt->nbErrors++;
3544 }
3545 } else {
3546 if (ctxt->error != NULL)
3547 ctxt->error(ctxt->userData,
3548 "Defines for %s use unknown combine value '%s''\n",
3549 name, combine);
3550 ctxt->nbErrors++;
3551 }
3552 xmlFree(combine);
3553 } else {
3554 if (missing == 0)
3555 missing = 1;
3556 else {
3557 if (ctxt->error != NULL)
3558 ctxt->error(ctxt->userData,
3559 "Some defines for %s lacks the combine attribute\n",
3560 name);
3561 ctxt->nbErrors++;
3562 }
3563 }
3564
3565 cur = cur->nextHash;
3566 }
3567#ifdef DEBUG
3568 xmlGenericError(xmlGenericErrorContext,
3569 "xmlRelaxNGCheckCombine(): merging %s defines: %d\n",
3570 name, choiceOrInterleave);
3571#endif
3572 if (choiceOrInterleave == -1)
3573 choiceOrInterleave = 0;
3574 cur = xmlRelaxNGNewDefine(ctxt, define->node);
3575 if (cur == NULL)
3576 return;
3577 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003578 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard154877e2003-01-30 12:17:05 +00003579 else
3580 cur->type = XML_RELAXNG_CHOICE;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003581 tmp = define;
3582 last = NULL;
3583 while (tmp != NULL) {
3584 if (tmp->content != NULL) {
3585 if (tmp->content->next != NULL) {
3586 /*
3587 * we need first to create a wrapper.
3588 */
3589 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
3590 if (tmp2 == NULL)
3591 break;
3592 tmp2->type = XML_RELAXNG_GROUP;
3593 tmp2->content = tmp->content;
3594 } else {
3595 tmp2 = tmp->content;
3596 }
3597 if (last == NULL) {
3598 cur->content = tmp2;
3599 } else {
3600 last->next = tmp2;
3601 }
3602 last = tmp2;
3603 tmp->content = NULL;
3604 }
3605 tmp = tmp->nextHash;
3606 }
3607 define->content = cur;
Daniel Veillard154877e2003-01-30 12:17:05 +00003608 if (choiceOrInterleave == 0) {
3609 if (ctxt->interleaves == NULL)
3610 ctxt->interleaves = xmlHashCreate(10);
3611 if (ctxt->interleaves == NULL) {
3612 if (ctxt->error != NULL)
3613 ctxt->error(ctxt->userData,
3614 "Failed to create interleaves hash table\n");
3615 ctxt->nbErrors++;
3616 } else {
3617 char tmpname[32];
3618
3619 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
3620 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
3621 if (ctxt->error != NULL)
3622 ctxt->error(ctxt->userData,
3623 "Failed to add %s to hash table\n", tmpname);
3624 ctxt->nbErrors++;
3625 }
3626 }
3627 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003628}
3629
3630/**
3631 * xmlRelaxNGCombineStart:
3632 * @ctxt: a Relax-NG parser context
3633 * @grammar: the grammar
3634 *
3635 * Applies the 4.17. combine rule for all the start
3636 * element of a given grammar.
3637 */
3638static void
3639xmlRelaxNGCombineStart(xmlRelaxNGParserCtxtPtr ctxt,
3640 xmlRelaxNGGrammarPtr grammar) {
3641 xmlRelaxNGDefinePtr starts;
3642 xmlChar *combine;
3643 int choiceOrInterleave = -1;
3644 int missing = 0;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003645 xmlRelaxNGDefinePtr cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003646
Daniel Veillard2df2de22003-02-17 23:34:33 +00003647 starts = grammar->start;
3648 if ((starts == NULL) || (starts->next == NULL))
Daniel Veillard6eadf632003-01-23 18:29:16 +00003649 return;
3650 cur = starts;
3651 while (cur != NULL) {
Daniel Veillard2df2de22003-02-17 23:34:33 +00003652 if ((cur->node == NULL) || (cur->node->parent == NULL) ||
3653 (!xmlStrEqual(cur->node->parent->name, BAD_CAST "start"))) {
3654 combine = NULL;
3655 if (ctxt->error != NULL)
3656 ctxt->error(ctxt->userData,
3657 "Internal error: start element not found\n");
3658 ctxt->nbErrors++;
3659 } else {
3660 combine = xmlGetProp(cur->node->parent, BAD_CAST "combine");
3661 }
3662
Daniel Veillard6eadf632003-01-23 18:29:16 +00003663 if (combine != NULL) {
3664 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3665 if (choiceOrInterleave == -1)
3666 choiceOrInterleave = 1;
3667 else if (choiceOrInterleave == 0) {
3668 if (ctxt->error != NULL)
3669 ctxt->error(ctxt->userData,
3670 "<start> use both 'choice' and 'interleave'\n");
3671 ctxt->nbErrors++;
3672 }
Daniel Veillard2df2de22003-02-17 23:34:33 +00003673 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003674 if (choiceOrInterleave == -1)
3675 choiceOrInterleave = 0;
3676 else if (choiceOrInterleave == 1) {
3677 if (ctxt->error != NULL)
3678 ctxt->error(ctxt->userData,
3679 "<start> use both 'choice' and 'interleave'\n");
3680 ctxt->nbErrors++;
3681 }
3682 } else {
3683 if (ctxt->error != NULL)
3684 ctxt->error(ctxt->userData,
3685 "<start> uses unknown combine value '%s''\n", combine);
3686 ctxt->nbErrors++;
3687 }
3688 xmlFree(combine);
3689 } else {
3690 if (missing == 0)
3691 missing = 1;
3692 else {
3693 if (ctxt->error != NULL)
3694 ctxt->error(ctxt->userData,
3695 "Some <start> elements lacks the combine attribute\n");
3696 ctxt->nbErrors++;
3697 }
3698 }
3699
Daniel Veillard2df2de22003-02-17 23:34:33 +00003700 cur = cur->next;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003701 }
3702#ifdef DEBUG
3703 xmlGenericError(xmlGenericErrorContext,
3704 "xmlRelaxNGCombineStart(): merging <start>: %d\n",
3705 choiceOrInterleave);
3706#endif
3707 if (choiceOrInterleave == -1)
3708 choiceOrInterleave = 0;
3709 cur = xmlRelaxNGNewDefine(ctxt, starts->node);
3710 if (cur == NULL)
3711 return;
3712 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003713 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003714 else
3715 cur->type = XML_RELAXNG_CHOICE;
3716 cur->content = grammar->start;
3717 grammar->start = cur;
3718 if (choiceOrInterleave == 0) {
3719 if (ctxt->interleaves == NULL)
3720 ctxt->interleaves = xmlHashCreate(10);
3721 if (ctxt->interleaves == NULL) {
3722 if (ctxt->error != NULL)
3723 ctxt->error(ctxt->userData,
3724 "Failed to create interleaves hash table\n");
3725 ctxt->nbErrors++;
3726 } else {
3727 char tmpname[32];
3728
3729 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
3730 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
3731 if (ctxt->error != NULL)
3732 ctxt->error(ctxt->userData,
3733 "Failed to add %s to hash table\n", tmpname);
3734 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003735 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003736 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003737 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003738}
3739
3740/**
Daniel Veillardd4310742003-02-18 21:12:46 +00003741 * xmlRelaxNGCheckCycles:
3742 * @ctxt: a Relax-NG parser context
3743 * @nodes: grammar children nodes
3744 * @depth: the counter
3745 *
3746 * Check for cycles.
3747 *
3748 * Returns 0 if check passed, and -1 in case of error
3749 */
3750static int
3751xmlRelaxNGCheckCycles(xmlRelaxNGParserCtxtPtr ctxt,
3752 xmlRelaxNGDefinePtr cur, int depth) {
3753 int ret = 0;
3754
3755 while ((ret == 0) && (cur != NULL)) {
3756 if ((cur->type == XML_RELAXNG_REF) ||
3757 (cur->type == XML_RELAXNG_PARENTREF)) {
3758 if (cur->depth == -1) {
3759 cur->depth = depth;
3760 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth);
3761 cur->depth = -2;
3762 } else if (depth == cur->depth) {
3763 if (ctxt->error != NULL)
3764 ctxt->error(ctxt->userData,
3765 "Detected a cycle in %s references\n", cur->name);
3766 ctxt->nbErrors++;
3767 return(-1);
3768 }
3769 } else if (cur->type == XML_RELAXNG_ELEMENT) {
3770 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth + 1);
3771 } else {
3772 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth);
3773 }
3774 cur = cur->next;
3775 }
3776 return(ret);
3777}
3778
3779/**
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003780 * xmlRelaxNGCheckRules:
3781 * @ctxt: a Relax-NG parser context
3782 * @nodes: grammar children nodes
3783 *
3784 * Check for simplification of empty and notAllowed
3785 */
3786static void
3787xmlRelaxNGSimplify(xmlRelaxNGParserCtxtPtr ctxt,
3788 xmlRelaxNGDefinePtr cur,
3789 xmlRelaxNGDefinePtr parent) {
3790 xmlRelaxNGDefinePtr prev = NULL;
3791
3792 while (cur != NULL) {
3793 if ((cur->type == XML_RELAXNG_REF) ||
3794 (cur->type == XML_RELAXNG_PARENTREF)) {
3795 if (cur->depth != -3) {
3796 cur->depth = -3;
3797 xmlRelaxNGSimplify(ctxt, cur->content, cur);
3798 }
3799 } else if (cur->type == XML_RELAXNG_NOT_ALLOWED) {
3800 if ((parent != NULL) &&
3801 ((parent->type == XML_RELAXNG_ATTRIBUTE) ||
3802 (parent->type == XML_RELAXNG_LIST) ||
3803 (parent->type == XML_RELAXNG_GROUP) ||
3804 (parent->type == XML_RELAXNG_INTERLEAVE) ||
3805 (parent->type == XML_RELAXNG_ONEORMORE) ||
3806 (parent->type == XML_RELAXNG_ZEROORMORE))) {
3807 parent->type = XML_RELAXNG_NOT_ALLOWED;
3808 break;
3809 }
3810 if ((parent != NULL) &&
3811 (parent->type == XML_RELAXNG_CHOICE)) {
3812 if (prev == NULL) {
3813 if (parent != NULL)
3814 parent->content = cur->next;
3815 } else
3816 prev->next = cur->next;
3817 } else
3818 prev = cur;
3819 } else if (cur->type == XML_RELAXNG_EMPTY){
3820 if ((parent != NULL) &&
3821 ((parent->type == XML_RELAXNG_ONEORMORE) ||
3822 (parent->type == XML_RELAXNG_ZEROORMORE))) {
3823 parent->type = XML_RELAXNG_EMPTY;
3824 break;
3825 }
3826 if ((parent != NULL) &&
3827 ((parent->type == XML_RELAXNG_GROUP) ||
3828 (parent->type == XML_RELAXNG_INTERLEAVE))) {
3829 if (prev == NULL) {
3830 if (parent != NULL)
3831 parent->content = cur->next;
3832 } else
3833 prev->next = cur->next;
3834 } else
3835 prev = cur;
3836 } else {
3837 if (cur->content != NULL)
3838 xmlRelaxNGSimplify(ctxt, cur->content, cur);
3839 /*
3840 * This may result in a simplification
3841 */
3842 if ((cur->type == XML_RELAXNG_GROUP) ||
3843 (cur->type == XML_RELAXNG_INTERLEAVE)) {
3844 if (cur->content == NULL)
3845 cur->type = XML_RELAXNG_EMPTY;
3846 else if (cur->content->next == NULL) {
3847 cur->content->next = cur->next;
3848 if (prev == NULL) {
3849 if (parent != NULL)
3850 parent->content = cur->content;
3851 } else {
3852 prev->next = cur->content;
3853 }
3854 cur = cur->content;
3855 }
3856 }
3857 /*
3858 * the current node may have been transformed back
3859 */
3860 if ((cur->type == XML_RELAXNG_EXCEPT) &&
3861 (cur->content != NULL) &&
3862 (cur->content->type == XML_RELAXNG_NOT_ALLOWED)) {
3863 if (prev == NULL) {
3864 if (parent != NULL)
3865 parent->content = cur->next;
3866 } else
3867 prev->next = cur->next;
3868 } else if (cur->type == XML_RELAXNG_NOT_ALLOWED) {
3869 if ((parent != NULL) &&
3870 ((parent->type == XML_RELAXNG_ATTRIBUTE) ||
3871 (parent->type == XML_RELAXNG_LIST) ||
3872 (parent->type == XML_RELAXNG_GROUP) ||
3873 (parent->type == XML_RELAXNG_INTERLEAVE) ||
3874 (parent->type == XML_RELAXNG_ONEORMORE) ||
3875 (parent->type == XML_RELAXNG_ZEROORMORE))) {
3876 parent->type = XML_RELAXNG_NOT_ALLOWED;
3877 break;
3878 }
3879 if ((parent != NULL) &&
3880 (parent->type == XML_RELAXNG_CHOICE)) {
3881 if (prev == NULL) {
3882 if (parent != NULL)
3883 parent->content = cur->next;
3884 } else
3885 prev->next = cur->next;
3886 } else
3887 prev = cur;
3888 } else if (cur->type == XML_RELAXNG_EMPTY){
3889 if ((parent != NULL) &&
3890 ((parent->type == XML_RELAXNG_ONEORMORE) ||
3891 (parent->type == XML_RELAXNG_ZEROORMORE))) {
3892 parent->type = XML_RELAXNG_EMPTY;
3893 break;
3894 }
3895 if ((parent != NULL) &&
3896 ((parent->type == XML_RELAXNG_GROUP) ||
3897 (parent->type == XML_RELAXNG_INTERLEAVE) ||
3898 (parent->type == XML_RELAXNG_CHOICE))) {
3899 if (prev == NULL) {
3900 if (parent != NULL)
3901 parent->content = cur->next;
3902 } else
3903 prev->next = cur->next;
3904 } else
3905 prev = cur;
3906 } else {
3907 prev = cur;
3908 }
3909 }
3910 cur = cur->next;
3911 }
3912}
3913
3914#if 0
3915/**
3916 * xmlRelaxNGCheckRules:
3917 * @ctxt: a Relax-NG parser context
3918 * @nodes: grammar children nodes
3919 * @state: a state
3920 *
3921 * Check for rules in
3922 *
3923 * Returns 0 if check passed, and -1 in case of error
3924 */
3925static int
3926xmlRelaxNGCheckRules(xmlRelaxNGParserCtxtPtr ctxt,
3927 xmlRelaxNGDefinePtr cur, int depth) {
3928 int ret = 0;
3929
3930 while ((ret == 0) && (cur != NULL)) {
3931 if ((cur->type == XML_RELAXNG_REF) ||
3932 (cur->type == XML_RELAXNG_PARENTREF)) {
3933 if (cur->depth == -1) {
3934 cur->depth = depth;
3935 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth);
3936 cur->depth = -2;
3937 } else if (depth == cur->depth) {
3938 if (ctxt->error != NULL)
3939 ctxt->error(ctxt->userData,
3940 "Detected a cycle in %s references\n", cur->name);
3941 ctxt->nbErrors++;
3942 return(-1);
3943 }
3944 } else if (cur->type == XML_RELAXNG_ELEMENT) {
3945 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth + 1);
3946 } else {
3947 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth);
3948 }
3949 cur = cur->next;
3950 }
3951 return(ret);
3952}
3953#endif
3954
3955/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00003956 * xmlRelaxNGParseGrammar:
3957 * @ctxt: a Relax-NG parser context
3958 * @nodes: grammar children nodes
3959 *
3960 * parse a Relax-NG <grammar> node
3961 *
3962 * Returns the internal xmlRelaxNGGrammarPtr built or
3963 * NULL in case of error
3964 */
3965static xmlRelaxNGGrammarPtr
3966xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
3967 xmlRelaxNGGrammarPtr ret, tmp, old;
3968
Daniel Veillard6eadf632003-01-23 18:29:16 +00003969 ret = xmlRelaxNGNewGrammar(ctxt);
3970 if (ret == NULL)
3971 return(NULL);
3972
3973 /*
3974 * Link the new grammar in the tree
3975 */
3976 ret->parent = ctxt->grammar;
3977 if (ctxt->grammar != NULL) {
3978 tmp = ctxt->grammar->children;
3979 if (tmp == NULL) {
3980 ctxt->grammar->children = ret;
3981 } else {
3982 while (tmp->next != NULL)
3983 tmp = tmp->next;
3984 tmp->next = ret;
3985 }
3986 }
3987
3988 old = ctxt->grammar;
3989 ctxt->grammar = ret;
3990 xmlRelaxNGParseGrammarContent(ctxt, nodes);
3991 ctxt->grammar = ret;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003992 if (ctxt->grammar == NULL) {
3993 if (ctxt->error != NULL)
3994 ctxt->error(ctxt->userData,
3995 "Failed to parse <grammar> content\n");
3996 ctxt->nbErrors++;
3997 } else if (ctxt->grammar->start == NULL) {
3998 if (ctxt->error != NULL)
3999 ctxt->error(ctxt->userData,
4000 "Element <grammar> has no <start>\n");
4001 ctxt->nbErrors++;
4002 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004003
4004 /*
4005 * Apply 4.17 mergingd rules to defines and starts
4006 */
4007 xmlRelaxNGCombineStart(ctxt, ret);
4008 if (ret->defs != NULL) {
4009 xmlHashScan(ret->defs, (xmlHashScanner) xmlRelaxNGCheckCombine,
4010 ctxt);
4011 }
4012
4013 /*
4014 * link together defines and refs in this grammar
4015 */
4016 if (ret->refs != NULL) {
4017 xmlHashScan(ret->refs, (xmlHashScanner) xmlRelaxNGCheckReference,
4018 ctxt);
4019 }
4020 ctxt->grammar = old;
4021 return(ret);
4022}
4023
4024/**
4025 * xmlRelaxNGParseDocument:
4026 * @ctxt: a Relax-NG parser context
4027 * @node: the root node of the RelaxNG schema
4028 *
4029 * parse a Relax-NG definition resource and build an internal
4030 * xmlRelaxNG struture which can be used to validate instances.
4031 *
4032 * Returns the internal XML RelaxNG structure built or
4033 * NULL in case of error
4034 */
4035static xmlRelaxNGPtr
4036xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
4037 xmlRelaxNGPtr schema = NULL;
Daniel Veillard276be4a2003-01-24 01:03:34 +00004038 const xmlChar *olddefine;
Daniel Veillarde431a272003-01-29 23:02:33 +00004039 xmlRelaxNGGrammarPtr old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004040
4041 if ((ctxt == NULL) || (node == NULL))
4042 return (NULL);
4043
4044 schema = xmlRelaxNGNewRelaxNG(ctxt);
4045 if (schema == NULL)
4046 return(NULL);
4047
Daniel Veillard276be4a2003-01-24 01:03:34 +00004048 olddefine = ctxt->define;
4049 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004050 if (IS_RELAXNG(node, "grammar")) {
4051 schema->topgrammar = xmlRelaxNGParseGrammar(ctxt, node->children);
4052 } else {
4053 schema->topgrammar = xmlRelaxNGNewGrammar(ctxt);
4054 if (schema->topgrammar == NULL) {
4055 return(schema);
4056 }
4057 schema->topgrammar->parent = NULL;
Daniel Veillarde431a272003-01-29 23:02:33 +00004058 old = ctxt->grammar;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004059 ctxt->grammar = schema->topgrammar;
4060 xmlRelaxNGParseStart(ctxt, node);
Daniel Veillarde431a272003-01-29 23:02:33 +00004061 if (old != NULL)
4062 ctxt->grammar = old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004063 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00004064 ctxt->define = olddefine;
Daniel Veillardd4310742003-02-18 21:12:46 +00004065 if (schema->topgrammar->start != NULL) {
4066 xmlRelaxNGCheckCycles(ctxt, schema->topgrammar->start, 0);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004067 xmlRelaxNGSimplify(ctxt, schema->topgrammar->start, NULL);
Daniel Veillardd4310742003-02-18 21:12:46 +00004068 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004069
4070#ifdef DEBUG
4071 if (schema == NULL)
4072 xmlGenericError(xmlGenericErrorContext,
4073 "xmlRelaxNGParseDocument() failed\n");
4074#endif
4075
4076 return (schema);
4077}
4078
4079/************************************************************************
4080 * *
4081 * Reading RelaxNGs *
4082 * *
4083 ************************************************************************/
4084
4085/**
4086 * xmlRelaxNGNewParserCtxt:
4087 * @URL: the location of the schema
4088 *
4089 * Create an XML RelaxNGs parse context for that file/resource expected
4090 * to contain an XML RelaxNGs file.
4091 *
4092 * Returns the parser context or NULL in case of error
4093 */
4094xmlRelaxNGParserCtxtPtr
4095xmlRelaxNGNewParserCtxt(const char *URL) {
4096 xmlRelaxNGParserCtxtPtr ret;
4097
4098 if (URL == NULL)
4099 return(NULL);
4100
4101 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
4102 if (ret == NULL) {
4103 xmlGenericError(xmlGenericErrorContext,
4104 "Failed to allocate new schama parser context for %s\n", URL);
4105 return (NULL);
4106 }
4107 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
4108 ret->URL = xmlStrdup((const xmlChar *)URL);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004109 ret->error = xmlGenericError;
4110 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004111 return (ret);
4112}
4113
4114/**
4115 * xmlRelaxNGNewMemParserCtxt:
4116 * @buffer: a pointer to a char array containing the schemas
4117 * @size: the size of the array
4118 *
4119 * Create an XML RelaxNGs parse context for that memory buffer expected
4120 * to contain an XML RelaxNGs file.
4121 *
4122 * Returns the parser context or NULL in case of error
4123 */
4124xmlRelaxNGParserCtxtPtr
4125xmlRelaxNGNewMemParserCtxt(const char *buffer, int size) {
4126 xmlRelaxNGParserCtxtPtr ret;
4127
4128 if ((buffer == NULL) || (size <= 0))
4129 return(NULL);
4130
4131 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
4132 if (ret == NULL) {
4133 xmlGenericError(xmlGenericErrorContext,
4134 "Failed to allocate new schama parser context\n");
4135 return (NULL);
4136 }
4137 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
4138 ret->buffer = buffer;
4139 ret->size = size;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004140 ret->error = xmlGenericError;
4141 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004142 return (ret);
4143}
4144
4145/**
4146 * xmlRelaxNGFreeParserCtxt:
4147 * @ctxt: the schema parser context
4148 *
4149 * Free the resources associated to the schema parser context
4150 */
4151void
4152xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxtPtr ctxt) {
4153 if (ctxt == NULL)
4154 return;
4155 if (ctxt->URL != NULL)
4156 xmlFree(ctxt->URL);
4157 if (ctxt->doc != NULL)
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004158 xmlFreeDoc(ctxt->document);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004159 if (ctxt->interleaves != NULL)
4160 xmlHashFree(ctxt->interleaves, NULL);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004161 if (ctxt->documents != NULL)
4162 xmlHashFree(ctxt->documents, (xmlHashDeallocator)
4163 xmlRelaxNGFreeDocument);
4164 if (ctxt->docTab != NULL)
4165 xmlFree(ctxt->docTab);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004166 if (ctxt->incTab != NULL)
4167 xmlFree(ctxt->incTab);
Daniel Veillard419a7682003-02-03 23:22:49 +00004168 if (ctxt->defTab != NULL) {
4169 int i;
4170
4171 for (i = 0;i < ctxt->defNr;i++)
4172 xmlRelaxNGFreeDefine(ctxt->defTab[i]);
4173 xmlFree(ctxt->defTab);
4174 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004175 xmlFree(ctxt);
4176}
4177
Daniel Veillard6eadf632003-01-23 18:29:16 +00004178/**
Daniel Veillardd2298792003-02-14 16:54:11 +00004179 * xmlRelaxNGNormExtSpace:
4180 * @value: a value
4181 *
4182 * Removes the leading and ending spaces of the value
4183 * The string is modified "in situ"
4184 */
4185static void
4186xmlRelaxNGNormExtSpace(xmlChar *value) {
4187 xmlChar *start = value;
4188 xmlChar *cur = value;
4189 if (value == NULL)
4190 return;
4191
4192 while (IS_BLANK(*cur)) cur++;
4193 if (cur == start) {
4194 do {
4195 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
4196 if (*cur == 0)
4197 return;
4198 start = cur;
4199 while (IS_BLANK(*cur)) cur++;
4200 if (*cur == 0) {
4201 *start = 0;
4202 return;
4203 }
4204 } while (1);
4205 } else {
4206 do {
4207 while ((*cur != 0) && (!IS_BLANK(*cur)))
4208 *start++ = *cur++;
4209 if (*cur == 0) {
4210 *start = 0;
4211 return;
4212 }
4213 /* don't try to normalize the inner spaces */
4214 while (IS_BLANK(*cur)) cur++;
4215 *start++ = *cur++;
4216 if (*cur == 0) {
4217 *start = 0;
4218 return;
4219 }
4220 } while (1);
4221 }
4222}
4223
4224/**
4225 * xmlRelaxNGCheckAttributes:
4226 * @ctxt: a Relax-NG parser context
4227 * @node: a Relax-NG node
4228 *
4229 * Check all the attributes on the given node
4230 */
4231static void
4232xmlRelaxNGCleanupAttributes(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
4233 xmlAttrPtr cur, next;
4234
4235 cur = node->properties;
4236 while (cur != NULL) {
4237 next = cur->next;
4238 if ((cur->ns == NULL) ||
4239 (xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
4240 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
4241 if ((!xmlStrEqual(node->name, BAD_CAST "element")) &&
4242 (!xmlStrEqual(node->name, BAD_CAST "attribute")) &&
4243 (!xmlStrEqual(node->name, BAD_CAST "ref")) &&
4244 (!xmlStrEqual(node->name, BAD_CAST "parentRef")) &&
Daniel Veillard2df2de22003-02-17 23:34:33 +00004245 (!xmlStrEqual(node->name, BAD_CAST "param")) &&
Daniel Veillardd2298792003-02-14 16:54:11 +00004246 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
4247 if (ctxt->error != NULL)
4248 ctxt->error(ctxt->userData,
4249 "Attribute %s is not allowed on %s\n",
4250 cur->name, node->name);
4251 ctxt->nbErrors++;
4252 }
4253 } else if (xmlStrEqual(cur->name, BAD_CAST "type")) {
4254 if ((!xmlStrEqual(node->name, BAD_CAST "value")) &&
4255 (!xmlStrEqual(node->name, BAD_CAST "data"))) {
4256 if (ctxt->error != NULL)
4257 ctxt->error(ctxt->userData,
4258 "Attribute %s is not allowed on %s\n",
4259 cur->name, node->name);
4260 ctxt->nbErrors++;
4261 }
4262 } else if (xmlStrEqual(cur->name, BAD_CAST "href")) {
4263 if ((!xmlStrEqual(node->name, BAD_CAST "externalRef")) &&
4264 (!xmlStrEqual(node->name, BAD_CAST "include"))) {
4265 if (ctxt->error != NULL)
4266 ctxt->error(ctxt->userData,
4267 "Attribute %s is not allowed on %s\n",
4268 cur->name, node->name);
4269 ctxt->nbErrors++;
4270 }
4271 } else if (xmlStrEqual(cur->name, BAD_CAST "combine")) {
4272 if ((!xmlStrEqual(node->name, BAD_CAST "start")) &&
4273 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
4274 if (ctxt->error != NULL)
4275 ctxt->error(ctxt->userData,
4276 "Attribute %s is not allowed on %s\n",
4277 cur->name, node->name);
4278 ctxt->nbErrors++;
4279 }
4280 } else if (xmlStrEqual(cur->name, BAD_CAST "datatypeLibrary")) {
4281 xmlChar *val;
4282 xmlURIPtr uri;
4283
4284 val = xmlNodeListGetString(node->doc, cur->children, 1);
4285 if (val != NULL) {
4286 if (val[0] != 0) {
4287 uri = xmlParseURI((const char *) val);
4288 if (uri == NULL) {
4289 if (ctxt->error != NULL)
4290 ctxt->error(ctxt->userData,
4291 "Attribute %s contains invalid URI %s\n",
4292 cur->name, val);
4293 ctxt->nbErrors++;
4294 } else {
4295 if (uri->scheme == NULL) {
4296 if (ctxt->error != NULL)
4297 ctxt->error(ctxt->userData,
4298 "Attribute %s URI %s is not absolute\n",
4299 cur->name, val);
4300 ctxt->nbErrors++;
4301 }
4302 if (uri->fragment != NULL) {
4303 if (ctxt->error != NULL)
4304 ctxt->error(ctxt->userData,
4305 "Attribute %s URI %s has a fragment ID\n",
4306 cur->name, val);
4307 ctxt->nbErrors++;
4308 }
4309 xmlFreeURI(uri);
4310 }
4311 }
4312 xmlFree(val);
4313 }
4314 } else if (!xmlStrEqual(cur->name, BAD_CAST "ns")) {
4315 if (ctxt->error != NULL)
4316 ctxt->error(ctxt->userData,
4317 "Unknown attribute %s on %s\n",
4318 cur->name, node->name);
4319 ctxt->nbErrors++;
4320 }
4321 }
4322 cur = next;
4323 }
4324}
4325
4326/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004327 * xmlRelaxNGCleanupDoc:
4328 * @ctxt: a Relax-NG parser context
4329 * @doc: an xmldocPtr document pointer
Daniel Veillard6eadf632003-01-23 18:29:16 +00004330 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004331 * Cleanup the document from unwanted nodes for parsing, resolve
4332 * Include and externalRef lookups.
Daniel Veillard6eadf632003-01-23 18:29:16 +00004333 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004334 * Returns the cleaned up document or NULL in case of error
Daniel Veillard6eadf632003-01-23 18:29:16 +00004335 */
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004336static xmlDocPtr
4337xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt, xmlDocPtr doc) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00004338 xmlNodePtr root, cur, delete;
4339
Daniel Veillard6eadf632003-01-23 18:29:16 +00004340 /*
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004341 * Extract the root
Daniel Veillard6eadf632003-01-23 18:29:16 +00004342 */
4343 root = xmlDocGetRootElement(doc);
4344 if (root == NULL) {
4345 if (ctxt->error != NULL)
4346 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
4347 ctxt->URL);
4348 ctxt->nbErrors++;
4349 return (NULL);
4350 }
4351
4352 /*
4353 * Remove all the blank text nodes
4354 */
4355 delete = NULL;
4356 cur = root;
4357 while (cur != NULL) {
4358 if (delete != NULL) {
4359 xmlUnlinkNode(delete);
4360 xmlFreeNode(delete);
4361 delete = NULL;
4362 }
4363 if (cur->type == XML_ELEMENT_NODE) {
4364 /*
4365 * Simplification 4.1. Annotations
4366 */
4367 if ((cur->ns == NULL) ||
4368 (!xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
Daniel Veillardd2298792003-02-14 16:54:11 +00004369 if ((cur->parent != NULL) &&
4370 (cur->parent->type == XML_ELEMENT_NODE) &&
4371 ((xmlStrEqual(cur->parent->name, BAD_CAST "name")) ||
4372 (xmlStrEqual(cur->parent->name, BAD_CAST "value")) ||
4373 (xmlStrEqual(cur->parent->name, BAD_CAST "param")))) {
4374 if (ctxt->error != NULL)
4375 ctxt->error(ctxt->userData,
4376 "element %s doesn't allow foreign elements\n",
4377 cur->parent->name);
4378 ctxt->nbErrors++;
4379 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004380 delete = cur;
4381 goto skip_children;
4382 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00004383 xmlRelaxNGCleanupAttributes(ctxt, cur);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004384 if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004385 xmlChar *href, *ns, *base, *URL;
4386 xmlRelaxNGDocumentPtr docu;
Daniel Veillard416589a2003-02-17 17:25:42 +00004387 xmlNodePtr tmp;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004388
4389 ns = xmlGetProp(cur, BAD_CAST "ns");
Daniel Veillard416589a2003-02-17 17:25:42 +00004390 if (ns == NULL) {
4391 tmp = cur->parent;
4392 while ((tmp != NULL) &&
4393 (tmp->type == XML_ELEMENT_NODE)) {
4394 ns = xmlGetProp(tmp, BAD_CAST "ns");
4395 if (ns != NULL)
4396 break;
4397 tmp = tmp->parent;
4398 }
4399 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004400 href = xmlGetProp(cur, BAD_CAST "href");
4401 if (href == NULL) {
4402 if (ctxt->error != NULL)
4403 ctxt->error(ctxt->userData,
4404 "xmlRelaxNGParse: externalRef has no href attribute\n");
4405 ctxt->nbErrors++;
4406 delete = cur;
4407 goto skip_children;
4408 }
4409 base = xmlNodeGetBase(cur->doc, cur);
4410 URL = xmlBuildURI(href, base);
4411 if (URL == NULL) {
4412 if (ctxt->error != NULL)
4413 ctxt->error(ctxt->userData,
4414 "Failed to compute URL for externalRef %s\n", href);
4415 ctxt->nbErrors++;
4416 if (href != NULL)
4417 xmlFree(href);
4418 if (base != NULL)
4419 xmlFree(base);
4420 delete = cur;
4421 goto skip_children;
4422 }
4423 if (href != NULL)
4424 xmlFree(href);
4425 if (base != NULL)
4426 xmlFree(base);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004427 docu = xmlRelaxNGLoadExternalRef(ctxt, URL, ns);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004428 if (docu == NULL) {
4429 if (ctxt->error != NULL)
4430 ctxt->error(ctxt->userData,
4431 "Failed to load externalRef %s\n", URL);
4432 ctxt->nbErrors++;
4433 xmlFree(URL);
4434 delete = cur;
4435 goto skip_children;
4436 }
4437 xmlFree(URL);
4438 cur->_private = docu;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004439 } else if (xmlStrEqual(cur->name, BAD_CAST "include")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00004440 xmlChar *href, *ns, *base, *URL;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004441 xmlRelaxNGIncludePtr incl;
Daniel Veillard416589a2003-02-17 17:25:42 +00004442 xmlNodePtr tmp;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004443
4444 href = xmlGetProp(cur, BAD_CAST "href");
4445 if (href == NULL) {
4446 if (ctxt->error != NULL)
4447 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004448 "xmlRelaxNGParse: include has no href attribute\n");
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004449 ctxt->nbErrors++;
4450 delete = cur;
4451 goto skip_children;
4452 }
4453 base = xmlNodeGetBase(cur->doc, cur);
4454 URL = xmlBuildURI(href, base);
4455 if (URL == NULL) {
4456 if (ctxt->error != NULL)
4457 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004458 "Failed to compute URL for include %s\n", href);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004459 ctxt->nbErrors++;
4460 if (href != NULL)
4461 xmlFree(href);
4462 if (base != NULL)
4463 xmlFree(base);
4464 delete = cur;
4465 goto skip_children;
4466 }
4467 if (href != NULL)
4468 xmlFree(href);
4469 if (base != NULL)
4470 xmlFree(base);
Daniel Veillard416589a2003-02-17 17:25:42 +00004471 ns = xmlGetProp(cur, BAD_CAST "ns");
4472 if (ns == NULL) {
4473 tmp = cur->parent;
4474 while ((tmp != NULL) &&
4475 (tmp->type == XML_ELEMENT_NODE)) {
4476 ns = xmlGetProp(tmp, BAD_CAST "ns");
4477 if (ns != NULL)
4478 break;
4479 tmp = tmp->parent;
4480 }
4481 }
4482 incl = xmlRelaxNGLoadInclude(ctxt, URL, cur, ns);
4483 if (ns != NULL)
4484 xmlFree(ns);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004485 if (incl == NULL) {
4486 if (ctxt->error != NULL)
4487 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004488 "Failed to load include %s\n", URL);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004489 ctxt->nbErrors++;
4490 xmlFree(URL);
4491 delete = cur;
4492 goto skip_children;
4493 }
4494 xmlFree(URL);
4495 cur->_private = incl;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004496 } else if ((xmlStrEqual(cur->name, BAD_CAST "element")) ||
4497 (xmlStrEqual(cur->name, BAD_CAST "attribute"))) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00004498 xmlChar *name, *ns;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004499 xmlNodePtr text = NULL;
4500
4501 /*
4502 * Simplification 4.8. name attribute of element
4503 * and attribute elements
4504 */
4505 name = xmlGetProp(cur, BAD_CAST "name");
4506 if (name != NULL) {
4507 if (cur->children == NULL) {
4508 text = xmlNewChild(cur, cur->ns, BAD_CAST "name",
4509 name);
4510 } else {
4511 xmlNodePtr node;
4512 node = xmlNewNode(cur->ns, BAD_CAST "name");
4513 if (node != NULL) {
4514 xmlAddPrevSibling(cur->children, node);
4515 text = xmlNewText(name);
4516 xmlAddChild(node, text);
4517 text = node;
4518 }
4519 }
Daniel Veillardfebcca42003-02-16 15:44:18 +00004520 if (text == NULL) {
4521 if (ctxt->error != NULL)
4522 ctxt->error(ctxt->userData,
4523 "Failed to create a name %s element\n", name);
4524 ctxt->nbErrors++;
4525 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004526 xmlUnsetProp(cur, BAD_CAST "name");
4527 xmlFree(name);
Daniel Veillardfebcca42003-02-16 15:44:18 +00004528 ns = xmlGetProp(cur, BAD_CAST "ns");
4529 if (ns != NULL) {
4530 if (text != NULL) {
4531 xmlSetProp(text, BAD_CAST "ns", ns);
4532 /* xmlUnsetProp(cur, BAD_CAST "ns"); */
Daniel Veillard6eadf632003-01-23 18:29:16 +00004533 }
Daniel Veillardfebcca42003-02-16 15:44:18 +00004534 xmlFree(ns);
4535 } else if (xmlStrEqual(cur->name,
4536 BAD_CAST "attribute")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00004537 xmlSetProp(text, BAD_CAST "ns", BAD_CAST "");
4538 }
4539 }
4540 } else if ((xmlStrEqual(cur->name, BAD_CAST "name")) ||
4541 (xmlStrEqual(cur->name, BAD_CAST "nsName")) ||
4542 (xmlStrEqual(cur->name, BAD_CAST "value"))) {
4543 /*
4544 * Simplification 4.8. name attribute of element
4545 * and attribute elements
4546 */
4547 if (xmlHasProp(cur, BAD_CAST "ns") == NULL) {
4548 xmlNodePtr node;
4549 xmlChar *ns = NULL;
4550
4551 node = cur->parent;
4552 while ((node != NULL) &&
4553 (node->type == XML_ELEMENT_NODE)) {
4554 ns = xmlGetProp(node, BAD_CAST "ns");
4555 if (ns != NULL) {
4556 break;
4557 }
4558 node = node->parent;
4559 }
4560 if (ns == NULL) {
4561 xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
4562 } else {
4563 xmlSetProp(cur, BAD_CAST "ns", ns);
4564 xmlFree(ns);
4565 }
4566 }
4567 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
4568 xmlChar *name, *local, *prefix;
4569
4570 /*
4571 * Simplification: 4.10. QNames
4572 */
4573 name = xmlNodeGetContent(cur);
4574 if (name != NULL) {
4575 local = xmlSplitQName2(name, &prefix);
4576 if (local != NULL) {
4577 xmlNsPtr ns;
4578
4579 ns = xmlSearchNs(cur->doc, cur, prefix);
4580 if (ns == NULL) {
4581 if (ctxt->error != NULL)
4582 ctxt->error(ctxt->userData,
4583 "xmlRelaxNGParse: no namespace for prefix %s\n", prefix);
4584 ctxt->nbErrors++;
4585 } else {
4586 xmlSetProp(cur, BAD_CAST "ns", ns->href);
4587 xmlNodeSetContent(cur, local);
4588 }
4589 xmlFree(local);
4590 xmlFree(prefix);
4591 }
4592 xmlFree(name);
4593 }
4594 }
4595 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004596 /*
4597 * Thisd is not an else since "include" is transformed
4598 * into a div
4599 */
4600 if (xmlStrEqual(cur->name, BAD_CAST "div")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00004601 xmlChar *ns;
4602 xmlNodePtr child, ins, tmp;
4603
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004604 /*
4605 * implements rule 4.11
4606 */
Daniel Veillard416589a2003-02-17 17:25:42 +00004607
4608 ns = xmlGetProp(cur, BAD_CAST "ns");
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004609
4610 child = cur->children;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004611 ins = cur;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004612 while (child != NULL) {
Daniel Veillard416589a2003-02-17 17:25:42 +00004613 if (ns != NULL) {
4614 if (!xmlHasProp(child, BAD_CAST "ns")) {
4615 xmlSetProp(child, BAD_CAST "ns", ns);
4616 }
4617 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004618 tmp = child->next;
4619 xmlUnlinkNode(child);
4620 ins = xmlAddNextSibling(ins, child);
4621 child = tmp;
4622 }
Daniel Veillard416589a2003-02-17 17:25:42 +00004623 if (ns != NULL)
4624 xmlFree(ns);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004625 delete = cur;
4626 goto skip_children;
4627 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004628 }
4629 }
4630 /*
4631 * Simplification 4.2 whitespaces
4632 */
4633 else if (cur->type == XML_TEXT_NODE) {
4634 if (IS_BLANK_NODE(cur)) {
4635 if (cur->parent->type == XML_ELEMENT_NODE) {
4636 if ((!xmlStrEqual(cur->parent->name, BAD_CAST "value")) &&
4637 (!xmlStrEqual(cur->parent->name, BAD_CAST "param")))
4638 delete = cur;
4639 } else {
4640 delete = cur;
4641 goto skip_children;
4642 }
4643 }
4644 } else if (cur->type != XML_CDATA_SECTION_NODE) {
4645 delete = cur;
4646 goto skip_children;
4647 }
4648
4649 /*
4650 * Skip to next node
4651 */
4652 if (cur->children != NULL) {
4653 if ((cur->children->type != XML_ENTITY_DECL) &&
4654 (cur->children->type != XML_ENTITY_REF_NODE) &&
4655 (cur->children->type != XML_ENTITY_NODE)) {
4656 cur = cur->children;
4657 continue;
4658 }
4659 }
4660skip_children:
4661 if (cur->next != NULL) {
4662 cur = cur->next;
4663 continue;
4664 }
4665
4666 do {
4667 cur = cur->parent;
4668 if (cur == NULL)
4669 break;
4670 if (cur == root) {
4671 cur = NULL;
4672 break;
4673 }
4674 if (cur->next != NULL) {
4675 cur = cur->next;
4676 break;
4677 }
4678 } while (cur != NULL);
4679 }
4680 if (delete != NULL) {
4681 xmlUnlinkNode(delete);
4682 xmlFreeNode(delete);
4683 delete = NULL;
4684 }
4685
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004686 return(doc);
4687}
4688
4689/**
4690 * xmlRelaxNGParse:
4691 * @ctxt: a Relax-NG parser context
4692 *
4693 * parse a schema definition resource and build an internal
4694 * XML Shema struture which can be used to validate instances.
4695 * *WARNING* this interface is highly subject to change
4696 *
4697 * Returns the internal XML RelaxNG structure built from the resource or
4698 * NULL in case of error
4699 */
4700xmlRelaxNGPtr
4701xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt)
4702{
4703 xmlRelaxNGPtr ret = NULL;
4704 xmlDocPtr doc;
4705 xmlNodePtr root;
4706
4707 xmlRelaxNGInitTypes();
4708
4709 if (ctxt == NULL)
4710 return (NULL);
4711
4712 /*
4713 * First step is to parse the input document into an DOM/Infoset
4714 */
4715 if (ctxt->URL != NULL) {
4716 doc = xmlParseFile((const char *) ctxt->URL);
4717 if (doc == NULL) {
4718 if (ctxt->error != NULL)
4719 ctxt->error(ctxt->userData,
4720 "xmlRelaxNGParse: could not load %s\n", ctxt->URL);
4721 ctxt->nbErrors++;
4722 return (NULL);
4723 }
4724 } else if (ctxt->buffer != NULL) {
4725 doc = xmlParseMemory(ctxt->buffer, ctxt->size);
4726 if (doc == NULL) {
4727 if (ctxt->error != NULL)
4728 ctxt->error(ctxt->userData,
4729 "xmlRelaxNGParse: could not parse schemas\n");
4730 ctxt->nbErrors++;
4731 return (NULL);
4732 }
4733 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
4734 ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
4735 } else {
4736 if (ctxt->error != NULL)
4737 ctxt->error(ctxt->userData,
4738 "xmlRelaxNGParse: nothing to parse\n");
4739 ctxt->nbErrors++;
4740 return (NULL);
4741 }
4742 ctxt->document = doc;
4743
4744 /*
4745 * Some preprocessing of the document content
4746 */
4747 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
4748 if (doc == NULL) {
4749 xmlFreeDoc(ctxt->document);
4750 ctxt->document = NULL;
4751 return(NULL);
4752 }
4753
Daniel Veillard6eadf632003-01-23 18:29:16 +00004754 /*
4755 * Then do the parsing for good
4756 */
4757 root = xmlDocGetRootElement(doc);
4758 if (root == NULL) {
4759 if (ctxt->error != NULL)
4760 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
4761 ctxt->URL);
4762 ctxt->nbErrors++;
4763 return (NULL);
4764 }
4765 ret = xmlRelaxNGParseDocument(ctxt, root);
4766 if (ret == NULL)
4767 return(NULL);
4768
4769 /*
4770 * Check the ref/defines links
4771 */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004772 /*
4773 * try to preprocess interleaves
4774 */
4775 if (ctxt->interleaves != NULL) {
4776 xmlHashScan(ctxt->interleaves,
4777 (xmlHashScanner)xmlRelaxNGComputeInterleaves, ctxt);
4778 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004779
4780 /*
4781 * if there was a parsing error return NULL
4782 */
4783 if (ctxt->nbErrors > 0) {
4784 xmlRelaxNGFree(ret);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004785 ctxt->document = NULL;
4786 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004787 return(NULL);
4788 }
4789
4790 /*
4791 * Transfer the pointer for cleanup at the schema level.
4792 */
4793 ret->doc = doc;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004794 ctxt->document = NULL;
4795 ret->documents = ctxt->documents;
4796 ctxt->documents = NULL;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004797 ret->includes = ctxt->includes;
4798 ctxt->includes = NULL;
Daniel Veillard419a7682003-02-03 23:22:49 +00004799 ret->defNr = ctxt->defNr;
4800 ret->defTab = ctxt->defTab;
4801 ctxt->defTab = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004802
4803 return (ret);
4804}
4805
4806/**
4807 * xmlRelaxNGSetParserErrors:
4808 * @ctxt: a Relax-NG validation context
4809 * @err: the error callback
4810 * @warn: the warning callback
4811 * @ctx: contextual data for the callbacks
4812 *
4813 * Set the callback functions used to handle errors for a validation context
4814 */
4815void
4816xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
4817 xmlRelaxNGValidityErrorFunc err,
4818 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
4819 if (ctxt == NULL)
4820 return;
4821 ctxt->error = err;
4822 ctxt->warning = warn;
4823 ctxt->userData = ctx;
4824}
4825/************************************************************************
4826 * *
4827 * Dump back a compiled form *
4828 * *
4829 ************************************************************************/
4830static void xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define);
4831
4832/**
4833 * xmlRelaxNGDumpDefines:
4834 * @output: the file output
4835 * @defines: a list of define structures
4836 *
4837 * Dump a RelaxNG structure back
4838 */
4839static void
4840xmlRelaxNGDumpDefines(FILE * output, xmlRelaxNGDefinePtr defines) {
4841 while (defines != NULL) {
4842 xmlRelaxNGDumpDefine(output, defines);
4843 defines = defines->next;
4844 }
4845}
4846
4847/**
4848 * xmlRelaxNGDumpDefine:
4849 * @output: the file output
4850 * @define: a define structure
4851 *
4852 * Dump a RelaxNG structure back
4853 */
4854static void
4855xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define) {
4856 if (define == NULL)
4857 return;
4858 switch(define->type) {
4859 case XML_RELAXNG_EMPTY:
4860 fprintf(output, "<empty/>\n");
4861 break;
4862 case XML_RELAXNG_NOT_ALLOWED:
4863 fprintf(output, "<notAllowed/>\n");
4864 break;
4865 case XML_RELAXNG_TEXT:
4866 fprintf(output, "<text/>\n");
4867 break;
4868 case XML_RELAXNG_ELEMENT:
4869 fprintf(output, "<element>\n");
4870 if (define->name != NULL) {
4871 fprintf(output, "<name");
4872 if (define->ns != NULL)
4873 fprintf(output, " ns=\"%s\"", define->ns);
4874 fprintf(output, ">%s</name>\n", define->name);
4875 }
4876 xmlRelaxNGDumpDefines(output, define->attrs);
4877 xmlRelaxNGDumpDefines(output, define->content);
4878 fprintf(output, "</element>\n");
4879 break;
4880 case XML_RELAXNG_LIST:
4881 fprintf(output, "<list>\n");
4882 xmlRelaxNGDumpDefines(output, define->content);
4883 fprintf(output, "</list>\n");
4884 break;
4885 case XML_RELAXNG_ONEORMORE:
4886 fprintf(output, "<oneOrMore>\n");
4887 xmlRelaxNGDumpDefines(output, define->content);
4888 fprintf(output, "</oneOrMore>\n");
4889 break;
4890 case XML_RELAXNG_ZEROORMORE:
4891 fprintf(output, "<zeroOrMore>\n");
4892 xmlRelaxNGDumpDefines(output, define->content);
4893 fprintf(output, "</zeroOrMore>\n");
4894 break;
4895 case XML_RELAXNG_CHOICE:
4896 fprintf(output, "<choice>\n");
4897 xmlRelaxNGDumpDefines(output, define->content);
4898 fprintf(output, "</choice>\n");
4899 break;
4900 case XML_RELAXNG_GROUP:
4901 fprintf(output, "<group>\n");
4902 xmlRelaxNGDumpDefines(output, define->content);
4903 fprintf(output, "</group>\n");
4904 break;
4905 case XML_RELAXNG_INTERLEAVE:
4906 fprintf(output, "<interleave>\n");
4907 xmlRelaxNGDumpDefines(output, define->content);
4908 fprintf(output, "</interleave>\n");
4909 break;
4910 case XML_RELAXNG_OPTIONAL:
4911 fprintf(output, "<optional>\n");
4912 xmlRelaxNGDumpDefines(output, define->content);
4913 fprintf(output, "</optional>\n");
4914 break;
4915 case XML_RELAXNG_ATTRIBUTE:
4916 fprintf(output, "<attribute>\n");
4917 xmlRelaxNGDumpDefines(output, define->content);
4918 fprintf(output, "</attribute>\n");
4919 break;
4920 case XML_RELAXNG_DEF:
4921 fprintf(output, "<define");
4922 if (define->name != NULL)
4923 fprintf(output, " name=\"%s\"", define->name);
4924 fprintf(output, ">\n");
4925 xmlRelaxNGDumpDefines(output, define->content);
4926 fprintf(output, "</define>\n");
4927 break;
4928 case XML_RELAXNG_REF:
4929 fprintf(output, "<ref");
4930 if (define->name != NULL)
4931 fprintf(output, " name=\"%s\"", define->name);
4932 fprintf(output, ">\n");
4933 xmlRelaxNGDumpDefines(output, define->content);
4934 fprintf(output, "</ref>\n");
4935 break;
Daniel Veillard419a7682003-02-03 23:22:49 +00004936 case XML_RELAXNG_PARENTREF:
4937 fprintf(output, "<parentRef");
4938 if (define->name != NULL)
4939 fprintf(output, " name=\"%s\"", define->name);
4940 fprintf(output, ">\n");
4941 xmlRelaxNGDumpDefines(output, define->content);
4942 fprintf(output, "</parentRef>\n");
4943 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004944 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard416589a2003-02-17 17:25:42 +00004945 fprintf(output, "<externalRef>");
Daniel Veillarde431a272003-01-29 23:02:33 +00004946 xmlRelaxNGDumpDefines(output, define->content);
4947 fprintf(output, "</externalRef>\n");
4948 break;
4949 case XML_RELAXNG_DATATYPE:
Daniel Veillard6eadf632003-01-23 18:29:16 +00004950 case XML_RELAXNG_VALUE:
4951 TODO
4952 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004953 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00004954 case XML_RELAXNG_EXCEPT:
Daniel Veillard8fe98712003-02-19 00:19:14 +00004955 case XML_RELAXNG_PARAM:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004956 TODO
4957 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004958 }
4959}
4960
4961/**
4962 * xmlRelaxNGDumpGrammar:
4963 * @output: the file output
4964 * @grammar: a grammar structure
4965 * @top: is this a top grammar
4966 *
4967 * Dump a RelaxNG structure back
4968 */
4969static void
4970xmlRelaxNGDumpGrammar(FILE * output, xmlRelaxNGGrammarPtr grammar, int top)
4971{
4972 if (grammar == NULL)
4973 return;
4974
4975 fprintf(output, "<grammar");
4976 if (top)
4977 fprintf(output,
4978 " xmlns=\"http://relaxng.org/ns/structure/1.0\"");
4979 switch(grammar->combine) {
4980 case XML_RELAXNG_COMBINE_UNDEFINED:
4981 break;
4982 case XML_RELAXNG_COMBINE_CHOICE:
4983 fprintf(output, " combine=\"choice\"");
4984 break;
4985 case XML_RELAXNG_COMBINE_INTERLEAVE:
4986 fprintf(output, " combine=\"interleave\"");
4987 break;
4988 default:
4989 fprintf(output, " <!-- invalid combine value -->");
4990 }
4991 fprintf(output, ">\n");
4992 if (grammar->start == NULL) {
4993 fprintf(output, " <!-- grammar had no start -->");
4994 } else {
4995 fprintf(output, "<start>\n");
4996 xmlRelaxNGDumpDefine(output, grammar->start);
4997 fprintf(output, "</start>\n");
4998 }
4999 /* TODO ? Dump the defines ? */
5000 fprintf(output, "</grammar>\n");
5001}
5002
5003/**
5004 * xmlRelaxNGDump:
5005 * @output: the file output
5006 * @schema: a schema structure
5007 *
5008 * Dump a RelaxNG structure back
5009 */
5010void
5011xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema)
5012{
5013 if (schema == NULL) {
5014 fprintf(output, "RelaxNG empty or failed to compile\n");
5015 return;
5016 }
5017 fprintf(output, "RelaxNG: ");
5018 if (schema->doc == NULL) {
5019 fprintf(output, "no document\n");
5020 } else if (schema->doc->URL != NULL) {
5021 fprintf(output, "%s\n", schema->doc->URL);
5022 } else {
5023 fprintf(output, "\n");
5024 }
5025 if (schema->topgrammar == NULL) {
5026 fprintf(output, "RelaxNG has no top grammar\n");
5027 return;
5028 }
5029 xmlRelaxNGDumpGrammar(output, schema->topgrammar, 1);
5030}
5031
Daniel Veillardfebcca42003-02-16 15:44:18 +00005032/**
5033 * xmlRelaxNGDumpTree:
5034 * @output: the file output
5035 * @schema: a schema structure
5036 *
5037 * Dump the transformed RelaxNG tree.
5038 */
5039void
5040xmlRelaxNGDumpTree(FILE * output, xmlRelaxNGPtr schema)
5041{
5042 if (schema == NULL) {
5043 fprintf(output, "RelaxNG empty or failed to compile\n");
5044 return;
5045 }
5046 if (schema->doc == NULL) {
5047 fprintf(output, "no document\n");
5048 } else {
5049 xmlDocDump(output, schema->doc);
5050 }
5051}
5052
Daniel Veillard6eadf632003-01-23 18:29:16 +00005053/************************************************************************
5054 * *
5055 * Validation implementation *
5056 * *
5057 ************************************************************************/
5058static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
5059 xmlRelaxNGDefinePtr define);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005060static int xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
5061 xmlRelaxNGDefinePtr define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005062
5063/**
5064 * xmlRelaxNGSkipIgnored:
5065 * @ctxt: a schema validation context
5066 * @node: the top node.
5067 *
5068 * Skip ignorable nodes in that context
5069 *
5070 * Returns the new sibling or NULL in case of error.
5071 */
5072static xmlNodePtr
5073xmlRelaxNGSkipIgnored(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
5074 xmlNodePtr node) {
5075 /*
5076 * TODO complete and handle entities
5077 */
5078 while ((node != NULL) &&
5079 ((node->type == XML_COMMENT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005080 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00005081 ((node->type == XML_TEXT_NODE) &&
5082 (IS_BLANK_NODE(node))))) {
5083 node = node->next;
5084 }
5085 return(node);
5086}
5087
5088/**
Daniel Veillardedc91922003-01-26 00:52:04 +00005089 * xmlRelaxNGNormalize:
5090 * @ctxt: a schema validation context
5091 * @str: the string to normalize
5092 *
5093 * Implements the normalizeWhiteSpace( s ) function from
5094 * section 6.2.9 of the spec
5095 *
5096 * Returns the new string or NULL in case of error.
5097 */
5098static xmlChar *
5099xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *str) {
5100 xmlChar *ret, *p;
5101 const xmlChar *tmp;
5102 int len;
5103
5104 if (str == NULL)
5105 return(NULL);
5106 tmp = str;
5107 while (*tmp != 0) tmp++;
5108 len = tmp - str;
5109
5110 ret = (xmlChar *) xmlMalloc((len + 1) * sizeof(xmlChar));
5111 if (ret == NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00005112 if (ctxt != NULL) {
5113 VALID_CTXT();
5114 VALID_ERROR("xmlRelaxNGNormalize: out of memory\n");
5115 } else {
5116 xmlGenericError(xmlGenericErrorContext,
5117 "xmlRelaxNGNormalize: out of memory\n");
5118 }
Daniel Veillardedc91922003-01-26 00:52:04 +00005119 return(NULL);
5120 }
5121 p = ret;
5122 while (IS_BLANK(*str)) str++;
5123 while (*str != 0) {
5124 if (IS_BLANK(*str)) {
5125 while (IS_BLANK(*str)) str++;
5126 if (*str == 0)
5127 break;
5128 *p++ = ' ';
5129 } else
5130 *p++ = *str++;
5131 }
5132 *p = 0;
5133 return(ret);
5134}
5135
5136/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005137 * xmlRelaxNGValidateDatatype:
5138 * @ctxt: a Relax-NG validation context
5139 * @value: the string value
5140 * @type: the datatype definition
5141 *
5142 * Validate the given value against the dataype
5143 *
5144 * Returns 0 if the validation succeeded or an error code.
5145 */
5146static int
5147xmlRelaxNGValidateDatatype(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *value,
5148 xmlRelaxNGDefinePtr define) {
5149 int ret;
5150 xmlRelaxNGTypeLibraryPtr lib;
5151
5152 if ((define == NULL) || (define->data == NULL)) {
5153 return(-1);
5154 }
5155 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
5156 if (lib->check != NULL)
5157 ret = lib->check(lib->data, define->name, value);
5158 else
5159 ret = -1;
5160 if (ret < 0) {
5161 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005162 VALID_ERROR2("Internal: failed to validate type %s\n", define->name);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005163 return(-1);
5164 } else if (ret == 1) {
5165 ret = 0;
5166 } else {
5167 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005168 VALID_ERROR3("Type %s doesn't allow value %s\n", define->name, value);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005169 return(-1);
5170 ret = -1;
5171 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005172 if ((ret == 0) && (define->content != NULL)) {
5173 const xmlChar *oldvalue, *oldendvalue;
5174
5175 oldvalue = ctxt->state->value;
5176 oldendvalue = ctxt->state->endvalue;
5177 ctxt->state->value = (xmlChar *) value;
5178 ctxt->state->endvalue = NULL;
5179 ret = xmlRelaxNGValidateValue(ctxt, define->content);
5180 ctxt->state->value = (xmlChar *) oldvalue;
5181 ctxt->state->endvalue = (xmlChar *) oldendvalue;
5182 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005183 return(ret);
5184}
5185
5186/**
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005187 * xmlRelaxNGNextValue:
5188 * @ctxt: a Relax-NG validation context
5189 *
5190 * Skip to the next value when validating within a list
5191 *
5192 * Returns 0 if the operation succeeded or an error code.
5193 */
5194static int
5195xmlRelaxNGNextValue(xmlRelaxNGValidCtxtPtr ctxt) {
5196 xmlChar *cur;
5197
5198 cur = ctxt->state->value;
5199 if ((cur == NULL) || (ctxt->state->endvalue == NULL)) {
5200 ctxt->state->value = NULL;
Daniel Veillarde5b110b2003-02-04 14:43:39 +00005201 ctxt->state->endvalue = NULL;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005202 return(0);
5203 }
5204 while (*cur != 0) cur++;
5205 while ((cur != ctxt->state->endvalue) && (*cur == 0)) cur++;
5206 if (cur == ctxt->state->endvalue)
5207 ctxt->state->value = NULL;
5208 else
5209 ctxt->state->value = cur;
5210 return(0);
5211}
5212
5213/**
5214 * xmlRelaxNGValidateValueList:
5215 * @ctxt: a Relax-NG validation context
5216 * @defines: the list of definitions to verify
5217 *
5218 * Validate the given set of definitions for the current value
5219 *
5220 * Returns 0 if the validation succeeded or an error code.
5221 */
5222static int
5223xmlRelaxNGValidateValueList(xmlRelaxNGValidCtxtPtr ctxt,
5224 xmlRelaxNGDefinePtr defines) {
5225 int ret = 0;
5226
5227 while (defines != NULL) {
5228 ret = xmlRelaxNGValidateValue(ctxt, defines);
5229 if (ret != 0)
5230 break;
5231 defines = defines->next;
5232 }
5233 return(ret);
5234}
5235
5236/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00005237 * xmlRelaxNGValidateValue:
5238 * @ctxt: a Relax-NG validation context
5239 * @define: the definition to verify
5240 *
5241 * Validate the given definition for the current value
5242 *
5243 * Returns 0 if the validation succeeded or an error code.
5244 */
5245static int
5246xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
5247 xmlRelaxNGDefinePtr define) {
Daniel Veillardedc91922003-01-26 00:52:04 +00005248 int ret = 0, oldflags;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005249 xmlChar *value;
5250
5251 value = ctxt->state->value;
5252 switch (define->type) {
Daniel Veillardd4310742003-02-18 21:12:46 +00005253 case XML_RELAXNG_EMPTY: {
5254 if ((value != NULL) && (value[0] != 0)) {
5255 int idx = 0;
5256
5257 while (IS_BLANK(value[idx]))
5258 idx++;
5259 if (value[idx] != 0)
5260 ret = -1;
5261 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005262 break;
Daniel Veillardd4310742003-02-18 21:12:46 +00005263 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005264 case XML_RELAXNG_TEXT:
5265 break;
Daniel Veillardedc91922003-01-26 00:52:04 +00005266 case XML_RELAXNG_VALUE: {
5267 if (!xmlStrEqual(value, define->value)) {
5268 if (define->name != NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00005269 xmlRelaxNGTypeLibraryPtr lib;
5270
5271 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
5272 if ((lib != NULL) && (lib->comp != NULL))
5273 ret = lib->comp(lib->data, define->name, value,
5274 define->value);
5275 else
5276 ret = -1;
5277 if (ret < 0) {
5278 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005279 VALID_ERROR2("Internal: failed to compare type %s\n",
Daniel Veillardea3f3982003-01-26 19:45:18 +00005280 define->name);
5281 return(-1);
5282 } else if (ret == 1) {
5283 ret = 0;
5284 } else {
5285 ret = -1;
5286 }
Daniel Veillardedc91922003-01-26 00:52:04 +00005287 } else {
5288 xmlChar *nval, *nvalue;
5289
5290 /*
5291 * TODO: trivial optimizations are possible by
5292 * computing at compile-time
5293 */
5294 nval = xmlRelaxNGNormalize(ctxt, define->value);
5295 nvalue = xmlRelaxNGNormalize(ctxt, value);
5296
Daniel Veillardea3f3982003-01-26 19:45:18 +00005297 if ((nval == NULL) || (nvalue == NULL) ||
5298 (!xmlStrEqual(nval, nvalue)))
Daniel Veillardedc91922003-01-26 00:52:04 +00005299 ret = -1;
5300 if (nval != NULL)
5301 xmlFree(nval);
5302 if (nvalue != NULL)
5303 xmlFree(nvalue);
5304 }
5305 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005306 if (ret == 0)
5307 xmlRelaxNGNextValue(ctxt);
Daniel Veillardedc91922003-01-26 00:52:04 +00005308 break;
5309 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005310 case XML_RELAXNG_DATATYPE: {
5311 ret = xmlRelaxNGValidateDatatype(ctxt, value, define);
5312 if (ret == 0)
5313 xmlRelaxNGNextValue(ctxt);
5314
5315 break;
5316 }
5317 case XML_RELAXNG_CHOICE: {
5318 xmlRelaxNGDefinePtr list = define->content;
5319 xmlChar *oldvalue;
5320
5321 oldflags = ctxt->flags;
5322 ctxt->flags |= FLAGS_IGNORABLE;
5323
5324 oldvalue = ctxt->state->value;
5325 while (list != NULL) {
5326 ret = xmlRelaxNGValidateValue(ctxt, list);
5327 if (ret == 0) {
5328 break;
5329 }
5330 ctxt->state->value = oldvalue;
5331 list = list->next;
5332 }
5333 ctxt->flags = oldflags;
Daniel Veillard416589a2003-02-17 17:25:42 +00005334 if (ret == 0)
5335 xmlRelaxNGNextValue(ctxt);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005336 break;
5337 }
5338 case XML_RELAXNG_LIST: {
5339 xmlRelaxNGDefinePtr list = define->content;
5340 xmlChar *oldvalue, *oldend, *val, *cur;
Daniel Veillard416589a2003-02-17 17:25:42 +00005341#ifdef DEBUG_LIST
5342 int nb_values = 0;
5343#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005344
5345 oldvalue = ctxt->state->value;
5346 oldend = ctxt->state->endvalue;
5347
5348 val = xmlStrdup(oldvalue);
5349 if (val == NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00005350 val = xmlStrdup(BAD_CAST "");
5351 }
5352 if (val == NULL) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005353 VALID_CTXT();
5354 VALID_ERROR("Internal: no state\n");
5355 return(-1);
5356 }
5357 cur = val;
5358 while (*cur != 0) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005359 if (IS_BLANK(*cur)) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005360 *cur = 0;
Daniel Veillard416589a2003-02-17 17:25:42 +00005361 cur++;
5362#ifdef DEBUG_LIST
5363 nb_values++;
5364#endif
5365 while (IS_BLANK(*cur))
5366 *cur++ = 0;
5367 } else
5368 cur++;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005369 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005370#ifdef DEBUG_LIST
5371 xmlGenericError(xmlGenericErrorContext,
5372 "list value: '%s' found %d items\n", oldvalue, nb_values);
5373 nb_values = 0;
5374#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005375 ctxt->state->endvalue = cur;
5376 cur = val;
5377 while ((*cur == 0) && (cur != ctxt->state->endvalue)) cur++;
5378
5379 ctxt->state->value = cur;
5380
5381 while (list != NULL) {
Daniel Veillard2e9b1652003-02-19 13:29:45 +00005382 if (ctxt->state->value == ctxt->state->endvalue)
5383 ctxt->state->value = NULL;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005384 ret = xmlRelaxNGValidateValue(ctxt, list);
5385 if (ret != 0) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005386#ifdef DEBUG_LIST
5387 xmlGenericError(xmlGenericErrorContext,
5388 "Failed to validate value: '%s' with %d rule\n",
5389 ctxt->state->value, nb_values);
5390#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005391 break;
5392 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005393#ifdef DEBUG_LIST
5394 nb_values++;
5395#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005396 list = list->next;
5397 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00005398
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005399 if ((ret == 0) && (ctxt->state->value != NULL) &&
5400 (ctxt->state->value != ctxt->state->endvalue)) {
5401 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005402 VALID_ERROR2("Extra data in list: %s\n", ctxt->state->value);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005403 ret = -1;
5404 }
5405 xmlFree(val);
5406 ctxt->state->value = oldvalue;
5407 ctxt->state->endvalue = oldend;
5408 break;
5409 }
5410 case XML_RELAXNG_ONEORMORE:
5411 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
5412 if (ret != 0) {
5413 break;
5414 }
5415 /* no break on purpose */
5416 case XML_RELAXNG_ZEROORMORE: {
5417 xmlChar *cur, *temp;
5418
5419 oldflags = ctxt->flags;
5420 ctxt->flags |= FLAGS_IGNORABLE;
5421 cur = ctxt->state->value;
5422 temp = NULL;
5423 while ((cur != NULL) && (cur != ctxt->state->endvalue) &&
5424 (temp != cur)) {
5425 temp = cur;
5426 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
5427 if (ret != 0) {
5428 ctxt->state->value = temp;
5429 ret = 0;
5430 break;
5431 }
5432 cur = ctxt->state->value;
5433 }
5434 ctxt->flags = oldflags;
5435 break;
5436 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005437 case XML_RELAXNG_EXCEPT: {
5438 xmlRelaxNGDefinePtr list;
5439
5440 list = define->content;
5441 while (list != NULL) {
5442 ret = xmlRelaxNGValidateValue(ctxt, list);
5443 if (ret == 0) {
5444 ret = -1;
5445 break;
5446 } else
5447 ret = 0;
5448 list = list->next;
5449 }
5450 break;
5451 }
Daniel Veillardd4310742003-02-18 21:12:46 +00005452 case XML_RELAXNG_GROUP: {
5453 xmlRelaxNGDefinePtr list;
5454
5455 list = define->content;
5456 while (list != NULL) {
5457 ret = xmlRelaxNGValidateValue(ctxt, list);
5458 if (ret != 0) {
5459 ret = -1;
5460 break;
5461 } else
5462 ret = 0;
5463 list = list->next;
5464 }
5465 break;
5466 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005467 default:
5468 TODO
5469 ret = -1;
5470 }
5471 return(ret);
5472}
5473
5474/**
5475 * xmlRelaxNGValidateValueContent:
5476 * @ctxt: a Relax-NG validation context
5477 * @defines: the list of definitions to verify
5478 *
5479 * Validate the given definitions for the current value
5480 *
5481 * Returns 0 if the validation succeeded or an error code.
5482 */
5483static int
5484xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt,
5485 xmlRelaxNGDefinePtr defines) {
5486 int ret = 0;
5487
5488 while (defines != NULL) {
5489 ret = xmlRelaxNGValidateValue(ctxt, defines);
5490 if (ret != 0)
5491 break;
5492 defines = defines->next;
5493 }
5494 return(ret);
5495}
5496
5497/**
Daniel Veillard144fae12003-02-03 13:17:57 +00005498 * xmlRelaxNGAttributeMatch:
5499 * @ctxt: a Relax-NG validation context
5500 * @define: the definition to check
5501 * @prop: the attribute
5502 *
5503 * Check if the attribute matches the definition nameClass
5504 *
5505 * Returns 1 if the attribute matches, 0 if no, or -1 in case of error
5506 */
5507static int
5508xmlRelaxNGAttributeMatch(xmlRelaxNGValidCtxtPtr ctxt,
5509 xmlRelaxNGDefinePtr define,
5510 xmlAttrPtr prop) {
5511 int ret;
5512
5513 if (define->name != NULL) {
5514 if (!xmlStrEqual(define->name, prop->name))
5515 return(0);
5516 }
5517 if (define->ns != NULL) {
5518 if (define->ns[0] == 0) {
5519 if (prop->ns != NULL)
5520 return(0);
5521 } else {
5522 if ((prop->ns == NULL) ||
5523 (!xmlStrEqual(define->ns, prop->ns->href)))
5524 return(0);
5525 }
5526 }
5527 if (define->nameClass == NULL)
5528 return(1);
5529 define = define->nameClass;
5530 if (define->type == XML_RELAXNG_EXCEPT) {
5531 xmlRelaxNGDefinePtr list;
5532
5533 list = define->content;
5534 while (list != NULL) {
5535 ret = xmlRelaxNGAttributeMatch(ctxt, list, prop);
5536 if (ret == 1)
5537 return(0);
5538 if (ret < 0)
5539 return(ret);
5540 list = list->next;
5541 }
5542 } else {
5543 TODO
5544 }
5545 return(1);
5546}
5547
5548/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00005549 * xmlRelaxNGValidateAttribute:
5550 * @ctxt: a Relax-NG validation context
5551 * @define: the definition to verify
5552 *
5553 * Validate the given attribute definition for that node
5554 *
5555 * Returns 0 if the validation succeeded or an error code.
5556 */
5557static int
5558xmlRelaxNGValidateAttribute(xmlRelaxNGValidCtxtPtr ctxt,
5559 xmlRelaxNGDefinePtr define) {
5560 int ret = 0, i;
5561 xmlChar *value, *oldvalue;
5562 xmlAttrPtr prop = NULL, tmp;
5563
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005564 if (ctxt->state->nbAttrLeft <= 0)
5565 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005566 if (define->name != NULL) {
5567 for (i = 0;i < ctxt->state->nbAttrs;i++) {
5568 tmp = ctxt->state->attrs[i];
5569 if ((tmp != NULL) && (xmlStrEqual(define->name, tmp->name))) {
5570 if ((((define->ns == NULL) || (define->ns[0] == 0)) &&
5571 (tmp->ns == NULL)) ||
5572 ((tmp->ns != NULL) &&
5573 (xmlStrEqual(define->ns, tmp->ns->href)))) {
5574 prop = tmp;
5575 break;
5576 }
5577 }
5578 }
5579 if (prop != NULL) {
5580 value = xmlNodeListGetString(prop->doc, prop->children, 1);
5581 oldvalue = ctxt->state->value;
5582 ctxt->state->value = value;
Daniel Veillard231d7912003-02-09 14:22:17 +00005583 ctxt->state->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005584 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00005585 if (ctxt->state->value != NULL)
5586 value = ctxt->state->value;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005587 if (value != NULL)
5588 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00005589 ctxt->state->value = oldvalue;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005590 if (ret == 0) {
5591 /*
5592 * flag the attribute as processed
5593 */
5594 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005595 ctxt->state->nbAttrLeft--;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005596 }
5597 } else {
5598 ret = -1;
5599 }
5600#ifdef DEBUG
5601 xmlGenericError(xmlGenericErrorContext,
5602 "xmlRelaxNGValidateAttribute(%s): %d\n", define->name, ret);
5603#endif
5604 } else {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005605 for (i = 0;i < ctxt->state->nbAttrs;i++) {
5606 tmp = ctxt->state->attrs[i];
Daniel Veillard144fae12003-02-03 13:17:57 +00005607 if ((tmp != NULL) &&
5608 (xmlRelaxNGAttributeMatch(ctxt, define, tmp) == 1)) {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005609 prop = tmp;
5610 break;
5611 }
5612 }
5613 if (prop != NULL) {
5614 value = xmlNodeListGetString(prop->doc, prop->children, 1);
5615 oldvalue = ctxt->state->value;
5616 ctxt->state->value = value;
5617 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00005618 if (ctxt->state->value != NULL)
5619 value = ctxt->state->value;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005620 if (value != NULL)
5621 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00005622 ctxt->state->value = oldvalue;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005623 if (ret == 0) {
5624 /*
5625 * flag the attribute as processed
5626 */
5627 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005628 ctxt->state->nbAttrLeft--;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005629 }
5630 } else {
5631 ret = -1;
5632 }
5633#ifdef DEBUG
Daniel Veillard144fae12003-02-03 13:17:57 +00005634 if (define->ns != NULL) {
5635 xmlGenericError(xmlGenericErrorContext,
5636 "xmlRelaxNGValidateAttribute(nsName ns = %s): %d\n",
5637 define->ns, ret);
5638 } else {
5639 xmlGenericError(xmlGenericErrorContext,
5640 "xmlRelaxNGValidateAttribute(anyName): %d\n",
5641 ret);
5642 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005643#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00005644 }
5645
5646 return(ret);
5647}
5648
5649/**
5650 * xmlRelaxNGValidateAttributeList:
5651 * @ctxt: a Relax-NG validation context
5652 * @define: the list of definition to verify
5653 *
5654 * Validate the given node against the list of attribute definitions
5655 *
5656 * Returns 0 if the validation succeeded or an error code.
5657 */
5658static int
5659xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt,
5660 xmlRelaxNGDefinePtr defines) {
5661 int ret = 0;
5662 while (defines != NULL) {
5663 if (xmlRelaxNGValidateAttribute(ctxt, defines) != 0)
5664 ret = -1;
5665 defines = defines->next;
5666 }
5667 return(ret);
5668}
5669
5670/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005671 * xmlRelaxNGValidateTryPermutation:
5672 * @ctxt: a Relax-NG validation context
5673 * @groups: the array of groups
5674 * @nbgroups: the number of groups in the array
5675 * @array: the permutation to try
5676 * @len: the size of the set
5677 *
5678 * Try to validate a permutation for the group of definitions.
5679 *
5680 * Returns 0 if the validation succeeded or an error code.
5681 */
5682static int
5683xmlRelaxNGValidateTryPermutation(xmlRelaxNGValidCtxtPtr ctxt,
5684 xmlRelaxNGDefinePtr rule,
5685 xmlNodePtr *array, int len) {
5686 int i, ret;
5687
5688 if (len > 0) {
5689 /*
5690 * One only need the next pointer set-up to do the validation
5691 */
5692 for (i = 0;i < (len - 1);i++)
5693 array[i]->next = array[i + 1];
5694 array[i]->next = NULL;
5695
5696 /*
5697 * Now try to validate the sequence
5698 */
5699 ctxt->state->seq = array[0];
5700 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
5701 } else {
5702 ctxt->state->seq = NULL;
5703 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
5704 }
5705
5706 /*
5707 * the sequence must be fully consumed
5708 */
5709 if (ctxt->state->seq != NULL)
5710 return(-1);
5711
5712 return(ret);
5713}
5714
5715/**
5716 * xmlRelaxNGValidateWalkPermutations:
5717 * @ctxt: a Relax-NG validation context
5718 * @groups: the array of groups
5719 * @nbgroups: the number of groups in the array
5720 * @nodes: the set of nodes
5721 * @array: the current state of the parmutation
5722 * @len: the size of the set
5723 * @level: a pointer to the level variable
5724 * @k: the index in the array to fill
5725 *
5726 * Validate a set of nodes for a groups of definitions, will try the
5727 * full set of permutations
5728 *
5729 * Returns 0 if the validation succeeded or an error code.
5730 */
5731static int
5732xmlRelaxNGValidateWalkPermutations(xmlRelaxNGValidCtxtPtr ctxt,
5733 xmlRelaxNGDefinePtr rule, xmlNodePtr *nodes,
5734 xmlNodePtr *array, int len,
5735 int *level, int k) {
5736 int i, ret;
5737
5738 if ((k >= 0) && (k < len))
5739 array[k] = nodes[*level];
5740 *level = *level + 1;
5741 if (*level == len) {
5742 ret = xmlRelaxNGValidateTryPermutation(ctxt, rule, array, len);
5743 if (ret == 0)
5744 return(0);
5745 } else {
5746 for (i = 0;i < len;i++) {
5747 if (array[i] == NULL) {
5748 ret = xmlRelaxNGValidateWalkPermutations(ctxt, rule,
5749 nodes, array, len, level, i);
5750 if (ret == 0)
5751 return(0);
5752 }
5753 }
5754 }
5755 *level = *level - 1;
5756 array[k] = NULL;
5757 return(-1);
5758}
5759
5760/**
5761 * xmlRelaxNGNodeMatchesList:
5762 * @node: the node
5763 * @list: a NULL terminated array of definitions
5764 *
5765 * Check if a node can be matched by one of the definitions
5766 *
5767 * Returns 1 if matches 0 otherwise
5768 */
5769static int
5770xmlRelaxNGNodeMatchesList(xmlNodePtr node, xmlRelaxNGDefinePtr *list) {
5771 xmlRelaxNGDefinePtr cur;
5772 int i = 0;
5773
5774 if ((node == NULL) || (list == NULL))
5775 return(0);
5776
5777 cur = list[i++];
5778 while (cur != NULL) {
5779 if ((node->type == XML_ELEMENT_NODE) &&
5780 (cur->type == XML_RELAXNG_ELEMENT)) {
5781 if (cur->name == NULL) {
5782 if ((node->ns != NULL) &&
5783 (xmlStrEqual(node->ns->href, cur->ns)))
5784 return(1);
5785 } else if (xmlStrEqual(cur->name, node->name)) {
5786 if ((cur->ns == NULL) || (cur->ns[0] == 0)) {
5787 if (node->ns == NULL)
5788 return(1);
5789 } else {
5790 if ((node->ns != NULL) &&
5791 (xmlStrEqual(node->ns->href, cur->ns)))
5792 return(1);
5793 }
5794 }
5795 } else if ((node->type == XML_TEXT_NODE) &&
5796 (cur->type == XML_RELAXNG_TEXT)) {
5797 return(1);
5798 }
5799 cur = list[i++];
5800 }
5801 return(0);
5802}
5803
5804/**
5805 * xmlRelaxNGValidatePartGroup:
5806 * @ctxt: a Relax-NG validation context
5807 * @groups: the array of groups
5808 * @nbgroups: the number of groups in the array
5809 * @nodes: the set of nodes
5810 * @len: the size of the set of nodes
5811 *
5812 * Validate a set of nodes for a groups of definitions
5813 *
5814 * Returns 0 if the validation succeeded or an error code.
5815 */
5816static int
5817xmlRelaxNGValidatePartGroup(xmlRelaxNGValidCtxtPtr ctxt,
5818 xmlRelaxNGInterleaveGroupPtr *groups,
5819 int nbgroups, xmlNodePtr *nodes, int len) {
Daniel Veillard231d7912003-02-09 14:22:17 +00005820 int level, ret = -1, i, j, k, top_j, max_j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005821 xmlNodePtr *array = NULL, *list, oldseq;
5822 xmlRelaxNGInterleaveGroupPtr group;
5823
5824 list = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
5825 if (list == NULL) {
5826 return(-1);
5827 }
5828 array = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
5829 if (array == NULL) {
5830 xmlFree(list);
5831 return(-1);
5832 }
5833 memset(array, 0, len * sizeof(xmlNodePtr));
5834
5835 /*
5836 * Partition the elements and validate the subsets.
5837 */
5838 oldseq = ctxt->state->seq;
Daniel Veillard231d7912003-02-09 14:22:17 +00005839 max_j = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005840 for (i = 0;i < nbgroups;i++) {
5841 group = groups[i];
5842 if (group == NULL)
5843 continue;
5844 k = 0;
Daniel Veillard231d7912003-02-09 14:22:17 +00005845 top_j = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005846 for (j = 0;j < len;j++) {
5847 if (nodes[j] == NULL)
5848 continue;
5849 if (xmlRelaxNGNodeMatchesList(nodes[j], group->defs)) {
5850 list[k++] = nodes[j];
5851 nodes[j] = NULL;
Daniel Veillard231d7912003-02-09 14:22:17 +00005852 top_j = j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005853 }
5854 }
Daniel Veillard231d7912003-02-09 14:22:17 +00005855 if (top_j > max_j)
5856 max_j = top_j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005857 ctxt->state->seq = oldseq;
5858 if (k > 1) {
5859 memset(array, 0, k * sizeof(xmlNodePtr));
Daniel Veillardb08c9812003-01-28 23:09:49 +00005860 level = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005861 ret = xmlRelaxNGValidateWalkPermutations(ctxt, group->rule,
5862 list, array, k, &level, -1);
5863 } else {
5864 ret = xmlRelaxNGValidateTryPermutation(ctxt, group->rule, list, k);
5865 }
5866 if (ret != 0) {
5867 ctxt->state->seq = oldseq;
5868 break;
5869 }
5870 }
5871
Daniel Veillard231d7912003-02-09 14:22:17 +00005872 for (j = 0;j < max_j;j++) {
5873 if (nodes[j] != NULL) {
5874 TODO /* problem, one of the nodes didn't got a match */
5875 }
5876 }
5877 if (ret == 0) {
5878 if (max_j + 1 < len)
5879 ctxt->state->seq = nodes[max_j + 1];
5880 else
5881 ctxt->state->seq = NULL;
5882 }
5883
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005884 xmlFree(list);
5885 xmlFree(array);
5886 return(ret);
5887}
5888
5889/**
5890 * xmlRelaxNGValidateInterleave:
5891 * @ctxt: a Relax-NG validation context
5892 * @define: the definition to verify
5893 *
5894 * Validate an interleave definition for a node.
5895 *
5896 * Returns 0 if the validation succeeded or an error code.
5897 */
5898static int
5899xmlRelaxNGValidateInterleave(xmlRelaxNGValidCtxtPtr ctxt,
5900 xmlRelaxNGDefinePtr define) {
5901 int ret = 0, nbchildren, nbtot, i, j;
5902 xmlRelaxNGPartitionPtr partitions;
5903 xmlNodePtr *children = NULL;
5904 xmlNodePtr *order = NULL;
Daniel Veillard231d7912003-02-09 14:22:17 +00005905 xmlNodePtr cur, oldseq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005906
5907 if (define->data != NULL) {
5908 partitions = (xmlRelaxNGPartitionPtr) define->data;
5909 } else {
5910 VALID_CTXT();
5911 VALID_ERROR("Internal: interleave block has no data\n");
5912 return(-1);
5913 }
5914
5915 /*
5916 * Build the sequence of child and an array preserving the children
5917 * initial order.
5918 */
5919 cur = ctxt->state->seq;
Daniel Veillard231d7912003-02-09 14:22:17 +00005920 oldseq = ctxt->state->seq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005921 nbchildren = 0;
5922 nbtot = 0;
5923 while (cur != NULL) {
5924 if ((cur->type == XML_COMMENT_NODE) ||
5925 (cur->type == XML_PI_NODE) ||
5926 ((cur->type == XML_TEXT_NODE) &&
5927 (IS_BLANK_NODE(cur)))) {
5928 nbtot++;
5929 } else {
5930 nbchildren++;
5931 nbtot++;
5932 }
5933 cur = cur->next;
5934 }
5935 children = (xmlNodePtr *) xmlMalloc(nbchildren * sizeof(xmlNodePtr));
5936 if (children == NULL)
5937 goto error;
5938 order = (xmlNodePtr *) xmlMalloc(nbtot * sizeof(xmlNodePtr));
5939 if (order == NULL)
5940 goto error;
5941 cur = ctxt->state->seq;
5942 i = 0;
5943 j = 0;
5944 while (cur != NULL) {
5945 if ((cur->type == XML_COMMENT_NODE) ||
5946 (cur->type == XML_PI_NODE) ||
5947 ((cur->type == XML_TEXT_NODE) &&
5948 (IS_BLANK_NODE(cur)))) {
5949 order[j++] = cur;
5950 } else {
5951 order[j++] = cur;
5952 children[i++] = cur;
5953 }
5954 cur = cur->next;
5955 }
5956
5957 /* TODO: retry with a maller set of child if there is a next... */
5958 ret = xmlRelaxNGValidatePartGroup(ctxt, partitions->groups,
5959 partitions->nbgroups, children, nbchildren);
Daniel Veillard231d7912003-02-09 14:22:17 +00005960 if (ret != 0)
5961 ctxt->state->seq = oldseq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005962
5963 /*
5964 * Cleanup: rebuid the child sequence and free the structure
5965 */
5966 if (order != NULL) {
5967 for (i = 0;i < nbtot;i++) {
5968 if (i == 0)
5969 order[i]->prev = NULL;
5970 else
5971 order[i]->prev = order[i - 1];
5972 if (i == nbtot - 1)
5973 order[i]->next = NULL;
5974 else
5975 order[i]->next = order[i + 1];
5976 }
5977 xmlFree(order);
5978 }
5979 if (children != NULL)
5980 xmlFree(children);
5981
5982 return(ret);
5983
5984error:
5985 if (order != NULL) {
5986 for (i = 0;i < nbtot;i++) {
5987 if (i == 0)
5988 order[i]->prev = NULL;
5989 else
5990 order[i]->prev = order[i - 1];
5991 if (i == nbtot - 1)
5992 order[i]->next = NULL;
5993 else
5994 order[i]->next = order[i + 1];
5995 }
5996 xmlFree(order);
5997 }
5998 if (children != NULL)
5999 xmlFree(children);
6000 return(-1);
6001}
6002
6003/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00006004 * xmlRelaxNGValidateElementContent:
6005 * @ctxt: a Relax-NG validation context
6006 * @define: the list of definition to verify
6007 *
6008 * Validate the given node content against the (list) of definitions
6009 *
6010 * Returns 0 if the validation succeeded or an error code.
6011 */
6012static int
6013xmlRelaxNGValidateElementContent(xmlRelaxNGValidCtxtPtr ctxt,
6014 xmlRelaxNGDefinePtr defines) {
6015 int ret = 0, res;
6016
6017 if (ctxt->state == NULL) {
6018 VALID_CTXT();
6019 VALID_ERROR("Internal: no state\n");
6020 return(-1);
6021 }
6022 while (defines != NULL) {
6023 res = xmlRelaxNGValidateDefinition(ctxt, defines);
6024 if (res < 0)
6025 ret = -1;
6026 defines = defines->next;
6027 }
6028
6029 return(ret);
6030}
6031
6032/**
Daniel Veillard416589a2003-02-17 17:25:42 +00006033 * xmlRelaxNGElementMatch:
6034 * @ctxt: a Relax-NG validation context
6035 * @define: the definition to check
6036 * @elem: the element
6037 *
6038 * Check if the element matches the definition nameClass
6039 *
6040 * Returns 1 if the element matches, 0 if no, or -1 in case of error
6041 */
6042static int
6043xmlRelaxNGElementMatch(xmlRelaxNGValidCtxtPtr ctxt,
6044 xmlRelaxNGDefinePtr define,
6045 xmlNodePtr elem) {
6046 int ret, oldflags;
6047
6048 if (define->name != NULL) {
6049 if (!xmlStrEqual(elem->name, define->name)) {
6050 VALID_CTXT();
6051 VALID_ERROR3("Expecting element %s, got %s\n",
6052 define->name, elem->name);
6053 return(0);
6054 }
6055 }
6056 if ((define->ns != NULL) && (define->ns[0] != 0)) {
6057 if (elem->ns == NULL) {
6058 VALID_CTXT();
6059 VALID_ERROR2("Expecting a namespace for element %s\n",
6060 elem->name);
6061 return(0);
6062 } else if (!xmlStrEqual(elem->ns->href, define->ns)) {
6063 VALID_CTXT();
6064 VALID_ERROR3("Expecting element %s has wrong namespace: expecting %s\n",
6065 elem->name, define->ns);
6066 return(0);
6067 }
Daniel Veillard8fe98712003-02-19 00:19:14 +00006068 } else if ((elem->ns != NULL) && (define->ns != NULL) &&
6069 (define->name == NULL)) {
6070 VALID_CTXT();
6071 VALID_ERROR2("Expecting no namespace for element %s\n",
6072 define->name);
6073 return(0);
6074 } else if ((elem->ns != NULL) && (define->name != NULL)) {
6075 VALID_CTXT();
6076 VALID_ERROR2("Expecting no namespace for element %s\n",
6077 define->name);
6078 return(0);
Daniel Veillard416589a2003-02-17 17:25:42 +00006079 }
6080
6081 if (define->nameClass == NULL)
6082 return(1);
6083
6084 define = define->nameClass;
6085 if (define->type == XML_RELAXNG_EXCEPT) {
6086 xmlRelaxNGDefinePtr list;
6087 oldflags = ctxt->flags;
6088 ctxt->flags |= FLAGS_IGNORABLE;
6089
6090 list = define->content;
6091 while (list != NULL) {
6092 ret = xmlRelaxNGElementMatch(ctxt, list, elem);
6093 if (ret == 1) {
6094 ctxt->flags = oldflags;
6095 return(0);
6096 }
6097 if (ret < 0) {
6098 ctxt->flags = oldflags;
6099 return(ret);
6100 }
6101 list = list->next;
6102 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006103 ret = 1;
6104 ctxt->flags = oldflags;
6105 } else if (define->type == XML_RELAXNG_CHOICE) {
6106 xmlRelaxNGDefinePtr list;
6107 oldflags = ctxt->flags;
6108 ctxt->flags |= FLAGS_IGNORABLE;
6109
6110 list = define->nameClass;
6111 while (list != NULL) {
6112 ret = xmlRelaxNGElementMatch(ctxt, list, elem);
6113 if (ret == 1) {
6114 ctxt->flags = oldflags;
6115 return(1);
6116 }
6117 if (ret < 0) {
6118 ctxt->flags = oldflags;
6119 return(ret);
6120 }
6121 list = list->next;
6122 }
6123 ret = 0;
Daniel Veillard416589a2003-02-17 17:25:42 +00006124 ctxt->flags = oldflags;
6125 } else {
6126 TODO
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006127 ret = -1;
Daniel Veillard416589a2003-02-17 17:25:42 +00006128 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006129 return(ret);
Daniel Veillard416589a2003-02-17 17:25:42 +00006130}
6131
6132/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00006133 * xmlRelaxNGValidateDefinition:
6134 * @ctxt: a Relax-NG validation context
6135 * @define: the definition to verify
6136 *
6137 * Validate the current node against the definition
6138 *
6139 * Returns 0 if the validation succeeded or an error code.
6140 */
6141static int
6142xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
6143 xmlRelaxNGDefinePtr define) {
6144 xmlNodePtr node;
6145 int ret = 0, i, tmp, oldflags;
6146 xmlRelaxNGValidStatePtr oldstate, state;
6147
6148 if (define == NULL) {
6149 VALID_CTXT();
6150 VALID_ERROR("internal error: define == NULL\n");
6151 return(-1);
6152 }
6153 if (ctxt->state != NULL) {
6154 node = ctxt->state->seq;
6155 } else {
6156 node = NULL;
6157 }
Daniel Veillard231d7912003-02-09 14:22:17 +00006158#ifdef DEBUG
6159 for (i = 0;i < ctxt->depth;i++)
6160 xmlGenericError(xmlGenericErrorContext, " ");
6161 xmlGenericError(xmlGenericErrorContext,
6162 "Start validating %s ", xmlRelaxNGDefName(define));
6163 if (define->name != NULL)
6164 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
6165 if ((node != NULL) && (node->name != NULL))
6166 xmlGenericError(xmlGenericErrorContext, "on %s\n", node->name);
6167 else
6168 xmlGenericError(xmlGenericErrorContext, "\n");
6169#endif
6170 ctxt->depth++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006171 switch (define->type) {
6172 case XML_RELAXNG_EMPTY:
Daniel Veillardd4310742003-02-18 21:12:46 +00006173 node = xmlRelaxNGSkipIgnored(ctxt, node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006174 if (node != NULL) {
6175 VALID_CTXT();
6176 VALID_ERROR("Expecting an empty element\n");
Daniel Veillard231d7912003-02-09 14:22:17 +00006177 ret = -1;
6178 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006179 }
Daniel Veillard231d7912003-02-09 14:22:17 +00006180 ret = 0;
6181 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006182 case XML_RELAXNG_NOT_ALLOWED:
Daniel Veillard231d7912003-02-09 14:22:17 +00006183 ret = -1;
6184 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006185 case XML_RELAXNG_TEXT:
Daniel Veillardce14fa52003-02-19 17:32:48 +00006186#if 0
Daniel Veillard231d7912003-02-09 14:22:17 +00006187 if (node == NULL) {
6188 ret = 0;
6189 break;
6190 }
Daniel Veillardce14fa52003-02-19 17:32:48 +00006191#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00006192 while ((node != NULL) &&
6193 ((node->type == XML_TEXT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006194 (node->type == XML_COMMENT_NODE) ||
6195 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00006196 (node->type == XML_CDATA_SECTION_NODE)))
6197 node = node->next;
Daniel Veillardce14fa52003-02-19 17:32:48 +00006198#if 0
Daniel Veillard276be4a2003-01-24 01:03:34 +00006199 if (node == ctxt->state->seq) {
6200 VALID_CTXT();
6201 VALID_ERROR("Expecting text content\n");
6202 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006203 }
Daniel Veillardce14fa52003-02-19 17:32:48 +00006204#endif
Daniel Veillard276be4a2003-01-24 01:03:34 +00006205 ctxt->state->seq = node;
6206 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006207 case XML_RELAXNG_ELEMENT:
6208 node = xmlRelaxNGSkipIgnored(ctxt, node);
Daniel Veillard231d7912003-02-09 14:22:17 +00006209 if (node == NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006210 VALID_CTXT();
Daniel Veillard231d7912003-02-09 14:22:17 +00006211 VALID_ERROR("Expecting an element, got empty\n");
6212 ret = -1;
6213 break;
6214 }
6215 if (node->type != XML_ELEMENT_NODE) {
6216 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006217 VALID_ERROR2("Expecting an element got %d type\n", node->type);
Daniel Veillard231d7912003-02-09 14:22:17 +00006218 ret = -1;
6219 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006220 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00006221 /*
6222 * This node was already validated successfully against
6223 * this definition.
6224 */
6225 if (node->_private == define)
6226 break;
Daniel Veillard416589a2003-02-17 17:25:42 +00006227
6228 ret = xmlRelaxNGElementMatch(ctxt, define, node);
6229 if (ret <= 0) {
6230 ret = -1;
6231 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006232 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006233 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006234
6235 state = xmlRelaxNGNewValidState(ctxt, node);
6236 if (state == NULL) {
Daniel Veillard231d7912003-02-09 14:22:17 +00006237 ret = -1;
6238 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006239 }
6240
6241 oldstate = ctxt->state;
6242 ctxt->state = state;
6243 if (define->attrs != NULL) {
6244 tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
Daniel Veillard231d7912003-02-09 14:22:17 +00006245 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006246 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00006247#ifdef DEBUG
6248 xmlGenericError(xmlGenericErrorContext,
6249 "E: Element %s failed to validate attributes\n",
6250 node->name);
6251#endif
6252 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006253 }
6254 if (define->content != NULL) {
6255 tmp = xmlRelaxNGValidateElementContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00006256 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006257 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00006258#ifdef DEBUG
6259 xmlGenericError(xmlGenericErrorContext,
6260 "E: Element %s failed to validate element content\n",
6261 node->name);
6262#endif
6263 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006264 }
6265 state = ctxt->state;
6266 if (state->seq != NULL) {
6267 state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq);
6268 if (state->seq != NULL) {
6269 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006270 VALID_ERROR3("Extra content for element %s: %s\n",
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006271 node->name, state->seq->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006272 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00006273#ifdef DEBUG
6274 xmlGenericError(xmlGenericErrorContext,
6275 "E: Element %s has extra content: %s\n",
6276 node->name, state->seq->name);
6277#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00006278 }
6279 }
6280 for (i = 0;i < state->nbAttrs;i++) {
6281 if (state->attrs[i] != NULL) {
6282 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006283 VALID_ERROR3("Invalid attribute %s for element %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00006284 state->attrs[i]->name, node->name);
6285 ret = -1;
6286 }
6287 }
6288 ctxt->state = oldstate;
6289 xmlRelaxNGFreeValidState(state);
6290 if (oldstate != NULL)
Daniel Veillarde5b110b2003-02-04 14:43:39 +00006291 oldstate->seq = xmlRelaxNGSkipIgnored(ctxt, node->next);
6292 if (ret == 0)
6293 node->_private = define;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006294
6295
6296#ifdef DEBUG
6297 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard231d7912003-02-09 14:22:17 +00006298 "xmlRelaxNGValidateDefinition(): validated %s : %d",
Daniel Veillard6eadf632003-01-23 18:29:16 +00006299 node->name, ret);
Daniel Veillard231d7912003-02-09 14:22:17 +00006300 if (oldstate == NULL)
6301 xmlGenericError(xmlGenericErrorContext, ": no state\n");
6302 else if (oldstate->seq == NULL)
6303 xmlGenericError(xmlGenericErrorContext, ": done\n");
6304 else if (oldstate->seq->type == XML_ELEMENT_NODE)
6305 xmlGenericError(xmlGenericErrorContext, ": next elem %s\n",
6306 oldstate->seq->name);
6307 else
6308 xmlGenericError(xmlGenericErrorContext, ": next %s %d\n",
6309 oldstate->seq->name, oldstate->seq->type);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006310#endif
6311 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006312 case XML_RELAXNG_OPTIONAL:
6313 oldflags = ctxt->flags;
6314 ctxt->flags |= FLAGS_IGNORABLE;
6315 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6316 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6317 if (ret != 0) {
6318 xmlRelaxNGFreeValidState(ctxt->state);
6319 ctxt->state = oldstate;
6320 ret = 0;
6321 break;
6322 }
6323 xmlRelaxNGFreeValidState(oldstate);
6324 ctxt->flags = oldflags;
6325 ret = 0;
6326 break;
6327 case XML_RELAXNG_ONEORMORE:
6328 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6329 if (ret != 0) {
6330 break;
6331 }
6332 /* no break on purpose */
Daniel Veillard276be4a2003-01-24 01:03:34 +00006333 case XML_RELAXNG_ZEROORMORE: {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006334 oldflags = ctxt->flags;
6335 ctxt->flags |= FLAGS_IGNORABLE;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006336 while (ctxt->state->nbAttrLeft != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006337 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6338 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6339 if (ret != 0) {
6340 xmlRelaxNGFreeValidState(ctxt->state);
6341 ctxt->state = oldstate;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006342 break;
6343 }
6344 xmlRelaxNGFreeValidState(oldstate);
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006345 }
6346 if (ret == 0) {
6347 /*
6348 * There is no attribute left to be consumed,
6349 * we can check the closure by looking at ctxt->state->seq
6350 */
6351 xmlNodePtr cur, temp;
6352
Daniel Veillard276be4a2003-01-24 01:03:34 +00006353 cur = ctxt->state->seq;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006354 temp = NULL;
6355 while ((cur != NULL) && (temp != cur)) {
6356 temp = cur;
6357 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6358 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6359 if (ret != 0) {
6360 xmlRelaxNGFreeValidState(ctxt->state);
6361 ctxt->state = oldstate;
6362 break;
6363 }
6364 xmlRelaxNGFreeValidState(oldstate);
6365 cur = ctxt->state->seq;
6366 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006367 }
6368 ctxt->flags = oldflags;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006369 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006370 break;
Daniel Veillard276be4a2003-01-24 01:03:34 +00006371 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006372 case XML_RELAXNG_CHOICE: {
6373 xmlRelaxNGDefinePtr list = define->content;
Daniel Veillardce14fa52003-02-19 17:32:48 +00006374 int success = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006375
6376 oldflags = ctxt->flags;
6377 ctxt->flags |= FLAGS_IGNORABLE;
6378
6379 while (list != NULL) {
6380 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6381 ret = xmlRelaxNGValidateDefinition(ctxt, list);
6382 if (ret == 0) {
Daniel Veillardce14fa52003-02-19 17:32:48 +00006383 if (xmlRelaxNGEqualValidState(ctxt, ctxt->state, oldstate)){
6384 /*
6385 * if that pattern was nullable flag it but try
6386 * to make more progresses
6387 */
6388 success = 1;
6389 } else {
6390 xmlRelaxNGFreeValidState(oldstate);
6391 break;
6392 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006393 }
6394 xmlRelaxNGFreeValidState(ctxt->state);
6395 ctxt->state = oldstate;
6396 list = list->next;
6397 }
6398 ctxt->flags = oldflags;
Daniel Veillardce14fa52003-02-19 17:32:48 +00006399 if (success == 1)
6400 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006401 break;
6402 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00006403 case XML_RELAXNG_DEF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00006404 case XML_RELAXNG_GROUP: {
6405 xmlRelaxNGDefinePtr list = define->content;
6406
6407 while (list != NULL) {
6408 ret = xmlRelaxNGValidateDefinition(ctxt, list);
6409 if (ret != 0)
6410 break;
6411 list = list->next;
6412 }
6413 break;
6414 }
6415 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006416 ret = xmlRelaxNGValidateInterleave(ctxt, define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006417 break;
6418 case XML_RELAXNG_ATTRIBUTE:
6419 ret = xmlRelaxNGValidateAttribute(ctxt, define);
6420 break;
6421 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00006422 case XML_RELAXNG_PARENTREF:
6423 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00006424 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6425 break;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006426 case XML_RELAXNG_DATATYPE: {
Daniel Veillardd4310742003-02-18 21:12:46 +00006427 xmlNodePtr child;
6428 xmlChar *content = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006429
Daniel Veillardd4310742003-02-18 21:12:46 +00006430 child = node;
6431 while (child != NULL) {
6432 if (child->type == XML_ELEMENT_NODE) {
6433 VALID_CTXT();
6434 VALID_ERROR2("Element %s has child elements\n",
6435 node->parent->name);
6436 ret = -1;
6437 break;
6438 } else if ((child->type == XML_TEXT_NODE) ||
6439 (child->type == XML_CDATA_SECTION_NODE)) {
6440 content = xmlStrcat(content, child->content);
6441 }
6442 /* TODO: handle entities ... */
6443 child = child->next;
6444 }
6445 if (ret == -1) {
6446 if (content != NULL)
6447 xmlFree(content);
6448 break;
6449 }
6450 if (content == NULL) {
6451 content = xmlStrdup(BAD_CAST "");
6452 if (content == NULL) {
6453 VALID_CTXT();
6454 VALID_ERROR("Allocation failure\n");
6455 ret = -1;
6456 break;
6457 }
6458 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006459 ret = xmlRelaxNGValidateDatatype(ctxt, content, define);
6460 if (ret == -1) {
6461 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006462 VALID_ERROR2("internal error validating %s\n", define->name);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006463 } else if (ret == 0) {
Daniel Veillardd4310742003-02-18 21:12:46 +00006464 ctxt->state->seq = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006465 }
6466 if (content != NULL)
6467 xmlFree(content);
6468 break;
6469 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00006470 case XML_RELAXNG_VALUE: {
Daniel Veillardd4310742003-02-18 21:12:46 +00006471 xmlChar *content = NULL;
Daniel Veillardea3f3982003-01-26 19:45:18 +00006472 xmlChar *oldvalue;
Daniel Veillardd4310742003-02-18 21:12:46 +00006473 xmlNodePtr child;
Daniel Veillardea3f3982003-01-26 19:45:18 +00006474
Daniel Veillardd4310742003-02-18 21:12:46 +00006475 child = node;
6476 while (child != NULL) {
6477 if (child->type == XML_ELEMENT_NODE) {
6478 VALID_CTXT();
6479 VALID_ERROR2("Element %s has child elements\n",
6480 node->parent->name);
6481 ret = -1;
6482 break;
6483 } else if ((child->type == XML_TEXT_NODE) ||
6484 (child->type == XML_CDATA_SECTION_NODE)) {
6485 content = xmlStrcat(content, child->content);
6486 }
6487 /* TODO: handle entities ... */
6488 child = child->next;
6489 }
6490 if (ret == -1) {
6491 if (content != NULL)
6492 xmlFree(content);
6493 break;
6494 }
6495 if (content == NULL) {
6496 content = xmlStrdup(BAD_CAST "");
6497 if (content == NULL) {
6498 VALID_CTXT();
6499 VALID_ERROR("Allocation failure\n");
6500 ret = -1;
6501 break;
6502 }
6503 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00006504 oldvalue = ctxt->state->value;
6505 ctxt->state->value = content;
6506 ret = xmlRelaxNGValidateValue(ctxt, define);
6507 ctxt->state->value = oldvalue;
6508 if (ret == -1) {
6509 VALID_CTXT();
Daniel Veillardd2298792003-02-14 16:54:11 +00006510 if (define->name != NULL) {
6511 VALID_ERROR2("error validating value %s\n", define->name);
6512 } else {
6513 VALID_ERROR("error validating value\n");
6514 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00006515 } else if (ret == 0) {
Daniel Veillardd4310742003-02-18 21:12:46 +00006516 ctxt->state->seq = NULL;
Daniel Veillardea3f3982003-01-26 19:45:18 +00006517 }
6518 if (content != NULL)
6519 xmlFree(content);
6520 break;
6521 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006522 case XML_RELAXNG_LIST: {
6523 xmlChar *content;
Daniel Veillardd4310742003-02-18 21:12:46 +00006524 xmlNodePtr child;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006525 xmlChar *oldvalue, *oldendvalue;
6526 int len;
Daniel Veillardea3f3982003-01-26 19:45:18 +00006527
Daniel Veillardd4310742003-02-18 21:12:46 +00006528 /*
6529 * Make sure it's only text nodes
6530 */
6531
6532 content = NULL;
6533 child = node;
6534 while (child != NULL) {
6535 if (child->type == XML_ELEMENT_NODE) {
6536 VALID_CTXT();
6537 VALID_ERROR2("Element %s has child elements\n",
6538 node->parent->name);
6539 ret = -1;
6540 break;
6541 } else if ((child->type == XML_TEXT_NODE) ||
6542 (child->type == XML_CDATA_SECTION_NODE)) {
6543 content = xmlStrcat(content, child->content);
6544 }
6545 /* TODO: handle entities ... */
6546 child = child->next;
6547 }
6548 if (ret == -1) {
6549 if (content != NULL)
6550 xmlFree(content);
6551 break;
6552 }
6553 if (content == NULL) {
6554 content = xmlStrdup(BAD_CAST "");
6555 if (content == NULL) {
6556 VALID_CTXT();
6557 VALID_ERROR("Allocation failure\n");
6558 ret = -1;
6559 break;
6560 }
6561 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006562 len = xmlStrlen(content);
6563 oldvalue = ctxt->state->value;
6564 oldendvalue = ctxt->state->endvalue;
6565 ctxt->state->value = content;
6566 ctxt->state->endvalue = content + len;
6567 ret = xmlRelaxNGValidateValue(ctxt, define);
6568 ctxt->state->value = oldvalue;
6569 ctxt->state->endvalue = oldendvalue;
6570 if (ret == -1) {
6571 VALID_CTXT();
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006572 VALID_ERROR("error validating list\n");
Daniel Veillardd4310742003-02-18 21:12:46 +00006573 } else if ((ret == 0) && (node != NULL)) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006574 ctxt->state->seq = node->next;
6575 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006576 if (content != NULL)
6577 xmlFree(content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006578 break;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006579 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006580 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00006581 case XML_RELAXNG_EXCEPT:
Daniel Veillard8fe98712003-02-19 00:19:14 +00006582 case XML_RELAXNG_PARAM:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006583 TODO
Daniel Veillard416589a2003-02-17 17:25:42 +00006584 ret = -1;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006585 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006586 }
Daniel Veillard231d7912003-02-09 14:22:17 +00006587 ctxt->depth--;
6588#ifdef DEBUG
6589 for (i = 0;i < ctxt->depth;i++)
6590 xmlGenericError(xmlGenericErrorContext, " ");
6591 xmlGenericError(xmlGenericErrorContext,
6592 "Validating %s ", xmlRelaxNGDefName(define));
6593 if (define->name != NULL)
6594 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
6595 if (ret == 0)
6596 xmlGenericError(xmlGenericErrorContext, "suceeded\n");
6597 else
6598 xmlGenericError(xmlGenericErrorContext, "failed\n");
6599#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00006600 return(ret);
6601}
6602
6603/**
6604 * xmlRelaxNGValidateDocument:
6605 * @ctxt: a Relax-NG validation context
6606 * @doc: the document
6607 *
6608 * Validate the given document
6609 *
6610 * Returns 0 if the validation succeeded or an error code.
6611 */
6612static int
6613xmlRelaxNGValidateDocument(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
6614 int ret;
6615 xmlRelaxNGPtr schema;
6616 xmlRelaxNGGrammarPtr grammar;
6617 xmlRelaxNGValidStatePtr state;
6618
6619 if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL))
6620 return(-1);
6621
6622 schema = ctxt->schema;
6623 grammar = schema->topgrammar;
6624 if (grammar == NULL) {
6625 VALID_CTXT();
6626 VALID_ERROR("No top grammar defined\n");
6627 return(-1);
6628 }
6629 state = xmlRelaxNGNewValidState(ctxt, NULL);
6630 ctxt->state = state;
6631 ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start);
6632 state = ctxt->state;
6633 if ((state != NULL) && (state->seq != NULL)) {
6634 xmlNodePtr node;
6635
6636 node = state->seq;
6637 node = xmlRelaxNGSkipIgnored(ctxt, node);
6638 if (node != NULL) {
6639 VALID_CTXT();
6640 VALID_ERROR("extra data on the document\n");
6641 ret = -1;
6642 }
6643 }
6644 xmlRelaxNGFreeValidState(state);
6645
6646 return(ret);
6647}
6648
6649/************************************************************************
6650 * *
6651 * Validation interfaces *
6652 * *
6653 ************************************************************************/
6654/**
6655 * xmlRelaxNGNewValidCtxt:
6656 * @schema: a precompiled XML RelaxNGs
6657 *
6658 * Create an XML RelaxNGs validation context based on the given schema
6659 *
6660 * Returns the validation context or NULL in case of error
6661 */
6662xmlRelaxNGValidCtxtPtr
6663xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema) {
6664 xmlRelaxNGValidCtxtPtr ret;
6665
6666 ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt));
6667 if (ret == NULL) {
6668 xmlGenericError(xmlGenericErrorContext,
6669 "Failed to allocate new schama validation context\n");
6670 return (NULL);
6671 }
6672 memset(ret, 0, sizeof(xmlRelaxNGValidCtxt));
6673 ret->schema = schema;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006674 ret->error = xmlGenericError;
6675 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006676 return (ret);
6677}
6678
6679/**
6680 * xmlRelaxNGFreeValidCtxt:
6681 * @ctxt: the schema validation context
6682 *
6683 * Free the resources associated to the schema validation context
6684 */
6685void
6686xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt) {
6687 if (ctxt == NULL)
6688 return;
6689 xmlFree(ctxt);
6690}
6691
6692/**
6693 * xmlRelaxNGSetValidErrors:
6694 * @ctxt: a Relax-NG validation context
6695 * @err: the error function
6696 * @warn: the warning function
6697 * @ctx: the functions context
6698 *
6699 * Set the error and warning callback informations
6700 */
6701void
6702xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
6703 xmlRelaxNGValidityErrorFunc err,
6704 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
6705 if (ctxt == NULL)
6706 return;
6707 ctxt->error = err;
6708 ctxt->warning = warn;
6709 ctxt->userData = ctx;
6710}
6711
6712/**
6713 * xmlRelaxNGValidateDoc:
6714 * @ctxt: a Relax-NG validation context
6715 * @doc: a parsed document tree
6716 *
6717 * Validate a document tree in memory.
6718 *
6719 * Returns 0 if the document is valid, a positive error code
6720 * number otherwise and -1 in case of internal or API error.
6721 */
6722int
6723xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
6724 int ret;
6725
6726 if ((ctxt == NULL) || (doc == NULL))
6727 return(-1);
6728
6729 ctxt->doc = doc;
6730
6731 ret = xmlRelaxNGValidateDocument(ctxt, doc);
Daniel Veillard71531f32003-02-05 13:19:53 +00006732 /*
6733 * TODO: build error codes
6734 */
6735 if (ret == -1)
6736 return(1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006737 return(ret);
6738}
6739
6740#endif /* LIBXML_SCHEMAS_ENABLED */
6741