blob: d6f1f17923e4f31bcb497cebbd42d46ff6a69756 [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 */
Daniel Veillard416589a2003-02-17 17:25:42 +0000103#if 0
104 XML_RELAXNG_MIXED, /* mixed content with single sub-content */
105#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +0000106 XML_RELAXNG_ELEMENT, /* an element */
107 XML_RELAXNG_DATATYPE, /* extenal data type definition */
108 XML_RELAXNG_VALUE, /* value from an extenal data type definition */
109 XML_RELAXNG_LIST, /* a list of patterns */
110 XML_RELAXNG_ATTRIBUTE, /* an attrbute following a pattern */
111 XML_RELAXNG_DEF, /* a definition */
112 XML_RELAXNG_REF, /* reference to a definition */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000113 XML_RELAXNG_EXTERNALREF, /* reference to an external def */
Daniel Veillard419a7682003-02-03 23:22:49 +0000114 XML_RELAXNG_PARENTREF, /* reference to a def in the parent grammar */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000115 XML_RELAXNG_OPTIONAL, /* optional patterns */
116 XML_RELAXNG_ZEROORMORE, /* zero or more non empty patterns */
117 XML_RELAXNG_ONEORMORE, /* one or more non empty patterns */
118 XML_RELAXNG_CHOICE, /* a choice between non empty patterns */
119 XML_RELAXNG_GROUP, /* a pair/group of non empty patterns */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000120 XML_RELAXNG_INTERLEAVE, /* interleaving choice of non-empty patterns */
121 XML_RELAXNG_START /* Used to keep track of starts on grammars */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000122} xmlRelaxNGType;
123
124struct _xmlRelaxNGDefine {
125 xmlRelaxNGType type; /* the type of definition */
126 xmlNodePtr node; /* the node in the source */
127 xmlChar *name; /* the element local name if present */
128 xmlChar *ns; /* the namespace local name if present */
Daniel Veillardedc91922003-01-26 00:52:04 +0000129 xmlChar *value; /* value when available */
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000130 void *data; /* data lib or specific pointer */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000131 xmlRelaxNGDefinePtr content;/* the expected content */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000132 xmlRelaxNGDefinePtr parent; /* the parent definition, if any */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000133 xmlRelaxNGDefinePtr next; /* list within grouping sequences */
134 xmlRelaxNGDefinePtr attrs; /* list of attributes for elements */
Daniel Veillard3b2e4e12003-02-03 08:52:58 +0000135 xmlRelaxNGDefinePtr nameClass;/* the nameClass definition if any */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000136 xmlRelaxNGDefinePtr nextHash;/* next define in defs/refs hash tables */
137};
138
139/**
140 * _xmlRelaxNG:
141 *
142 * A RelaxNGs definition
143 */
144struct _xmlRelaxNG {
145 xmlRelaxNGGrammarPtr topgrammar;
146 xmlDocPtr doc;
147
148 xmlHashTablePtr defs; /* define */
149 xmlHashTablePtr refs; /* references */
Daniel Veillard419a7682003-02-03 23:22:49 +0000150 xmlHashTablePtr documents; /* all the documents loaded */
151 xmlHashTablePtr includes; /* all the includes loaded */
152 int defNr; /* number of defines used */
153 xmlRelaxNGDefinePtr *defTab;/* pointer to the allocated definitions */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000154 void *_private; /* unused by the library for users or bindings */
155};
156
157typedef enum {
158 XML_RELAXNG_ERR_OK = 0,
159 XML_RELAXNG_ERR_NOROOT = 1,
160 XML_RELAXNG_ERR_
161} xmlRelaxNGValidError;
162
163#define XML_RELAXNG_IN_ATTRIBUTE 1
164
165struct _xmlRelaxNGParserCtxt {
166 void *userData; /* user specific data block */
167 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
168 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
169 xmlRelaxNGValidError err;
170
171 xmlRelaxNGPtr schema; /* The schema in use */
172 xmlRelaxNGGrammarPtr grammar; /* the current grammar */
Daniel Veillard419a7682003-02-03 23:22:49 +0000173 xmlRelaxNGGrammarPtr parentgrammar;/* the parent grammar */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000174 int flags; /* parser flags */
175 int nbErrors; /* number of errors at parse time */
176 int nbWarnings; /* number of warnings at parse time */
Daniel Veillard276be4a2003-01-24 01:03:34 +0000177 const xmlChar *define; /* the current define scope */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000178 xmlRelaxNGDefinePtr def; /* the current define */
179
180 int nbInterleaves;
181 xmlHashTablePtr interleaves; /* keep track of all the interleaves */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000182
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000183 xmlHashTablePtr documents; /* all the documents loaded */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000184 xmlHashTablePtr includes; /* all the includes loaded */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000185 xmlChar *URL;
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000186 xmlDocPtr document;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000187
Daniel Veillard419a7682003-02-03 23:22:49 +0000188 int defNr; /* number of defines used */
189 int defMax; /* number of defines aloocated */
190 xmlRelaxNGDefinePtr *defTab; /* pointer to the allocated definitions */
191
Daniel Veillard6eadf632003-01-23 18:29:16 +0000192 const char *buffer;
193 int size;
194
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000195 /* the document stack */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000196 xmlRelaxNGDocumentPtr doc; /* Current parsed external ref */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000197 int docNr; /* Depth of the parsing stack */
198 int docMax; /* Max depth of the parsing stack */
199 xmlRelaxNGDocumentPtr *docTab; /* array of docs */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000200
201 /* the include stack */
202 xmlRelaxNGIncludePtr inc; /* Current parsed include */
203 int incNr; /* Depth of the include parsing stack */
204 int incMax; /* Max depth of the parsing stack */
205 xmlRelaxNGIncludePtr *incTab; /* array of incs */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000206};
207
208#define FLAGS_IGNORABLE 1
209#define FLAGS_NEGATIVE 2
210
211/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000212 * xmlRelaxNGInterleaveGroup:
213 *
214 * A RelaxNGs partition set associated to lists of definitions
215 */
216typedef struct _xmlRelaxNGInterleaveGroup xmlRelaxNGInterleaveGroup;
217typedef xmlRelaxNGInterleaveGroup *xmlRelaxNGInterleaveGroupPtr;
218struct _xmlRelaxNGInterleaveGroup {
219 xmlRelaxNGDefinePtr rule; /* the rule to satisfy */
220 xmlRelaxNGDefinePtr *defs; /* the array of element definitions */
221};
222
223/**
224 * xmlRelaxNGPartitions:
225 *
226 * A RelaxNGs partition associated to an interleave group
227 */
228typedef struct _xmlRelaxNGPartition xmlRelaxNGPartition;
229typedef xmlRelaxNGPartition *xmlRelaxNGPartitionPtr;
230struct _xmlRelaxNGPartition {
231 int nbgroups; /* number of groups in the partitions */
232 xmlRelaxNGInterleaveGroupPtr *groups;
233};
234
235/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000236 * xmlRelaxNGValidState:
237 *
238 * A RelaxNGs validation state
239 */
240#define MAX_ATTR 20
241typedef struct _xmlRelaxNGValidState xmlRelaxNGValidState;
242typedef xmlRelaxNGValidState *xmlRelaxNGValidStatePtr;
243struct _xmlRelaxNGValidState {
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000244 xmlNodePtr node; /* the current node */
245 xmlNodePtr seq; /* the sequence of children left to validate */
246 int nbAttrs; /* the number of attributes */
Daniel Veillard1ed7f362003-02-03 10:57:45 +0000247 int nbAttrLeft; /* the number of attributes left to validate */
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000248 xmlChar *value; /* the value when operating on string */
249 xmlChar *endvalue; /* the end value when operating on string */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000250 xmlAttrPtr attrs[1]; /* the array of attributes */
251};
252
253/**
254 * xmlRelaxNGValidCtxt:
255 *
256 * A RelaxNGs validation context
257 */
258
259struct _xmlRelaxNGValidCtxt {
260 void *userData; /* user specific data block */
261 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
262 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
263
264 xmlRelaxNGPtr schema; /* The schema in use */
265 xmlDocPtr doc; /* the document being validated */
266 xmlRelaxNGValidStatePtr state; /* the current validation state */
267 int flags; /* validation flags */
Daniel Veillard231d7912003-02-09 14:22:17 +0000268 int depth; /* validation depth */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000269};
270
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000271/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000272 * xmlRelaxNGInclude:
273 *
274 * Structure associated to a RelaxNGs document element
275 */
276struct _xmlRelaxNGInclude {
277 xmlChar *href; /* the normalized href value */
278 xmlDocPtr doc; /* the associated XML document */
279 xmlRelaxNGDefinePtr content;/* the definitions */
280 xmlRelaxNGPtr schema; /* the schema */
281};
282
283/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000284 * xmlRelaxNGDocument:
285 *
286 * Structure associated to a RelaxNGs document element
287 */
288struct _xmlRelaxNGDocument {
289 xmlChar *href; /* the normalized href value */
290 xmlDocPtr doc; /* the associated XML document */
291 xmlRelaxNGDefinePtr content;/* the definitions */
292 xmlRelaxNGPtr schema; /* the schema */
293};
294
Daniel Veillard6eadf632003-01-23 18:29:16 +0000295/************************************************************************
296 * *
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000297 * Preliminary type checking interfaces *
298 * *
299 ************************************************************************/
300/**
301 * xmlRelaxNGTypeHave:
302 * @data: data needed for the library
303 * @type: the type name
304 * @value: the value to check
305 *
306 * Function provided by a type library to check if a type is exported
307 *
308 * Returns 1 if yes, 0 if no and -1 in case of error.
309 */
310typedef int (*xmlRelaxNGTypeHave) (void *data, const xmlChar *type);
311
312/**
313 * xmlRelaxNGTypeCheck:
314 * @data: data needed for the library
315 * @type: the type name
316 * @value: the value to check
317 *
318 * Function provided by a type library to check if a value match a type
319 *
320 * Returns 1 if yes, 0 if no and -1 in case of error.
321 */
322typedef int (*xmlRelaxNGTypeCheck) (void *data, const xmlChar *type,
323 const xmlChar *value);
324
325/**
326 * xmlRelaxNGTypeCompare:
327 * @data: data needed for the library
328 * @type: the type name
329 * @value1: the first value
330 * @value2: the second value
331 *
332 * Function provided by a type library to compare two values accordingly
333 * to a type.
334 *
335 * Returns 1 if yes, 0 if no and -1 in case of error.
336 */
337typedef int (*xmlRelaxNGTypeCompare) (void *data, const xmlChar *type,
338 const xmlChar *value1,
339 const xmlChar *value2);
340typedef struct _xmlRelaxNGTypeLibrary xmlRelaxNGTypeLibrary;
341typedef xmlRelaxNGTypeLibrary *xmlRelaxNGTypeLibraryPtr;
342struct _xmlRelaxNGTypeLibrary {
343 const xmlChar *namespace; /* the datatypeLibrary value */
344 void *data; /* data needed for the library */
345 xmlRelaxNGTypeHave have; /* the export function */
346 xmlRelaxNGTypeCheck check; /* the checking function */
347 xmlRelaxNGTypeCompare comp; /* the compare function */
348};
349
350/************************************************************************
351 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +0000352 * Allocation functions *
353 * *
354 ************************************************************************/
Daniel Veillard6eadf632003-01-23 18:29:16 +0000355static void xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar);
356static void xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define);
Daniel Veillardd2298792003-02-14 16:54:11 +0000357static void xmlRelaxNGNormExtSpace(xmlChar *value);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000358
359/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000360 * xmlRelaxNGFreeDocument:
361 * @docu: a document structure
362 *
363 * Deallocate a RelaxNG document structure.
364 */
365static void
366xmlRelaxNGFreeDocument(xmlRelaxNGDocumentPtr docu)
367{
368 if (docu == NULL)
369 return;
370
371 if (docu->href != NULL)
372 xmlFree(docu->href);
373 if (docu->doc != NULL)
374 xmlFreeDoc(docu->doc);
375 if (docu->schema != NULL)
376 xmlRelaxNGFree(docu->schema);
377 xmlFree(docu);
378}
379
380/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000381 * xmlRelaxNGFreeInclude:
382 * @incl: a include structure
383 *
384 * Deallocate a RelaxNG include structure.
385 */
386static void
387xmlRelaxNGFreeInclude(xmlRelaxNGIncludePtr incl)
388{
389 if (incl == NULL)
390 return;
391
392 if (incl->href != NULL)
393 xmlFree(incl->href);
394 if (incl->doc != NULL)
395 xmlFreeDoc(incl->doc);
396 if (incl->schema != NULL)
397 xmlRelaxNGFree(incl->schema);
398 xmlFree(incl);
399}
400
401/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000402 * xmlRelaxNGNewRelaxNG:
403 * @ctxt: a Relax-NG validation context (optional)
404 *
405 * Allocate a new RelaxNG structure.
406 *
407 * Returns the newly allocated structure or NULL in case or error
408 */
409static xmlRelaxNGPtr
410xmlRelaxNGNewRelaxNG(xmlRelaxNGParserCtxtPtr ctxt)
411{
412 xmlRelaxNGPtr ret;
413
414 ret = (xmlRelaxNGPtr) xmlMalloc(sizeof(xmlRelaxNG));
415 if (ret == NULL) {
416 if ((ctxt != NULL) && (ctxt->error != NULL))
417 ctxt->error(ctxt->userData, "Out of memory\n");
418 ctxt->nbErrors++;
419 return (NULL);
420 }
421 memset(ret, 0, sizeof(xmlRelaxNG));
422
423 return (ret);
424}
425
426/**
427 * xmlRelaxNGFree:
428 * @schema: a schema structure
429 *
430 * Deallocate a RelaxNG structure.
431 */
432void
433xmlRelaxNGFree(xmlRelaxNGPtr schema)
434{
435 if (schema == NULL)
436 return;
437
Daniel Veillard6eadf632003-01-23 18:29:16 +0000438 if (schema->topgrammar != NULL)
439 xmlRelaxNGFreeGrammar(schema->topgrammar);
440 if (schema->doc != NULL)
441 xmlFreeDoc(schema->doc);
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000442 if (schema->documents != NULL)
443 xmlHashFree(schema->documents, (xmlHashDeallocator)
444 xmlRelaxNGFreeDocument);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000445 if (schema->includes != NULL)
446 xmlHashFree(schema->includes, (xmlHashDeallocator)
447 xmlRelaxNGFreeInclude);
Daniel Veillard419a7682003-02-03 23:22:49 +0000448 if (schema->defTab != NULL) {
449 int i;
450
451 for (i = 0;i < schema->defNr;i++)
452 xmlRelaxNGFreeDefine(schema->defTab[i]);
453 xmlFree(schema->defTab);
454 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000455
456 xmlFree(schema);
457}
458
459/**
460 * xmlRelaxNGNewGrammar:
461 * @ctxt: a Relax-NG validation context (optional)
462 *
463 * Allocate a new RelaxNG grammar.
464 *
465 * Returns the newly allocated structure or NULL in case or error
466 */
467static xmlRelaxNGGrammarPtr
468xmlRelaxNGNewGrammar(xmlRelaxNGParserCtxtPtr ctxt)
469{
470 xmlRelaxNGGrammarPtr ret;
471
472 ret = (xmlRelaxNGGrammarPtr) xmlMalloc(sizeof(xmlRelaxNGGrammar));
473 if (ret == NULL) {
474 if ((ctxt != NULL) && (ctxt->error != NULL))
475 ctxt->error(ctxt->userData, "Out of memory\n");
476 ctxt->nbErrors++;
477 return (NULL);
478 }
479 memset(ret, 0, sizeof(xmlRelaxNGGrammar));
480
481 return (ret);
482}
483
484/**
485 * xmlRelaxNGFreeGrammar:
486 * @grammar: a grammar structure
487 *
488 * Deallocate a RelaxNG grammar structure.
489 */
490static void
491xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar)
492{
493 if (grammar == NULL)
494 return;
495
Daniel Veillard419a7682003-02-03 23:22:49 +0000496 if (grammar->next != NULL) {
497 xmlRelaxNGFreeGrammar(grammar->next);
498 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000499 if (grammar->refs != NULL) {
500 xmlHashFree(grammar->refs, NULL);
501 }
502 if (grammar->defs != NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000503 xmlHashFree(grammar->defs, NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000504 }
505
506 xmlFree(grammar);
507}
508
509/**
510 * xmlRelaxNGNewDefine:
511 * @ctxt: a Relax-NG validation context
512 * @node: the node in the input document.
513 *
514 * Allocate a new RelaxNG define.
515 *
516 * Returns the newly allocated structure or NULL in case or error
517 */
518static xmlRelaxNGDefinePtr
519xmlRelaxNGNewDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
520{
521 xmlRelaxNGDefinePtr ret;
522
Daniel Veillard419a7682003-02-03 23:22:49 +0000523 if (ctxt->defMax == 0) {
524 ctxt->defMax = 16;
525 ctxt->defNr = 0;
526 ctxt->defTab = (xmlRelaxNGDefinePtr *)
527 xmlMalloc(ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
528 if (ctxt->defTab == NULL) {
529 if ((ctxt != NULL) && (ctxt->error != NULL))
530 ctxt->error(ctxt->userData, "Out of memory\n");
531 ctxt->nbErrors++;
532 return (NULL);
533 }
534 } else if (ctxt->defMax <= ctxt->defNr) {
535 xmlRelaxNGDefinePtr *tmp;
536 ctxt->defMax *= 2;
537 tmp = (xmlRelaxNGDefinePtr *) xmlRealloc(ctxt->defTab,
538 ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
539 if (tmp == NULL) {
540 if ((ctxt != NULL) && (ctxt->error != NULL))
541 ctxt->error(ctxt->userData, "Out of memory\n");
542 ctxt->nbErrors++;
543 return (NULL);
544 }
545 ctxt->defTab = tmp;
546 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000547 ret = (xmlRelaxNGDefinePtr) xmlMalloc(sizeof(xmlRelaxNGDefine));
548 if (ret == NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000549 if ((ctxt != NULL) && (ctxt->error != NULL))
550 ctxt->error(ctxt->userData, "Out of memory\n");
Daniel Veillard6eadf632003-01-23 18:29:16 +0000551 ctxt->nbErrors++;
Daniel Veillard419a7682003-02-03 23:22:49 +0000552 return(NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000553 }
554 memset(ret, 0, sizeof(xmlRelaxNGDefine));
Daniel Veillard419a7682003-02-03 23:22:49 +0000555 ctxt->defTab[ctxt->defNr++] = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000556 ret->node = node;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000557 return (ret);
558}
559
560/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000561 * xmlRelaxNGFreePartition:
562 * @partitions: a partition set structure
563 *
564 * Deallocate RelaxNG partition set structures.
565 */
566static void
567xmlRelaxNGFreePartition(xmlRelaxNGPartitionPtr partitions) {
568 xmlRelaxNGInterleaveGroupPtr group;
569 int j;
570
571 if (partitions != NULL) {
572 if (partitions->groups != NULL) {
573 for (j = 0;j < partitions->nbgroups;j++) {
574 group = partitions->groups[j];
575 if (group != NULL) {
576 if (group->defs != NULL)
577 xmlFree(group->defs);
578 xmlFree(group);
579 }
580 }
581 xmlFree(partitions->groups);
582 }
583 xmlFree(partitions);
584 }
585}
586/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000587 * xmlRelaxNGFreeDefine:
588 * @define: a define structure
589 *
590 * Deallocate a RelaxNG define structure.
591 */
592static void
593xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define)
594{
595 if (define == NULL)
596 return;
597
Daniel Veillard419a7682003-02-03 23:22:49 +0000598 if ((define->data != NULL) &&
599 (define->type == XML_RELAXNG_INTERLEAVE))
600 xmlRelaxNGFreePartition((xmlRelaxNGPartitionPtr) define->data);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000601 if (define->name != NULL)
602 xmlFree(define->name);
603 if (define->ns != NULL)
604 xmlFree(define->ns);
Daniel Veillardedc91922003-01-26 00:52:04 +0000605 if (define->value != NULL)
606 xmlFree(define->value);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000607 xmlFree(define);
608}
609
610/**
611 * xmlRelaxNGNewValidState:
612 * @ctxt: a Relax-NG validation context
613 * @node: the current node or NULL for the document
614 *
615 * Allocate a new RelaxNG validation state
616 *
617 * Returns the newly allocated structure or NULL in case or error
618 */
619static xmlRelaxNGValidStatePtr
620xmlRelaxNGNewValidState(xmlRelaxNGValidCtxtPtr ctxt, xmlNodePtr node)
621{
622 xmlRelaxNGValidStatePtr ret;
623 xmlAttrPtr attr;
624 xmlAttrPtr attrs[MAX_ATTR];
625 int nbAttrs = 0;
626 xmlNodePtr root = NULL;
627
628 if (node == NULL) {
629 root = xmlDocGetRootElement(ctxt->doc);
630 if (root == NULL)
631 return(NULL);
632 } else {
633 attr = node->properties;
634 while (attr != NULL) {
635 if (nbAttrs < MAX_ATTR)
636 attrs[nbAttrs++] = attr;
637 else
638 nbAttrs++;
639 attr = attr->next;
640 }
641 }
642
643 if (nbAttrs < MAX_ATTR)
644 attrs[nbAttrs] = NULL;
645 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(sizeof(xmlRelaxNGValidState) +
646 nbAttrs * sizeof(xmlAttrPtr));
647 if (ret == NULL) {
648 if ((ctxt != NULL) && (ctxt->error != NULL))
649 ctxt->error(ctxt->userData, "Out of memory\n");
650 return (NULL);
651 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +0000652 ret->value = NULL;
653 ret->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000654 if (node == NULL) {
655 ret->node = (xmlNodePtr) ctxt->doc;
656 ret->seq = root;
657 ret->nbAttrs = 0;
658 } else {
659 ret->node = node;
660 ret->seq = node->children;
661 ret->nbAttrs = nbAttrs;
662 if (nbAttrs > 0) {
663 if (nbAttrs < MAX_ATTR) {
664 memcpy(&(ret->attrs[0]), attrs,
665 sizeof(xmlAttrPtr) * (nbAttrs + 1));
666 } else {
667 attr = node->properties;
668 nbAttrs = 0;
669 while (attr != NULL) {
670 ret->attrs[nbAttrs++] = attr;
671 attr = attr->next;
672 }
673 ret->attrs[nbAttrs] = NULL;
674 }
675 }
676 }
Daniel Veillard1ed7f362003-02-03 10:57:45 +0000677 ret->nbAttrLeft = ret->nbAttrs;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000678 return (ret);
679}
680
681/**
682 * xmlRelaxNGCopyValidState:
683 * @ctxt: a Relax-NG validation context
684 * @state: a validation state
685 *
686 * Copy the validation state
687 *
688 * Returns the newly allocated structure or NULL in case or error
689 */
690static xmlRelaxNGValidStatePtr
691xmlRelaxNGCopyValidState(xmlRelaxNGValidCtxtPtr ctxt,
692 xmlRelaxNGValidStatePtr state)
693{
694 xmlRelaxNGValidStatePtr ret;
695 unsigned int size;
696
697 if (state == NULL)
698 return(NULL);
699
700 size = sizeof(xmlRelaxNGValidState) +
701 state->nbAttrs * sizeof(xmlAttrPtr);
702 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(size);
703 if (ret == NULL) {
704 if ((ctxt != NULL) && (ctxt->error != NULL))
705 ctxt->error(ctxt->userData, "Out of memory\n");
706 return (NULL);
707 }
708 memcpy(ret, state, size);
709 return(ret);
710}
711
712/**
713 * xmlRelaxNGFreeValidState:
714 * @state: a validation state structure
715 *
716 * Deallocate a RelaxNG validation state structure.
717 */
718static void
719xmlRelaxNGFreeValidState(xmlRelaxNGValidStatePtr state)
720{
721 if (state == NULL)
722 return;
723
724 xmlFree(state);
725}
726
727/************************************************************************
728 * *
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000729 * Document functions *
730 * *
731 ************************************************************************/
732static xmlDocPtr xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt,
733 xmlDocPtr doc);
734
735/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000736 * xmlRelaxNGIncludePush:
737 * @ctxt: the parser context
738 * @value: the element doc
739 *
740 * Pushes a new include on top of the include stack
741 *
742 * Returns 0 in case of error, the index in the stack otherwise
743 */
744static int
745xmlRelaxNGIncludePush(xmlRelaxNGParserCtxtPtr ctxt,
746 xmlRelaxNGIncludePtr value)
747{
748 if (ctxt->incTab == NULL) {
749 ctxt->incMax = 4;
750 ctxt->incNr = 0;
751 ctxt->incTab = (xmlRelaxNGIncludePtr *) xmlMalloc(
752 ctxt->incMax * sizeof(ctxt->incTab[0]));
753 if (ctxt->incTab == NULL) {
754 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
755 return (0);
756 }
757 }
758 if (ctxt->incNr >= ctxt->incMax) {
759 ctxt->incMax *= 2;
760 ctxt->incTab =
761 (xmlRelaxNGIncludePtr *) xmlRealloc(ctxt->incTab,
762 ctxt->incMax *
763 sizeof(ctxt->incTab[0]));
764 if (ctxt->incTab == NULL) {
765 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
766 return (0);
767 }
768 }
769 ctxt->incTab[ctxt->incNr] = value;
770 ctxt->inc = value;
771 return (ctxt->incNr++);
772}
773
774/**
775 * xmlRelaxNGIncludePop:
776 * @ctxt: the parser context
777 *
778 * Pops the top include from the include stack
779 *
780 * Returns the include just removed
781 */
782static xmlRelaxNGIncludePtr
783xmlRelaxNGIncludePop(xmlRelaxNGParserCtxtPtr ctxt)
784{
785 xmlRelaxNGIncludePtr ret;
786
787 if (ctxt->incNr <= 0)
788 return (0);
789 ctxt->incNr--;
790 if (ctxt->incNr > 0)
791 ctxt->inc = ctxt->incTab[ctxt->incNr - 1];
792 else
793 ctxt->inc = NULL;
794 ret = ctxt->incTab[ctxt->incNr];
795 ctxt->incTab[ctxt->incNr] = 0;
796 return (ret);
797}
798
799/**
800 * xmlRelaxNGLoadInclude:
801 * @ctxt: the parser context
802 * @URL: the normalized URL
803 * @node: the include node.
Daniel Veillard416589a2003-02-17 17:25:42 +0000804 * @ns: the namespace passed from the context.
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000805 *
806 * First lookup if the document is already loaded into the parser context,
807 * check against recursion. If not found the resource is loaded and
808 * the content is preprocessed before being returned back to the caller.
809 *
810 * Returns the xmlRelaxNGIncludePtr or NULL in case of error
811 */
812static xmlRelaxNGIncludePtr
813xmlRelaxNGLoadInclude(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillard416589a2003-02-17 17:25:42 +0000814 xmlNodePtr node, const xmlChar *ns) {
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000815 xmlRelaxNGIncludePtr ret = NULL;
816 xmlDocPtr doc;
817 int i;
818 xmlNodePtr root, tmp, tmp2, cur;
819
820 /*
821 * check against recursion in the stack
822 */
823 for (i = 0;i < ctxt->incNr;i++) {
824 if (xmlStrEqual(ctxt->incTab[i]->href, URL)) {
825 if (ctxt->error != NULL)
826 ctxt->error(ctxt->userData,
827 "Detected an externalRef recursion for %s\n",
828 URL);
829 ctxt->nbErrors++;
830 return(NULL);
831 }
832 }
833
834 /*
835 * Lookup in the hash table
836 */
837 if (ctxt->includes == NULL) {
838 ctxt->includes = xmlHashCreate(10);
839 if (ctxt->includes == NULL) {
840 if (ctxt->error != NULL)
841 ctxt->error(ctxt->userData,
842 "Failed to allocate hash table for document\n");
843 ctxt->nbErrors++;
844 return(NULL);
845 }
846 } else {
Daniel Veillard416589a2003-02-17 17:25:42 +0000847 if (ns == NULL)
848 ret = xmlHashLookup2(ctxt->includes, BAD_CAST "", URL);
849 else
850 ret = xmlHashLookup2(ctxt->includes, ns, URL);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000851 if (ret != NULL)
852 return(ret);
853 }
854
855
856 /*
857 * load the document
858 */
859 doc = xmlParseFile((const char *) URL);
860 if (doc == NULL) {
861 if (ctxt->error != NULL)
862 ctxt->error(ctxt->userData,
863 "xmlRelaxNG: could not load %s\n", URL);
864 ctxt->nbErrors++;
865 return (NULL);
866 }
867
868 /*
869 * Allocate the document structures and register it first.
870 */
871 ret = (xmlRelaxNGIncludePtr) xmlMalloc(sizeof(xmlRelaxNGInclude));
872 if (ret == NULL) {
873 if (ctxt->error != NULL)
874 ctxt->error(ctxt->userData,
875 "xmlRelaxNG: allocate memory for doc %s\n", URL);
876 ctxt->nbErrors++;
877 xmlFreeDoc(doc);
878 return (NULL);
879 }
880 memset(ret, 0, sizeof(xmlRelaxNGInclude));
881 ret->doc = doc;
882 ret->href = xmlStrdup(URL);
883
884 /*
Daniel Veillard416589a2003-02-17 17:25:42 +0000885 * transmit the ns if needed
886 */
887 if (ns != NULL) {
888 root = xmlDocGetRootElement(doc);
889 if (root != NULL) {
890 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
891 xmlSetProp(root, BAD_CAST"ns", ns);
892 }
893 }
894 }
895
896 /*
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000897 * push it on the stack and register it in the hash table
898 */
Daniel Veillard416589a2003-02-17 17:25:42 +0000899 if (ns == NULL)
900 xmlHashAddEntry2(ctxt->includes, BAD_CAST "", URL, ret);
901 else
902 xmlHashAddEntry2(ctxt->includes, ns, URL, ret);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000903 xmlRelaxNGIncludePush(ctxt, ret);
904
905 /*
906 * Some preprocessing of the document content, this include recursing
907 * in the include stack.
908 */
909 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
910 if (doc == NULL) {
911 /* xmlFreeDoc(ctxt->include); */
912 ctxt->inc = NULL;
913 return(NULL);
914 }
915
916 /*
917 * Pop up the include from the stack
918 */
919 xmlRelaxNGIncludePop(ctxt);
920
921 /*
922 * Check that the top element is a grammar
923 */
924 root = xmlDocGetRootElement(doc);
925 if (root == NULL) {
926 if (ctxt->error != NULL)
927 ctxt->error(ctxt->userData,
928 "xmlRelaxNG: included document is empty %s\n", URL);
929 ctxt->nbErrors++;
930 xmlFreeDoc(doc);
931 return (NULL);
932 }
933 if (!IS_RELAXNG(root, "grammar")) {
934 if (ctxt->error != NULL)
935 ctxt->error(ctxt->userData,
936 "xmlRelaxNG: included document %s root is not a grammar\n",
937 URL);
938 ctxt->nbErrors++;
939 xmlFreeDoc(doc);
940 return (NULL);
941 }
942
943 /*
944 * Elimination of redefined rules in the include.
945 */
946 cur = node->children;
947 while (cur != NULL) {
948 if (IS_RELAXNG(cur, "start")) {
949 int found = 0;
950
951 tmp = root->children;
952 while (tmp != NULL) {
953 tmp2 = tmp->next;
954 if (IS_RELAXNG(tmp, "start")) {
955 found = 1;
956 xmlUnlinkNode(tmp);
957 xmlFreeNode(tmp);
958 }
959 tmp = tmp2;
960 }
961 if (!found) {
962 if (ctxt->error != NULL)
963 ctxt->error(ctxt->userData,
964 "xmlRelaxNG: include %s has a start but not the included grammar\n",
965 URL);
966 ctxt->nbErrors++;
967 }
968 } else if (IS_RELAXNG(cur, "define")) {
969 xmlChar *name, *name2;
970
971 name = xmlGetProp(cur, BAD_CAST "name");
972 if (name == NULL) {
973 if (ctxt->error != NULL)
974 ctxt->error(ctxt->userData,
975 "xmlRelaxNG: include %s has define without name\n",
976 URL);
977 ctxt->nbErrors++;
978 } else {
979 int found = 0;
980
Daniel Veillardd2298792003-02-14 16:54:11 +0000981 xmlRelaxNGNormExtSpace(name);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000982 tmp = root->children;
983 while (tmp != NULL) {
984 tmp2 = tmp->next;
985 if (IS_RELAXNG(tmp, "define")) {
986 name2 = xmlGetProp(tmp, BAD_CAST "name");
Daniel Veillardd2298792003-02-14 16:54:11 +0000987 xmlRelaxNGNormExtSpace(name2);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000988 if (name2 != NULL) {
989 if (xmlStrEqual(name, name2)) {
990 found = 1;
991 xmlUnlinkNode(tmp);
992 xmlFreeNode(tmp);
993 }
994 xmlFree(name2);
995 }
996 }
997 tmp = tmp2;
998 }
999 if (!found) {
1000 if (ctxt->error != NULL)
1001 ctxt->error(ctxt->userData,
1002 "xmlRelaxNG: include %s has a define %s but not the included grammar\n",
1003 URL, name);
1004 ctxt->nbErrors++;
1005 }
1006 xmlFree(name);
1007 }
1008 }
1009 cur = cur->next;
1010 }
1011
1012
1013 return(ret);
1014}
1015
1016/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001017 * xmlRelaxNGDocumentPush:
1018 * @ctxt: the parser context
1019 * @value: the element doc
1020 *
1021 * Pushes a new doc on top of the doc stack
1022 *
1023 * Returns 0 in case of error, the index in the stack otherwise
1024 */
1025static int
1026xmlRelaxNGDocumentPush(xmlRelaxNGParserCtxtPtr ctxt,
1027 xmlRelaxNGDocumentPtr value)
1028{
1029 if (ctxt->docTab == NULL) {
1030 ctxt->docMax = 4;
1031 ctxt->docNr = 0;
1032 ctxt->docTab = (xmlRelaxNGDocumentPtr *) xmlMalloc(
1033 ctxt->docMax * sizeof(ctxt->docTab[0]));
1034 if (ctxt->docTab == NULL) {
1035 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
1036 return (0);
1037 }
1038 }
1039 if (ctxt->docNr >= ctxt->docMax) {
1040 ctxt->docMax *= 2;
1041 ctxt->docTab =
1042 (xmlRelaxNGDocumentPtr *) xmlRealloc(ctxt->docTab,
1043 ctxt->docMax *
1044 sizeof(ctxt->docTab[0]));
1045 if (ctxt->docTab == NULL) {
1046 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
1047 return (0);
1048 }
1049 }
1050 ctxt->docTab[ctxt->docNr] = value;
1051 ctxt->doc = value;
1052 return (ctxt->docNr++);
1053}
1054
1055/**
1056 * xmlRelaxNGDocumentPop:
1057 * @ctxt: the parser context
1058 *
1059 * Pops the top doc from the doc stack
1060 *
1061 * Returns the doc just removed
1062 */
1063static xmlRelaxNGDocumentPtr
1064xmlRelaxNGDocumentPop(xmlRelaxNGParserCtxtPtr ctxt)
1065{
1066 xmlRelaxNGDocumentPtr ret;
1067
1068 if (ctxt->docNr <= 0)
1069 return (0);
1070 ctxt->docNr--;
1071 if (ctxt->docNr > 0)
1072 ctxt->doc = ctxt->docTab[ctxt->docNr - 1];
1073 else
1074 ctxt->doc = NULL;
1075 ret = ctxt->docTab[ctxt->docNr];
1076 ctxt->docTab[ctxt->docNr] = 0;
1077 return (ret);
1078}
1079
1080/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001081 * xmlRelaxNGLoadExternalRef:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001082 * @ctxt: the parser context
1083 * @URL: the normalized URL
1084 * @ns: the inherited ns if any
1085 *
1086 * First lookup if the document is already loaded into the parser context,
1087 * check against recursion. If not found the resource is loaded and
1088 * the content is preprocessed before being returned back to the caller.
1089 *
1090 * Returns the xmlRelaxNGDocumentPtr or NULL in case of error
1091 */
1092static xmlRelaxNGDocumentPtr
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001093xmlRelaxNGLoadExternalRef(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001094 const xmlChar *ns) {
1095 xmlRelaxNGDocumentPtr ret = NULL;
1096 xmlDocPtr doc;
1097 xmlNodePtr root;
1098 int i;
1099
1100 /*
1101 * check against recursion in the stack
1102 */
1103 for (i = 0;i < ctxt->docNr;i++) {
1104 if (xmlStrEqual(ctxt->docTab[i]->href, URL)) {
1105 if (ctxt->error != NULL)
1106 ctxt->error(ctxt->userData,
1107 "Detected an externalRef recursion for %s\n",
1108 URL);
1109 ctxt->nbErrors++;
1110 return(NULL);
1111 }
1112 }
1113
1114 /*
1115 * Lookup in the hash table
1116 */
1117 if (ctxt->documents == NULL) {
1118 ctxt->documents = xmlHashCreate(10);
1119 if (ctxt->documents == NULL) {
1120 if (ctxt->error != NULL)
1121 ctxt->error(ctxt->userData,
1122 "Failed to allocate hash table for document\n");
1123 ctxt->nbErrors++;
1124 return(NULL);
1125 }
1126 } else {
1127 if (ns == NULL)
1128 ret = xmlHashLookup2(ctxt->documents, BAD_CAST "", URL);
1129 else
1130 ret = xmlHashLookup2(ctxt->documents, ns, URL);
1131 if (ret != NULL)
1132 return(ret);
1133 }
1134
1135
1136 /*
1137 * load the document
1138 */
1139 doc = xmlParseFile((const char *) URL);
1140 if (doc == NULL) {
1141 if (ctxt->error != NULL)
1142 ctxt->error(ctxt->userData,
1143 "xmlRelaxNG: could not load %s\n", URL);
1144 ctxt->nbErrors++;
1145 return (NULL);
1146 }
1147
1148 /*
1149 * Allocate the document structures and register it first.
1150 */
1151 ret = (xmlRelaxNGDocumentPtr) xmlMalloc(sizeof(xmlRelaxNGDocument));
1152 if (ret == NULL) {
1153 if (ctxt->error != NULL)
1154 ctxt->error(ctxt->userData,
1155 "xmlRelaxNG: allocate memory for doc %s\n", URL);
1156 ctxt->nbErrors++;
1157 xmlFreeDoc(doc);
1158 return (NULL);
1159 }
1160 memset(ret, 0, sizeof(xmlRelaxNGDocument));
1161 ret->doc = doc;
1162 ret->href = xmlStrdup(URL);
1163
1164 /*
1165 * transmit the ns if needed
1166 */
1167 if (ns != NULL) {
1168 root = xmlDocGetRootElement(doc);
1169 if (root != NULL) {
1170 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
1171 xmlSetProp(root, BAD_CAST"ns", ns);
1172 }
1173 }
1174 }
1175
1176 /*
1177 * push it on the stack and register it in the hash table
1178 */
1179 if (ns == NULL)
1180 xmlHashAddEntry2(ctxt->documents, BAD_CAST "", URL, ret);
1181 else
1182 xmlHashAddEntry2(ctxt->documents, ns, URL, ret);
1183 xmlRelaxNGDocumentPush(ctxt, ret);
1184
1185 /*
1186 * Some preprocessing of the document content
1187 */
1188 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
1189 if (doc == NULL) {
1190 xmlFreeDoc(ctxt->document);
1191 ctxt->doc = NULL;
1192 return(NULL);
1193 }
1194
1195 xmlRelaxNGDocumentPop(ctxt);
1196
1197 return(ret);
1198}
1199
1200/************************************************************************
1201 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +00001202 * Error functions *
1203 * *
1204 ************************************************************************/
1205
1206#define VALID_CTXT() \
Daniel Veillard231d7912003-02-09 14:22:17 +00001207 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1208 xmlGenericError(xmlGenericErrorContext, \
Daniel Veillard6eadf632003-01-23 18:29:16 +00001209 "error detected at %s:%d\n", \
1210 __FILE__, __LINE__);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001211
1212#define VALID_ERROR(a) \
Daniel Veillard231d7912003-02-09 14:22:17 +00001213 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001214 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a)
1215#define VALID_ERROR2(a, b) \
1216 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1217 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a, b)
1218#define VALID_ERROR3(a, b, c) \
1219 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1220 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a, b, c)
Daniel Veillard6eadf632003-01-23 18:29:16 +00001221
Daniel Veillardd2298792003-02-14 16:54:11 +00001222#ifdef DEBUG
Daniel Veillard231d7912003-02-09 14:22:17 +00001223static const char *
1224xmlRelaxNGDefName(xmlRelaxNGDefinePtr def) {
1225 if (def == NULL)
1226 return("none");
1227 switch(def->type) {
1228 case XML_RELAXNG_EMPTY: return("empty");
1229 case XML_RELAXNG_NOT_ALLOWED: return("notAllowed");
1230 case XML_RELAXNG_EXCEPT: return("except");
1231 case XML_RELAXNG_TEXT: return("text");
1232 case XML_RELAXNG_ELEMENT: return("element");
1233 case XML_RELAXNG_DATATYPE: return("datatype");
1234 case XML_RELAXNG_VALUE: return("value");
1235 case XML_RELAXNG_LIST: return("list");
1236 case XML_RELAXNG_ATTRIBUTE: return("attribute");
1237 case XML_RELAXNG_DEF: return("def");
1238 case XML_RELAXNG_REF: return("ref");
1239 case XML_RELAXNG_EXTERNALREF: return("externalRef");
1240 case XML_RELAXNG_PARENTREF: return("parentRef");
1241 case XML_RELAXNG_OPTIONAL: return("optional");
1242 case XML_RELAXNG_ZEROORMORE: return("zeroOrMore");
1243 case XML_RELAXNG_ONEORMORE: return("oneOrMore");
1244 case XML_RELAXNG_CHOICE: return("choice");
1245 case XML_RELAXNG_GROUP: return("group");
1246 case XML_RELAXNG_INTERLEAVE: return("interleave");
1247 case XML_RELAXNG_START: return("start");
1248 }
1249 return("unknown");
1250}
Daniel Veillardd2298792003-02-14 16:54:11 +00001251#endif
1252
Daniel Veillard6eadf632003-01-23 18:29:16 +00001253#if 0
1254/**
1255 * xmlRelaxNGErrorContext:
1256 * @ctxt: the parsing context
1257 * @schema: the schema being built
1258 * @node: the node being processed
1259 * @child: the child being processed
1260 *
1261 * Dump a RelaxNGType structure
1262 */
1263static void
1264xmlRelaxNGErrorContext(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGPtr schema,
1265 xmlNodePtr node, xmlNodePtr child)
1266{
1267 int line = 0;
1268 const xmlChar *file = NULL;
1269 const xmlChar *name = NULL;
1270 const char *type = "error";
1271
1272 if ((ctxt == NULL) || (ctxt->error == NULL))
1273 return;
1274
1275 if (child != NULL)
1276 node = child;
1277
1278 if (node != NULL) {
1279 if ((node->type == XML_DOCUMENT_NODE) ||
1280 (node->type == XML_HTML_DOCUMENT_NODE)) {
1281 xmlDocPtr doc = (xmlDocPtr) node;
1282
1283 file = doc->URL;
1284 } else {
1285 /*
1286 * Try to find contextual informations to report
1287 */
1288 if (node->type == XML_ELEMENT_NODE) {
1289 line = (int) node->content;
1290 } else if ((node->prev != NULL) &&
1291 (node->prev->type == XML_ELEMENT_NODE)) {
1292 line = (int) node->prev->content;
1293 } else if ((node->parent != NULL) &&
1294 (node->parent->type == XML_ELEMENT_NODE)) {
1295 line = (int) node->parent->content;
1296 }
1297 if ((node->doc != NULL) && (node->doc->URL != NULL))
1298 file = node->doc->URL;
1299 if (node->name != NULL)
1300 name = node->name;
1301 }
1302 }
1303
1304 if (ctxt != NULL)
1305 type = "compilation error";
1306 else if (schema != NULL)
1307 type = "runtime error";
1308
1309 if ((file != NULL) && (line != 0) && (name != NULL))
1310 ctxt->error(ctxt->userData, "%s: file %s line %d element %s\n",
1311 type, file, line, name);
1312 else if ((file != NULL) && (name != NULL))
1313 ctxt->error(ctxt->userData, "%s: file %s element %s\n",
1314 type, file, name);
1315 else if ((file != NULL) && (line != 0))
1316 ctxt->error(ctxt->userData, "%s: file %s line %d\n", type, file, line);
1317 else if (file != NULL)
1318 ctxt->error(ctxt->userData, "%s: file %s\n", type, file);
1319 else if (name != NULL)
1320 ctxt->error(ctxt->userData, "%s: element %s\n", type, name);
1321 else
1322 ctxt->error(ctxt->userData, "%s\n", type);
1323}
1324#endif
1325
1326/************************************************************************
1327 * *
1328 * Type library hooks *
1329 * *
1330 ************************************************************************/
Daniel Veillardea3f3982003-01-26 19:45:18 +00001331static xmlChar *xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt,
1332 const xmlChar *str);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001333
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001334/**
1335 * xmlRelaxNGSchemaTypeHave:
1336 * @data: data needed for the library
1337 * @type: the type name
1338 *
1339 * Check if the given type is provided by
1340 * the W3C XMLSchema Datatype library.
1341 *
1342 * Returns 1 if yes, 0 if no and -1 in case of error.
1343 */
1344static int
1345xmlRelaxNGSchemaTypeHave(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001346 const xmlChar *type) {
1347 xmlSchemaTypePtr typ;
1348
1349 if (type == NULL)
1350 return(-1);
1351 typ = xmlSchemaGetPredefinedType(type,
1352 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1353 if (typ == NULL)
1354 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001355 return(1);
1356}
1357
1358/**
1359 * xmlRelaxNGSchemaTypeCheck:
1360 * @data: data needed for the library
1361 * @type: the type name
1362 * @value: the value to check
1363 *
1364 * Check if the given type and value are validated by
1365 * the W3C XMLSchema Datatype library.
1366 *
1367 * Returns 1 if yes, 0 if no and -1 in case of error.
1368 */
1369static int
1370xmlRelaxNGSchemaTypeCheck(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001371 const xmlChar *type,
1372 const xmlChar *value) {
1373 xmlSchemaTypePtr typ;
1374 int ret;
1375
1376 /*
1377 * TODO: the type should be cached ab provided back, interface subject
1378 * to changes.
1379 * TODO: handle facets, may require an additional interface and keep
1380 * the value returned from the validation.
1381 */
1382 if ((type == NULL) || (value == NULL))
1383 return(-1);
1384 typ = xmlSchemaGetPredefinedType(type,
1385 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1386 if (typ == NULL)
1387 return(-1);
1388 ret = xmlSchemaValidatePredefinedType(typ, value, NULL);
1389 if (ret == 0)
1390 return(1);
1391 if (ret > 0)
1392 return(0);
1393 return(-1);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001394}
1395
1396/**
1397 * xmlRelaxNGSchemaTypeCompare:
1398 * @data: data needed for the library
1399 * @type: the type name
1400 * @value1: the first value
1401 * @value2: the second value
1402 *
1403 * Compare two values accordingly a type from the W3C XMLSchema
1404 * Datatype library.
1405 *
1406 * Returns 1 if yes, 0 if no and -1 in case of error.
1407 */
1408static int
1409xmlRelaxNGSchemaTypeCompare(void *data ATTRIBUTE_UNUSED,
1410 const xmlChar *type ATTRIBUTE_UNUSED,
1411 const xmlChar *value1 ATTRIBUTE_UNUSED,
1412 const xmlChar *value2 ATTRIBUTE_UNUSED) {
1413 TODO
1414 return(1);
1415}
1416
1417/**
1418 * xmlRelaxNGDefaultTypeHave:
1419 * @data: data needed for the library
1420 * @type: the type name
1421 *
1422 * Check if the given type is provided by
1423 * the default datatype library.
1424 *
1425 * Returns 1 if yes, 0 if no and -1 in case of error.
1426 */
1427static int
1428xmlRelaxNGDefaultTypeHave(void *data ATTRIBUTE_UNUSED, const xmlChar *type) {
1429 if (type == NULL)
1430 return(-1);
1431 if (xmlStrEqual(type, BAD_CAST "string"))
1432 return(1);
1433 if (xmlStrEqual(type, BAD_CAST "token"))
1434 return(1);
1435 return(0);
1436}
1437
1438/**
1439 * xmlRelaxNGDefaultTypeCheck:
1440 * @data: data needed for the library
1441 * @type: the type name
1442 * @value: the value to check
1443 *
1444 * Check if the given type and value are validated by
1445 * the default datatype library.
1446 *
1447 * Returns 1 if yes, 0 if no and -1 in case of error.
1448 */
1449static int
1450xmlRelaxNGDefaultTypeCheck(void *data ATTRIBUTE_UNUSED,
1451 const xmlChar *type ATTRIBUTE_UNUSED,
1452 const xmlChar *value ATTRIBUTE_UNUSED) {
1453 return(1);
1454}
1455
1456/**
1457 * xmlRelaxNGDefaultTypeCompare:
1458 * @data: data needed for the library
1459 * @type: the type name
1460 * @value1: the first value
1461 * @value2: the second value
1462 *
1463 * Compare two values accordingly a type from the default
1464 * datatype library.
1465 *
1466 * Returns 1 if yes, 0 if no and -1 in case of error.
1467 */
1468static int
1469xmlRelaxNGDefaultTypeCompare(void *data ATTRIBUTE_UNUSED,
1470 const xmlChar *type ATTRIBUTE_UNUSED,
1471 const xmlChar *value1 ATTRIBUTE_UNUSED,
1472 const xmlChar *value2 ATTRIBUTE_UNUSED) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00001473 int ret = -1;
1474
1475 if (xmlStrEqual(type, BAD_CAST "string")) {
1476 ret = xmlStrEqual(value1, value2);
1477 } else if (xmlStrEqual(type, BAD_CAST "token")) {
1478 if (!xmlStrEqual(value1, value2)) {
1479 xmlChar *nval, *nvalue;
1480
1481 /*
1482 * TODO: trivial optimizations are possible by
1483 * computing at compile-time
1484 */
1485 nval = xmlRelaxNGNormalize(NULL, value1);
1486 nvalue = xmlRelaxNGNormalize(NULL, value2);
1487
1488 if ((nval == NULL) || (nvalue == NULL) ||
1489 (!xmlStrEqual(nval, nvalue)))
1490 ret = -1;
1491 if (nval != NULL)
1492 xmlFree(nval);
1493 if (nvalue != NULL)
1494 xmlFree(nvalue);
1495 }
1496 }
1497 return(ret);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001498}
1499
1500static int xmlRelaxNGTypeInitialized = 0;
1501static xmlHashTablePtr xmlRelaxNGRegisteredTypes = NULL;
1502
1503/**
1504 * xmlRelaxNGFreeTypeLibrary:
1505 * @lib: the type library structure
1506 * @namespace: the URI bound to the library
1507 *
1508 * Free the structure associated to the type library
1509 */
Daniel Veillard6eadf632003-01-23 18:29:16 +00001510static void
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001511xmlRelaxNGFreeTypeLibrary(xmlRelaxNGTypeLibraryPtr lib,
1512 const xmlChar *namespace ATTRIBUTE_UNUSED) {
1513 if (lib == NULL)
1514 return;
1515 if (lib->namespace != NULL)
1516 xmlFree((xmlChar *)lib->namespace);
1517 xmlFree(lib);
1518}
1519
1520/**
1521 * xmlRelaxNGRegisterTypeLibrary:
1522 * @namespace: the URI bound to the library
1523 * @data: data associated to the library
1524 * @have: the provide function
1525 * @check: the checking function
1526 * @comp: the comparison function
1527 *
1528 * Register a new type library
1529 *
1530 * Returns 0 in case of success and -1 in case of error.
1531 */
1532static int
1533xmlRelaxNGRegisterTypeLibrary(const xmlChar *namespace, void *data,
1534 xmlRelaxNGTypeHave have, xmlRelaxNGTypeCheck check,
1535 xmlRelaxNGTypeCompare comp) {
1536 xmlRelaxNGTypeLibraryPtr lib;
1537 int ret;
1538
1539 if ((xmlRelaxNGRegisteredTypes == NULL) || (namespace == NULL) ||
1540 (check == NULL) || (comp == NULL))
1541 return(-1);
1542 if (xmlHashLookup(xmlRelaxNGRegisteredTypes, namespace) != NULL) {
1543 xmlGenericError(xmlGenericErrorContext,
1544 "Relax-NG types library '%s' already registered\n",
1545 namespace);
1546 return(-1);
1547 }
1548 lib = (xmlRelaxNGTypeLibraryPtr) xmlMalloc(sizeof(xmlRelaxNGTypeLibrary));
1549 if (lib == NULL) {
1550 xmlGenericError(xmlGenericErrorContext,
1551 "Relax-NG types library '%s' malloc() failed\n",
1552 namespace);
1553 return (-1);
1554 }
1555 memset(lib, 0, sizeof(xmlRelaxNGTypeLibrary));
1556 lib->namespace = xmlStrdup(namespace);
1557 lib->data = data;
1558 lib->have = have;
1559 lib->comp = comp;
1560 lib->check = check;
1561 ret = xmlHashAddEntry(xmlRelaxNGRegisteredTypes, namespace, lib);
1562 if (ret < 0) {
1563 xmlGenericError(xmlGenericErrorContext,
1564 "Relax-NG types library failed to register '%s'\n",
1565 namespace);
1566 xmlRelaxNGFreeTypeLibrary(lib, namespace);
1567 return(-1);
1568 }
1569 return(0);
1570}
1571
1572/**
1573 * xmlRelaxNGInitTypes:
1574 *
1575 * Initilize the default type libraries.
1576 *
1577 * Returns 0 in case of success and -1 in case of error.
1578 */
1579static int
Daniel Veillard6eadf632003-01-23 18:29:16 +00001580xmlRelaxNGInitTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001581 if (xmlRelaxNGTypeInitialized != 0)
1582 return(0);
1583 xmlRelaxNGRegisteredTypes = xmlHashCreate(10);
1584 if (xmlRelaxNGRegisteredTypes == NULL) {
1585 xmlGenericError(xmlGenericErrorContext,
1586 "Failed to allocate sh table for Relax-NG types\n");
1587 return(-1);
1588 }
1589 xmlRelaxNGRegisterTypeLibrary(
1590 BAD_CAST "http://www.w3.org/2001/XMLSchema-datatypes",
1591 NULL,
1592 xmlRelaxNGSchemaTypeHave,
1593 xmlRelaxNGSchemaTypeCheck,
1594 xmlRelaxNGSchemaTypeCompare);
1595 xmlRelaxNGRegisterTypeLibrary(
1596 xmlRelaxNGNs,
1597 NULL,
1598 xmlRelaxNGDefaultTypeHave,
1599 xmlRelaxNGDefaultTypeCheck,
1600 xmlRelaxNGDefaultTypeCompare);
1601 xmlRelaxNGTypeInitialized = 1;
1602 return(0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001603}
1604
1605/**
1606 * xmlRelaxNGCleanupTypes:
1607 *
1608 * Cleanup the default Schemas type library associated to RelaxNG
1609 */
1610void
1611xmlRelaxNGCleanupTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001612 if (xmlRelaxNGTypeInitialized == 0)
1613 return;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001614 xmlSchemaCleanupTypes();
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001615 xmlHashFree(xmlRelaxNGRegisteredTypes, (xmlHashDeallocator)
1616 xmlRelaxNGFreeTypeLibrary);
1617 xmlRelaxNGTypeInitialized = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001618}
1619
1620/************************************************************************
1621 * *
1622 * Parsing functions *
1623 * *
1624 ************************************************************************/
1625
1626static xmlRelaxNGDefinePtr xmlRelaxNGParseAttribute(
1627 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1628static xmlRelaxNGDefinePtr xmlRelaxNGParseElement(
1629 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1630static xmlRelaxNGDefinePtr xmlRelaxNGParsePatterns(
Daniel Veillard154877e2003-01-30 12:17:05 +00001631 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes, int group);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001632static xmlRelaxNGDefinePtr xmlRelaxNGParsePattern(
1633 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001634static xmlRelaxNGPtr xmlRelaxNGParseDocument(
1635 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00001636static int xmlRelaxNGParseGrammarContent(
1637 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00001638static xmlRelaxNGDefinePtr xmlRelaxNGParseNameClass(
1639 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
1640 xmlRelaxNGDefinePtr def);
Daniel Veillard419a7682003-02-03 23:22:49 +00001641static xmlRelaxNGGrammarPtr xmlRelaxNGParseGrammar(
1642 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001643
1644
1645#define IS_BLANK_NODE(n) \
1646 (((n)->type == XML_TEXT_NODE) && (xmlRelaxNGIsBlank((n)->content)))
1647
1648/**
1649 * xmlRelaxNGIsBlank:
1650 * @str: a string
1651 *
1652 * Check if a string is ignorable c.f. 4.2. Whitespace
1653 *
1654 * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
1655 */
1656static int
1657xmlRelaxNGIsBlank(xmlChar *str) {
1658 if (str == NULL)
1659 return(1);
1660 while (*str != 0) {
1661 if (!(IS_BLANK(*str))) return(0);
1662 str++;
1663 }
1664 return(1);
1665}
1666
Daniel Veillard6eadf632003-01-23 18:29:16 +00001667/**
1668 * xmlRelaxNGGetDataTypeLibrary:
1669 * @ctxt: a Relax-NG parser context
1670 * @node: the current data or value element
1671 *
1672 * Applies algorithm from 4.3. datatypeLibrary attribute
1673 *
1674 * Returns the datatypeLibary value or NULL if not found
1675 */
1676static xmlChar *
1677xmlRelaxNGGetDataTypeLibrary(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1678 xmlNodePtr node) {
1679 xmlChar *ret, *escape;
1680
Daniel Veillard6eadf632003-01-23 18:29:16 +00001681 if ((IS_RELAXNG(node, "data")) || (IS_RELAXNG(node, "value"))) {
1682 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1683 if (ret != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001684 if (ret[0] == 0) {
1685 xmlFree(ret);
1686 return(NULL);
1687 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001688 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +00001689 if (escape == NULL) {
1690 return(ret);
1691 }
1692 xmlFree(ret);
1693 return(escape);
1694 }
1695 }
1696 node = node->parent;
1697 while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) {
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001698 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1699 if (ret != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001700 if (ret[0] == 0) {
1701 xmlFree(ret);
1702 return(NULL);
1703 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001704 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
1705 if (escape == NULL) {
1706 return(ret);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001707 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001708 xmlFree(ret);
1709 return(escape);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001710 }
1711 node = node->parent;
1712 }
1713 return(NULL);
1714}
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001715
1716/**
Daniel Veillardedc91922003-01-26 00:52:04 +00001717 * xmlRelaxNGParseValue:
1718 * @ctxt: a Relax-NG parser context
1719 * @node: the data node.
1720 *
1721 * parse the content of a RelaxNG value node.
1722 *
1723 * Returns the definition pointer or NULL in case of error
1724 */
1725static xmlRelaxNGDefinePtr
1726xmlRelaxNGParseValue(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1727 xmlRelaxNGDefinePtr def = NULL;
1728 xmlRelaxNGTypeLibraryPtr lib;
1729 xmlChar *type;
1730 xmlChar *library;
1731 int tmp;
1732
1733 def = xmlRelaxNGNewDefine(ctxt, node);
1734 if (def == NULL)
1735 return(NULL);
1736 def->type = XML_RELAXNG_VALUE;
1737
1738 type = xmlGetProp(node, BAD_CAST "type");
1739 if (type != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001740 xmlRelaxNGNormExtSpace(type);
1741 if (xmlValidateNCName(type, 0)) {
1742 if (ctxt->error != NULL)
1743 ctxt->error(ctxt->userData,
1744 "value type '%s' is not an NCName\n",
1745 type);
1746 ctxt->nbErrors++;
1747 }
Daniel Veillardedc91922003-01-26 00:52:04 +00001748 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1749 if (library == NULL)
1750 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1751
1752 def->name = type;
1753 def->ns = library;
1754
1755 lib = (xmlRelaxNGTypeLibraryPtr)
1756 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1757 if (lib == NULL) {
1758 if (ctxt->error != NULL)
1759 ctxt->error(ctxt->userData,
1760 "Use of unregistered type library '%s'\n",
1761 library);
1762 ctxt->nbErrors++;
1763 def->data = NULL;
1764 } else {
1765 def->data = lib;
1766 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001767 if (ctxt->error != NULL)
1768 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00001769 "Internal error with type library '%s': no 'have'\n",
1770 library);
1771 ctxt->nbErrors++;
1772 } else {
1773 tmp = lib->have(lib->data, def->name);
1774 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001775 if (ctxt->error != NULL)
1776 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00001777 "Error type '%s' is not exported by type library '%s'\n",
1778 def->name, library);
1779 ctxt->nbErrors++;
1780 }
1781 }
1782 }
1783 }
1784 if (node->children == NULL) {
1785 if (ctxt->error != NULL)
1786 ctxt->error(ctxt->userData,
1787 "Element <value> has no content\n");
1788 ctxt->nbErrors++;
1789 } else if ((node->children->type != XML_TEXT_NODE) ||
1790 (node->children->next != NULL)) {
1791 if (ctxt->error != NULL)
1792 ctxt->error(ctxt->userData,
1793 "Expecting a single text value for <value>content\n");
1794 ctxt->nbErrors++;
1795 } else {
1796 def->value = xmlNodeGetContent(node);
1797 if (def->value == NULL) {
1798 if (ctxt->error != NULL)
1799 ctxt->error(ctxt->userData,
1800 "Element <value> has no content\n");
1801 ctxt->nbErrors++;
1802 }
1803 }
1804 /* TODO check ahead of time that the value is okay per the type */
1805 return(def);
1806}
1807
1808/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001809 * xmlRelaxNGParseData:
1810 * @ctxt: a Relax-NG parser context
1811 * @node: the data node.
1812 *
1813 * parse the content of a RelaxNG data node.
1814 *
1815 * Returns the definition pointer or NULL in case of error
1816 */
1817static xmlRelaxNGDefinePtr
1818xmlRelaxNGParseData(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
Daniel Veillard416589a2003-02-17 17:25:42 +00001819 xmlRelaxNGDefinePtr def = NULL, except, last = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001820 xmlRelaxNGTypeLibraryPtr lib;
1821 xmlChar *type;
1822 xmlChar *library;
1823 xmlNodePtr content;
1824 int tmp;
1825
1826 type = xmlGetProp(node, BAD_CAST "type");
1827 if (type == NULL) {
1828 if (ctxt->error != NULL)
1829 ctxt->error(ctxt->userData,
1830 "data has no type\n");
1831 ctxt->nbErrors++;
1832 return(NULL);
1833 }
Daniel Veillardd2298792003-02-14 16:54:11 +00001834 xmlRelaxNGNormExtSpace(type);
1835 if (xmlValidateNCName(type, 0)) {
1836 if (ctxt->error != NULL)
1837 ctxt->error(ctxt->userData,
1838 "data type '%s' is not an NCName\n",
1839 type);
1840 ctxt->nbErrors++;
1841 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001842 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1843 if (library == NULL)
1844 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1845
1846 def = xmlRelaxNGNewDefine(ctxt, node);
1847 if (def == NULL) {
1848 xmlFree(type);
1849 return(NULL);
1850 }
1851 def->type = XML_RELAXNG_DATATYPE;
1852 def->name = type;
1853 def->ns = library;
1854
1855 lib = (xmlRelaxNGTypeLibraryPtr)
1856 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1857 if (lib == NULL) {
1858 if (ctxt->error != NULL)
1859 ctxt->error(ctxt->userData,
1860 "Use of unregistered type library '%s'\n",
1861 library);
1862 ctxt->nbErrors++;
1863 def->data = NULL;
1864 } else {
1865 def->data = lib;
1866 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001867 if (ctxt->error != NULL)
1868 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001869 "Internal error with type library '%s': no 'have'\n",
1870 library);
1871 ctxt->nbErrors++;
1872 } else {
1873 tmp = lib->have(lib->data, def->name);
1874 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001875 if (ctxt->error != NULL)
1876 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001877 "Error type '%s' is not exported by type library '%s'\n",
1878 def->name, library);
1879 ctxt->nbErrors++;
1880 }
1881 }
1882 }
1883 content = node->children;
Daniel Veillard416589a2003-02-17 17:25:42 +00001884
1885 /*
1886 * Handle optional params
1887 */
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001888 while (content != NULL) {
Daniel Veillard416589a2003-02-17 17:25:42 +00001889 if (!xmlStrEqual(content->name, BAD_CAST "param"))
1890 break;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001891 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001892 ctxt->nbErrors++;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001893 content = content->next;
1894 }
Daniel Veillard416589a2003-02-17 17:25:42 +00001895 /*
1896 * Handle optional except
1897 */
1898 if ((content != NULL) && (xmlStrEqual(content->name, BAD_CAST "except"))) {
1899 xmlNodePtr child;
1900 xmlRelaxNGDefinePtr tmp2, last2 = NULL;
1901
1902 except = xmlRelaxNGNewDefine(ctxt, node);
1903 if (except == NULL) {
1904 return(def);
1905 }
1906 except->type = XML_RELAXNG_EXCEPT;
1907 child = content->children;
1908 if (last == NULL) {
1909 def->content = except;
1910 } else {
1911 last->next = except;
1912 }
1913 if (child == NULL) {
1914 if (ctxt->error != NULL)
1915 ctxt->error(ctxt->userData,
1916 "except has no content\n");
1917 ctxt->nbErrors++;
1918 }
1919 while (child != NULL) {
1920 tmp2 = xmlRelaxNGParsePattern(ctxt, child);
1921 if (tmp2 != NULL) {
1922 if (last2 == NULL) {
1923 except->content = last2 = tmp2;
1924 } else {
1925 last2->next = tmp2;
1926 last2 = tmp2;
1927 }
1928 }
1929 child = child->next;
1930 }
1931 content = content->next;
1932 }
1933 /*
1934 * Check there is no unhandled data
1935 */
1936 if (content != NULL) {
1937 if (ctxt->error != NULL)
1938 ctxt->error(ctxt->userData,
1939 "Element data has unexpected content %s\n", content->name);
1940 ctxt->nbErrors++;
1941 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001942
1943 return(def);
1944}
1945
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001946/**
1947 * xmlRelaxNGCompareElemDefLists:
1948 * @ctxt: a Relax-NG parser context
1949 * @defs1: the first list of element defs
1950 * @defs2: the second list of element defs
1951 *
1952 * Compare the 2 lists of element definitions. The comparison is
1953 * that if both lists do not accept the same QNames, it returns 1
1954 * If the 2 lists can accept the same QName the comparison returns 0
1955 *
1956 * Returns 1 disttinct, 0 if equal
1957 */
1958static int
1959xmlRelaxNGCompareElemDefLists(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1960 xmlRelaxNGDefinePtr *def1,
1961 xmlRelaxNGDefinePtr *def2) {
1962 xmlRelaxNGDefinePtr *basedef2 = def2;
1963
Daniel Veillard154877e2003-01-30 12:17:05 +00001964 if ((def1 == NULL) || (def2 == NULL))
1965 return(1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001966 if ((*def1 == NULL) || (*def2 == NULL))
1967 return(1);
1968 while (*def1 != NULL) {
1969 while ((*def2) != NULL) {
1970 if ((*def1)->name == NULL) {
1971 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1972 return(0);
1973 } else if ((*def2)->name == NULL) {
1974 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1975 return(0);
1976 } else if (xmlStrEqual((*def1)->name, (*def2)->name)) {
1977 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1978 return(0);
1979 }
1980 def2++;
1981 }
1982 def2 = basedef2;
1983 def1++;
1984 }
1985 return(1);
1986}
1987
1988/**
1989 * xmlRelaxNGGetElements:
1990 * @ctxt: a Relax-NG parser context
1991 * @def: the interleave definition
1992 *
1993 * Compute the list of top elements a definition can generate
1994 *
1995 * Returns a list of elements or NULL if none was found.
1996 */
1997static xmlRelaxNGDefinePtr *
1998xmlRelaxNGGetElements(xmlRelaxNGParserCtxtPtr ctxt,
1999 xmlRelaxNGDefinePtr def) {
2000 xmlRelaxNGDefinePtr *ret = NULL, parent, cur, tmp;
2001 int len = 0;
2002 int max = 0;
2003
2004 parent = NULL;
2005 cur = def;
2006 while (cur != NULL) {
Daniel Veillardb08c9812003-01-28 23:09:49 +00002007 if ((cur->type == XML_RELAXNG_ELEMENT) ||
2008 (cur->type == XML_RELAXNG_TEXT)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002009 if (ret == NULL) {
2010 max = 10;
2011 ret = (xmlRelaxNGDefinePtr *)
2012 xmlMalloc((max + 1) * sizeof(xmlRelaxNGDefinePtr));
2013 if (ret == NULL) {
2014 if (ctxt->error != NULL)
2015 ctxt->error(ctxt->userData,
2016 "Out of memory in element search\n");
2017 ctxt->nbErrors++;
2018 return(NULL);
2019 }
2020 } else if (max <= len) {
2021 max *= 2;
2022 ret = xmlRealloc(ret, (max + 1) * sizeof(xmlRelaxNGDefinePtr));
2023 if (ret == NULL) {
2024 if (ctxt->error != NULL)
2025 ctxt->error(ctxt->userData,
2026 "Out of memory in element search\n");
2027 ctxt->nbErrors++;
2028 return(NULL);
2029 }
2030 }
Daniel Veillardb08c9812003-01-28 23:09:49 +00002031 ret[len++] = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002032 ret[len] = NULL;
2033 } else if ((cur->type == XML_RELAXNG_CHOICE) ||
2034 (cur->type == XML_RELAXNG_INTERLEAVE) ||
2035 (cur->type == XML_RELAXNG_GROUP) ||
2036 (cur->type == XML_RELAXNG_ONEORMORE) ||
Daniel Veillardb08c9812003-01-28 23:09:49 +00002037 (cur->type == XML_RELAXNG_ZEROORMORE) ||
2038 (cur->type == XML_RELAXNG_OPTIONAL) ||
2039 (cur->type == XML_RELAXNG_REF) ||
2040 (cur->type == XML_RELAXNG_DEF)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002041 /*
2042 * Don't go within elements or attributes or string values.
2043 * Just gather the element top list
2044 */
2045 if (cur->content != NULL) {
2046 parent = cur;
2047 cur = cur->content;
2048 tmp = cur;
2049 while (tmp != NULL) {
2050 tmp->parent = parent;
2051 tmp = tmp->next;
2052 }
2053 continue;
2054 }
2055 }
Daniel Veillard154877e2003-01-30 12:17:05 +00002056 if (cur == def)
2057 return(ret);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002058 if (cur->next != NULL) {
2059 cur = cur->next;
2060 continue;
2061 }
2062 do {
2063 cur = cur->parent;
2064 if (cur == NULL) break;
2065 if (cur == def) return(ret);
2066 if (cur->next != NULL) {
2067 cur = cur->next;
2068 break;
2069 }
2070 } while (cur != NULL);
2071 }
2072 return(ret);
2073}
2074
2075/**
2076 * xmlRelaxNGComputeInterleaves:
2077 * @def: the interleave definition
2078 * @ctxt: a Relax-NG parser context
2079 * @node: the data node.
2080 *
2081 * A lot of work for preprocessing interleave definitions
2082 * is potentially needed to get a decent execution speed at runtime
2083 * - trying to get a total order on the element nodes generated
2084 * by the interleaves, order the list of interleave definitions
2085 * following that order.
2086 * - if <text/> is used to handle mixed content, it is better to
2087 * flag this in the define and simplify the runtime checking
2088 * algorithm
2089 */
2090static void
2091xmlRelaxNGComputeInterleaves(xmlRelaxNGDefinePtr def,
2092 xmlRelaxNGParserCtxtPtr ctxt,
2093 xmlChar *name ATTRIBUTE_UNUSED) {
2094 xmlRelaxNGDefinePtr cur;
2095
2096 xmlRelaxNGDefinePtr *list = NULL;
2097 xmlRelaxNGPartitionPtr partitions = NULL;
2098 xmlRelaxNGInterleaveGroupPtr *groups = NULL;
2099 xmlRelaxNGInterleaveGroupPtr group;
2100 int i,j,ret;
2101 int nbgroups = 0;
2102 int nbchild = 0;
2103
2104#ifdef DEBUG_INTERLEAVE
2105 xmlGenericError(xmlGenericErrorContext,
2106 "xmlRelaxNGComputeInterleaves(%s)\n",
2107 name);
2108#endif
2109 cur = def->content;
2110 while (cur != NULL) {
2111 nbchild++;
2112 cur = cur->next;
2113 }
2114
2115#ifdef DEBUG_INTERLEAVE
2116 xmlGenericError(xmlGenericErrorContext, " %d child\n", nbchild);
2117#endif
2118 groups = (xmlRelaxNGInterleaveGroupPtr *)
2119 xmlMalloc(nbchild * sizeof(xmlRelaxNGInterleaveGroupPtr));
2120 if (groups == NULL)
2121 goto error;
2122 cur = def->content;
2123 while (cur != NULL) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002124 groups[nbgroups] = (xmlRelaxNGInterleaveGroupPtr)
2125 xmlMalloc(sizeof(xmlRelaxNGInterleaveGroup));
2126 if (groups[nbgroups] == NULL)
2127 goto error;
2128 groups[nbgroups]->rule = cur;
2129 groups[nbgroups]->defs = xmlRelaxNGGetElements(ctxt, cur);
2130 nbgroups++;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002131 cur = cur->next;
2132 }
2133 list = NULL;
2134#ifdef DEBUG_INTERLEAVE
2135 xmlGenericError(xmlGenericErrorContext, " %d groups\n", nbgroups);
2136#endif
2137
2138 /*
2139 * Let's check that all rules makes a partitions according to 7.4
2140 */
2141 partitions = (xmlRelaxNGPartitionPtr)
2142 xmlMalloc(sizeof(xmlRelaxNGPartition));
2143 if (partitions == NULL)
2144 goto error;
2145 partitions->nbgroups = nbgroups;
2146 for (i = 0;i < nbgroups;i++) {
2147 group = groups[i];
2148 for (j = i+1;j < nbgroups;j++) {
2149 if (groups[j] == NULL)
2150 continue;
2151 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->defs,
2152 groups[j]->defs);
2153 if (ret == 0) {
2154 if (ctxt->error != NULL)
2155 ctxt->error(ctxt->userData,
2156 "Element or text conflicts in interleave\n");
2157 ctxt->nbErrors++;
2158 }
2159 }
2160 }
2161 partitions->groups = groups;
2162
2163 /*
2164 * Free Up the child list, and save the partition list back in the def
2165 */
2166 def->data = partitions;
2167 return;
2168
2169error:
2170 if (ctxt->error != NULL)
2171 ctxt->error(ctxt->userData,
2172 "Out of memory in interleave computation\n");
2173 ctxt->nbErrors++;
2174 if (list == NULL)
2175 xmlFree(list);
2176 if (groups != NULL) {
2177 for (i = 0;i < nbgroups;i++)
2178 if (groups[i] != NULL) {
2179 if (groups[i]->defs != NULL)
2180 xmlFree(groups[i]->defs);
2181 xmlFree(groups[i]);
2182 }
2183 xmlFree(groups);
2184 }
2185 xmlRelaxNGFreePartition(partitions);
2186}
2187
2188/**
2189 * xmlRelaxNGParseInterleave:
2190 * @ctxt: a Relax-NG parser context
2191 * @node: the data node.
2192 *
2193 * parse the content of a RelaxNG interleave node.
2194 *
2195 * Returns the definition pointer or NULL in case of error
2196 */
2197static xmlRelaxNGDefinePtr
2198xmlRelaxNGParseInterleave(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2199 xmlRelaxNGDefinePtr def = NULL;
2200 xmlRelaxNGDefinePtr last = NULL, cur;
2201 xmlNodePtr child;
2202
2203 def = xmlRelaxNGNewDefine(ctxt, node);
2204 if (def == NULL) {
2205 return(NULL);
2206 }
2207 def->type = XML_RELAXNG_INTERLEAVE;
2208
2209 if (ctxt->interleaves == NULL)
2210 ctxt->interleaves = xmlHashCreate(10);
2211 if (ctxt->interleaves == NULL) {
2212 if (ctxt->error != NULL)
2213 ctxt->error(ctxt->userData,
2214 "Failed to create interleaves hash table\n");
2215 ctxt->nbErrors++;
2216 } else {
2217 char name[32];
2218
2219 snprintf(name, 32, "interleave%d", ctxt->nbInterleaves++);
2220 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST name, def) < 0) {
2221 if (ctxt->error != NULL)
2222 ctxt->error(ctxt->userData,
2223 "Failed to add %s to hash table\n", name);
2224 ctxt->nbErrors++;
2225 }
2226 }
2227 child = node->children;
Daniel Veillardd2298792003-02-14 16:54:11 +00002228 if (child == NULL) {
2229 if (ctxt->error != NULL)
2230 ctxt->error(ctxt->userData, "Element interleave is empty\n");
2231 ctxt->nbErrors++;
2232 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002233 while (child != NULL) {
2234 if (IS_RELAXNG(child, "element")) {
2235 cur = xmlRelaxNGParseElement(ctxt, child);
2236 } else {
2237 cur = xmlRelaxNGParsePattern(ctxt, child);
2238 }
2239 if (cur != NULL) {
2240 cur->parent = def;
2241 if (last == NULL) {
2242 def->content = last = cur;
2243 } else {
2244 last->next = cur;
2245 last = cur;
2246 }
2247 }
2248 child = child->next;
2249 }
2250
2251 return(def);
2252}
Daniel Veillard6eadf632003-01-23 18:29:16 +00002253
2254/**
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002255 * xmlRelaxNGParseInclude:
2256 * @ctxt: a Relax-NG parser context
2257 * @node: the include node
2258 *
2259 * Integrate the content of an include node in the current grammar
2260 *
2261 * Returns 0 in case of success or -1 in case of error
2262 */
2263static int
2264xmlRelaxNGParseInclude(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2265 xmlRelaxNGIncludePtr incl;
2266 xmlNodePtr root;
2267 int ret = 0, tmp;
2268
2269 incl = node->_private;
2270 if (incl == NULL) {
2271 if (ctxt->error != NULL)
2272 ctxt->error(ctxt->userData,
2273 "Include node has no data\n");
2274 ctxt->nbErrors++;
2275 return(-1);
2276 }
2277 root = xmlDocGetRootElement(incl->doc);
2278 if (root == NULL) {
2279 if (ctxt->error != NULL)
2280 ctxt->error(ctxt->userData,
2281 "Include document is empty\n");
2282 ctxt->nbErrors++;
2283 return(-1);
2284 }
2285 if (!xmlStrEqual(root->name, BAD_CAST "grammar")) {
2286 if (ctxt->error != NULL)
2287 ctxt->error(ctxt->userData,
2288 "Include document root is not a grammar\n");
2289 ctxt->nbErrors++;
2290 return(-1);
2291 }
2292
2293 /*
2294 * Merge the definition from both the include and the internal list
2295 */
2296 if (root->children != NULL) {
2297 tmp = xmlRelaxNGParseGrammarContent(ctxt, root->children);
2298 if (tmp != 0)
2299 ret = -1;
2300 }
2301 if (node->children != NULL) {
2302 tmp = xmlRelaxNGParseGrammarContent(ctxt, node->children);
2303 if (tmp != 0)
2304 ret = -1;
2305 }
2306 return(ret);
2307}
2308
2309/**
Daniel Veillard276be4a2003-01-24 01:03:34 +00002310 * xmlRelaxNGParseDefine:
2311 * @ctxt: a Relax-NG parser context
2312 * @node: the define node
2313 *
2314 * parse the content of a RelaxNG define element node.
2315 *
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002316 * Returns 0 in case of success or -1 in case of error
Daniel Veillard276be4a2003-01-24 01:03:34 +00002317 */
2318static int
2319xmlRelaxNGParseDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2320 xmlChar *name;
2321 int ret = 0, tmp;
2322 xmlRelaxNGDefinePtr def;
2323 const xmlChar *olddefine;
2324
2325 name = xmlGetProp(node, BAD_CAST "name");
2326 if (name == NULL) {
2327 if (ctxt->error != NULL)
2328 ctxt->error(ctxt->userData,
2329 "define has no name\n");
2330 ctxt->nbErrors++;
2331 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00002332 xmlRelaxNGNormExtSpace(name);
2333 if (xmlValidateNCName(name, 0)) {
2334 if (ctxt->error != NULL)
2335 ctxt->error(ctxt->userData,
2336 "define name '%s' is not an NCName\n",
2337 name);
2338 ctxt->nbErrors++;
2339 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002340 def = xmlRelaxNGNewDefine(ctxt, node);
2341 if (def == NULL) {
2342 xmlFree(name);
2343 return(-1);
2344 }
2345 def->type = XML_RELAXNG_DEF;
2346 def->name = name;
2347 if (node->children == NULL) {
2348 if (ctxt->error != NULL)
2349 ctxt->error(ctxt->userData,
2350 "define has no children\n");
2351 ctxt->nbErrors++;
2352 } else {
2353 olddefine = ctxt->define;
2354 ctxt->define = name;
Daniel Veillard154877e2003-01-30 12:17:05 +00002355 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard276be4a2003-01-24 01:03:34 +00002356 ctxt->define = olddefine;
2357 }
2358 if (ctxt->grammar->defs == NULL)
2359 ctxt->grammar->defs = xmlHashCreate(10);
2360 if (ctxt->grammar->defs == NULL) {
2361 if (ctxt->error != NULL)
2362 ctxt->error(ctxt->userData,
2363 "Could not create definition hash\n");
2364 ctxt->nbErrors++;
2365 ret = -1;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002366 } else {
2367 tmp = xmlHashAddEntry(ctxt->grammar->defs, name, def);
2368 if (tmp < 0) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002369 xmlRelaxNGDefinePtr prev;
2370
2371 prev = xmlHashLookup(ctxt->grammar->defs, name);
2372 if (prev == NULL) {
2373 if (ctxt->error != NULL)
2374 ctxt->error(ctxt->userData,
2375 "Internal error on define aggregation of %s\n",
2376 name);
2377 ctxt->nbErrors++;
2378 ret = -1;
Daniel Veillard154877e2003-01-30 12:17:05 +00002379 } else {
2380 while (prev->nextHash != NULL)
2381 prev = prev->nextHash;
2382 prev->nextHash = def;
2383 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002384 }
2385 }
2386 }
2387 return(ret);
2388}
2389
2390/**
Daniel Veillardfebcca42003-02-16 15:44:18 +00002391 * xmlRelaxNGProcessExternalRef:
2392 * @ctxt: the parser context
2393 * @node: the externlRef node
2394 *
2395 * Process and compile an externlRef node
2396 *
2397 * Returns the xmlRelaxNGDefinePtr or NULL in case of error
2398 */
2399static xmlRelaxNGDefinePtr
2400xmlRelaxNGProcessExternalRef(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2401 xmlRelaxNGDocumentPtr docu;
2402 xmlNodePtr root, tmp;
2403 xmlChar *ns;
2404 int newNs = 0;
2405 xmlRelaxNGDefinePtr def;
2406
2407 docu = node->_private;
2408 if (docu != NULL) {
2409 def = xmlRelaxNGNewDefine(ctxt, node);
2410 if (def == NULL)
2411 return(NULL);
2412 def->type = XML_RELAXNG_EXTERNALREF;
2413
2414 if (docu->content == NULL) {
2415 /*
2416 * Then do the parsing for good
2417 */
2418 root = xmlDocGetRootElement(docu->doc);
2419 if (root == NULL) {
2420 if (ctxt->error != NULL)
2421 ctxt->error(ctxt->userData,
2422 "xmlRelaxNGParse: %s is empty\n",
2423 ctxt->URL);
2424 ctxt->nbErrors++;
2425 return (NULL);
2426 }
2427 /*
2428 * ns transmission rules
2429 */
2430 ns = xmlGetProp(root, BAD_CAST "ns");
2431 if (ns == NULL) {
2432 tmp = node;
2433 while ((tmp != NULL) &&
2434 (tmp->type == XML_ELEMENT_NODE)) {
2435 ns = xmlGetProp(tmp, BAD_CAST "ns");
2436 if (ns != NULL) {
2437 break;
2438 }
2439 tmp = tmp->parent;
2440 }
2441 if (ns != NULL) {
2442 xmlSetProp(root, BAD_CAST "ns", ns);
2443 newNs = 1;
2444 xmlFree(ns);
2445 }
2446 } else {
2447 xmlFree(ns);
2448 }
2449
2450 /*
2451 * Parsing to get a precompiled schemas.
2452 */
2453 docu->schema = xmlRelaxNGParseDocument(ctxt, root);
2454 if ((docu->schema != NULL) &&
2455 (docu->schema->topgrammar != NULL)) {
2456 docu->content = docu->schema->topgrammar->start;
2457 }
2458
2459 /*
2460 * the externalRef may be reused in a different ns context
2461 */
2462 if (newNs == 1) {
2463 xmlUnsetProp(root, BAD_CAST "ns");
2464 }
2465 }
2466 def->content = docu->content;
2467 } else {
2468 def = NULL;
2469 }
2470 return(def);
2471}
2472
2473/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00002474 * xmlRelaxNGParsePattern:
2475 * @ctxt: a Relax-NG parser context
2476 * @node: the pattern node.
2477 *
2478 * parse the content of a RelaxNG pattern node.
2479 *
Daniel Veillard276be4a2003-01-24 01:03:34 +00002480 * Returns the definition pointer or NULL in case of error or if no
2481 * pattern is generated.
Daniel Veillard6eadf632003-01-23 18:29:16 +00002482 */
2483static xmlRelaxNGDefinePtr
2484xmlRelaxNGParsePattern(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2485 xmlRelaxNGDefinePtr def = NULL;
2486
Daniel Veillardd2298792003-02-14 16:54:11 +00002487 if (node == NULL) {
2488 return(NULL);
2489 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002490 if (IS_RELAXNG(node, "element")) {
2491 def = xmlRelaxNGParseElement(ctxt, node);
2492 } else if (IS_RELAXNG(node, "attribute")) {
2493 def = xmlRelaxNGParseAttribute(ctxt, node);
2494 } else if (IS_RELAXNG(node, "empty")) {
2495 def = xmlRelaxNGNewDefine(ctxt, node);
2496 if (def == NULL)
2497 return(NULL);
2498 def->type = XML_RELAXNG_EMPTY;
Daniel Veillardd2298792003-02-14 16:54:11 +00002499 if (node->children != NULL) {
2500 if (ctxt->error != NULL)
2501 ctxt->error(ctxt->userData, "empty: had a child node\n");
2502 ctxt->nbErrors++;
2503 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002504 } else if (IS_RELAXNG(node, "text")) {
2505 def = xmlRelaxNGNewDefine(ctxt, node);
2506 if (def == NULL)
2507 return(NULL);
2508 def->type = XML_RELAXNG_TEXT;
2509 if (node->children != NULL) {
2510 if (ctxt->error != NULL)
2511 ctxt->error(ctxt->userData, "text: had a child node\n");
2512 ctxt->nbErrors++;
2513 }
2514 } else if (IS_RELAXNG(node, "zeroOrMore")) {
2515 def = xmlRelaxNGNewDefine(ctxt, node);
2516 if (def == NULL)
2517 return(NULL);
2518 def->type = XML_RELAXNG_ZEROORMORE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002519 if (node->children == NULL) {
2520 if (ctxt->error != NULL)
2521 ctxt->error(ctxt->userData,
2522 "Element %s is empty\n", node->name);
2523 ctxt->nbErrors++;
2524 } else {
2525 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2526 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002527 } else if (IS_RELAXNG(node, "oneOrMore")) {
2528 def = xmlRelaxNGNewDefine(ctxt, node);
2529 if (def == NULL)
2530 return(NULL);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002531 def->type = XML_RELAXNG_ONEORMORE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002532 if (node->children == NULL) {
2533 if (ctxt->error != NULL)
2534 ctxt->error(ctxt->userData,
2535 "Element %s is empty\n", node->name);
2536 ctxt->nbErrors++;
2537 } else {
2538 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2539 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002540 } else if (IS_RELAXNG(node, "optional")) {
2541 def = xmlRelaxNGNewDefine(ctxt, node);
2542 if (def == NULL)
2543 return(NULL);
2544 def->type = XML_RELAXNG_OPTIONAL;
Daniel Veillardd2298792003-02-14 16:54:11 +00002545 if (node->children == NULL) {
2546 if (ctxt->error != NULL)
2547 ctxt->error(ctxt->userData,
2548 "Element %s is empty\n", node->name);
2549 ctxt->nbErrors++;
2550 } else {
2551 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2552 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002553 } else if (IS_RELAXNG(node, "choice")) {
2554 def = xmlRelaxNGNewDefine(ctxt, node);
2555 if (def == NULL)
2556 return(NULL);
2557 def->type = XML_RELAXNG_CHOICE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002558 if (node->children == NULL) {
2559 if (ctxt->error != NULL)
2560 ctxt->error(ctxt->userData,
2561 "Element %s is empty\n", node->name);
2562 ctxt->nbErrors++;
2563 } else {
2564 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2565 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002566 } else if (IS_RELAXNG(node, "group")) {
2567 def = xmlRelaxNGNewDefine(ctxt, node);
2568 if (def == NULL)
2569 return(NULL);
2570 def->type = XML_RELAXNG_GROUP;
Daniel Veillardd2298792003-02-14 16:54:11 +00002571 if (node->children == NULL) {
2572 if (ctxt->error != NULL)
2573 ctxt->error(ctxt->userData,
2574 "Element %s is empty\n", node->name);
2575 ctxt->nbErrors++;
2576 } else {
2577 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2578 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002579 } else if (IS_RELAXNG(node, "ref")) {
2580 def = xmlRelaxNGNewDefine(ctxt, node);
2581 if (def == NULL)
2582 return(NULL);
2583 def->type = XML_RELAXNG_REF;
2584 def->name = xmlGetProp(node, BAD_CAST "name");
2585 if (def->name == NULL) {
2586 if (ctxt->error != NULL)
2587 ctxt->error(ctxt->userData,
2588 "ref has no name\n");
2589 ctxt->nbErrors++;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002590 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00002591 xmlRelaxNGNormExtSpace(def->name);
2592 if (xmlValidateNCName(def->name, 0)) {
2593 if (ctxt->error != NULL)
2594 ctxt->error(ctxt->userData,
2595 "ref name '%s' is not an NCName\n",
2596 def->name);
2597 ctxt->nbErrors++;
2598 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002599 if ((ctxt->define != NULL) &&
2600 (xmlStrEqual(ctxt->define, def->name))) {
2601 if (ctxt->error != NULL)
2602 ctxt->error(ctxt->userData,
2603 "Recursive reference to %s not in an element\n",
2604 def->name);
2605 ctxt->nbErrors++;
2606 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002607 }
2608 if (node->children != NULL) {
2609 if (ctxt->error != NULL)
2610 ctxt->error(ctxt->userData,
2611 "ref is not empty\n");
2612 ctxt->nbErrors++;
2613 }
2614 if (ctxt->grammar->refs == NULL)
2615 ctxt->grammar->refs = xmlHashCreate(10);
2616 if (ctxt->grammar->refs == NULL) {
2617 if (ctxt->error != NULL)
2618 ctxt->error(ctxt->userData,
2619 "Could not create references hash\n");
2620 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002621 def = NULL;
2622 } else {
2623 int tmp;
2624
2625 tmp = xmlHashAddEntry(ctxt->grammar->refs, def->name, def);
2626 if (tmp < 0) {
2627 xmlRelaxNGDefinePtr prev;
2628
2629 prev = (xmlRelaxNGDefinePtr)
2630 xmlHashLookup(ctxt->grammar->refs, def->name);
2631 if (prev == NULL) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00002632 if (def->name != NULL) {
2633 if (ctxt->error != NULL)
2634 ctxt->error(ctxt->userData,
2635 "Error refs definitions '%s'\n",
2636 def->name);
2637 } else {
2638 if (ctxt->error != NULL)
2639 ctxt->error(ctxt->userData,
2640 "Error refs definitions\n");
2641 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002642 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002643 def = NULL;
2644 } else {
2645 def->nextHash = prev->nextHash;
2646 prev->nextHash = def;
2647 }
2648 }
2649 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002650 } else if (IS_RELAXNG(node, "data")) {
2651 def = xmlRelaxNGParseData(ctxt, node);
Daniel Veillardd2298792003-02-14 16:54:11 +00002652#if 0
Daniel Veillard276be4a2003-01-24 01:03:34 +00002653 } else if (IS_RELAXNG(node, "define")) {
2654 xmlRelaxNGParseDefine(ctxt, node);
2655 def = NULL;
Daniel Veillardd2298792003-02-14 16:54:11 +00002656#endif
Daniel Veillardedc91922003-01-26 00:52:04 +00002657 } else if (IS_RELAXNG(node, "value")) {
2658 def = xmlRelaxNGParseValue(ctxt, node);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002659 } else if (IS_RELAXNG(node, "list")) {
2660 def = xmlRelaxNGNewDefine(ctxt, node);
2661 if (def == NULL)
2662 return(NULL);
2663 def->type = XML_RELAXNG_LIST;
Daniel Veillardd2298792003-02-14 16:54:11 +00002664 if (node->children == NULL) {
2665 if (ctxt->error != NULL)
2666 ctxt->error(ctxt->userData,
2667 "Element %s is empty\n", node->name);
2668 ctxt->nbErrors++;
2669 } else {
2670 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2671 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002672 } else if (IS_RELAXNG(node, "interleave")) {
2673 def = xmlRelaxNGParseInterleave(ctxt, node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002674 } else if (IS_RELAXNG(node, "externalRef")) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00002675 def = xmlRelaxNGProcessExternalRef(ctxt, node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002676 } else if (IS_RELAXNG(node, "notAllowed")) {
2677 def = xmlRelaxNGNewDefine(ctxt, node);
2678 if (def == NULL)
2679 return(NULL);
2680 def->type = XML_RELAXNG_NOT_ALLOWED;
2681 if (node->children != NULL) {
2682 if (ctxt->error != NULL)
2683 ctxt->error(ctxt->userData,
2684 "xmlRelaxNGParse: notAllowed element is not empty\n");
2685 ctxt->nbErrors++;
2686 }
Daniel Veillard419a7682003-02-03 23:22:49 +00002687 } else if (IS_RELAXNG(node, "grammar")) {
2688 xmlRelaxNGGrammarPtr grammar, old;
2689 xmlRelaxNGGrammarPtr oldparent;
2690
2691 oldparent = ctxt->parentgrammar;
2692 old = ctxt->grammar;
2693 ctxt->parentgrammar = old;
2694 grammar = xmlRelaxNGParseGrammar(ctxt, node->children);
2695 if (old != NULL) {
2696 ctxt->grammar = old;
2697 ctxt->parentgrammar = oldparent;
2698 if (grammar != NULL) {
2699 grammar->next = old->next;
2700 old->next = grammar;
2701 }
2702 }
2703 if (grammar != NULL)
2704 def = grammar->start;
2705 else
2706 def = NULL;
2707 } else if (IS_RELAXNG(node, "parentRef")) {
2708 if (ctxt->parentgrammar == NULL) {
2709 if (ctxt->error != NULL)
2710 ctxt->error(ctxt->userData,
2711 "Use of parentRef without a parent grammar\n");
2712 ctxt->nbErrors++;
2713 return(NULL);
2714 }
2715 def = xmlRelaxNGNewDefine(ctxt, node);
2716 if (def == NULL)
2717 return(NULL);
2718 def->type = XML_RELAXNG_PARENTREF;
2719 def->name = xmlGetProp(node, BAD_CAST "name");
2720 if (def->name == NULL) {
2721 if (ctxt->error != NULL)
2722 ctxt->error(ctxt->userData,
2723 "parentRef has no name\n");
2724 ctxt->nbErrors++;
Daniel Veillardd2298792003-02-14 16:54:11 +00002725 } else {
2726 xmlRelaxNGNormExtSpace(def->name);
2727 if (xmlValidateNCName(def->name, 0)) {
2728 if (ctxt->error != NULL)
2729 ctxt->error(ctxt->userData,
2730 "parentRef name '%s' is not an NCName\n",
2731 def->name);
2732 ctxt->nbErrors++;
2733 }
Daniel Veillard419a7682003-02-03 23:22:49 +00002734 }
2735 if (node->children != NULL) {
2736 if (ctxt->error != NULL)
2737 ctxt->error(ctxt->userData,
2738 "parentRef is not empty\n");
2739 ctxt->nbErrors++;
2740 }
2741 if (ctxt->parentgrammar->refs == NULL)
2742 ctxt->parentgrammar->refs = xmlHashCreate(10);
2743 if (ctxt->parentgrammar->refs == NULL) {
2744 if (ctxt->error != NULL)
2745 ctxt->error(ctxt->userData,
2746 "Could not create references hash\n");
2747 ctxt->nbErrors++;
2748 def = NULL;
Daniel Veillardd2298792003-02-14 16:54:11 +00002749 } else if (def->name != NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +00002750 int tmp;
2751
2752 tmp = xmlHashAddEntry(ctxt->parentgrammar->refs, def->name, def);
2753 if (tmp < 0) {
2754 xmlRelaxNGDefinePtr prev;
2755
2756 prev = (xmlRelaxNGDefinePtr)
2757 xmlHashLookup(ctxt->parentgrammar->refs, def->name);
2758 if (prev == NULL) {
2759 if (ctxt->error != NULL)
2760 ctxt->error(ctxt->userData,
2761 "Internal error parentRef definitions '%s'\n",
2762 def->name);
2763 ctxt->nbErrors++;
2764 def = NULL;
2765 } else {
2766 def->nextHash = prev->nextHash;
2767 prev->nextHash = def;
2768 }
2769 }
2770 }
Daniel Veillardd2298792003-02-14 16:54:11 +00002771 } else if (IS_RELAXNG(node, "mixed")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00002772 if (node->children == NULL) {
2773 if (ctxt->error != NULL)
2774 ctxt->error(ctxt->userData,
2775 "Mixed is empty\n");
2776 ctxt->nbErrors++;
2777 def = NULL;
2778#if 0
2779 } else if (node->children->next == NULL) {
2780 def = xmlRelaxNGNewDefine(ctxt, node);
2781 if (def == NULL)
2782 return(NULL);
2783 def->type = XML_RELAXNG_MIXED;
2784#endif
2785 } else {
2786 def = xmlRelaxNGParseInterleave(ctxt, node);
2787 if (def != NULL) {
2788 xmlRelaxNGDefinePtr tmp;
2789 tmp = xmlRelaxNGNewDefine(ctxt, node);
2790 if (tmp == NULL)
2791 return(def);
2792 tmp->type = XML_RELAXNG_TEXT;
2793 tmp->next = def->content;
2794 def->content = tmp;
2795 }
2796 }
Daniel Veillardd2298792003-02-14 16:54:11 +00002797 } else {
2798 if (ctxt->error != NULL)
2799 ctxt->error(ctxt->userData,
2800 "Unexpected node %s is not a pattern\n",
2801 node->name);
2802 ctxt->nbErrors++;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002803 def = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002804 }
2805 return(def);
2806}
2807
2808/**
2809 * xmlRelaxNGParseAttribute:
2810 * @ctxt: a Relax-NG parser context
2811 * @node: the element node
2812 *
2813 * parse the content of a RelaxNG attribute node.
2814 *
2815 * Returns the definition pointer or NULL in case of error.
2816 */
2817static xmlRelaxNGDefinePtr
2818xmlRelaxNGParseAttribute(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
Daniel Veillardd2298792003-02-14 16:54:11 +00002819 xmlRelaxNGDefinePtr ret, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002820 xmlNodePtr child;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002821 int old_flags;
2822
2823 ret = xmlRelaxNGNewDefine(ctxt, node);
2824 if (ret == NULL)
2825 return(NULL);
2826 ret->type = XML_RELAXNG_ATTRIBUTE;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002827 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002828 child = node->children;
2829 if (child == NULL) {
2830 if (ctxt->error != NULL)
2831 ctxt->error(ctxt->userData,
2832 "xmlRelaxNGParseattribute: attribute has no children\n");
2833 ctxt->nbErrors++;
2834 return(ret);
2835 }
2836 old_flags = ctxt->flags;
2837 ctxt->flags |= XML_RELAXNG_IN_ATTRIBUTE;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002838 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
2839 if (cur != NULL)
2840 child = child->next;
2841
Daniel Veillardd2298792003-02-14 16:54:11 +00002842 if (child != NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00002843 cur = xmlRelaxNGParsePattern(ctxt, child);
2844 if (cur != NULL) {
2845 switch (cur->type) {
2846 case XML_RELAXNG_EMPTY:
2847 case XML_RELAXNG_NOT_ALLOWED:
2848 case XML_RELAXNG_TEXT:
2849 case XML_RELAXNG_ELEMENT:
2850 case XML_RELAXNG_DATATYPE:
2851 case XML_RELAXNG_VALUE:
2852 case XML_RELAXNG_LIST:
2853 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00002854 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002855 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00002856 case XML_RELAXNG_DEF:
2857 case XML_RELAXNG_ONEORMORE:
2858 case XML_RELAXNG_ZEROORMORE:
2859 case XML_RELAXNG_OPTIONAL:
2860 case XML_RELAXNG_CHOICE:
2861 case XML_RELAXNG_GROUP:
Daniel Veillard416589a2003-02-17 17:25:42 +00002862#if 0
2863 case XML_RELAXNG_MIXED:
2864#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00002865 case XML_RELAXNG_INTERLEAVE:
Daniel Veillardd2298792003-02-14 16:54:11 +00002866 ret->content = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002867 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002868 break;
2869 case XML_RELAXNG_ATTRIBUTE:
Daniel Veillardd2298792003-02-14 16:54:11 +00002870 if (ctxt->error != NULL)
2871 ctxt->error(ctxt->userData,
2872 "attribute has an attribute child\n");
2873 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002874 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002875 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00002876 case XML_RELAXNG_EXCEPT:
Daniel Veillardd2298792003-02-14 16:54:11 +00002877 if (ctxt->error != NULL)
2878 ctxt->error(ctxt->userData,
2879 "attribute has invalid content\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002880 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002881 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002882 }
2883 }
2884 child = child->next;
2885 }
Daniel Veillardd2298792003-02-14 16:54:11 +00002886 if (child != NULL) {
2887 if (ctxt->error != NULL)
2888 ctxt->error(ctxt->userData, "attribute has multiple children\n");
2889 ctxt->nbErrors++;
2890 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002891 ctxt->flags = old_flags;
2892 return(ret);
2893}
2894
2895/**
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002896 * xmlRelaxNGParseExceptNameClass:
2897 * @ctxt: a Relax-NG parser context
2898 * @node: the except node
Daniel Veillard144fae12003-02-03 13:17:57 +00002899 * @attr: 1 if within an attribute, 0 if within an element
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002900 *
2901 * parse the content of a RelaxNG nameClass node.
2902 *
2903 * Returns the definition pointer or NULL in case of error.
2904 */
2905static xmlRelaxNGDefinePtr
Daniel Veillard144fae12003-02-03 13:17:57 +00002906xmlRelaxNGParseExceptNameClass(xmlRelaxNGParserCtxtPtr ctxt,
2907 xmlNodePtr node, int attr) {
2908 xmlRelaxNGDefinePtr ret, cur, last = NULL;
2909 xmlNodePtr child;
2910
Daniel Veillardd2298792003-02-14 16:54:11 +00002911 if (!IS_RELAXNG(node, "except")) {
2912 if (ctxt->error != NULL)
2913 ctxt->error(ctxt->userData,
2914 "Expecting an except node\n");
2915 ctxt->nbErrors++;
Daniel Veillard144fae12003-02-03 13:17:57 +00002916 return(NULL);
Daniel Veillardd2298792003-02-14 16:54:11 +00002917 }
2918 if (node->next != NULL) {
2919 if (ctxt->error != NULL)
2920 ctxt->error(ctxt->userData,
2921 "exceptNameClass allows only a single except node\n");
2922 ctxt->nbErrors++;
2923 }
Daniel Veillard144fae12003-02-03 13:17:57 +00002924 if (node->children == NULL) {
2925 if (ctxt->error != NULL)
2926 ctxt->error(ctxt->userData,
2927 "except has no content\n");
2928 ctxt->nbErrors++;
2929 return(NULL);
2930 }
2931
2932 ret = xmlRelaxNGNewDefine(ctxt, node);
2933 if (ret == NULL)
2934 return(NULL);
2935 ret->type = XML_RELAXNG_EXCEPT;
2936 child = node->children;
2937 while (child != NULL) {
2938 cur = xmlRelaxNGNewDefine(ctxt, child);
2939 if (cur == NULL)
2940 break;
2941 if (attr)
2942 cur->type = XML_RELAXNG_ATTRIBUTE;
2943 else
2944 cur->type = XML_RELAXNG_ELEMENT;
2945
Daniel Veillard419a7682003-02-03 23:22:49 +00002946 if (xmlRelaxNGParseNameClass(ctxt, child, cur) != NULL) {
Daniel Veillard144fae12003-02-03 13:17:57 +00002947 if (last == NULL) {
2948 ret->content = cur;
2949 } else {
2950 last->next = cur;
2951 }
2952 last = cur;
2953 }
2954 child = child->next;
2955 }
2956
2957 return(ret);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002958}
2959
2960/**
2961 * xmlRelaxNGParseNameClass:
2962 * @ctxt: a Relax-NG parser context
2963 * @node: the nameClass node
2964 * @def: the current definition
2965 *
2966 * parse the content of a RelaxNG nameClass node.
2967 *
2968 * Returns the definition pointer or NULL in case of error.
2969 */
2970static xmlRelaxNGDefinePtr
2971xmlRelaxNGParseNameClass(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
2972 xmlRelaxNGDefinePtr def) {
2973 xmlRelaxNGDefinePtr ret = def;
2974 xmlChar *val;
2975
2976 if (IS_RELAXNG(node, "name")) {
2977 val = xmlNodeGetContent(node);
Daniel Veillardd2298792003-02-14 16:54:11 +00002978 xmlRelaxNGNormExtSpace(val);
2979 if (xmlValidateNCName(val, 0)) {
2980 if (ctxt->error != NULL) {
2981 if (node->parent != NULL)
2982 ctxt->error(ctxt->userData,
2983 "Element %s name '%s' is not an NCName\n",
2984 node->parent->name, val);
2985 else
2986 ctxt->error(ctxt->userData,
2987 "name '%s' is not an NCName\n",
2988 val);
2989 }
2990 ctxt->nbErrors++;
2991 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002992 ret->name = val;
2993 val = xmlGetProp(node, BAD_CAST "ns");
2994 ret->ns = val;
Daniel Veillard416589a2003-02-17 17:25:42 +00002995 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
2996 (val != NULL) &&
2997 (xmlStrEqual(val, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
2998 ctxt->error(ctxt->userData,
2999 "Attribute with namespace '%s' is not allowed\n",
3000 val);
3001 ctxt->nbErrors++;
3002 }
3003 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3004 (val != NULL) &&
3005 (val[0] == 0) &&
3006 (xmlStrEqual(ret->name, BAD_CAST "xmlns"))) {
3007 ctxt->error(ctxt->userData,
3008 "Attribute with QName 'xmlns' is not allowed\n",
3009 val);
3010 ctxt->nbErrors++;
3011 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003012 } else if (IS_RELAXNG(node, "anyName")) {
3013 ret->name = NULL;
3014 ret->ns = NULL;
3015 if (node->children != NULL) {
3016 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00003017 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
3018 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003019 }
3020 } else if (IS_RELAXNG(node, "nsName")) {
3021 ret->name = NULL;
3022 ret->ns = xmlGetProp(node, BAD_CAST "ns");
3023 if (ret->ns == NULL) {
3024 if (ctxt->error != NULL)
3025 ctxt->error(ctxt->userData,
3026 "nsName has no ns attribute\n");
3027 ctxt->nbErrors++;
3028 }
Daniel Veillard416589a2003-02-17 17:25:42 +00003029 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3030 (ret->ns != NULL) &&
3031 (xmlStrEqual(ret->ns, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
3032 ctxt->error(ctxt->userData,
3033 "Attribute with namespace '%s' is not allowed\n",
3034 ret->ns);
3035 ctxt->nbErrors++;
3036 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003037 if (node->children != NULL) {
3038 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00003039 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
3040 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003041 }
3042 } else if (IS_RELAXNG(node, "choice")) {
Daniel Veillardd2298792003-02-14 16:54:11 +00003043 if (node->children == NULL) {
3044 if (ctxt->error != NULL)
3045 ctxt->error(ctxt->userData,
3046 "Element choice is empty\n");
3047 ctxt->nbErrors++;
3048 } else {
3049 xmlNodePtr child;
3050 xmlRelaxNGDefinePtr last = NULL, tmp;
3051
3052 child = node->children;
3053 while (child != NULL) {
Daniel Veillard416589a2003-02-17 17:25:42 +00003054 tmp = xmlRelaxNGParseNameClass(ctxt, child, def);
Daniel Veillardd2298792003-02-14 16:54:11 +00003055 if (tmp != NULL) {
3056 if (last == NULL) {
3057 last = ret->nameClass = tmp;
3058 } else {
3059 last->next = tmp;
3060 last = tmp;
3061 }
3062 }
3063 child = child->next;
3064 }
3065 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003066 } else {
3067 if (ctxt->error != NULL)
3068 ctxt->error(ctxt->userData,
3069 "expecting name, anyName, nsName or choice : got %s\n",
3070 node->name);
3071 ctxt->nbErrors++;
3072 return(NULL);
3073 }
3074 return(ret);
3075}
3076
3077/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00003078 * xmlRelaxNGParseElement:
3079 * @ctxt: a Relax-NG parser context
3080 * @node: the element node
3081 *
3082 * parse the content of a RelaxNG element node.
3083 *
3084 * Returns the definition pointer or NULL in case of error.
3085 */
3086static xmlRelaxNGDefinePtr
3087xmlRelaxNGParseElement(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3088 xmlRelaxNGDefinePtr ret, cur, last;
3089 xmlNodePtr child;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003090 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003091
3092 ret = xmlRelaxNGNewDefine(ctxt, node);
3093 if (ret == NULL)
3094 return(NULL);
3095 ret->type = XML_RELAXNG_ELEMENT;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003096 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003097 child = node->children;
3098 if (child == NULL) {
3099 if (ctxt->error != NULL)
3100 ctxt->error(ctxt->userData,
3101 "xmlRelaxNGParseElement: element has no children\n");
3102 ctxt->nbErrors++;
3103 return(ret);
3104 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003105 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
3106 if (cur != NULL)
3107 child = child->next;
3108
Daniel Veillard6eadf632003-01-23 18:29:16 +00003109 if (child == NULL) {
3110 if (ctxt->error != NULL)
3111 ctxt->error(ctxt->userData,
3112 "xmlRelaxNGParseElement: element has no content\n");
3113 ctxt->nbErrors++;
3114 return(ret);
3115 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003116 olddefine = ctxt->define;
3117 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003118 last = NULL;
3119 while (child != NULL) {
3120 cur = xmlRelaxNGParsePattern(ctxt, child);
3121 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003122 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003123 switch (cur->type) {
3124 case XML_RELAXNG_EMPTY:
3125 case XML_RELAXNG_NOT_ALLOWED:
3126 case XML_RELAXNG_TEXT:
3127 case XML_RELAXNG_ELEMENT:
3128 case XML_RELAXNG_DATATYPE:
3129 case XML_RELAXNG_VALUE:
3130 case XML_RELAXNG_LIST:
3131 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00003132 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003133 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00003134 case XML_RELAXNG_DEF:
3135 case XML_RELAXNG_ZEROORMORE:
3136 case XML_RELAXNG_ONEORMORE:
3137 case XML_RELAXNG_OPTIONAL:
3138 case XML_RELAXNG_CHOICE:
3139 case XML_RELAXNG_GROUP:
Daniel Veillard416589a2003-02-17 17:25:42 +00003140#if 0
3141 case XML_RELAXNG_MIXED:
3142#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00003143 case XML_RELAXNG_INTERLEAVE:
3144 if (last == NULL) {
3145 ret->content = last = cur;
3146 } else {
3147 if ((last->type == XML_RELAXNG_ELEMENT) &&
3148 (ret->content == last)) {
3149 ret->content = xmlRelaxNGNewDefine(ctxt, node);
3150 if (ret->content != NULL) {
3151 ret->content->type = XML_RELAXNG_GROUP;
3152 ret->content->content = last;
3153 } else {
3154 ret->content = last;
3155 }
3156 }
3157 last->next = cur;
3158 last = cur;
3159 }
3160 break;
3161 case XML_RELAXNG_ATTRIBUTE:
3162 cur->next = ret->attrs;
3163 ret->attrs = cur;
3164 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003165 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00003166 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003167 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003168 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003169 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003170 }
3171 }
3172 child = child->next;
3173 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003174 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003175 return(ret);
3176}
3177
3178/**
3179 * xmlRelaxNGParsePatterns:
3180 * @ctxt: a Relax-NG parser context
3181 * @nodes: list of nodes
Daniel Veillard154877e2003-01-30 12:17:05 +00003182 * @group: use an implicit <group> for elements
Daniel Veillard6eadf632003-01-23 18:29:16 +00003183 *
3184 * parse the content of a RelaxNG start node.
3185 *
3186 * Returns the definition pointer or NULL in case of error.
3187 */
3188static xmlRelaxNGDefinePtr
Daniel Veillard154877e2003-01-30 12:17:05 +00003189xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes,
3190 int group) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003191 xmlRelaxNGDefinePtr def = NULL, last = NULL, cur, parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003192
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003193 parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003194 while (nodes != NULL) {
3195 if (IS_RELAXNG(nodes, "element")) {
3196 cur = xmlRelaxNGParseElement(ctxt, nodes);
3197 if (def == NULL) {
3198 def = last = cur;
3199 } else {
Daniel Veillard154877e2003-01-30 12:17:05 +00003200 if ((group == 1) && (def->type == XML_RELAXNG_ELEMENT) &&
3201 (def == last)) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003202 def = xmlRelaxNGNewDefine(ctxt, nodes);
3203 def->type = XML_RELAXNG_GROUP;
3204 def->content = last;
3205 }
3206 last->next = cur;
3207 last = cur;
3208 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003209 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003210 } else {
3211 cur = xmlRelaxNGParsePattern(ctxt, nodes);
Daniel Veillard419a7682003-02-03 23:22:49 +00003212 if (cur != NULL) {
3213 if (def == NULL) {
3214 def = last = cur;
3215 } else {
3216 last->next = cur;
3217 last = cur;
3218 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003219 }
3220 }
3221 nodes = nodes->next;
3222 }
3223 return(def);
3224}
3225
3226/**
3227 * xmlRelaxNGParseStart:
3228 * @ctxt: a Relax-NG parser context
3229 * @nodes: start children nodes
3230 *
3231 * parse the content of a RelaxNG start node.
3232 *
3233 * Returns 0 in case of success, -1 in case of error
3234 */
3235static int
3236xmlRelaxNGParseStart(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
3237 int ret = 0;
3238 xmlRelaxNGDefinePtr def = NULL;
3239
Daniel Veillardd2298792003-02-14 16:54:11 +00003240 if (nodes == NULL) {
3241 if (ctxt->error != NULL)
3242 ctxt->error(ctxt->userData,
3243 "start has no children\n");
3244 ctxt->nbErrors++;
3245 return(-1);
3246 }
3247 if (IS_RELAXNG(nodes, "empty")) {
3248 def = xmlRelaxNGNewDefine(ctxt, nodes);
3249 if (def == NULL)
3250 return(-1);
3251 def->type = XML_RELAXNG_EMPTY;
3252 if (nodes->children != NULL) {
3253 if (ctxt->error != NULL)
3254 ctxt->error(ctxt->userData, "element empty is not empty\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003255 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003256 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003257 ctxt->grammar->start = def;
3258 } else if (IS_RELAXNG(nodes, "notAllowed")) {
3259 def = xmlRelaxNGNewDefine(ctxt, nodes);
3260 if (def == NULL)
3261 return(-1);
3262 def->type = XML_RELAXNG_NOT_ALLOWED;
3263 if (nodes->children != NULL) {
3264 if (ctxt->error != NULL)
3265 ctxt->error(ctxt->userData,
3266 "element notAllowed is not empty\n");
3267 ctxt->nbErrors++;
3268 }
3269 ctxt->grammar->start = def;
3270 } else {
3271 def = xmlRelaxNGParsePatterns(ctxt, nodes, 1);
3272 ctxt->grammar->start = def;
3273 }
3274 nodes = nodes->next;
3275 if (nodes != NULL) {
3276 if (ctxt->error != NULL)
3277 ctxt->error(ctxt->userData,
3278 "start more than one children\n");
3279 ctxt->nbErrors++;
3280 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003281 }
3282 return(ret);
3283}
3284
3285/**
3286 * xmlRelaxNGParseGrammarContent:
3287 * @ctxt: a Relax-NG parser context
3288 * @nodes: grammar children nodes
3289 *
3290 * parse the content of a RelaxNG grammar node.
3291 *
3292 * Returns 0 in case of success, -1 in case of error
3293 */
3294static int
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003295xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003296{
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003297 int ret = 0, tmp;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003298
3299 if (nodes == NULL) {
3300 if (ctxt->error != NULL)
3301 ctxt->error(ctxt->userData,
3302 "grammar has no children\n");
3303 ctxt->nbErrors++;
3304 return(-1);
3305 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003306 while (nodes != NULL) {
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003307 if (IS_RELAXNG(nodes, "start")) {
3308 if (nodes->children == NULL) {
3309 if (ctxt->error != NULL)
3310 ctxt->error(ctxt->userData,
Daniel Veillardd2298792003-02-14 16:54:11 +00003311 "start has no children\n");
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003312 ctxt->nbErrors++;
3313 } else {
3314 tmp = xmlRelaxNGParseStart(ctxt, nodes->children);
3315 if (tmp != 0)
3316 ret = -1;
3317 }
3318 } else if (IS_RELAXNG(nodes, "define")) {
3319 tmp = xmlRelaxNGParseDefine(ctxt, nodes);
3320 if (tmp != 0)
3321 ret = -1;
3322 } else if (IS_RELAXNG(nodes, "include")) {
3323 tmp = xmlRelaxNGParseInclude(ctxt, nodes);
3324 if (tmp != 0)
3325 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003326 } else {
3327 if (ctxt->error != NULL)
3328 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003329 "grammar has unexpected child %s\n", nodes->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003330 ctxt->nbErrors++;
3331 ret = -1;
3332 }
3333 nodes = nodes->next;
3334 }
3335 return (ret);
3336}
3337
3338/**
3339 * xmlRelaxNGCheckReference:
3340 * @ref: the ref
3341 * @ctxt: a Relax-NG parser context
3342 * @name: the name associated to the defines
3343 *
3344 * Applies the 4.17. combine attribute rule for all the define
3345 * element of a given grammar using the same name.
3346 */
3347static void
3348xmlRelaxNGCheckReference(xmlRelaxNGDefinePtr ref,
3349 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
3350 xmlRelaxNGGrammarPtr grammar;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003351 xmlRelaxNGDefinePtr def, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003352
3353 grammar = ctxt->grammar;
3354 if (grammar == NULL) {
3355 if (ctxt->error != NULL)
3356 ctxt->error(ctxt->userData,
3357 "Internal error: no grammar in CheckReference %s\n",
3358 name);
3359 ctxt->nbErrors++;
3360 return;
3361 }
3362 if (ref->content != NULL) {
3363 if (ctxt->error != NULL)
3364 ctxt->error(ctxt->userData,
3365 "Internal error: reference has content in CheckReference %s\n",
3366 name);
3367 ctxt->nbErrors++;
3368 return;
3369 }
3370 if (grammar->defs != NULL) {
3371 def = xmlHashLookup(grammar->defs, name);
3372 if (def != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00003373 cur = ref;
3374 while (cur != NULL) {
3375 cur->content = def;
3376 cur = cur->nextHash;
3377 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003378 } else {
3379 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003380 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003381 }
3382 }
3383 /*
3384 * TODO: make a closure and verify there is no loop !
3385 */
3386}
3387
3388/**
3389 * xmlRelaxNGCheckCombine:
3390 * @define: the define(s) list
3391 * @ctxt: a Relax-NG parser context
3392 * @name: the name associated to the defines
3393 *
3394 * Applies the 4.17. combine attribute rule for all the define
3395 * element of a given grammar using the same name.
3396 */
3397static void
3398xmlRelaxNGCheckCombine(xmlRelaxNGDefinePtr define,
3399 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
3400 xmlChar *combine;
3401 int choiceOrInterleave = -1;
3402 int missing = 0;
3403 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
3404
3405 if (define->nextHash == NULL)
3406 return;
3407 cur = define;
3408 while (cur != NULL) {
3409 combine = xmlGetProp(cur->node, BAD_CAST "combine");
3410 if (combine != NULL) {
3411 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3412 if (choiceOrInterleave == -1)
3413 choiceOrInterleave = 1;
3414 else if (choiceOrInterleave == 0) {
3415 if (ctxt->error != NULL)
3416 ctxt->error(ctxt->userData,
3417 "Defines for %s use both 'choice' and 'interleave'\n",
3418 name);
3419 ctxt->nbErrors++;
3420 }
Daniel Veillard154877e2003-01-30 12:17:05 +00003421 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003422 if (choiceOrInterleave == -1)
3423 choiceOrInterleave = 0;
3424 else if (choiceOrInterleave == 1) {
3425 if (ctxt->error != NULL)
3426 ctxt->error(ctxt->userData,
3427 "Defines for %s use both 'choice' and 'interleave'\n",
3428 name);
3429 ctxt->nbErrors++;
3430 }
3431 } else {
3432 if (ctxt->error != NULL)
3433 ctxt->error(ctxt->userData,
3434 "Defines for %s use unknown combine value '%s''\n",
3435 name, combine);
3436 ctxt->nbErrors++;
3437 }
3438 xmlFree(combine);
3439 } else {
3440 if (missing == 0)
3441 missing = 1;
3442 else {
3443 if (ctxt->error != NULL)
3444 ctxt->error(ctxt->userData,
3445 "Some defines for %s lacks the combine attribute\n",
3446 name);
3447 ctxt->nbErrors++;
3448 }
3449 }
3450
3451 cur = cur->nextHash;
3452 }
3453#ifdef DEBUG
3454 xmlGenericError(xmlGenericErrorContext,
3455 "xmlRelaxNGCheckCombine(): merging %s defines: %d\n",
3456 name, choiceOrInterleave);
3457#endif
3458 if (choiceOrInterleave == -1)
3459 choiceOrInterleave = 0;
3460 cur = xmlRelaxNGNewDefine(ctxt, define->node);
3461 if (cur == NULL)
3462 return;
3463 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003464 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard154877e2003-01-30 12:17:05 +00003465 else
3466 cur->type = XML_RELAXNG_CHOICE;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003467 tmp = define;
3468 last = NULL;
3469 while (tmp != NULL) {
3470 if (tmp->content != NULL) {
3471 if (tmp->content->next != NULL) {
3472 /*
3473 * we need first to create a wrapper.
3474 */
3475 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
3476 if (tmp2 == NULL)
3477 break;
3478 tmp2->type = XML_RELAXNG_GROUP;
3479 tmp2->content = tmp->content;
3480 } else {
3481 tmp2 = tmp->content;
3482 }
3483 if (last == NULL) {
3484 cur->content = tmp2;
3485 } else {
3486 last->next = tmp2;
3487 }
3488 last = tmp2;
3489 tmp->content = NULL;
3490 }
3491 tmp = tmp->nextHash;
3492 }
3493 define->content = cur;
Daniel Veillard154877e2003-01-30 12:17:05 +00003494 if (choiceOrInterleave == 0) {
3495 if (ctxt->interleaves == NULL)
3496 ctxt->interleaves = xmlHashCreate(10);
3497 if (ctxt->interleaves == NULL) {
3498 if (ctxt->error != NULL)
3499 ctxt->error(ctxt->userData,
3500 "Failed to create interleaves hash table\n");
3501 ctxt->nbErrors++;
3502 } else {
3503 char tmpname[32];
3504
3505 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
3506 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
3507 if (ctxt->error != NULL)
3508 ctxt->error(ctxt->userData,
3509 "Failed to add %s to hash table\n", tmpname);
3510 ctxt->nbErrors++;
3511 }
3512 }
3513 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003514}
3515
3516/**
3517 * xmlRelaxNGCombineStart:
3518 * @ctxt: a Relax-NG parser context
3519 * @grammar: the grammar
3520 *
3521 * Applies the 4.17. combine rule for all the start
3522 * element of a given grammar.
3523 */
3524static void
3525xmlRelaxNGCombineStart(xmlRelaxNGParserCtxtPtr ctxt,
3526 xmlRelaxNGGrammarPtr grammar) {
3527 xmlRelaxNGDefinePtr starts;
3528 xmlChar *combine;
3529 int choiceOrInterleave = -1;
3530 int missing = 0;
3531 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
3532
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003533 starts = grammar->startList;
3534 if ((starts == NULL) || (starts->nextHash == NULL))
Daniel Veillard6eadf632003-01-23 18:29:16 +00003535 return;
3536 cur = starts;
3537 while (cur != NULL) {
3538 combine = xmlGetProp(cur->node, BAD_CAST "combine");
3539 if (combine != NULL) {
3540 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3541 if (choiceOrInterleave == -1)
3542 choiceOrInterleave = 1;
3543 else if (choiceOrInterleave == 0) {
3544 if (ctxt->error != NULL)
3545 ctxt->error(ctxt->userData,
3546 "<start> use both 'choice' and 'interleave'\n");
3547 ctxt->nbErrors++;
3548 }
3549 } else if (xmlStrEqual(combine, BAD_CAST "choice")) {
3550 if (choiceOrInterleave == -1)
3551 choiceOrInterleave = 0;
3552 else if (choiceOrInterleave == 1) {
3553 if (ctxt->error != NULL)
3554 ctxt->error(ctxt->userData,
3555 "<start> use both 'choice' and 'interleave'\n");
3556 ctxt->nbErrors++;
3557 }
3558 } else {
3559 if (ctxt->error != NULL)
3560 ctxt->error(ctxt->userData,
3561 "<start> uses unknown combine value '%s''\n", combine);
3562 ctxt->nbErrors++;
3563 }
3564 xmlFree(combine);
3565 } else {
3566 if (missing == 0)
3567 missing = 1;
3568 else {
3569 if (ctxt->error != NULL)
3570 ctxt->error(ctxt->userData,
3571 "Some <start> elements lacks the combine attribute\n");
3572 ctxt->nbErrors++;
3573 }
3574 }
3575
3576 cur = cur->nextHash;
3577 }
3578#ifdef DEBUG
3579 xmlGenericError(xmlGenericErrorContext,
3580 "xmlRelaxNGCombineStart(): merging <start>: %d\n",
3581 choiceOrInterleave);
3582#endif
3583 if (choiceOrInterleave == -1)
3584 choiceOrInterleave = 0;
3585 cur = xmlRelaxNGNewDefine(ctxt, starts->node);
3586 if (cur == NULL)
3587 return;
3588 if (choiceOrInterleave == 0)
3589 cur->type = XML_RELAXNG_CHOICE;
3590 else
3591 cur->type = XML_RELAXNG_INTERLEAVE;
3592 tmp = starts;
3593 last = NULL;
3594 while (tmp != NULL) {
3595 if (tmp->content != NULL) {
3596 if (tmp->content->next != NULL) {
3597 /*
3598 * we need first to create a wrapper.
3599 */
3600 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
3601 if (tmp2 == NULL)
3602 break;
3603 tmp2->type = XML_RELAXNG_GROUP;
3604 tmp2->content = tmp->content;
3605 } else {
3606 tmp2 = tmp->content;
3607 }
3608 if (last == NULL) {
3609 cur->content = tmp2;
3610 } else {
3611 last->next = tmp2;
3612 }
3613 last = tmp2;
3614 tmp->content = NULL;
3615 }
3616 tmp = tmp->nextHash;
3617 }
3618 starts->content = cur;
3619}
3620
3621/**
3622 * xmlRelaxNGParseGrammar:
3623 * @ctxt: a Relax-NG parser context
3624 * @nodes: grammar children nodes
3625 *
3626 * parse a Relax-NG <grammar> node
3627 *
3628 * Returns the internal xmlRelaxNGGrammarPtr built or
3629 * NULL in case of error
3630 */
3631static xmlRelaxNGGrammarPtr
3632xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
3633 xmlRelaxNGGrammarPtr ret, tmp, old;
3634
Daniel Veillard6eadf632003-01-23 18:29:16 +00003635 ret = xmlRelaxNGNewGrammar(ctxt);
3636 if (ret == NULL)
3637 return(NULL);
3638
3639 /*
3640 * Link the new grammar in the tree
3641 */
3642 ret->parent = ctxt->grammar;
3643 if (ctxt->grammar != NULL) {
3644 tmp = ctxt->grammar->children;
3645 if (tmp == NULL) {
3646 ctxt->grammar->children = ret;
3647 } else {
3648 while (tmp->next != NULL)
3649 tmp = tmp->next;
3650 tmp->next = ret;
3651 }
3652 }
3653
3654 old = ctxt->grammar;
3655 ctxt->grammar = ret;
3656 xmlRelaxNGParseGrammarContent(ctxt, nodes);
3657 ctxt->grammar = ret;
3658
3659 /*
3660 * Apply 4.17 mergingd rules to defines and starts
3661 */
3662 xmlRelaxNGCombineStart(ctxt, ret);
3663 if (ret->defs != NULL) {
3664 xmlHashScan(ret->defs, (xmlHashScanner) xmlRelaxNGCheckCombine,
3665 ctxt);
3666 }
3667
3668 /*
3669 * link together defines and refs in this grammar
3670 */
3671 if (ret->refs != NULL) {
3672 xmlHashScan(ret->refs, (xmlHashScanner) xmlRelaxNGCheckReference,
3673 ctxt);
3674 }
3675 ctxt->grammar = old;
3676 return(ret);
3677}
3678
3679/**
3680 * xmlRelaxNGParseDocument:
3681 * @ctxt: a Relax-NG parser context
3682 * @node: the root node of the RelaxNG schema
3683 *
3684 * parse a Relax-NG definition resource and build an internal
3685 * xmlRelaxNG struture which can be used to validate instances.
3686 *
3687 * Returns the internal XML RelaxNG structure built or
3688 * NULL in case of error
3689 */
3690static xmlRelaxNGPtr
3691xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3692 xmlRelaxNGPtr schema = NULL;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003693 const xmlChar *olddefine;
Daniel Veillarde431a272003-01-29 23:02:33 +00003694 xmlRelaxNGGrammarPtr old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003695
3696 if ((ctxt == NULL) || (node == NULL))
3697 return (NULL);
3698
3699 schema = xmlRelaxNGNewRelaxNG(ctxt);
3700 if (schema == NULL)
3701 return(NULL);
3702
Daniel Veillard276be4a2003-01-24 01:03:34 +00003703 olddefine = ctxt->define;
3704 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003705 if (IS_RELAXNG(node, "grammar")) {
3706 schema->topgrammar = xmlRelaxNGParseGrammar(ctxt, node->children);
3707 } else {
3708 schema->topgrammar = xmlRelaxNGNewGrammar(ctxt);
3709 if (schema->topgrammar == NULL) {
3710 return(schema);
3711 }
3712 schema->topgrammar->parent = NULL;
Daniel Veillarde431a272003-01-29 23:02:33 +00003713 old = ctxt->grammar;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003714 ctxt->grammar = schema->topgrammar;
3715 xmlRelaxNGParseStart(ctxt, node);
Daniel Veillarde431a272003-01-29 23:02:33 +00003716 if (old != NULL)
3717 ctxt->grammar = old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003718 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003719 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003720
3721#ifdef DEBUG
3722 if (schema == NULL)
3723 xmlGenericError(xmlGenericErrorContext,
3724 "xmlRelaxNGParseDocument() failed\n");
3725#endif
3726
3727 return (schema);
3728}
3729
3730/************************************************************************
3731 * *
3732 * Reading RelaxNGs *
3733 * *
3734 ************************************************************************/
3735
3736/**
3737 * xmlRelaxNGNewParserCtxt:
3738 * @URL: the location of the schema
3739 *
3740 * Create an XML RelaxNGs parse context for that file/resource expected
3741 * to contain an XML RelaxNGs file.
3742 *
3743 * Returns the parser context or NULL in case of error
3744 */
3745xmlRelaxNGParserCtxtPtr
3746xmlRelaxNGNewParserCtxt(const char *URL) {
3747 xmlRelaxNGParserCtxtPtr ret;
3748
3749 if (URL == NULL)
3750 return(NULL);
3751
3752 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
3753 if (ret == NULL) {
3754 xmlGenericError(xmlGenericErrorContext,
3755 "Failed to allocate new schama parser context for %s\n", URL);
3756 return (NULL);
3757 }
3758 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
3759 ret->URL = xmlStrdup((const xmlChar *)URL);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003760 ret->error = xmlGenericError;
3761 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003762 return (ret);
3763}
3764
3765/**
3766 * xmlRelaxNGNewMemParserCtxt:
3767 * @buffer: a pointer to a char array containing the schemas
3768 * @size: the size of the array
3769 *
3770 * Create an XML RelaxNGs parse context for that memory buffer expected
3771 * to contain an XML RelaxNGs file.
3772 *
3773 * Returns the parser context or NULL in case of error
3774 */
3775xmlRelaxNGParserCtxtPtr
3776xmlRelaxNGNewMemParserCtxt(const char *buffer, int size) {
3777 xmlRelaxNGParserCtxtPtr ret;
3778
3779 if ((buffer == NULL) || (size <= 0))
3780 return(NULL);
3781
3782 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
3783 if (ret == NULL) {
3784 xmlGenericError(xmlGenericErrorContext,
3785 "Failed to allocate new schama parser context\n");
3786 return (NULL);
3787 }
3788 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
3789 ret->buffer = buffer;
3790 ret->size = size;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003791 ret->error = xmlGenericError;
3792 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003793 return (ret);
3794}
3795
3796/**
3797 * xmlRelaxNGFreeParserCtxt:
3798 * @ctxt: the schema parser context
3799 *
3800 * Free the resources associated to the schema parser context
3801 */
3802void
3803xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxtPtr ctxt) {
3804 if (ctxt == NULL)
3805 return;
3806 if (ctxt->URL != NULL)
3807 xmlFree(ctxt->URL);
3808 if (ctxt->doc != NULL)
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003809 xmlFreeDoc(ctxt->document);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003810 if (ctxt->interleaves != NULL)
3811 xmlHashFree(ctxt->interleaves, NULL);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003812 if (ctxt->documents != NULL)
3813 xmlHashFree(ctxt->documents, (xmlHashDeallocator)
3814 xmlRelaxNGFreeDocument);
3815 if (ctxt->docTab != NULL)
3816 xmlFree(ctxt->docTab);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003817 if (ctxt->incTab != NULL)
3818 xmlFree(ctxt->incTab);
Daniel Veillard419a7682003-02-03 23:22:49 +00003819 if (ctxt->defTab != NULL) {
3820 int i;
3821
3822 for (i = 0;i < ctxt->defNr;i++)
3823 xmlRelaxNGFreeDefine(ctxt->defTab[i]);
3824 xmlFree(ctxt->defTab);
3825 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003826 xmlFree(ctxt);
3827}
3828
Daniel Veillard6eadf632003-01-23 18:29:16 +00003829/**
Daniel Veillardd2298792003-02-14 16:54:11 +00003830 * xmlRelaxNGNormExtSpace:
3831 * @value: a value
3832 *
3833 * Removes the leading and ending spaces of the value
3834 * The string is modified "in situ"
3835 */
3836static void
3837xmlRelaxNGNormExtSpace(xmlChar *value) {
3838 xmlChar *start = value;
3839 xmlChar *cur = value;
3840 if (value == NULL)
3841 return;
3842
3843 while (IS_BLANK(*cur)) cur++;
3844 if (cur == start) {
3845 do {
3846 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
3847 if (*cur == 0)
3848 return;
3849 start = cur;
3850 while (IS_BLANK(*cur)) cur++;
3851 if (*cur == 0) {
3852 *start = 0;
3853 return;
3854 }
3855 } while (1);
3856 } else {
3857 do {
3858 while ((*cur != 0) && (!IS_BLANK(*cur)))
3859 *start++ = *cur++;
3860 if (*cur == 0) {
3861 *start = 0;
3862 return;
3863 }
3864 /* don't try to normalize the inner spaces */
3865 while (IS_BLANK(*cur)) cur++;
3866 *start++ = *cur++;
3867 if (*cur == 0) {
3868 *start = 0;
3869 return;
3870 }
3871 } while (1);
3872 }
3873}
3874
3875/**
3876 * xmlRelaxNGCheckAttributes:
3877 * @ctxt: a Relax-NG parser context
3878 * @node: a Relax-NG node
3879 *
3880 * Check all the attributes on the given node
3881 */
3882static void
3883xmlRelaxNGCleanupAttributes(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3884 xmlAttrPtr cur, next;
3885
3886 cur = node->properties;
3887 while (cur != NULL) {
3888 next = cur->next;
3889 if ((cur->ns == NULL) ||
3890 (xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
3891 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
3892 if ((!xmlStrEqual(node->name, BAD_CAST "element")) &&
3893 (!xmlStrEqual(node->name, BAD_CAST "attribute")) &&
3894 (!xmlStrEqual(node->name, BAD_CAST "ref")) &&
3895 (!xmlStrEqual(node->name, BAD_CAST "parentRef")) &&
3896 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
3897 if (ctxt->error != NULL)
3898 ctxt->error(ctxt->userData,
3899 "Attribute %s is not allowed on %s\n",
3900 cur->name, node->name);
3901 ctxt->nbErrors++;
3902 }
3903 } else if (xmlStrEqual(cur->name, BAD_CAST "type")) {
3904 if ((!xmlStrEqual(node->name, BAD_CAST "value")) &&
3905 (!xmlStrEqual(node->name, BAD_CAST "data"))) {
3906 if (ctxt->error != NULL)
3907 ctxt->error(ctxt->userData,
3908 "Attribute %s is not allowed on %s\n",
3909 cur->name, node->name);
3910 ctxt->nbErrors++;
3911 }
3912 } else if (xmlStrEqual(cur->name, BAD_CAST "href")) {
3913 if ((!xmlStrEqual(node->name, BAD_CAST "externalRef")) &&
3914 (!xmlStrEqual(node->name, BAD_CAST "include"))) {
3915 if (ctxt->error != NULL)
3916 ctxt->error(ctxt->userData,
3917 "Attribute %s is not allowed on %s\n",
3918 cur->name, node->name);
3919 ctxt->nbErrors++;
3920 }
3921 } else if (xmlStrEqual(cur->name, BAD_CAST "combine")) {
3922 if ((!xmlStrEqual(node->name, BAD_CAST "start")) &&
3923 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
3924 if (ctxt->error != NULL)
3925 ctxt->error(ctxt->userData,
3926 "Attribute %s is not allowed on %s\n",
3927 cur->name, node->name);
3928 ctxt->nbErrors++;
3929 }
3930 } else if (xmlStrEqual(cur->name, BAD_CAST "datatypeLibrary")) {
3931 xmlChar *val;
3932 xmlURIPtr uri;
3933
3934 val = xmlNodeListGetString(node->doc, cur->children, 1);
3935 if (val != NULL) {
3936 if (val[0] != 0) {
3937 uri = xmlParseURI((const char *) val);
3938 if (uri == NULL) {
3939 if (ctxt->error != NULL)
3940 ctxt->error(ctxt->userData,
3941 "Attribute %s contains invalid URI %s\n",
3942 cur->name, val);
3943 ctxt->nbErrors++;
3944 } else {
3945 if (uri->scheme == NULL) {
3946 if (ctxt->error != NULL)
3947 ctxt->error(ctxt->userData,
3948 "Attribute %s URI %s is not absolute\n",
3949 cur->name, val);
3950 ctxt->nbErrors++;
3951 }
3952 if (uri->fragment != NULL) {
3953 if (ctxt->error != NULL)
3954 ctxt->error(ctxt->userData,
3955 "Attribute %s URI %s has a fragment ID\n",
3956 cur->name, val);
3957 ctxt->nbErrors++;
3958 }
3959 xmlFreeURI(uri);
3960 }
3961 }
3962 xmlFree(val);
3963 }
3964 } else if (!xmlStrEqual(cur->name, BAD_CAST "ns")) {
3965 if (ctxt->error != NULL)
3966 ctxt->error(ctxt->userData,
3967 "Unknown attribute %s on %s\n",
3968 cur->name, node->name);
3969 ctxt->nbErrors++;
3970 }
3971 }
3972 cur = next;
3973 }
3974}
3975
3976/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003977 * xmlRelaxNGCleanupDoc:
3978 * @ctxt: a Relax-NG parser context
3979 * @doc: an xmldocPtr document pointer
Daniel Veillard6eadf632003-01-23 18:29:16 +00003980 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003981 * Cleanup the document from unwanted nodes for parsing, resolve
3982 * Include and externalRef lookups.
Daniel Veillard6eadf632003-01-23 18:29:16 +00003983 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003984 * Returns the cleaned up document or NULL in case of error
Daniel Veillard6eadf632003-01-23 18:29:16 +00003985 */
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003986static xmlDocPtr
3987xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt, xmlDocPtr doc) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003988 xmlNodePtr root, cur, delete;
3989
Daniel Veillard6eadf632003-01-23 18:29:16 +00003990 /*
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003991 * Extract the root
Daniel Veillard6eadf632003-01-23 18:29:16 +00003992 */
3993 root = xmlDocGetRootElement(doc);
3994 if (root == NULL) {
3995 if (ctxt->error != NULL)
3996 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
3997 ctxt->URL);
3998 ctxt->nbErrors++;
3999 return (NULL);
4000 }
4001
4002 /*
4003 * Remove all the blank text nodes
4004 */
4005 delete = NULL;
4006 cur = root;
4007 while (cur != NULL) {
4008 if (delete != NULL) {
4009 xmlUnlinkNode(delete);
4010 xmlFreeNode(delete);
4011 delete = NULL;
4012 }
4013 if (cur->type == XML_ELEMENT_NODE) {
4014 /*
4015 * Simplification 4.1. Annotations
4016 */
4017 if ((cur->ns == NULL) ||
4018 (!xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
Daniel Veillardd2298792003-02-14 16:54:11 +00004019 if ((cur->parent != NULL) &&
4020 (cur->parent->type == XML_ELEMENT_NODE) &&
4021 ((xmlStrEqual(cur->parent->name, BAD_CAST "name")) ||
4022 (xmlStrEqual(cur->parent->name, BAD_CAST "value")) ||
4023 (xmlStrEqual(cur->parent->name, BAD_CAST "param")))) {
4024 if (ctxt->error != NULL)
4025 ctxt->error(ctxt->userData,
4026 "element %s doesn't allow foreign elements\n",
4027 cur->parent->name);
4028 ctxt->nbErrors++;
4029 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004030 delete = cur;
4031 goto skip_children;
4032 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00004033 xmlRelaxNGCleanupAttributes(ctxt, cur);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004034 if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004035 xmlChar *href, *ns, *base, *URL;
4036 xmlRelaxNGDocumentPtr docu;
Daniel Veillard416589a2003-02-17 17:25:42 +00004037 xmlNodePtr tmp;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004038
4039 ns = xmlGetProp(cur, BAD_CAST "ns");
Daniel Veillard416589a2003-02-17 17:25:42 +00004040 if (ns == NULL) {
4041 tmp = cur->parent;
4042 while ((tmp != NULL) &&
4043 (tmp->type == XML_ELEMENT_NODE)) {
4044 ns = xmlGetProp(tmp, BAD_CAST "ns");
4045 if (ns != NULL)
4046 break;
4047 tmp = tmp->parent;
4048 }
4049 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004050 href = xmlGetProp(cur, BAD_CAST "href");
4051 if (href == NULL) {
4052 if (ctxt->error != NULL)
4053 ctxt->error(ctxt->userData,
4054 "xmlRelaxNGParse: externalRef has no href attribute\n");
4055 ctxt->nbErrors++;
4056 delete = cur;
4057 goto skip_children;
4058 }
4059 base = xmlNodeGetBase(cur->doc, cur);
4060 URL = xmlBuildURI(href, base);
4061 if (URL == NULL) {
4062 if (ctxt->error != NULL)
4063 ctxt->error(ctxt->userData,
4064 "Failed to compute URL for externalRef %s\n", href);
4065 ctxt->nbErrors++;
4066 if (href != NULL)
4067 xmlFree(href);
4068 if (base != NULL)
4069 xmlFree(base);
4070 delete = cur;
4071 goto skip_children;
4072 }
4073 if (href != NULL)
4074 xmlFree(href);
4075 if (base != NULL)
4076 xmlFree(base);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004077 docu = xmlRelaxNGLoadExternalRef(ctxt, URL, ns);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004078 if (docu == NULL) {
4079 if (ctxt->error != NULL)
4080 ctxt->error(ctxt->userData,
4081 "Failed to load externalRef %s\n", URL);
4082 ctxt->nbErrors++;
4083 xmlFree(URL);
4084 delete = cur;
4085 goto skip_children;
4086 }
4087 xmlFree(URL);
4088 cur->_private = docu;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004089 } else if (xmlStrEqual(cur->name, BAD_CAST "include")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00004090 xmlChar *href, *ns, *base, *URL;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004091 xmlRelaxNGIncludePtr incl;
Daniel Veillard416589a2003-02-17 17:25:42 +00004092 xmlNodePtr tmp;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004093
4094 href = xmlGetProp(cur, BAD_CAST "href");
4095 if (href == NULL) {
4096 if (ctxt->error != NULL)
4097 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004098 "xmlRelaxNGParse: include has no href attribute\n");
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004099 ctxt->nbErrors++;
4100 delete = cur;
4101 goto skip_children;
4102 }
4103 base = xmlNodeGetBase(cur->doc, cur);
4104 URL = xmlBuildURI(href, base);
4105 if (URL == NULL) {
4106 if (ctxt->error != NULL)
4107 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004108 "Failed to compute URL for include %s\n", href);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004109 ctxt->nbErrors++;
4110 if (href != NULL)
4111 xmlFree(href);
4112 if (base != NULL)
4113 xmlFree(base);
4114 delete = cur;
4115 goto skip_children;
4116 }
4117 if (href != NULL)
4118 xmlFree(href);
4119 if (base != NULL)
4120 xmlFree(base);
Daniel Veillard416589a2003-02-17 17:25:42 +00004121 ns = xmlGetProp(cur, BAD_CAST "ns");
4122 if (ns == NULL) {
4123 tmp = cur->parent;
4124 while ((tmp != NULL) &&
4125 (tmp->type == XML_ELEMENT_NODE)) {
4126 ns = xmlGetProp(tmp, BAD_CAST "ns");
4127 if (ns != NULL)
4128 break;
4129 tmp = tmp->parent;
4130 }
4131 }
4132 incl = xmlRelaxNGLoadInclude(ctxt, URL, cur, ns);
4133 if (ns != NULL)
4134 xmlFree(ns);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004135 if (incl == NULL) {
4136 if (ctxt->error != NULL)
4137 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004138 "Failed to load include %s\n", URL);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004139 ctxt->nbErrors++;
4140 xmlFree(URL);
4141 delete = cur;
4142 goto skip_children;
4143 }
4144 xmlFree(URL);
4145 cur->_private = incl;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004146 } else if ((xmlStrEqual(cur->name, BAD_CAST "element")) ||
4147 (xmlStrEqual(cur->name, BAD_CAST "attribute"))) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00004148 xmlChar *name, *ns;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004149 xmlNodePtr text = NULL;
4150
4151 /*
4152 * Simplification 4.8. name attribute of element
4153 * and attribute elements
4154 */
4155 name = xmlGetProp(cur, BAD_CAST "name");
4156 if (name != NULL) {
4157 if (cur->children == NULL) {
4158 text = xmlNewChild(cur, cur->ns, BAD_CAST "name",
4159 name);
4160 } else {
4161 xmlNodePtr node;
4162 node = xmlNewNode(cur->ns, BAD_CAST "name");
4163 if (node != NULL) {
4164 xmlAddPrevSibling(cur->children, node);
4165 text = xmlNewText(name);
4166 xmlAddChild(node, text);
4167 text = node;
4168 }
4169 }
Daniel Veillardfebcca42003-02-16 15:44:18 +00004170 if (text == NULL) {
4171 if (ctxt->error != NULL)
4172 ctxt->error(ctxt->userData,
4173 "Failed to create a name %s element\n", name);
4174 ctxt->nbErrors++;
4175 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004176 xmlUnsetProp(cur, BAD_CAST "name");
4177 xmlFree(name);
Daniel Veillardfebcca42003-02-16 15:44:18 +00004178 ns = xmlGetProp(cur, BAD_CAST "ns");
4179 if (ns != NULL) {
4180 if (text != NULL) {
4181 xmlSetProp(text, BAD_CAST "ns", ns);
4182 /* xmlUnsetProp(cur, BAD_CAST "ns"); */
Daniel Veillard6eadf632003-01-23 18:29:16 +00004183 }
Daniel Veillardfebcca42003-02-16 15:44:18 +00004184 xmlFree(ns);
4185 } else if (xmlStrEqual(cur->name,
4186 BAD_CAST "attribute")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00004187 xmlSetProp(text, BAD_CAST "ns", BAD_CAST "");
4188 }
4189 }
4190 } else if ((xmlStrEqual(cur->name, BAD_CAST "name")) ||
4191 (xmlStrEqual(cur->name, BAD_CAST "nsName")) ||
4192 (xmlStrEqual(cur->name, BAD_CAST "value"))) {
4193 /*
4194 * Simplification 4.8. name attribute of element
4195 * and attribute elements
4196 */
4197 if (xmlHasProp(cur, BAD_CAST "ns") == NULL) {
4198 xmlNodePtr node;
4199 xmlChar *ns = NULL;
4200
4201 node = cur->parent;
4202 while ((node != NULL) &&
4203 (node->type == XML_ELEMENT_NODE)) {
4204 ns = xmlGetProp(node, BAD_CAST "ns");
4205 if (ns != NULL) {
4206 break;
4207 }
4208 node = node->parent;
4209 }
4210 if (ns == NULL) {
4211 xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
4212 } else {
4213 xmlSetProp(cur, BAD_CAST "ns", ns);
4214 xmlFree(ns);
4215 }
4216 }
4217 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
4218 xmlChar *name, *local, *prefix;
4219
4220 /*
4221 * Simplification: 4.10. QNames
4222 */
4223 name = xmlNodeGetContent(cur);
4224 if (name != NULL) {
4225 local = xmlSplitQName2(name, &prefix);
4226 if (local != NULL) {
4227 xmlNsPtr ns;
4228
4229 ns = xmlSearchNs(cur->doc, cur, prefix);
4230 if (ns == NULL) {
4231 if (ctxt->error != NULL)
4232 ctxt->error(ctxt->userData,
4233 "xmlRelaxNGParse: no namespace for prefix %s\n", prefix);
4234 ctxt->nbErrors++;
4235 } else {
4236 xmlSetProp(cur, BAD_CAST "ns", ns->href);
4237 xmlNodeSetContent(cur, local);
4238 }
4239 xmlFree(local);
4240 xmlFree(prefix);
4241 }
4242 xmlFree(name);
4243 }
4244 }
4245 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004246 /*
4247 * Thisd is not an else since "include" is transformed
4248 * into a div
4249 */
4250 if (xmlStrEqual(cur->name, BAD_CAST "div")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00004251 xmlChar *ns;
4252 xmlNodePtr child, ins, tmp;
4253
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004254 /*
4255 * implements rule 4.11
4256 */
Daniel Veillard416589a2003-02-17 17:25:42 +00004257
4258 ns = xmlGetProp(cur, BAD_CAST "ns");
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004259
4260 child = cur->children;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004261 ins = cur;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004262 while (child != NULL) {
Daniel Veillard416589a2003-02-17 17:25:42 +00004263 if (ns != NULL) {
4264 if (!xmlHasProp(child, BAD_CAST "ns")) {
4265 xmlSetProp(child, BAD_CAST "ns", ns);
4266 }
4267 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004268 tmp = child->next;
4269 xmlUnlinkNode(child);
4270 ins = xmlAddNextSibling(ins, child);
4271 child = tmp;
4272 }
Daniel Veillard416589a2003-02-17 17:25:42 +00004273 if (ns != NULL)
4274 xmlFree(ns);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004275 delete = cur;
4276 goto skip_children;
4277 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004278 }
4279 }
4280 /*
4281 * Simplification 4.2 whitespaces
4282 */
4283 else if (cur->type == XML_TEXT_NODE) {
4284 if (IS_BLANK_NODE(cur)) {
4285 if (cur->parent->type == XML_ELEMENT_NODE) {
4286 if ((!xmlStrEqual(cur->parent->name, BAD_CAST "value")) &&
4287 (!xmlStrEqual(cur->parent->name, BAD_CAST "param")))
4288 delete = cur;
4289 } else {
4290 delete = cur;
4291 goto skip_children;
4292 }
4293 }
4294 } else if (cur->type != XML_CDATA_SECTION_NODE) {
4295 delete = cur;
4296 goto skip_children;
4297 }
4298
4299 /*
4300 * Skip to next node
4301 */
4302 if (cur->children != NULL) {
4303 if ((cur->children->type != XML_ENTITY_DECL) &&
4304 (cur->children->type != XML_ENTITY_REF_NODE) &&
4305 (cur->children->type != XML_ENTITY_NODE)) {
4306 cur = cur->children;
4307 continue;
4308 }
4309 }
4310skip_children:
4311 if (cur->next != NULL) {
4312 cur = cur->next;
4313 continue;
4314 }
4315
4316 do {
4317 cur = cur->parent;
4318 if (cur == NULL)
4319 break;
4320 if (cur == root) {
4321 cur = NULL;
4322 break;
4323 }
4324 if (cur->next != NULL) {
4325 cur = cur->next;
4326 break;
4327 }
4328 } while (cur != NULL);
4329 }
4330 if (delete != NULL) {
4331 xmlUnlinkNode(delete);
4332 xmlFreeNode(delete);
4333 delete = NULL;
4334 }
4335
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004336 return(doc);
4337}
4338
4339/**
4340 * xmlRelaxNGParse:
4341 * @ctxt: a Relax-NG parser context
4342 *
4343 * parse a schema definition resource and build an internal
4344 * XML Shema struture which can be used to validate instances.
4345 * *WARNING* this interface is highly subject to change
4346 *
4347 * Returns the internal XML RelaxNG structure built from the resource or
4348 * NULL in case of error
4349 */
4350xmlRelaxNGPtr
4351xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt)
4352{
4353 xmlRelaxNGPtr ret = NULL;
4354 xmlDocPtr doc;
4355 xmlNodePtr root;
4356
4357 xmlRelaxNGInitTypes();
4358
4359 if (ctxt == NULL)
4360 return (NULL);
4361
4362 /*
4363 * First step is to parse the input document into an DOM/Infoset
4364 */
4365 if (ctxt->URL != NULL) {
4366 doc = xmlParseFile((const char *) ctxt->URL);
4367 if (doc == NULL) {
4368 if (ctxt->error != NULL)
4369 ctxt->error(ctxt->userData,
4370 "xmlRelaxNGParse: could not load %s\n", ctxt->URL);
4371 ctxt->nbErrors++;
4372 return (NULL);
4373 }
4374 } else if (ctxt->buffer != NULL) {
4375 doc = xmlParseMemory(ctxt->buffer, ctxt->size);
4376 if (doc == NULL) {
4377 if (ctxt->error != NULL)
4378 ctxt->error(ctxt->userData,
4379 "xmlRelaxNGParse: could not parse schemas\n");
4380 ctxt->nbErrors++;
4381 return (NULL);
4382 }
4383 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
4384 ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
4385 } else {
4386 if (ctxt->error != NULL)
4387 ctxt->error(ctxt->userData,
4388 "xmlRelaxNGParse: nothing to parse\n");
4389 ctxt->nbErrors++;
4390 return (NULL);
4391 }
4392 ctxt->document = doc;
4393
4394 /*
4395 * Some preprocessing of the document content
4396 */
4397 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
4398 if (doc == NULL) {
4399 xmlFreeDoc(ctxt->document);
4400 ctxt->document = NULL;
4401 return(NULL);
4402 }
4403
Daniel Veillard6eadf632003-01-23 18:29:16 +00004404 /*
4405 * Then do the parsing for good
4406 */
4407 root = xmlDocGetRootElement(doc);
4408 if (root == NULL) {
4409 if (ctxt->error != NULL)
4410 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
4411 ctxt->URL);
4412 ctxt->nbErrors++;
4413 return (NULL);
4414 }
4415 ret = xmlRelaxNGParseDocument(ctxt, root);
4416 if (ret == NULL)
4417 return(NULL);
4418
4419 /*
4420 * Check the ref/defines links
4421 */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004422 /*
4423 * try to preprocess interleaves
4424 */
4425 if (ctxt->interleaves != NULL) {
4426 xmlHashScan(ctxt->interleaves,
4427 (xmlHashScanner)xmlRelaxNGComputeInterleaves, ctxt);
4428 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004429
4430 /*
4431 * if there was a parsing error return NULL
4432 */
4433 if (ctxt->nbErrors > 0) {
4434 xmlRelaxNGFree(ret);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004435 ctxt->document = NULL;
4436 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004437 return(NULL);
4438 }
4439
4440 /*
4441 * Transfer the pointer for cleanup at the schema level.
4442 */
4443 ret->doc = doc;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004444 ctxt->document = NULL;
4445 ret->documents = ctxt->documents;
4446 ctxt->documents = NULL;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004447 ret->includes = ctxt->includes;
4448 ctxt->includes = NULL;
Daniel Veillard419a7682003-02-03 23:22:49 +00004449 ret->defNr = ctxt->defNr;
4450 ret->defTab = ctxt->defTab;
4451 ctxt->defTab = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004452
4453 return (ret);
4454}
4455
4456/**
4457 * xmlRelaxNGSetParserErrors:
4458 * @ctxt: a Relax-NG validation context
4459 * @err: the error callback
4460 * @warn: the warning callback
4461 * @ctx: contextual data for the callbacks
4462 *
4463 * Set the callback functions used to handle errors for a validation context
4464 */
4465void
4466xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
4467 xmlRelaxNGValidityErrorFunc err,
4468 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
4469 if (ctxt == NULL)
4470 return;
4471 ctxt->error = err;
4472 ctxt->warning = warn;
4473 ctxt->userData = ctx;
4474}
4475/************************************************************************
4476 * *
4477 * Dump back a compiled form *
4478 * *
4479 ************************************************************************/
4480static void xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define);
4481
4482/**
4483 * xmlRelaxNGDumpDefines:
4484 * @output: the file output
4485 * @defines: a list of define structures
4486 *
4487 * Dump a RelaxNG structure back
4488 */
4489static void
4490xmlRelaxNGDumpDefines(FILE * output, xmlRelaxNGDefinePtr defines) {
4491 while (defines != NULL) {
4492 xmlRelaxNGDumpDefine(output, defines);
4493 defines = defines->next;
4494 }
4495}
4496
4497/**
4498 * xmlRelaxNGDumpDefine:
4499 * @output: the file output
4500 * @define: a define structure
4501 *
4502 * Dump a RelaxNG structure back
4503 */
4504static void
4505xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define) {
4506 if (define == NULL)
4507 return;
4508 switch(define->type) {
4509 case XML_RELAXNG_EMPTY:
4510 fprintf(output, "<empty/>\n");
4511 break;
4512 case XML_RELAXNG_NOT_ALLOWED:
4513 fprintf(output, "<notAllowed/>\n");
4514 break;
4515 case XML_RELAXNG_TEXT:
4516 fprintf(output, "<text/>\n");
4517 break;
4518 case XML_RELAXNG_ELEMENT:
4519 fprintf(output, "<element>\n");
4520 if (define->name != NULL) {
4521 fprintf(output, "<name");
4522 if (define->ns != NULL)
4523 fprintf(output, " ns=\"%s\"", define->ns);
4524 fprintf(output, ">%s</name>\n", define->name);
4525 }
4526 xmlRelaxNGDumpDefines(output, define->attrs);
4527 xmlRelaxNGDumpDefines(output, define->content);
4528 fprintf(output, "</element>\n");
4529 break;
4530 case XML_RELAXNG_LIST:
4531 fprintf(output, "<list>\n");
4532 xmlRelaxNGDumpDefines(output, define->content);
4533 fprintf(output, "</list>\n");
4534 break;
4535 case XML_RELAXNG_ONEORMORE:
4536 fprintf(output, "<oneOrMore>\n");
4537 xmlRelaxNGDumpDefines(output, define->content);
4538 fprintf(output, "</oneOrMore>\n");
4539 break;
4540 case XML_RELAXNG_ZEROORMORE:
4541 fprintf(output, "<zeroOrMore>\n");
4542 xmlRelaxNGDumpDefines(output, define->content);
4543 fprintf(output, "</zeroOrMore>\n");
4544 break;
4545 case XML_RELAXNG_CHOICE:
4546 fprintf(output, "<choice>\n");
4547 xmlRelaxNGDumpDefines(output, define->content);
4548 fprintf(output, "</choice>\n");
4549 break;
4550 case XML_RELAXNG_GROUP:
4551 fprintf(output, "<group>\n");
4552 xmlRelaxNGDumpDefines(output, define->content);
4553 fprintf(output, "</group>\n");
4554 break;
4555 case XML_RELAXNG_INTERLEAVE:
4556 fprintf(output, "<interleave>\n");
4557 xmlRelaxNGDumpDefines(output, define->content);
4558 fprintf(output, "</interleave>\n");
4559 break;
4560 case XML_RELAXNG_OPTIONAL:
4561 fprintf(output, "<optional>\n");
4562 xmlRelaxNGDumpDefines(output, define->content);
4563 fprintf(output, "</optional>\n");
4564 break;
4565 case XML_RELAXNG_ATTRIBUTE:
4566 fprintf(output, "<attribute>\n");
4567 xmlRelaxNGDumpDefines(output, define->content);
4568 fprintf(output, "</attribute>\n");
4569 break;
4570 case XML_RELAXNG_DEF:
4571 fprintf(output, "<define");
4572 if (define->name != NULL)
4573 fprintf(output, " name=\"%s\"", define->name);
4574 fprintf(output, ">\n");
4575 xmlRelaxNGDumpDefines(output, define->content);
4576 fprintf(output, "</define>\n");
4577 break;
4578 case XML_RELAXNG_REF:
4579 fprintf(output, "<ref");
4580 if (define->name != NULL)
4581 fprintf(output, " name=\"%s\"", define->name);
4582 fprintf(output, ">\n");
4583 xmlRelaxNGDumpDefines(output, define->content);
4584 fprintf(output, "</ref>\n");
4585 break;
Daniel Veillard419a7682003-02-03 23:22:49 +00004586 case XML_RELAXNG_PARENTREF:
4587 fprintf(output, "<parentRef");
4588 if (define->name != NULL)
4589 fprintf(output, " name=\"%s\"", define->name);
4590 fprintf(output, ">\n");
4591 xmlRelaxNGDumpDefines(output, define->content);
4592 fprintf(output, "</parentRef>\n");
4593 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004594 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard416589a2003-02-17 17:25:42 +00004595 fprintf(output, "<externalRef>");
Daniel Veillarde431a272003-01-29 23:02:33 +00004596 xmlRelaxNGDumpDefines(output, define->content);
4597 fprintf(output, "</externalRef>\n");
4598 break;
Daniel Veillard416589a2003-02-17 17:25:42 +00004599#if 0
4600 case XML_RELAXNG_MIXED:
4601 fprintf(output, "<mixed>");
4602 xmlRelaxNGDumpDefines(output, define->content);
4603 fprintf(output, "</mixed>\n");
4604 break;
4605#endif
Daniel Veillarde431a272003-01-29 23:02:33 +00004606 case XML_RELAXNG_DATATYPE:
Daniel Veillard6eadf632003-01-23 18:29:16 +00004607 case XML_RELAXNG_VALUE:
4608 TODO
4609 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004610 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00004611 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004612 TODO
4613 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004614 }
4615}
4616
4617/**
4618 * xmlRelaxNGDumpGrammar:
4619 * @output: the file output
4620 * @grammar: a grammar structure
4621 * @top: is this a top grammar
4622 *
4623 * Dump a RelaxNG structure back
4624 */
4625static void
4626xmlRelaxNGDumpGrammar(FILE * output, xmlRelaxNGGrammarPtr grammar, int top)
4627{
4628 if (grammar == NULL)
4629 return;
4630
4631 fprintf(output, "<grammar");
4632 if (top)
4633 fprintf(output,
4634 " xmlns=\"http://relaxng.org/ns/structure/1.0\"");
4635 switch(grammar->combine) {
4636 case XML_RELAXNG_COMBINE_UNDEFINED:
4637 break;
4638 case XML_RELAXNG_COMBINE_CHOICE:
4639 fprintf(output, " combine=\"choice\"");
4640 break;
4641 case XML_RELAXNG_COMBINE_INTERLEAVE:
4642 fprintf(output, " combine=\"interleave\"");
4643 break;
4644 default:
4645 fprintf(output, " <!-- invalid combine value -->");
4646 }
4647 fprintf(output, ">\n");
4648 if (grammar->start == NULL) {
4649 fprintf(output, " <!-- grammar had no start -->");
4650 } else {
4651 fprintf(output, "<start>\n");
4652 xmlRelaxNGDumpDefine(output, grammar->start);
4653 fprintf(output, "</start>\n");
4654 }
4655 /* TODO ? Dump the defines ? */
4656 fprintf(output, "</grammar>\n");
4657}
4658
4659/**
4660 * xmlRelaxNGDump:
4661 * @output: the file output
4662 * @schema: a schema structure
4663 *
4664 * Dump a RelaxNG structure back
4665 */
4666void
4667xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema)
4668{
4669 if (schema == NULL) {
4670 fprintf(output, "RelaxNG empty or failed to compile\n");
4671 return;
4672 }
4673 fprintf(output, "RelaxNG: ");
4674 if (schema->doc == NULL) {
4675 fprintf(output, "no document\n");
4676 } else if (schema->doc->URL != NULL) {
4677 fprintf(output, "%s\n", schema->doc->URL);
4678 } else {
4679 fprintf(output, "\n");
4680 }
4681 if (schema->topgrammar == NULL) {
4682 fprintf(output, "RelaxNG has no top grammar\n");
4683 return;
4684 }
4685 xmlRelaxNGDumpGrammar(output, schema->topgrammar, 1);
4686}
4687
Daniel Veillardfebcca42003-02-16 15:44:18 +00004688/**
4689 * xmlRelaxNGDumpTree:
4690 * @output: the file output
4691 * @schema: a schema structure
4692 *
4693 * Dump the transformed RelaxNG tree.
4694 */
4695void
4696xmlRelaxNGDumpTree(FILE * output, xmlRelaxNGPtr schema)
4697{
4698 if (schema == NULL) {
4699 fprintf(output, "RelaxNG empty or failed to compile\n");
4700 return;
4701 }
4702 if (schema->doc == NULL) {
4703 fprintf(output, "no document\n");
4704 } else {
4705 xmlDocDump(output, schema->doc);
4706 }
4707}
4708
Daniel Veillard6eadf632003-01-23 18:29:16 +00004709/************************************************************************
4710 * *
4711 * Validation implementation *
4712 * *
4713 ************************************************************************/
4714static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
4715 xmlRelaxNGDefinePtr define);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004716static int xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
4717 xmlRelaxNGDefinePtr define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004718
4719/**
4720 * xmlRelaxNGSkipIgnored:
4721 * @ctxt: a schema validation context
4722 * @node: the top node.
4723 *
4724 * Skip ignorable nodes in that context
4725 *
4726 * Returns the new sibling or NULL in case of error.
4727 */
4728static xmlNodePtr
4729xmlRelaxNGSkipIgnored(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
4730 xmlNodePtr node) {
4731 /*
4732 * TODO complete and handle entities
4733 */
4734 while ((node != NULL) &&
4735 ((node->type == XML_COMMENT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004736 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00004737 ((node->type == XML_TEXT_NODE) &&
4738 (IS_BLANK_NODE(node))))) {
4739 node = node->next;
4740 }
4741 return(node);
4742}
4743
4744/**
Daniel Veillardedc91922003-01-26 00:52:04 +00004745 * xmlRelaxNGNormalize:
4746 * @ctxt: a schema validation context
4747 * @str: the string to normalize
4748 *
4749 * Implements the normalizeWhiteSpace( s ) function from
4750 * section 6.2.9 of the spec
4751 *
4752 * Returns the new string or NULL in case of error.
4753 */
4754static xmlChar *
4755xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *str) {
4756 xmlChar *ret, *p;
4757 const xmlChar *tmp;
4758 int len;
4759
4760 if (str == NULL)
4761 return(NULL);
4762 tmp = str;
4763 while (*tmp != 0) tmp++;
4764 len = tmp - str;
4765
4766 ret = (xmlChar *) xmlMalloc((len + 1) * sizeof(xmlChar));
4767 if (ret == NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00004768 if (ctxt != NULL) {
4769 VALID_CTXT();
4770 VALID_ERROR("xmlRelaxNGNormalize: out of memory\n");
4771 } else {
4772 xmlGenericError(xmlGenericErrorContext,
4773 "xmlRelaxNGNormalize: out of memory\n");
4774 }
Daniel Veillardedc91922003-01-26 00:52:04 +00004775 return(NULL);
4776 }
4777 p = ret;
4778 while (IS_BLANK(*str)) str++;
4779 while (*str != 0) {
4780 if (IS_BLANK(*str)) {
4781 while (IS_BLANK(*str)) str++;
4782 if (*str == 0)
4783 break;
4784 *p++ = ' ';
4785 } else
4786 *p++ = *str++;
4787 }
4788 *p = 0;
4789 return(ret);
4790}
4791
4792/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004793 * xmlRelaxNGValidateDatatype:
4794 * @ctxt: a Relax-NG validation context
4795 * @value: the string value
4796 * @type: the datatype definition
4797 *
4798 * Validate the given value against the dataype
4799 *
4800 * Returns 0 if the validation succeeded or an error code.
4801 */
4802static int
4803xmlRelaxNGValidateDatatype(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *value,
4804 xmlRelaxNGDefinePtr define) {
4805 int ret;
4806 xmlRelaxNGTypeLibraryPtr lib;
4807
4808 if ((define == NULL) || (define->data == NULL)) {
4809 return(-1);
4810 }
4811 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
4812 if (lib->check != NULL)
4813 ret = lib->check(lib->data, define->name, value);
4814 else
4815 ret = -1;
4816 if (ret < 0) {
4817 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004818 VALID_ERROR2("Internal: failed to validate type %s\n", define->name);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004819 return(-1);
4820 } else if (ret == 1) {
4821 ret = 0;
4822 } else {
4823 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004824 VALID_ERROR3("Type %s doesn't allow value %s\n", define->name, value);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004825 return(-1);
4826 ret = -1;
4827 }
Daniel Veillard416589a2003-02-17 17:25:42 +00004828 if ((ret == 0) && (define->content != NULL)) {
4829 const xmlChar *oldvalue, *oldendvalue;
4830
4831 oldvalue = ctxt->state->value;
4832 oldendvalue = ctxt->state->endvalue;
4833 ctxt->state->value = (xmlChar *) value;
4834 ctxt->state->endvalue = NULL;
4835 ret = xmlRelaxNGValidateValue(ctxt, define->content);
4836 ctxt->state->value = (xmlChar *) oldvalue;
4837 ctxt->state->endvalue = (xmlChar *) oldendvalue;
4838 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004839 return(ret);
4840}
4841
4842/**
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004843 * xmlRelaxNGNextValue:
4844 * @ctxt: a Relax-NG validation context
4845 *
4846 * Skip to the next value when validating within a list
4847 *
4848 * Returns 0 if the operation succeeded or an error code.
4849 */
4850static int
4851xmlRelaxNGNextValue(xmlRelaxNGValidCtxtPtr ctxt) {
4852 xmlChar *cur;
4853
4854 cur = ctxt->state->value;
4855 if ((cur == NULL) || (ctxt->state->endvalue == NULL)) {
4856 ctxt->state->value = NULL;
Daniel Veillarde5b110b2003-02-04 14:43:39 +00004857 ctxt->state->endvalue = NULL;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004858 return(0);
4859 }
4860 while (*cur != 0) cur++;
4861 while ((cur != ctxt->state->endvalue) && (*cur == 0)) cur++;
4862 if (cur == ctxt->state->endvalue)
4863 ctxt->state->value = NULL;
4864 else
4865 ctxt->state->value = cur;
4866 return(0);
4867}
4868
4869/**
4870 * xmlRelaxNGValidateValueList:
4871 * @ctxt: a Relax-NG validation context
4872 * @defines: the list of definitions to verify
4873 *
4874 * Validate the given set of definitions for the current value
4875 *
4876 * Returns 0 if the validation succeeded or an error code.
4877 */
4878static int
4879xmlRelaxNGValidateValueList(xmlRelaxNGValidCtxtPtr ctxt,
4880 xmlRelaxNGDefinePtr defines) {
4881 int ret = 0;
4882
4883 while (defines != NULL) {
4884 ret = xmlRelaxNGValidateValue(ctxt, defines);
4885 if (ret != 0)
4886 break;
4887 defines = defines->next;
4888 }
4889 return(ret);
4890}
4891
4892/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004893 * xmlRelaxNGValidateValue:
4894 * @ctxt: a Relax-NG validation context
4895 * @define: the definition to verify
4896 *
4897 * Validate the given definition for the current value
4898 *
4899 * Returns 0 if the validation succeeded or an error code.
4900 */
4901static int
4902xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
4903 xmlRelaxNGDefinePtr define) {
Daniel Veillardedc91922003-01-26 00:52:04 +00004904 int ret = 0, oldflags;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004905 xmlChar *value;
4906
4907 value = ctxt->state->value;
4908 switch (define->type) {
4909 case XML_RELAXNG_EMPTY:
4910 if ((value != NULL) && (value[0] != '0'))
4911 ret = -1;
4912 break;
4913 case XML_RELAXNG_TEXT:
4914 break;
Daniel Veillardedc91922003-01-26 00:52:04 +00004915 case XML_RELAXNG_VALUE: {
4916 if (!xmlStrEqual(value, define->value)) {
4917 if (define->name != NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00004918 xmlRelaxNGTypeLibraryPtr lib;
4919
4920 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
4921 if ((lib != NULL) && (lib->comp != NULL))
4922 ret = lib->comp(lib->data, define->name, value,
4923 define->value);
4924 else
4925 ret = -1;
4926 if (ret < 0) {
4927 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004928 VALID_ERROR2("Internal: failed to compare type %s\n",
Daniel Veillardea3f3982003-01-26 19:45:18 +00004929 define->name);
4930 return(-1);
4931 } else if (ret == 1) {
4932 ret = 0;
4933 } else {
4934 ret = -1;
4935 }
Daniel Veillardedc91922003-01-26 00:52:04 +00004936 } else {
4937 xmlChar *nval, *nvalue;
4938
4939 /*
4940 * TODO: trivial optimizations are possible by
4941 * computing at compile-time
4942 */
4943 nval = xmlRelaxNGNormalize(ctxt, define->value);
4944 nvalue = xmlRelaxNGNormalize(ctxt, value);
4945
Daniel Veillardea3f3982003-01-26 19:45:18 +00004946 if ((nval == NULL) || (nvalue == NULL) ||
4947 (!xmlStrEqual(nval, nvalue)))
Daniel Veillardedc91922003-01-26 00:52:04 +00004948 ret = -1;
4949 if (nval != NULL)
4950 xmlFree(nval);
4951 if (nvalue != NULL)
4952 xmlFree(nvalue);
4953 }
4954 }
Daniel Veillard416589a2003-02-17 17:25:42 +00004955 if (ret == 0)
4956 xmlRelaxNGNextValue(ctxt);
Daniel Veillardedc91922003-01-26 00:52:04 +00004957 break;
4958 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004959 case XML_RELAXNG_DATATYPE: {
4960 ret = xmlRelaxNGValidateDatatype(ctxt, value, define);
4961 if (ret == 0)
4962 xmlRelaxNGNextValue(ctxt);
4963
4964 break;
4965 }
4966 case XML_RELAXNG_CHOICE: {
4967 xmlRelaxNGDefinePtr list = define->content;
4968 xmlChar *oldvalue;
4969
4970 oldflags = ctxt->flags;
4971 ctxt->flags |= FLAGS_IGNORABLE;
4972
4973 oldvalue = ctxt->state->value;
4974 while (list != NULL) {
4975 ret = xmlRelaxNGValidateValue(ctxt, list);
4976 if (ret == 0) {
4977 break;
4978 }
4979 ctxt->state->value = oldvalue;
4980 list = list->next;
4981 }
4982 ctxt->flags = oldflags;
Daniel Veillard416589a2003-02-17 17:25:42 +00004983 if (ret == 0)
4984 xmlRelaxNGNextValue(ctxt);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004985 break;
4986 }
4987 case XML_RELAXNG_LIST: {
4988 xmlRelaxNGDefinePtr list = define->content;
4989 xmlChar *oldvalue, *oldend, *val, *cur;
Daniel Veillard416589a2003-02-17 17:25:42 +00004990#ifdef DEBUG_LIST
4991 int nb_values = 0;
4992#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004993
4994 oldvalue = ctxt->state->value;
4995 oldend = ctxt->state->endvalue;
4996
4997 val = xmlStrdup(oldvalue);
4998 if (val == NULL) {
4999 VALID_CTXT();
5000 VALID_ERROR("Internal: no state\n");
5001 return(-1);
5002 }
5003 cur = val;
5004 while (*cur != 0) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005005 if (IS_BLANK(*cur)) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005006 *cur = 0;
Daniel Veillard416589a2003-02-17 17:25:42 +00005007 cur++;
5008#ifdef DEBUG_LIST
5009 nb_values++;
5010#endif
5011 while (IS_BLANK(*cur))
5012 *cur++ = 0;
5013 } else
5014 cur++;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005015 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005016#ifdef DEBUG_LIST
5017 xmlGenericError(xmlGenericErrorContext,
5018 "list value: '%s' found %d items\n", oldvalue, nb_values);
5019 nb_values = 0;
5020#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005021 ctxt->state->endvalue = cur;
5022 cur = val;
5023 while ((*cur == 0) && (cur != ctxt->state->endvalue)) cur++;
5024
5025 ctxt->state->value = cur;
5026
5027 while (list != NULL) {
5028 ret = xmlRelaxNGValidateValue(ctxt, list);
5029 if (ret != 0) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005030#ifdef DEBUG_LIST
5031 xmlGenericError(xmlGenericErrorContext,
5032 "Failed to validate value: '%s' with %d rule\n",
5033 ctxt->state->value, nb_values);
5034#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005035 break;
5036 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005037#ifdef DEBUG_LIST
5038 nb_values++;
5039#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005040 list = list->next;
5041 }
5042 if ((ret == 0) && (ctxt->state->value != NULL) &&
5043 (ctxt->state->value != ctxt->state->endvalue)) {
5044 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005045 VALID_ERROR2("Extra data in list: %s\n", ctxt->state->value);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005046 ret = -1;
5047 }
5048 xmlFree(val);
5049 ctxt->state->value = oldvalue;
5050 ctxt->state->endvalue = oldend;
5051 break;
5052 }
5053 case XML_RELAXNG_ONEORMORE:
5054 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
5055 if (ret != 0) {
5056 break;
5057 }
5058 /* no break on purpose */
5059 case XML_RELAXNG_ZEROORMORE: {
5060 xmlChar *cur, *temp;
5061
5062 oldflags = ctxt->flags;
5063 ctxt->flags |= FLAGS_IGNORABLE;
5064 cur = ctxt->state->value;
5065 temp = NULL;
5066 while ((cur != NULL) && (cur != ctxt->state->endvalue) &&
5067 (temp != cur)) {
5068 temp = cur;
5069 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
5070 if (ret != 0) {
5071 ctxt->state->value = temp;
5072 ret = 0;
5073 break;
5074 }
5075 cur = ctxt->state->value;
5076 }
5077 ctxt->flags = oldflags;
5078 break;
5079 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005080 case XML_RELAXNG_EXCEPT: {
5081 xmlRelaxNGDefinePtr list;
5082
5083 list = define->content;
5084 while (list != NULL) {
5085 ret = xmlRelaxNGValidateValue(ctxt, list);
5086 if (ret == 0) {
5087 ret = -1;
5088 break;
5089 } else
5090 ret = 0;
5091 list = list->next;
5092 }
5093 break;
5094 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005095 default:
5096 TODO
5097 ret = -1;
5098 }
5099 return(ret);
5100}
5101
5102/**
5103 * xmlRelaxNGValidateValueContent:
5104 * @ctxt: a Relax-NG validation context
5105 * @defines: the list of definitions to verify
5106 *
5107 * Validate the given definitions for the current value
5108 *
5109 * Returns 0 if the validation succeeded or an error code.
5110 */
5111static int
5112xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt,
5113 xmlRelaxNGDefinePtr defines) {
5114 int ret = 0;
5115
5116 while (defines != NULL) {
5117 ret = xmlRelaxNGValidateValue(ctxt, defines);
5118 if (ret != 0)
5119 break;
5120 defines = defines->next;
5121 }
5122 return(ret);
5123}
5124
5125/**
Daniel Veillard144fae12003-02-03 13:17:57 +00005126 * xmlRelaxNGAttributeMatch:
5127 * @ctxt: a Relax-NG validation context
5128 * @define: the definition to check
5129 * @prop: the attribute
5130 *
5131 * Check if the attribute matches the definition nameClass
5132 *
5133 * Returns 1 if the attribute matches, 0 if no, or -1 in case of error
5134 */
5135static int
5136xmlRelaxNGAttributeMatch(xmlRelaxNGValidCtxtPtr ctxt,
5137 xmlRelaxNGDefinePtr define,
5138 xmlAttrPtr prop) {
5139 int ret;
5140
5141 if (define->name != NULL) {
5142 if (!xmlStrEqual(define->name, prop->name))
5143 return(0);
5144 }
5145 if (define->ns != NULL) {
5146 if (define->ns[0] == 0) {
5147 if (prop->ns != NULL)
5148 return(0);
5149 } else {
5150 if ((prop->ns == NULL) ||
5151 (!xmlStrEqual(define->ns, prop->ns->href)))
5152 return(0);
5153 }
5154 }
5155 if (define->nameClass == NULL)
5156 return(1);
5157 define = define->nameClass;
5158 if (define->type == XML_RELAXNG_EXCEPT) {
5159 xmlRelaxNGDefinePtr list;
5160
5161 list = define->content;
5162 while (list != NULL) {
5163 ret = xmlRelaxNGAttributeMatch(ctxt, list, prop);
5164 if (ret == 1)
5165 return(0);
5166 if (ret < 0)
5167 return(ret);
5168 list = list->next;
5169 }
5170 } else {
5171 TODO
5172 }
5173 return(1);
5174}
5175
5176/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00005177 * xmlRelaxNGValidateAttribute:
5178 * @ctxt: a Relax-NG validation context
5179 * @define: the definition to verify
5180 *
5181 * Validate the given attribute definition for that node
5182 *
5183 * Returns 0 if the validation succeeded or an error code.
5184 */
5185static int
5186xmlRelaxNGValidateAttribute(xmlRelaxNGValidCtxtPtr ctxt,
5187 xmlRelaxNGDefinePtr define) {
5188 int ret = 0, i;
5189 xmlChar *value, *oldvalue;
5190 xmlAttrPtr prop = NULL, tmp;
5191
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005192 if (ctxt->state->nbAttrLeft <= 0)
5193 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005194 if (define->name != NULL) {
5195 for (i = 0;i < ctxt->state->nbAttrs;i++) {
5196 tmp = ctxt->state->attrs[i];
5197 if ((tmp != NULL) && (xmlStrEqual(define->name, tmp->name))) {
5198 if ((((define->ns == NULL) || (define->ns[0] == 0)) &&
5199 (tmp->ns == NULL)) ||
5200 ((tmp->ns != NULL) &&
5201 (xmlStrEqual(define->ns, tmp->ns->href)))) {
5202 prop = tmp;
5203 break;
5204 }
5205 }
5206 }
5207 if (prop != NULL) {
5208 value = xmlNodeListGetString(prop->doc, prop->children, 1);
5209 oldvalue = ctxt->state->value;
5210 ctxt->state->value = value;
Daniel Veillard231d7912003-02-09 14:22:17 +00005211 ctxt->state->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005212 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00005213 if (ctxt->state->value != NULL)
5214 value = ctxt->state->value;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005215 if (value != NULL)
5216 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00005217 ctxt->state->value = oldvalue;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005218 if (ret == 0) {
5219 /*
5220 * flag the attribute as processed
5221 */
5222 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005223 ctxt->state->nbAttrLeft--;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005224 }
5225 } else {
5226 ret = -1;
5227 }
5228#ifdef DEBUG
5229 xmlGenericError(xmlGenericErrorContext,
5230 "xmlRelaxNGValidateAttribute(%s): %d\n", define->name, ret);
5231#endif
5232 } else {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005233 for (i = 0;i < ctxt->state->nbAttrs;i++) {
5234 tmp = ctxt->state->attrs[i];
Daniel Veillard144fae12003-02-03 13:17:57 +00005235 if ((tmp != NULL) &&
5236 (xmlRelaxNGAttributeMatch(ctxt, define, tmp) == 1)) {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005237 prop = tmp;
5238 break;
5239 }
5240 }
5241 if (prop != NULL) {
5242 value = xmlNodeListGetString(prop->doc, prop->children, 1);
5243 oldvalue = ctxt->state->value;
5244 ctxt->state->value = value;
5245 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00005246 if (ctxt->state->value != NULL)
5247 value = ctxt->state->value;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005248 if (value != NULL)
5249 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00005250 ctxt->state->value = oldvalue;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005251 if (ret == 0) {
5252 /*
5253 * flag the attribute as processed
5254 */
5255 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005256 ctxt->state->nbAttrLeft--;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005257 }
5258 } else {
5259 ret = -1;
5260 }
5261#ifdef DEBUG
Daniel Veillard144fae12003-02-03 13:17:57 +00005262 if (define->ns != NULL) {
5263 xmlGenericError(xmlGenericErrorContext,
5264 "xmlRelaxNGValidateAttribute(nsName ns = %s): %d\n",
5265 define->ns, ret);
5266 } else {
5267 xmlGenericError(xmlGenericErrorContext,
5268 "xmlRelaxNGValidateAttribute(anyName): %d\n",
5269 ret);
5270 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005271#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00005272 }
5273
5274 return(ret);
5275}
5276
5277/**
5278 * xmlRelaxNGValidateAttributeList:
5279 * @ctxt: a Relax-NG validation context
5280 * @define: the list of definition to verify
5281 *
5282 * Validate the given node against the list of attribute definitions
5283 *
5284 * Returns 0 if the validation succeeded or an error code.
5285 */
5286static int
5287xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt,
5288 xmlRelaxNGDefinePtr defines) {
5289 int ret = 0;
5290 while (defines != NULL) {
5291 if (xmlRelaxNGValidateAttribute(ctxt, defines) != 0)
5292 ret = -1;
5293 defines = defines->next;
5294 }
5295 return(ret);
5296}
5297
5298/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005299 * xmlRelaxNGValidateTryPermutation:
5300 * @ctxt: a Relax-NG validation context
5301 * @groups: the array of groups
5302 * @nbgroups: the number of groups in the array
5303 * @array: the permutation to try
5304 * @len: the size of the set
5305 *
5306 * Try to validate a permutation for the group of definitions.
5307 *
5308 * Returns 0 if the validation succeeded or an error code.
5309 */
5310static int
5311xmlRelaxNGValidateTryPermutation(xmlRelaxNGValidCtxtPtr ctxt,
5312 xmlRelaxNGDefinePtr rule,
5313 xmlNodePtr *array, int len) {
5314 int i, ret;
5315
5316 if (len > 0) {
5317 /*
5318 * One only need the next pointer set-up to do the validation
5319 */
5320 for (i = 0;i < (len - 1);i++)
5321 array[i]->next = array[i + 1];
5322 array[i]->next = NULL;
5323
5324 /*
5325 * Now try to validate the sequence
5326 */
5327 ctxt->state->seq = array[0];
5328 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
5329 } else {
5330 ctxt->state->seq = NULL;
5331 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
5332 }
5333
5334 /*
5335 * the sequence must be fully consumed
5336 */
5337 if (ctxt->state->seq != NULL)
5338 return(-1);
5339
5340 return(ret);
5341}
5342
5343/**
5344 * xmlRelaxNGValidateWalkPermutations:
5345 * @ctxt: a Relax-NG validation context
5346 * @groups: the array of groups
5347 * @nbgroups: the number of groups in the array
5348 * @nodes: the set of nodes
5349 * @array: the current state of the parmutation
5350 * @len: the size of the set
5351 * @level: a pointer to the level variable
5352 * @k: the index in the array to fill
5353 *
5354 * Validate a set of nodes for a groups of definitions, will try the
5355 * full set of permutations
5356 *
5357 * Returns 0 if the validation succeeded or an error code.
5358 */
5359static int
5360xmlRelaxNGValidateWalkPermutations(xmlRelaxNGValidCtxtPtr ctxt,
5361 xmlRelaxNGDefinePtr rule, xmlNodePtr *nodes,
5362 xmlNodePtr *array, int len,
5363 int *level, int k) {
5364 int i, ret;
5365
5366 if ((k >= 0) && (k < len))
5367 array[k] = nodes[*level];
5368 *level = *level + 1;
5369 if (*level == len) {
5370 ret = xmlRelaxNGValidateTryPermutation(ctxt, rule, array, len);
5371 if (ret == 0)
5372 return(0);
5373 } else {
5374 for (i = 0;i < len;i++) {
5375 if (array[i] == NULL) {
5376 ret = xmlRelaxNGValidateWalkPermutations(ctxt, rule,
5377 nodes, array, len, level, i);
5378 if (ret == 0)
5379 return(0);
5380 }
5381 }
5382 }
5383 *level = *level - 1;
5384 array[k] = NULL;
5385 return(-1);
5386}
5387
5388/**
5389 * xmlRelaxNGNodeMatchesList:
5390 * @node: the node
5391 * @list: a NULL terminated array of definitions
5392 *
5393 * Check if a node can be matched by one of the definitions
5394 *
5395 * Returns 1 if matches 0 otherwise
5396 */
5397static int
5398xmlRelaxNGNodeMatchesList(xmlNodePtr node, xmlRelaxNGDefinePtr *list) {
5399 xmlRelaxNGDefinePtr cur;
5400 int i = 0;
5401
5402 if ((node == NULL) || (list == NULL))
5403 return(0);
5404
5405 cur = list[i++];
5406 while (cur != NULL) {
5407 if ((node->type == XML_ELEMENT_NODE) &&
5408 (cur->type == XML_RELAXNG_ELEMENT)) {
5409 if (cur->name == NULL) {
5410 if ((node->ns != NULL) &&
5411 (xmlStrEqual(node->ns->href, cur->ns)))
5412 return(1);
5413 } else if (xmlStrEqual(cur->name, node->name)) {
5414 if ((cur->ns == NULL) || (cur->ns[0] == 0)) {
5415 if (node->ns == NULL)
5416 return(1);
5417 } else {
5418 if ((node->ns != NULL) &&
5419 (xmlStrEqual(node->ns->href, cur->ns)))
5420 return(1);
5421 }
5422 }
5423 } else if ((node->type == XML_TEXT_NODE) &&
5424 (cur->type == XML_RELAXNG_TEXT)) {
5425 return(1);
5426 }
5427 cur = list[i++];
5428 }
5429 return(0);
5430}
5431
5432/**
5433 * xmlRelaxNGValidatePartGroup:
5434 * @ctxt: a Relax-NG validation context
5435 * @groups: the array of groups
5436 * @nbgroups: the number of groups in the array
5437 * @nodes: the set of nodes
5438 * @len: the size of the set of nodes
5439 *
5440 * Validate a set of nodes for a groups of definitions
5441 *
5442 * Returns 0 if the validation succeeded or an error code.
5443 */
5444static int
5445xmlRelaxNGValidatePartGroup(xmlRelaxNGValidCtxtPtr ctxt,
5446 xmlRelaxNGInterleaveGroupPtr *groups,
5447 int nbgroups, xmlNodePtr *nodes, int len) {
Daniel Veillard231d7912003-02-09 14:22:17 +00005448 int level, ret = -1, i, j, k, top_j, max_j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005449 xmlNodePtr *array = NULL, *list, oldseq;
5450 xmlRelaxNGInterleaveGroupPtr group;
5451
5452 list = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
5453 if (list == NULL) {
5454 return(-1);
5455 }
5456 array = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
5457 if (array == NULL) {
5458 xmlFree(list);
5459 return(-1);
5460 }
5461 memset(array, 0, len * sizeof(xmlNodePtr));
5462
5463 /*
5464 * Partition the elements and validate the subsets.
5465 */
5466 oldseq = ctxt->state->seq;
Daniel Veillard231d7912003-02-09 14:22:17 +00005467 max_j = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005468 for (i = 0;i < nbgroups;i++) {
5469 group = groups[i];
5470 if (group == NULL)
5471 continue;
5472 k = 0;
Daniel Veillard231d7912003-02-09 14:22:17 +00005473 top_j = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005474 for (j = 0;j < len;j++) {
5475 if (nodes[j] == NULL)
5476 continue;
5477 if (xmlRelaxNGNodeMatchesList(nodes[j], group->defs)) {
5478 list[k++] = nodes[j];
5479 nodes[j] = NULL;
Daniel Veillard231d7912003-02-09 14:22:17 +00005480 top_j = j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005481 }
5482 }
Daniel Veillard231d7912003-02-09 14:22:17 +00005483 if (top_j > max_j)
5484 max_j = top_j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005485 ctxt->state->seq = oldseq;
5486 if (k > 1) {
5487 memset(array, 0, k * sizeof(xmlNodePtr));
Daniel Veillardb08c9812003-01-28 23:09:49 +00005488 level = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005489 ret = xmlRelaxNGValidateWalkPermutations(ctxt, group->rule,
5490 list, array, k, &level, -1);
5491 } else {
5492 ret = xmlRelaxNGValidateTryPermutation(ctxt, group->rule, list, k);
5493 }
5494 if (ret != 0) {
5495 ctxt->state->seq = oldseq;
5496 break;
5497 }
5498 }
5499
Daniel Veillard231d7912003-02-09 14:22:17 +00005500 for (j = 0;j < max_j;j++) {
5501 if (nodes[j] != NULL) {
5502 TODO /* problem, one of the nodes didn't got a match */
5503 }
5504 }
5505 if (ret == 0) {
5506 if (max_j + 1 < len)
5507 ctxt->state->seq = nodes[max_j + 1];
5508 else
5509 ctxt->state->seq = NULL;
5510 }
5511
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005512 xmlFree(list);
5513 xmlFree(array);
5514 return(ret);
5515}
5516
5517/**
5518 * xmlRelaxNGValidateInterleave:
5519 * @ctxt: a Relax-NG validation context
5520 * @define: the definition to verify
5521 *
5522 * Validate an interleave definition for a node.
5523 *
5524 * Returns 0 if the validation succeeded or an error code.
5525 */
5526static int
5527xmlRelaxNGValidateInterleave(xmlRelaxNGValidCtxtPtr ctxt,
5528 xmlRelaxNGDefinePtr define) {
5529 int ret = 0, nbchildren, nbtot, i, j;
5530 xmlRelaxNGPartitionPtr partitions;
5531 xmlNodePtr *children = NULL;
5532 xmlNodePtr *order = NULL;
Daniel Veillard231d7912003-02-09 14:22:17 +00005533 xmlNodePtr cur, oldseq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005534
5535 if (define->data != NULL) {
5536 partitions = (xmlRelaxNGPartitionPtr) define->data;
5537 } else {
5538 VALID_CTXT();
5539 VALID_ERROR("Internal: interleave block has no data\n");
5540 return(-1);
5541 }
5542
5543 /*
5544 * Build the sequence of child and an array preserving the children
5545 * initial order.
5546 */
5547 cur = ctxt->state->seq;
Daniel Veillard231d7912003-02-09 14:22:17 +00005548 oldseq = ctxt->state->seq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005549 nbchildren = 0;
5550 nbtot = 0;
5551 while (cur != NULL) {
5552 if ((cur->type == XML_COMMENT_NODE) ||
5553 (cur->type == XML_PI_NODE) ||
5554 ((cur->type == XML_TEXT_NODE) &&
5555 (IS_BLANK_NODE(cur)))) {
5556 nbtot++;
5557 } else {
5558 nbchildren++;
5559 nbtot++;
5560 }
5561 cur = cur->next;
5562 }
5563 children = (xmlNodePtr *) xmlMalloc(nbchildren * sizeof(xmlNodePtr));
5564 if (children == NULL)
5565 goto error;
5566 order = (xmlNodePtr *) xmlMalloc(nbtot * sizeof(xmlNodePtr));
5567 if (order == NULL)
5568 goto error;
5569 cur = ctxt->state->seq;
5570 i = 0;
5571 j = 0;
5572 while (cur != NULL) {
5573 if ((cur->type == XML_COMMENT_NODE) ||
5574 (cur->type == XML_PI_NODE) ||
5575 ((cur->type == XML_TEXT_NODE) &&
5576 (IS_BLANK_NODE(cur)))) {
5577 order[j++] = cur;
5578 } else {
5579 order[j++] = cur;
5580 children[i++] = cur;
5581 }
5582 cur = cur->next;
5583 }
5584
5585 /* TODO: retry with a maller set of child if there is a next... */
5586 ret = xmlRelaxNGValidatePartGroup(ctxt, partitions->groups,
5587 partitions->nbgroups, children, nbchildren);
Daniel Veillard231d7912003-02-09 14:22:17 +00005588 if (ret != 0)
5589 ctxt->state->seq = oldseq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005590
5591 /*
5592 * Cleanup: rebuid the child sequence and free the structure
5593 */
5594 if (order != NULL) {
5595 for (i = 0;i < nbtot;i++) {
5596 if (i == 0)
5597 order[i]->prev = NULL;
5598 else
5599 order[i]->prev = order[i - 1];
5600 if (i == nbtot - 1)
5601 order[i]->next = NULL;
5602 else
5603 order[i]->next = order[i + 1];
5604 }
5605 xmlFree(order);
5606 }
5607 if (children != NULL)
5608 xmlFree(children);
5609
5610 return(ret);
5611
5612error:
5613 if (order != NULL) {
5614 for (i = 0;i < nbtot;i++) {
5615 if (i == 0)
5616 order[i]->prev = NULL;
5617 else
5618 order[i]->prev = order[i - 1];
5619 if (i == nbtot - 1)
5620 order[i]->next = NULL;
5621 else
5622 order[i]->next = order[i + 1];
5623 }
5624 xmlFree(order);
5625 }
5626 if (children != NULL)
5627 xmlFree(children);
5628 return(-1);
5629}
5630
5631/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00005632 * xmlRelaxNGValidateElementContent:
5633 * @ctxt: a Relax-NG validation context
5634 * @define: the list of definition to verify
5635 *
5636 * Validate the given node content against the (list) of definitions
5637 *
5638 * Returns 0 if the validation succeeded or an error code.
5639 */
5640static int
5641xmlRelaxNGValidateElementContent(xmlRelaxNGValidCtxtPtr ctxt,
5642 xmlRelaxNGDefinePtr defines) {
5643 int ret = 0, res;
5644
5645 if (ctxt->state == NULL) {
5646 VALID_CTXT();
5647 VALID_ERROR("Internal: no state\n");
5648 return(-1);
5649 }
5650 while (defines != NULL) {
5651 res = xmlRelaxNGValidateDefinition(ctxt, defines);
5652 if (res < 0)
5653 ret = -1;
5654 defines = defines->next;
5655 }
5656
5657 return(ret);
5658}
5659
5660/**
Daniel Veillard416589a2003-02-17 17:25:42 +00005661 * xmlRelaxNGElementMatch:
5662 * @ctxt: a Relax-NG validation context
5663 * @define: the definition to check
5664 * @elem: the element
5665 *
5666 * Check if the element matches the definition nameClass
5667 *
5668 * Returns 1 if the element matches, 0 if no, or -1 in case of error
5669 */
5670static int
5671xmlRelaxNGElementMatch(xmlRelaxNGValidCtxtPtr ctxt,
5672 xmlRelaxNGDefinePtr define,
5673 xmlNodePtr elem) {
5674 int ret, oldflags;
5675
5676 if (define->name != NULL) {
5677 if (!xmlStrEqual(elem->name, define->name)) {
5678 VALID_CTXT();
5679 VALID_ERROR3("Expecting element %s, got %s\n",
5680 define->name, elem->name);
5681 return(0);
5682 }
5683 }
5684 if ((define->ns != NULL) && (define->ns[0] != 0)) {
5685 if (elem->ns == NULL) {
5686 VALID_CTXT();
5687 VALID_ERROR2("Expecting a namespace for element %s\n",
5688 elem->name);
5689 return(0);
5690 } else if (!xmlStrEqual(elem->ns->href, define->ns)) {
5691 VALID_CTXT();
5692 VALID_ERROR3("Expecting element %s has wrong namespace: expecting %s\n",
5693 elem->name, define->ns);
5694 return(0);
5695 }
5696 } else if (define->name != NULL) {
5697 if (elem->ns != NULL) {
5698 VALID_CTXT();
5699 VALID_ERROR2("Expecting no namespace for element %s\n",
5700 define->name);
5701 return(0);
5702 }
5703 }
5704
5705 if (define->nameClass == NULL)
5706 return(1);
5707
5708 define = define->nameClass;
5709 if (define->type == XML_RELAXNG_EXCEPT) {
5710 xmlRelaxNGDefinePtr list;
5711 oldflags = ctxt->flags;
5712 ctxt->flags |= FLAGS_IGNORABLE;
5713
5714 list = define->content;
5715 while (list != NULL) {
5716 ret = xmlRelaxNGElementMatch(ctxt, list, elem);
5717 if (ret == 1) {
5718 ctxt->flags = oldflags;
5719 return(0);
5720 }
5721 if (ret < 0) {
5722 ctxt->flags = oldflags;
5723 return(ret);
5724 }
5725 list = list->next;
5726 }
5727 ctxt->flags = oldflags;
5728 } else {
5729 TODO
5730 }
5731 return(1);
5732}
5733
5734/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00005735 * xmlRelaxNGValidateDefinition:
5736 * @ctxt: a Relax-NG validation context
5737 * @define: the definition to verify
5738 *
5739 * Validate the current node against the definition
5740 *
5741 * Returns 0 if the validation succeeded or an error code.
5742 */
5743static int
5744xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
5745 xmlRelaxNGDefinePtr define) {
5746 xmlNodePtr node;
5747 int ret = 0, i, tmp, oldflags;
5748 xmlRelaxNGValidStatePtr oldstate, state;
5749
5750 if (define == NULL) {
5751 VALID_CTXT();
5752 VALID_ERROR("internal error: define == NULL\n");
5753 return(-1);
5754 }
5755 if (ctxt->state != NULL) {
5756 node = ctxt->state->seq;
5757 } else {
5758 node = NULL;
5759 }
Daniel Veillard231d7912003-02-09 14:22:17 +00005760#ifdef DEBUG
5761 for (i = 0;i < ctxt->depth;i++)
5762 xmlGenericError(xmlGenericErrorContext, " ");
5763 xmlGenericError(xmlGenericErrorContext,
5764 "Start validating %s ", xmlRelaxNGDefName(define));
5765 if (define->name != NULL)
5766 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
5767 if ((node != NULL) && (node->name != NULL))
5768 xmlGenericError(xmlGenericErrorContext, "on %s\n", node->name);
5769 else
5770 xmlGenericError(xmlGenericErrorContext, "\n");
5771#endif
5772 ctxt->depth++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005773 switch (define->type) {
5774 case XML_RELAXNG_EMPTY:
5775 if (node != NULL) {
5776 VALID_CTXT();
5777 VALID_ERROR("Expecting an empty element\n");
Daniel Veillard231d7912003-02-09 14:22:17 +00005778 ret = -1;
5779 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005780 }
Daniel Veillard231d7912003-02-09 14:22:17 +00005781 ret = 0;
5782 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005783 case XML_RELAXNG_NOT_ALLOWED:
Daniel Veillard231d7912003-02-09 14:22:17 +00005784 ret = -1;
5785 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005786 case XML_RELAXNG_TEXT:
Daniel Veillard231d7912003-02-09 14:22:17 +00005787 if (node == NULL) {
5788 ret = 0;
5789 break;
5790 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005791 while ((node != NULL) &&
5792 ((node->type == XML_TEXT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005793 (node->type == XML_COMMENT_NODE) ||
5794 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00005795 (node->type == XML_CDATA_SECTION_NODE)))
5796 node = node->next;
Daniel Veillard276be4a2003-01-24 01:03:34 +00005797 if (node == ctxt->state->seq) {
5798 VALID_CTXT();
5799 VALID_ERROR("Expecting text content\n");
5800 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005801 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00005802 ctxt->state->seq = node;
5803 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005804 case XML_RELAXNG_ELEMENT:
5805 node = xmlRelaxNGSkipIgnored(ctxt, node);
Daniel Veillard231d7912003-02-09 14:22:17 +00005806 if (node == NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005807 VALID_CTXT();
Daniel Veillard231d7912003-02-09 14:22:17 +00005808 VALID_ERROR("Expecting an element, got empty\n");
5809 ret = -1;
5810 break;
5811 }
5812 if (node->type != XML_ELEMENT_NODE) {
5813 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005814 VALID_ERROR2("Expecting an element got %d type\n", node->type);
Daniel Veillard231d7912003-02-09 14:22:17 +00005815 ret = -1;
5816 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005817 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00005818 /*
5819 * This node was already validated successfully against
5820 * this definition.
5821 */
5822 if (node->_private == define)
5823 break;
Daniel Veillard416589a2003-02-17 17:25:42 +00005824
5825 ret = xmlRelaxNGElementMatch(ctxt, define, node);
5826 if (ret <= 0) {
5827 ret = -1;
5828 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005829 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005830 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005831
5832 state = xmlRelaxNGNewValidState(ctxt, node);
5833 if (state == NULL) {
Daniel Veillard231d7912003-02-09 14:22:17 +00005834 ret = -1;
5835 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005836 }
5837
5838 oldstate = ctxt->state;
5839 ctxt->state = state;
5840 if (define->attrs != NULL) {
5841 tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
Daniel Veillard231d7912003-02-09 14:22:17 +00005842 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005843 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00005844#ifdef DEBUG
5845 xmlGenericError(xmlGenericErrorContext,
5846 "E: Element %s failed to validate attributes\n",
5847 node->name);
5848#endif
5849 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005850 }
5851 if (define->content != NULL) {
5852 tmp = xmlRelaxNGValidateElementContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00005853 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005854 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00005855#ifdef DEBUG
5856 xmlGenericError(xmlGenericErrorContext,
5857 "E: Element %s failed to validate element content\n",
5858 node->name);
5859#endif
5860 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005861 }
5862 state = ctxt->state;
5863 if (state->seq != NULL) {
5864 state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq);
5865 if (state->seq != NULL) {
5866 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005867 VALID_ERROR3("Extra content for element %s: %s\n",
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005868 node->name, state->seq->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005869 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00005870#ifdef DEBUG
5871 xmlGenericError(xmlGenericErrorContext,
5872 "E: Element %s has extra content: %s\n",
5873 node->name, state->seq->name);
5874#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00005875 }
5876 }
5877 for (i = 0;i < state->nbAttrs;i++) {
5878 if (state->attrs[i] != NULL) {
5879 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005880 VALID_ERROR3("Invalid attribute %s for element %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00005881 state->attrs[i]->name, node->name);
5882 ret = -1;
5883 }
5884 }
5885 ctxt->state = oldstate;
5886 xmlRelaxNGFreeValidState(state);
5887 if (oldstate != NULL)
Daniel Veillarde5b110b2003-02-04 14:43:39 +00005888 oldstate->seq = xmlRelaxNGSkipIgnored(ctxt, node->next);
5889 if (ret == 0)
5890 node->_private = define;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005891
5892
5893#ifdef DEBUG
5894 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard231d7912003-02-09 14:22:17 +00005895 "xmlRelaxNGValidateDefinition(): validated %s : %d",
Daniel Veillard6eadf632003-01-23 18:29:16 +00005896 node->name, ret);
Daniel Veillard231d7912003-02-09 14:22:17 +00005897 if (oldstate == NULL)
5898 xmlGenericError(xmlGenericErrorContext, ": no state\n");
5899 else if (oldstate->seq == NULL)
5900 xmlGenericError(xmlGenericErrorContext, ": done\n");
5901 else if (oldstate->seq->type == XML_ELEMENT_NODE)
5902 xmlGenericError(xmlGenericErrorContext, ": next elem %s\n",
5903 oldstate->seq->name);
5904 else
5905 xmlGenericError(xmlGenericErrorContext, ": next %s %d\n",
5906 oldstate->seq->name, oldstate->seq->type);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005907#endif
5908 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005909 case XML_RELAXNG_OPTIONAL:
5910 oldflags = ctxt->flags;
5911 ctxt->flags |= FLAGS_IGNORABLE;
5912 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5913 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5914 if (ret != 0) {
5915 xmlRelaxNGFreeValidState(ctxt->state);
5916 ctxt->state = oldstate;
5917 ret = 0;
5918 break;
5919 }
5920 xmlRelaxNGFreeValidState(oldstate);
5921 ctxt->flags = oldflags;
5922 ret = 0;
5923 break;
5924 case XML_RELAXNG_ONEORMORE:
5925 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5926 if (ret != 0) {
5927 break;
5928 }
5929 /* no break on purpose */
Daniel Veillard276be4a2003-01-24 01:03:34 +00005930 case XML_RELAXNG_ZEROORMORE: {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005931 oldflags = ctxt->flags;
5932 ctxt->flags |= FLAGS_IGNORABLE;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005933 while (ctxt->state->nbAttrLeft != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005934 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5935 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5936 if (ret != 0) {
5937 xmlRelaxNGFreeValidState(ctxt->state);
5938 ctxt->state = oldstate;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005939 break;
5940 }
5941 xmlRelaxNGFreeValidState(oldstate);
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005942 }
5943 if (ret == 0) {
5944 /*
5945 * There is no attribute left to be consumed,
5946 * we can check the closure by looking at ctxt->state->seq
5947 */
5948 xmlNodePtr cur, temp;
5949
Daniel Veillard276be4a2003-01-24 01:03:34 +00005950 cur = ctxt->state->seq;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005951 temp = NULL;
5952 while ((cur != NULL) && (temp != cur)) {
5953 temp = cur;
5954 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5955 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5956 if (ret != 0) {
5957 xmlRelaxNGFreeValidState(ctxt->state);
5958 ctxt->state = oldstate;
5959 break;
5960 }
5961 xmlRelaxNGFreeValidState(oldstate);
5962 cur = ctxt->state->seq;
5963 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005964 }
5965 ctxt->flags = oldflags;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005966 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005967 break;
Daniel Veillard276be4a2003-01-24 01:03:34 +00005968 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005969 case XML_RELAXNG_CHOICE: {
5970 xmlRelaxNGDefinePtr list = define->content;
5971
5972 oldflags = ctxt->flags;
5973 ctxt->flags |= FLAGS_IGNORABLE;
5974
5975 while (list != NULL) {
5976 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5977 ret = xmlRelaxNGValidateDefinition(ctxt, list);
5978 if (ret == 0) {
5979 xmlRelaxNGFreeValidState(oldstate);
5980 break;
5981 }
5982 xmlRelaxNGFreeValidState(ctxt->state);
5983 ctxt->state = oldstate;
5984 list = list->next;
5985 }
5986 ctxt->flags = oldflags;
5987 break;
5988 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005989 case XML_RELAXNG_DEF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00005990 case XML_RELAXNG_GROUP: {
5991 xmlRelaxNGDefinePtr list = define->content;
5992
5993 while (list != NULL) {
5994 ret = xmlRelaxNGValidateDefinition(ctxt, list);
5995 if (ret != 0)
5996 break;
5997 list = list->next;
5998 }
5999 break;
6000 }
6001 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006002 ret = xmlRelaxNGValidateInterleave(ctxt, define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006003 break;
6004 case XML_RELAXNG_ATTRIBUTE:
6005 ret = xmlRelaxNGValidateAttribute(ctxt, define);
6006 break;
6007 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00006008 case XML_RELAXNG_PARENTREF:
6009 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00006010 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6011 break;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006012 case XML_RELAXNG_DATATYPE: {
6013 xmlChar *content;
6014
6015 content = xmlNodeGetContent(node);
6016 ret = xmlRelaxNGValidateDatatype(ctxt, content, define);
6017 if (ret == -1) {
6018 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006019 VALID_ERROR2("internal error validating %s\n", define->name);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006020 } else if (ret == 0) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006021 if (node != NULL)
6022 ctxt->state->seq = node->next;
6023 else
6024 ctxt->state->seq = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006025 }
6026 /*
6027 * TODO cover the problems with
6028 * <p>12<!-- comment -->34</p>
6029 * TODO detect full element coverage at compilation time.
6030 */
6031 if ((node != NULL) && (node->next != NULL)) {
6032 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006033 VALID_ERROR2("The data does not cover the full element %s\n",
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006034 node->parent->name);
6035 ret = -1;
6036 }
6037 if (content != NULL)
6038 xmlFree(content);
6039 break;
6040 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00006041 case XML_RELAXNG_VALUE: {
6042 xmlChar *content;
6043 xmlChar *oldvalue;
6044
6045 content = xmlNodeGetContent(node);
6046 oldvalue = ctxt->state->value;
6047 ctxt->state->value = content;
6048 ret = xmlRelaxNGValidateValue(ctxt, define);
6049 ctxt->state->value = oldvalue;
6050 if (ret == -1) {
6051 VALID_CTXT();
Daniel Veillardd2298792003-02-14 16:54:11 +00006052 if (define->name != NULL) {
6053 VALID_ERROR2("error validating value %s\n", define->name);
6054 } else {
6055 VALID_ERROR("error validating value\n");
6056 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00006057 } else if (ret == 0) {
6058 ctxt->state->seq = node->next;
6059 }
6060 /*
6061 * TODO cover the problems with
6062 * <p>12<!-- comment -->34</p>
6063 * TODO detect full element coverage at compilation time.
6064 */
6065 if ((node != NULL) && (node->next != NULL)) {
6066 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006067 VALID_ERROR2("The value does not cover the full element %s\n",
Daniel Veillardea3f3982003-01-26 19:45:18 +00006068 node->parent->name);
6069 ret = -1;
6070 }
6071 if (content != NULL)
6072 xmlFree(content);
6073 break;
6074 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006075 case XML_RELAXNG_LIST: {
6076 xmlChar *content;
6077 xmlChar *oldvalue, *oldendvalue;
6078 int len;
Daniel Veillardea3f3982003-01-26 19:45:18 +00006079
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006080 content = xmlNodeGetContent(node);
6081 len = xmlStrlen(content);
6082 oldvalue = ctxt->state->value;
6083 oldendvalue = ctxt->state->endvalue;
6084 ctxt->state->value = content;
6085 ctxt->state->endvalue = content + len;
6086 ret = xmlRelaxNGValidateValue(ctxt, define);
6087 ctxt->state->value = oldvalue;
6088 ctxt->state->endvalue = oldendvalue;
6089 if (ret == -1) {
6090 VALID_CTXT();
6091 VALID_ERROR("internal error validating list\n");
6092 } else if (ret == 0) {
6093 ctxt->state->seq = node->next;
6094 }
6095 /*
6096 * TODO cover the problems with
6097 * <p>12<!-- comment -->34</p>
6098 * TODO detect full element coverage at compilation time.
6099 */
6100 if ((node != NULL) && (node->next != NULL)) {
6101 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006102 VALID_ERROR2("The list does not cover the full element %s\n",
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006103 node->parent->name);
6104 ret = -1;
6105 }
6106 if (content != NULL)
6107 xmlFree(content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006108 break;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006109 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006110#if 0
6111 case XML_RELAXNG_MIXED:
6112 TODO
6113 ret = -1;
6114 break;
6115#endif
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006116 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00006117 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006118 TODO
Daniel Veillard416589a2003-02-17 17:25:42 +00006119 ret = -1;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006120 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006121 }
Daniel Veillard231d7912003-02-09 14:22:17 +00006122 ctxt->depth--;
6123#ifdef DEBUG
6124 for (i = 0;i < ctxt->depth;i++)
6125 xmlGenericError(xmlGenericErrorContext, " ");
6126 xmlGenericError(xmlGenericErrorContext,
6127 "Validating %s ", xmlRelaxNGDefName(define));
6128 if (define->name != NULL)
6129 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
6130 if (ret == 0)
6131 xmlGenericError(xmlGenericErrorContext, "suceeded\n");
6132 else
6133 xmlGenericError(xmlGenericErrorContext, "failed\n");
6134#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00006135 return(ret);
6136}
6137
6138/**
6139 * xmlRelaxNGValidateDocument:
6140 * @ctxt: a Relax-NG validation context
6141 * @doc: the document
6142 *
6143 * Validate the given document
6144 *
6145 * Returns 0 if the validation succeeded or an error code.
6146 */
6147static int
6148xmlRelaxNGValidateDocument(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
6149 int ret;
6150 xmlRelaxNGPtr schema;
6151 xmlRelaxNGGrammarPtr grammar;
6152 xmlRelaxNGValidStatePtr state;
6153
6154 if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL))
6155 return(-1);
6156
6157 schema = ctxt->schema;
6158 grammar = schema->topgrammar;
6159 if (grammar == NULL) {
6160 VALID_CTXT();
6161 VALID_ERROR("No top grammar defined\n");
6162 return(-1);
6163 }
6164 state = xmlRelaxNGNewValidState(ctxt, NULL);
6165 ctxt->state = state;
6166 ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start);
6167 state = ctxt->state;
6168 if ((state != NULL) && (state->seq != NULL)) {
6169 xmlNodePtr node;
6170
6171 node = state->seq;
6172 node = xmlRelaxNGSkipIgnored(ctxt, node);
6173 if (node != NULL) {
6174 VALID_CTXT();
6175 VALID_ERROR("extra data on the document\n");
6176 ret = -1;
6177 }
6178 }
6179 xmlRelaxNGFreeValidState(state);
6180
6181 return(ret);
6182}
6183
6184/************************************************************************
6185 * *
6186 * Validation interfaces *
6187 * *
6188 ************************************************************************/
6189/**
6190 * xmlRelaxNGNewValidCtxt:
6191 * @schema: a precompiled XML RelaxNGs
6192 *
6193 * Create an XML RelaxNGs validation context based on the given schema
6194 *
6195 * Returns the validation context or NULL in case of error
6196 */
6197xmlRelaxNGValidCtxtPtr
6198xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema) {
6199 xmlRelaxNGValidCtxtPtr ret;
6200
6201 ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt));
6202 if (ret == NULL) {
6203 xmlGenericError(xmlGenericErrorContext,
6204 "Failed to allocate new schama validation context\n");
6205 return (NULL);
6206 }
6207 memset(ret, 0, sizeof(xmlRelaxNGValidCtxt));
6208 ret->schema = schema;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006209 ret->error = xmlGenericError;
6210 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006211 return (ret);
6212}
6213
6214/**
6215 * xmlRelaxNGFreeValidCtxt:
6216 * @ctxt: the schema validation context
6217 *
6218 * Free the resources associated to the schema validation context
6219 */
6220void
6221xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt) {
6222 if (ctxt == NULL)
6223 return;
6224 xmlFree(ctxt);
6225}
6226
6227/**
6228 * xmlRelaxNGSetValidErrors:
6229 * @ctxt: a Relax-NG validation context
6230 * @err: the error function
6231 * @warn: the warning function
6232 * @ctx: the functions context
6233 *
6234 * Set the error and warning callback informations
6235 */
6236void
6237xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
6238 xmlRelaxNGValidityErrorFunc err,
6239 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
6240 if (ctxt == NULL)
6241 return;
6242 ctxt->error = err;
6243 ctxt->warning = warn;
6244 ctxt->userData = ctx;
6245}
6246
6247/**
6248 * xmlRelaxNGValidateDoc:
6249 * @ctxt: a Relax-NG validation context
6250 * @doc: a parsed document tree
6251 *
6252 * Validate a document tree in memory.
6253 *
6254 * Returns 0 if the document is valid, a positive error code
6255 * number otherwise and -1 in case of internal or API error.
6256 */
6257int
6258xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
6259 int ret;
6260
6261 if ((ctxt == NULL) || (doc == NULL))
6262 return(-1);
6263
6264 ctxt->doc = doc;
6265
6266 ret = xmlRelaxNGValidateDocument(ctxt, doc);
Daniel Veillard71531f32003-02-05 13:19:53 +00006267 /*
6268 * TODO: build error codes
6269 */
6270 if (ret == -1)
6271 return(1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006272 return(ret);
6273}
6274
6275#endif /* LIBXML_SCHEMAS_ENABLED */
6276