blob: ac8194ca093bcbb7654c1ba2baa33032728e0455 [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;
Daniel Veillard2df2de22003-02-17 23:34:33 +00002789
2790 if ((def->content != NULL) && (def->content->next != NULL)) {
2791 tmp = xmlRelaxNGNewDefine(ctxt, node);
2792 if (tmp != NULL) {
2793 tmp->type = XML_RELAXNG_GROUP;
2794 tmp->content = def->content;
2795 def->content = tmp;
2796 }
2797 }
2798
Daniel Veillard416589a2003-02-17 17:25:42 +00002799 tmp = xmlRelaxNGNewDefine(ctxt, node);
2800 if (tmp == NULL)
2801 return(def);
2802 tmp->type = XML_RELAXNG_TEXT;
2803 tmp->next = def->content;
2804 def->content = tmp;
2805 }
2806 }
Daniel Veillardd2298792003-02-14 16:54:11 +00002807 } else {
2808 if (ctxt->error != NULL)
2809 ctxt->error(ctxt->userData,
2810 "Unexpected node %s is not a pattern\n",
2811 node->name);
2812 ctxt->nbErrors++;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002813 def = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002814 }
2815 return(def);
2816}
2817
2818/**
2819 * xmlRelaxNGParseAttribute:
2820 * @ctxt: a Relax-NG parser context
2821 * @node: the element node
2822 *
2823 * parse the content of a RelaxNG attribute node.
2824 *
2825 * Returns the definition pointer or NULL in case of error.
2826 */
2827static xmlRelaxNGDefinePtr
2828xmlRelaxNGParseAttribute(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
Daniel Veillardd2298792003-02-14 16:54:11 +00002829 xmlRelaxNGDefinePtr ret, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002830 xmlNodePtr child;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002831 int old_flags;
2832
2833 ret = xmlRelaxNGNewDefine(ctxt, node);
2834 if (ret == NULL)
2835 return(NULL);
2836 ret->type = XML_RELAXNG_ATTRIBUTE;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002837 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002838 child = node->children;
2839 if (child == NULL) {
2840 if (ctxt->error != NULL)
2841 ctxt->error(ctxt->userData,
2842 "xmlRelaxNGParseattribute: attribute has no children\n");
2843 ctxt->nbErrors++;
2844 return(ret);
2845 }
2846 old_flags = ctxt->flags;
2847 ctxt->flags |= XML_RELAXNG_IN_ATTRIBUTE;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002848 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
2849 if (cur != NULL)
2850 child = child->next;
2851
Daniel Veillardd2298792003-02-14 16:54:11 +00002852 if (child != NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00002853 cur = xmlRelaxNGParsePattern(ctxt, child);
2854 if (cur != NULL) {
2855 switch (cur->type) {
2856 case XML_RELAXNG_EMPTY:
2857 case XML_RELAXNG_NOT_ALLOWED:
2858 case XML_RELAXNG_TEXT:
2859 case XML_RELAXNG_ELEMENT:
2860 case XML_RELAXNG_DATATYPE:
2861 case XML_RELAXNG_VALUE:
2862 case XML_RELAXNG_LIST:
2863 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00002864 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002865 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00002866 case XML_RELAXNG_DEF:
2867 case XML_RELAXNG_ONEORMORE:
2868 case XML_RELAXNG_ZEROORMORE:
2869 case XML_RELAXNG_OPTIONAL:
2870 case XML_RELAXNG_CHOICE:
2871 case XML_RELAXNG_GROUP:
Daniel Veillard416589a2003-02-17 17:25:42 +00002872#if 0
2873 case XML_RELAXNG_MIXED:
2874#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00002875 case XML_RELAXNG_INTERLEAVE:
Daniel Veillardd2298792003-02-14 16:54:11 +00002876 ret->content = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002877 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002878 break;
2879 case XML_RELAXNG_ATTRIBUTE:
Daniel Veillardd2298792003-02-14 16:54:11 +00002880 if (ctxt->error != NULL)
2881 ctxt->error(ctxt->userData,
2882 "attribute has an attribute child\n");
2883 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002884 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002885 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00002886 case XML_RELAXNG_EXCEPT:
Daniel Veillardd2298792003-02-14 16:54:11 +00002887 if (ctxt->error != NULL)
2888 ctxt->error(ctxt->userData,
2889 "attribute has invalid content\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002890 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002891 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002892 }
2893 }
2894 child = child->next;
2895 }
Daniel Veillardd2298792003-02-14 16:54:11 +00002896 if (child != NULL) {
2897 if (ctxt->error != NULL)
2898 ctxt->error(ctxt->userData, "attribute has multiple children\n");
2899 ctxt->nbErrors++;
2900 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002901 ctxt->flags = old_flags;
2902 return(ret);
2903}
2904
2905/**
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002906 * xmlRelaxNGParseExceptNameClass:
2907 * @ctxt: a Relax-NG parser context
2908 * @node: the except node
Daniel Veillard144fae12003-02-03 13:17:57 +00002909 * @attr: 1 if within an attribute, 0 if within an element
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002910 *
2911 * parse the content of a RelaxNG nameClass node.
2912 *
2913 * Returns the definition pointer or NULL in case of error.
2914 */
2915static xmlRelaxNGDefinePtr
Daniel Veillard144fae12003-02-03 13:17:57 +00002916xmlRelaxNGParseExceptNameClass(xmlRelaxNGParserCtxtPtr ctxt,
2917 xmlNodePtr node, int attr) {
2918 xmlRelaxNGDefinePtr ret, cur, last = NULL;
2919 xmlNodePtr child;
2920
Daniel Veillardd2298792003-02-14 16:54:11 +00002921 if (!IS_RELAXNG(node, "except")) {
2922 if (ctxt->error != NULL)
2923 ctxt->error(ctxt->userData,
2924 "Expecting an except node\n");
2925 ctxt->nbErrors++;
Daniel Veillard144fae12003-02-03 13:17:57 +00002926 return(NULL);
Daniel Veillardd2298792003-02-14 16:54:11 +00002927 }
2928 if (node->next != NULL) {
2929 if (ctxt->error != NULL)
2930 ctxt->error(ctxt->userData,
2931 "exceptNameClass allows only a single except node\n");
2932 ctxt->nbErrors++;
2933 }
Daniel Veillard144fae12003-02-03 13:17:57 +00002934 if (node->children == NULL) {
2935 if (ctxt->error != NULL)
2936 ctxt->error(ctxt->userData,
2937 "except has no content\n");
2938 ctxt->nbErrors++;
2939 return(NULL);
2940 }
2941
2942 ret = xmlRelaxNGNewDefine(ctxt, node);
2943 if (ret == NULL)
2944 return(NULL);
2945 ret->type = XML_RELAXNG_EXCEPT;
2946 child = node->children;
2947 while (child != NULL) {
2948 cur = xmlRelaxNGNewDefine(ctxt, child);
2949 if (cur == NULL)
2950 break;
2951 if (attr)
2952 cur->type = XML_RELAXNG_ATTRIBUTE;
2953 else
2954 cur->type = XML_RELAXNG_ELEMENT;
2955
Daniel Veillard419a7682003-02-03 23:22:49 +00002956 if (xmlRelaxNGParseNameClass(ctxt, child, cur) != NULL) {
Daniel Veillard144fae12003-02-03 13:17:57 +00002957 if (last == NULL) {
2958 ret->content = cur;
2959 } else {
2960 last->next = cur;
2961 }
2962 last = cur;
2963 }
2964 child = child->next;
2965 }
2966
2967 return(ret);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002968}
2969
2970/**
2971 * xmlRelaxNGParseNameClass:
2972 * @ctxt: a Relax-NG parser context
2973 * @node: the nameClass node
2974 * @def: the current definition
2975 *
2976 * parse the content of a RelaxNG nameClass node.
2977 *
2978 * Returns the definition pointer or NULL in case of error.
2979 */
2980static xmlRelaxNGDefinePtr
2981xmlRelaxNGParseNameClass(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
2982 xmlRelaxNGDefinePtr def) {
2983 xmlRelaxNGDefinePtr ret = def;
2984 xmlChar *val;
2985
2986 if (IS_RELAXNG(node, "name")) {
2987 val = xmlNodeGetContent(node);
Daniel Veillardd2298792003-02-14 16:54:11 +00002988 xmlRelaxNGNormExtSpace(val);
2989 if (xmlValidateNCName(val, 0)) {
2990 if (ctxt->error != NULL) {
2991 if (node->parent != NULL)
2992 ctxt->error(ctxt->userData,
2993 "Element %s name '%s' is not an NCName\n",
2994 node->parent->name, val);
2995 else
2996 ctxt->error(ctxt->userData,
2997 "name '%s' is not an NCName\n",
2998 val);
2999 }
3000 ctxt->nbErrors++;
3001 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003002 ret->name = val;
3003 val = xmlGetProp(node, BAD_CAST "ns");
3004 ret->ns = val;
Daniel Veillard416589a2003-02-17 17:25:42 +00003005 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3006 (val != NULL) &&
3007 (xmlStrEqual(val, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
3008 ctxt->error(ctxt->userData,
3009 "Attribute with namespace '%s' is not allowed\n",
3010 val);
3011 ctxt->nbErrors++;
3012 }
3013 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3014 (val != NULL) &&
3015 (val[0] == 0) &&
3016 (xmlStrEqual(ret->name, BAD_CAST "xmlns"))) {
3017 ctxt->error(ctxt->userData,
3018 "Attribute with QName 'xmlns' is not allowed\n",
3019 val);
3020 ctxt->nbErrors++;
3021 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003022 } else if (IS_RELAXNG(node, "anyName")) {
3023 ret->name = NULL;
3024 ret->ns = NULL;
3025 if (node->children != NULL) {
3026 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00003027 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
3028 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003029 }
3030 } else if (IS_RELAXNG(node, "nsName")) {
3031 ret->name = NULL;
3032 ret->ns = xmlGetProp(node, BAD_CAST "ns");
3033 if (ret->ns == NULL) {
3034 if (ctxt->error != NULL)
3035 ctxt->error(ctxt->userData,
3036 "nsName has no ns attribute\n");
3037 ctxt->nbErrors++;
3038 }
Daniel Veillard416589a2003-02-17 17:25:42 +00003039 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3040 (ret->ns != NULL) &&
3041 (xmlStrEqual(ret->ns, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
3042 ctxt->error(ctxt->userData,
3043 "Attribute with namespace '%s' is not allowed\n",
3044 ret->ns);
3045 ctxt->nbErrors++;
3046 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003047 if (node->children != NULL) {
3048 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00003049 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
3050 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003051 }
3052 } else if (IS_RELAXNG(node, "choice")) {
Daniel Veillardd2298792003-02-14 16:54:11 +00003053 if (node->children == NULL) {
3054 if (ctxt->error != NULL)
3055 ctxt->error(ctxt->userData,
3056 "Element choice is empty\n");
3057 ctxt->nbErrors++;
3058 } else {
3059 xmlNodePtr child;
3060 xmlRelaxNGDefinePtr last = NULL, tmp;
3061
3062 child = node->children;
3063 while (child != NULL) {
Daniel Veillard416589a2003-02-17 17:25:42 +00003064 tmp = xmlRelaxNGParseNameClass(ctxt, child, def);
Daniel Veillardd2298792003-02-14 16:54:11 +00003065 if (tmp != NULL) {
3066 if (last == NULL) {
3067 last = ret->nameClass = tmp;
3068 } else {
3069 last->next = tmp;
3070 last = tmp;
3071 }
3072 }
3073 child = child->next;
3074 }
3075 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003076 } else {
3077 if (ctxt->error != NULL)
3078 ctxt->error(ctxt->userData,
3079 "expecting name, anyName, nsName or choice : got %s\n",
3080 node->name);
3081 ctxt->nbErrors++;
3082 return(NULL);
3083 }
3084 return(ret);
3085}
3086
3087/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00003088 * xmlRelaxNGParseElement:
3089 * @ctxt: a Relax-NG parser context
3090 * @node: the element node
3091 *
3092 * parse the content of a RelaxNG element node.
3093 *
3094 * Returns the definition pointer or NULL in case of error.
3095 */
3096static xmlRelaxNGDefinePtr
3097xmlRelaxNGParseElement(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3098 xmlRelaxNGDefinePtr ret, cur, last;
3099 xmlNodePtr child;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003100 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003101
3102 ret = xmlRelaxNGNewDefine(ctxt, node);
3103 if (ret == NULL)
3104 return(NULL);
3105 ret->type = XML_RELAXNG_ELEMENT;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003106 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003107 child = node->children;
3108 if (child == NULL) {
3109 if (ctxt->error != NULL)
3110 ctxt->error(ctxt->userData,
3111 "xmlRelaxNGParseElement: element has no children\n");
3112 ctxt->nbErrors++;
3113 return(ret);
3114 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003115 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
3116 if (cur != NULL)
3117 child = child->next;
3118
Daniel Veillard6eadf632003-01-23 18:29:16 +00003119 if (child == NULL) {
3120 if (ctxt->error != NULL)
3121 ctxt->error(ctxt->userData,
3122 "xmlRelaxNGParseElement: element has no content\n");
3123 ctxt->nbErrors++;
3124 return(ret);
3125 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003126 olddefine = ctxt->define;
3127 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003128 last = NULL;
3129 while (child != NULL) {
3130 cur = xmlRelaxNGParsePattern(ctxt, child);
3131 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003132 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003133 switch (cur->type) {
3134 case XML_RELAXNG_EMPTY:
3135 case XML_RELAXNG_NOT_ALLOWED:
3136 case XML_RELAXNG_TEXT:
3137 case XML_RELAXNG_ELEMENT:
3138 case XML_RELAXNG_DATATYPE:
3139 case XML_RELAXNG_VALUE:
3140 case XML_RELAXNG_LIST:
3141 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00003142 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003143 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00003144 case XML_RELAXNG_DEF:
3145 case XML_RELAXNG_ZEROORMORE:
3146 case XML_RELAXNG_ONEORMORE:
3147 case XML_RELAXNG_OPTIONAL:
3148 case XML_RELAXNG_CHOICE:
3149 case XML_RELAXNG_GROUP:
Daniel Veillard416589a2003-02-17 17:25:42 +00003150#if 0
3151 case XML_RELAXNG_MIXED:
3152#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00003153 case XML_RELAXNG_INTERLEAVE:
3154 if (last == NULL) {
3155 ret->content = last = cur;
3156 } else {
3157 if ((last->type == XML_RELAXNG_ELEMENT) &&
3158 (ret->content == last)) {
3159 ret->content = xmlRelaxNGNewDefine(ctxt, node);
3160 if (ret->content != NULL) {
3161 ret->content->type = XML_RELAXNG_GROUP;
3162 ret->content->content = last;
3163 } else {
3164 ret->content = last;
3165 }
3166 }
3167 last->next = cur;
3168 last = cur;
3169 }
3170 break;
3171 case XML_RELAXNG_ATTRIBUTE:
3172 cur->next = ret->attrs;
3173 ret->attrs = cur;
3174 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003175 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00003176 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003177 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003178 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003179 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003180 }
3181 }
3182 child = child->next;
3183 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003184 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003185 return(ret);
3186}
3187
3188/**
3189 * xmlRelaxNGParsePatterns:
3190 * @ctxt: a Relax-NG parser context
3191 * @nodes: list of nodes
Daniel Veillard154877e2003-01-30 12:17:05 +00003192 * @group: use an implicit <group> for elements
Daniel Veillard6eadf632003-01-23 18:29:16 +00003193 *
3194 * parse the content of a RelaxNG start node.
3195 *
3196 * Returns the definition pointer or NULL in case of error.
3197 */
3198static xmlRelaxNGDefinePtr
Daniel Veillard154877e2003-01-30 12:17:05 +00003199xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes,
3200 int group) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003201 xmlRelaxNGDefinePtr def = NULL, last = NULL, cur, parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003202
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003203 parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003204 while (nodes != NULL) {
3205 if (IS_RELAXNG(nodes, "element")) {
3206 cur = xmlRelaxNGParseElement(ctxt, nodes);
3207 if (def == NULL) {
3208 def = last = cur;
3209 } else {
Daniel Veillard154877e2003-01-30 12:17:05 +00003210 if ((group == 1) && (def->type == XML_RELAXNG_ELEMENT) &&
3211 (def == last)) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003212 def = xmlRelaxNGNewDefine(ctxt, nodes);
3213 def->type = XML_RELAXNG_GROUP;
3214 def->content = last;
3215 }
3216 last->next = cur;
3217 last = cur;
3218 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003219 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003220 } else {
3221 cur = xmlRelaxNGParsePattern(ctxt, nodes);
Daniel Veillard419a7682003-02-03 23:22:49 +00003222 if (cur != NULL) {
3223 if (def == NULL) {
3224 def = last = cur;
3225 } else {
3226 last->next = cur;
3227 last = cur;
3228 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003229 }
3230 }
3231 nodes = nodes->next;
3232 }
3233 return(def);
3234}
3235
3236/**
3237 * xmlRelaxNGParseStart:
3238 * @ctxt: a Relax-NG parser context
3239 * @nodes: start children nodes
3240 *
3241 * parse the content of a RelaxNG start node.
3242 *
3243 * Returns 0 in case of success, -1 in case of error
3244 */
3245static int
3246xmlRelaxNGParseStart(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
3247 int ret = 0;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003248 xmlRelaxNGDefinePtr def = NULL, last;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003249
Daniel Veillardd2298792003-02-14 16:54:11 +00003250 if (nodes == NULL) {
3251 if (ctxt->error != NULL)
3252 ctxt->error(ctxt->userData,
3253 "start has no children\n");
3254 ctxt->nbErrors++;
3255 return(-1);
3256 }
3257 if (IS_RELAXNG(nodes, "empty")) {
3258 def = xmlRelaxNGNewDefine(ctxt, nodes);
3259 if (def == NULL)
3260 return(-1);
3261 def->type = XML_RELAXNG_EMPTY;
3262 if (nodes->children != NULL) {
3263 if (ctxt->error != NULL)
3264 ctxt->error(ctxt->userData, "element empty is not empty\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003265 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003266 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003267 } else if (IS_RELAXNG(nodes, "notAllowed")) {
3268 def = xmlRelaxNGNewDefine(ctxt, nodes);
3269 if (def == NULL)
3270 return(-1);
3271 def->type = XML_RELAXNG_NOT_ALLOWED;
3272 if (nodes->children != NULL) {
3273 if (ctxt->error != NULL)
3274 ctxt->error(ctxt->userData,
3275 "element notAllowed is not empty\n");
3276 ctxt->nbErrors++;
3277 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003278 } else {
3279 def = xmlRelaxNGParsePatterns(ctxt, nodes, 1);
Daniel Veillard2df2de22003-02-17 23:34:33 +00003280 }
3281 if (ctxt->grammar->start != NULL) {
3282 last = ctxt->grammar->start;
3283 while (last->next != NULL)
3284 last = last->next;
3285 last->next = def;
3286 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00003287 ctxt->grammar->start = def;
3288 }
3289 nodes = nodes->next;
3290 if (nodes != NULL) {
3291 if (ctxt->error != NULL)
3292 ctxt->error(ctxt->userData,
3293 "start more than one children\n");
3294 ctxt->nbErrors++;
3295 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003296 }
3297 return(ret);
3298}
3299
3300/**
3301 * xmlRelaxNGParseGrammarContent:
3302 * @ctxt: a Relax-NG parser context
3303 * @nodes: grammar children nodes
3304 *
3305 * parse the content of a RelaxNG grammar node.
3306 *
3307 * Returns 0 in case of success, -1 in case of error
3308 */
3309static int
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003310xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003311{
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003312 int ret = 0, tmp;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003313
3314 if (nodes == NULL) {
3315 if (ctxt->error != NULL)
3316 ctxt->error(ctxt->userData,
3317 "grammar has no children\n");
3318 ctxt->nbErrors++;
3319 return(-1);
3320 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003321 while (nodes != NULL) {
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003322 if (IS_RELAXNG(nodes, "start")) {
3323 if (nodes->children == NULL) {
3324 if (ctxt->error != NULL)
3325 ctxt->error(ctxt->userData,
Daniel Veillardd2298792003-02-14 16:54:11 +00003326 "start has no children\n");
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003327 ctxt->nbErrors++;
3328 } else {
3329 tmp = xmlRelaxNGParseStart(ctxt, nodes->children);
3330 if (tmp != 0)
3331 ret = -1;
3332 }
3333 } else if (IS_RELAXNG(nodes, "define")) {
3334 tmp = xmlRelaxNGParseDefine(ctxt, nodes);
3335 if (tmp != 0)
3336 ret = -1;
3337 } else if (IS_RELAXNG(nodes, "include")) {
3338 tmp = xmlRelaxNGParseInclude(ctxt, nodes);
3339 if (tmp != 0)
3340 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003341 } else {
3342 if (ctxt->error != NULL)
3343 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003344 "grammar has unexpected child %s\n", nodes->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003345 ctxt->nbErrors++;
3346 ret = -1;
3347 }
3348 nodes = nodes->next;
3349 }
3350 return (ret);
3351}
3352
3353/**
3354 * xmlRelaxNGCheckReference:
3355 * @ref: the ref
3356 * @ctxt: a Relax-NG parser context
3357 * @name: the name associated to the defines
3358 *
3359 * Applies the 4.17. combine attribute rule for all the define
3360 * element of a given grammar using the same name.
3361 */
3362static void
3363xmlRelaxNGCheckReference(xmlRelaxNGDefinePtr ref,
3364 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
3365 xmlRelaxNGGrammarPtr grammar;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003366 xmlRelaxNGDefinePtr def, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003367
3368 grammar = ctxt->grammar;
3369 if (grammar == NULL) {
3370 if (ctxt->error != NULL)
3371 ctxt->error(ctxt->userData,
3372 "Internal error: no grammar in CheckReference %s\n",
3373 name);
3374 ctxt->nbErrors++;
3375 return;
3376 }
3377 if (ref->content != NULL) {
3378 if (ctxt->error != NULL)
3379 ctxt->error(ctxt->userData,
3380 "Internal error: reference has content in CheckReference %s\n",
3381 name);
3382 ctxt->nbErrors++;
3383 return;
3384 }
3385 if (grammar->defs != NULL) {
3386 def = xmlHashLookup(grammar->defs, name);
3387 if (def != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00003388 cur = ref;
3389 while (cur != NULL) {
3390 cur->content = def;
3391 cur = cur->nextHash;
3392 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003393 } else {
3394 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003395 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003396 }
3397 }
3398 /*
3399 * TODO: make a closure and verify there is no loop !
3400 */
3401}
3402
3403/**
3404 * xmlRelaxNGCheckCombine:
3405 * @define: the define(s) list
3406 * @ctxt: a Relax-NG parser context
3407 * @name: the name associated to the defines
3408 *
3409 * Applies the 4.17. combine attribute rule for all the define
3410 * element of a given grammar using the same name.
3411 */
3412static void
3413xmlRelaxNGCheckCombine(xmlRelaxNGDefinePtr define,
3414 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
3415 xmlChar *combine;
3416 int choiceOrInterleave = -1;
3417 int missing = 0;
3418 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
3419
3420 if (define->nextHash == NULL)
3421 return;
3422 cur = define;
3423 while (cur != NULL) {
3424 combine = xmlGetProp(cur->node, BAD_CAST "combine");
3425 if (combine != NULL) {
3426 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3427 if (choiceOrInterleave == -1)
3428 choiceOrInterleave = 1;
3429 else if (choiceOrInterleave == 0) {
3430 if (ctxt->error != NULL)
3431 ctxt->error(ctxt->userData,
3432 "Defines for %s use both 'choice' and 'interleave'\n",
3433 name);
3434 ctxt->nbErrors++;
3435 }
Daniel Veillard154877e2003-01-30 12:17:05 +00003436 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003437 if (choiceOrInterleave == -1)
3438 choiceOrInterleave = 0;
3439 else if (choiceOrInterleave == 1) {
3440 if (ctxt->error != NULL)
3441 ctxt->error(ctxt->userData,
3442 "Defines for %s use both 'choice' and 'interleave'\n",
3443 name);
3444 ctxt->nbErrors++;
3445 }
3446 } else {
3447 if (ctxt->error != NULL)
3448 ctxt->error(ctxt->userData,
3449 "Defines for %s use unknown combine value '%s''\n",
3450 name, combine);
3451 ctxt->nbErrors++;
3452 }
3453 xmlFree(combine);
3454 } else {
3455 if (missing == 0)
3456 missing = 1;
3457 else {
3458 if (ctxt->error != NULL)
3459 ctxt->error(ctxt->userData,
3460 "Some defines for %s lacks the combine attribute\n",
3461 name);
3462 ctxt->nbErrors++;
3463 }
3464 }
3465
3466 cur = cur->nextHash;
3467 }
3468#ifdef DEBUG
3469 xmlGenericError(xmlGenericErrorContext,
3470 "xmlRelaxNGCheckCombine(): merging %s defines: %d\n",
3471 name, choiceOrInterleave);
3472#endif
3473 if (choiceOrInterleave == -1)
3474 choiceOrInterleave = 0;
3475 cur = xmlRelaxNGNewDefine(ctxt, define->node);
3476 if (cur == NULL)
3477 return;
3478 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003479 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard154877e2003-01-30 12:17:05 +00003480 else
3481 cur->type = XML_RELAXNG_CHOICE;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003482 tmp = define;
3483 last = NULL;
3484 while (tmp != NULL) {
3485 if (tmp->content != NULL) {
3486 if (tmp->content->next != NULL) {
3487 /*
3488 * we need first to create a wrapper.
3489 */
3490 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
3491 if (tmp2 == NULL)
3492 break;
3493 tmp2->type = XML_RELAXNG_GROUP;
3494 tmp2->content = tmp->content;
3495 } else {
3496 tmp2 = tmp->content;
3497 }
3498 if (last == NULL) {
3499 cur->content = tmp2;
3500 } else {
3501 last->next = tmp2;
3502 }
3503 last = tmp2;
3504 tmp->content = NULL;
3505 }
3506 tmp = tmp->nextHash;
3507 }
3508 define->content = cur;
Daniel Veillard154877e2003-01-30 12:17:05 +00003509 if (choiceOrInterleave == 0) {
3510 if (ctxt->interleaves == NULL)
3511 ctxt->interleaves = xmlHashCreate(10);
3512 if (ctxt->interleaves == NULL) {
3513 if (ctxt->error != NULL)
3514 ctxt->error(ctxt->userData,
3515 "Failed to create interleaves hash table\n");
3516 ctxt->nbErrors++;
3517 } else {
3518 char tmpname[32];
3519
3520 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
3521 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
3522 if (ctxt->error != NULL)
3523 ctxt->error(ctxt->userData,
3524 "Failed to add %s to hash table\n", tmpname);
3525 ctxt->nbErrors++;
3526 }
3527 }
3528 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003529}
3530
3531/**
3532 * xmlRelaxNGCombineStart:
3533 * @ctxt: a Relax-NG parser context
3534 * @grammar: the grammar
3535 *
3536 * Applies the 4.17. combine rule for all the start
3537 * element of a given grammar.
3538 */
3539static void
3540xmlRelaxNGCombineStart(xmlRelaxNGParserCtxtPtr ctxt,
3541 xmlRelaxNGGrammarPtr grammar) {
3542 xmlRelaxNGDefinePtr starts;
3543 xmlChar *combine;
3544 int choiceOrInterleave = -1;
3545 int missing = 0;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003546 xmlRelaxNGDefinePtr cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003547
Daniel Veillard2df2de22003-02-17 23:34:33 +00003548 starts = grammar->start;
3549 if ((starts == NULL) || (starts->next == NULL))
Daniel Veillard6eadf632003-01-23 18:29:16 +00003550 return;
3551 cur = starts;
3552 while (cur != NULL) {
Daniel Veillard2df2de22003-02-17 23:34:33 +00003553 if ((cur->node == NULL) || (cur->node->parent == NULL) ||
3554 (!xmlStrEqual(cur->node->parent->name, BAD_CAST "start"))) {
3555 combine = NULL;
3556 if (ctxt->error != NULL)
3557 ctxt->error(ctxt->userData,
3558 "Internal error: start element not found\n");
3559 ctxt->nbErrors++;
3560 } else {
3561 combine = xmlGetProp(cur->node->parent, BAD_CAST "combine");
3562 }
3563
Daniel Veillard6eadf632003-01-23 18:29:16 +00003564 if (combine != NULL) {
3565 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3566 if (choiceOrInterleave == -1)
3567 choiceOrInterleave = 1;
3568 else if (choiceOrInterleave == 0) {
3569 if (ctxt->error != NULL)
3570 ctxt->error(ctxt->userData,
3571 "<start> use both 'choice' and 'interleave'\n");
3572 ctxt->nbErrors++;
3573 }
Daniel Veillard2df2de22003-02-17 23:34:33 +00003574 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003575 if (choiceOrInterleave == -1)
3576 choiceOrInterleave = 0;
3577 else if (choiceOrInterleave == 1) {
3578 if (ctxt->error != NULL)
3579 ctxt->error(ctxt->userData,
3580 "<start> use both 'choice' and 'interleave'\n");
3581 ctxt->nbErrors++;
3582 }
3583 } else {
3584 if (ctxt->error != NULL)
3585 ctxt->error(ctxt->userData,
3586 "<start> uses unknown combine value '%s''\n", combine);
3587 ctxt->nbErrors++;
3588 }
3589 xmlFree(combine);
3590 } else {
3591 if (missing == 0)
3592 missing = 1;
3593 else {
3594 if (ctxt->error != NULL)
3595 ctxt->error(ctxt->userData,
3596 "Some <start> elements lacks the combine attribute\n");
3597 ctxt->nbErrors++;
3598 }
3599 }
3600
Daniel Veillard2df2de22003-02-17 23:34:33 +00003601 cur = cur->next;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003602 }
3603#ifdef DEBUG
3604 xmlGenericError(xmlGenericErrorContext,
3605 "xmlRelaxNGCombineStart(): merging <start>: %d\n",
3606 choiceOrInterleave);
3607#endif
3608 if (choiceOrInterleave == -1)
3609 choiceOrInterleave = 0;
3610 cur = xmlRelaxNGNewDefine(ctxt, starts->node);
3611 if (cur == NULL)
3612 return;
3613 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003614 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003615 else
3616 cur->type = XML_RELAXNG_CHOICE;
3617 cur->content = grammar->start;
3618 grammar->start = cur;
3619 if (choiceOrInterleave == 0) {
3620 if (ctxt->interleaves == NULL)
3621 ctxt->interleaves = xmlHashCreate(10);
3622 if (ctxt->interleaves == NULL) {
3623 if (ctxt->error != NULL)
3624 ctxt->error(ctxt->userData,
3625 "Failed to create interleaves hash table\n");
3626 ctxt->nbErrors++;
3627 } else {
3628 char tmpname[32];
3629
3630 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
3631 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
3632 if (ctxt->error != NULL)
3633 ctxt->error(ctxt->userData,
3634 "Failed to add %s to hash table\n", tmpname);
3635 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003636 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003637 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003638 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003639}
3640
3641/**
3642 * xmlRelaxNGParseGrammar:
3643 * @ctxt: a Relax-NG parser context
3644 * @nodes: grammar children nodes
3645 *
3646 * parse a Relax-NG <grammar> node
3647 *
3648 * Returns the internal xmlRelaxNGGrammarPtr built or
3649 * NULL in case of error
3650 */
3651static xmlRelaxNGGrammarPtr
3652xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
3653 xmlRelaxNGGrammarPtr ret, tmp, old;
3654
Daniel Veillard6eadf632003-01-23 18:29:16 +00003655 ret = xmlRelaxNGNewGrammar(ctxt);
3656 if (ret == NULL)
3657 return(NULL);
3658
3659 /*
3660 * Link the new grammar in the tree
3661 */
3662 ret->parent = ctxt->grammar;
3663 if (ctxt->grammar != NULL) {
3664 tmp = ctxt->grammar->children;
3665 if (tmp == NULL) {
3666 ctxt->grammar->children = ret;
3667 } else {
3668 while (tmp->next != NULL)
3669 tmp = tmp->next;
3670 tmp->next = ret;
3671 }
3672 }
3673
3674 old = ctxt->grammar;
3675 ctxt->grammar = ret;
3676 xmlRelaxNGParseGrammarContent(ctxt, nodes);
3677 ctxt->grammar = ret;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003678 if (ctxt->grammar == NULL) {
3679 if (ctxt->error != NULL)
3680 ctxt->error(ctxt->userData,
3681 "Failed to parse <grammar> content\n");
3682 ctxt->nbErrors++;
3683 } else if (ctxt->grammar->start == NULL) {
3684 if (ctxt->error != NULL)
3685 ctxt->error(ctxt->userData,
3686 "Element <grammar> has no <start>\n");
3687 ctxt->nbErrors++;
3688 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003689
3690 /*
3691 * Apply 4.17 mergingd rules to defines and starts
3692 */
3693 xmlRelaxNGCombineStart(ctxt, ret);
3694 if (ret->defs != NULL) {
3695 xmlHashScan(ret->defs, (xmlHashScanner) xmlRelaxNGCheckCombine,
3696 ctxt);
3697 }
3698
3699 /*
3700 * link together defines and refs in this grammar
3701 */
3702 if (ret->refs != NULL) {
3703 xmlHashScan(ret->refs, (xmlHashScanner) xmlRelaxNGCheckReference,
3704 ctxt);
3705 }
3706 ctxt->grammar = old;
3707 return(ret);
3708}
3709
3710/**
3711 * xmlRelaxNGParseDocument:
3712 * @ctxt: a Relax-NG parser context
3713 * @node: the root node of the RelaxNG schema
3714 *
3715 * parse a Relax-NG definition resource and build an internal
3716 * xmlRelaxNG struture which can be used to validate instances.
3717 *
3718 * Returns the internal XML RelaxNG structure built or
3719 * NULL in case of error
3720 */
3721static xmlRelaxNGPtr
3722xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3723 xmlRelaxNGPtr schema = NULL;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003724 const xmlChar *olddefine;
Daniel Veillarde431a272003-01-29 23:02:33 +00003725 xmlRelaxNGGrammarPtr old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003726
3727 if ((ctxt == NULL) || (node == NULL))
3728 return (NULL);
3729
3730 schema = xmlRelaxNGNewRelaxNG(ctxt);
3731 if (schema == NULL)
3732 return(NULL);
3733
Daniel Veillard276be4a2003-01-24 01:03:34 +00003734 olddefine = ctxt->define;
3735 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003736 if (IS_RELAXNG(node, "grammar")) {
3737 schema->topgrammar = xmlRelaxNGParseGrammar(ctxt, node->children);
3738 } else {
3739 schema->topgrammar = xmlRelaxNGNewGrammar(ctxt);
3740 if (schema->topgrammar == NULL) {
3741 return(schema);
3742 }
3743 schema->topgrammar->parent = NULL;
Daniel Veillarde431a272003-01-29 23:02:33 +00003744 old = ctxt->grammar;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003745 ctxt->grammar = schema->topgrammar;
3746 xmlRelaxNGParseStart(ctxt, node);
Daniel Veillarde431a272003-01-29 23:02:33 +00003747 if (old != NULL)
3748 ctxt->grammar = old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003749 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003750 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003751
3752#ifdef DEBUG
3753 if (schema == NULL)
3754 xmlGenericError(xmlGenericErrorContext,
3755 "xmlRelaxNGParseDocument() failed\n");
3756#endif
3757
3758 return (schema);
3759}
3760
3761/************************************************************************
3762 * *
3763 * Reading RelaxNGs *
3764 * *
3765 ************************************************************************/
3766
3767/**
3768 * xmlRelaxNGNewParserCtxt:
3769 * @URL: the location of the schema
3770 *
3771 * Create an XML RelaxNGs parse context for that file/resource expected
3772 * to contain an XML RelaxNGs file.
3773 *
3774 * Returns the parser context or NULL in case of error
3775 */
3776xmlRelaxNGParserCtxtPtr
3777xmlRelaxNGNewParserCtxt(const char *URL) {
3778 xmlRelaxNGParserCtxtPtr ret;
3779
3780 if (URL == NULL)
3781 return(NULL);
3782
3783 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
3784 if (ret == NULL) {
3785 xmlGenericError(xmlGenericErrorContext,
3786 "Failed to allocate new schama parser context for %s\n", URL);
3787 return (NULL);
3788 }
3789 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
3790 ret->URL = xmlStrdup((const xmlChar *)URL);
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 * xmlRelaxNGNewMemParserCtxt:
3798 * @buffer: a pointer to a char array containing the schemas
3799 * @size: the size of the array
3800 *
3801 * Create an XML RelaxNGs parse context for that memory buffer expected
3802 * to contain an XML RelaxNGs file.
3803 *
3804 * Returns the parser context or NULL in case of error
3805 */
3806xmlRelaxNGParserCtxtPtr
3807xmlRelaxNGNewMemParserCtxt(const char *buffer, int size) {
3808 xmlRelaxNGParserCtxtPtr ret;
3809
3810 if ((buffer == NULL) || (size <= 0))
3811 return(NULL);
3812
3813 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
3814 if (ret == NULL) {
3815 xmlGenericError(xmlGenericErrorContext,
3816 "Failed to allocate new schama parser context\n");
3817 return (NULL);
3818 }
3819 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
3820 ret->buffer = buffer;
3821 ret->size = size;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003822 ret->error = xmlGenericError;
3823 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003824 return (ret);
3825}
3826
3827/**
3828 * xmlRelaxNGFreeParserCtxt:
3829 * @ctxt: the schema parser context
3830 *
3831 * Free the resources associated to the schema parser context
3832 */
3833void
3834xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxtPtr ctxt) {
3835 if (ctxt == NULL)
3836 return;
3837 if (ctxt->URL != NULL)
3838 xmlFree(ctxt->URL);
3839 if (ctxt->doc != NULL)
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003840 xmlFreeDoc(ctxt->document);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003841 if (ctxt->interleaves != NULL)
3842 xmlHashFree(ctxt->interleaves, NULL);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003843 if (ctxt->documents != NULL)
3844 xmlHashFree(ctxt->documents, (xmlHashDeallocator)
3845 xmlRelaxNGFreeDocument);
3846 if (ctxt->docTab != NULL)
3847 xmlFree(ctxt->docTab);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003848 if (ctxt->incTab != NULL)
3849 xmlFree(ctxt->incTab);
Daniel Veillard419a7682003-02-03 23:22:49 +00003850 if (ctxt->defTab != NULL) {
3851 int i;
3852
3853 for (i = 0;i < ctxt->defNr;i++)
3854 xmlRelaxNGFreeDefine(ctxt->defTab[i]);
3855 xmlFree(ctxt->defTab);
3856 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003857 xmlFree(ctxt);
3858}
3859
Daniel Veillard6eadf632003-01-23 18:29:16 +00003860/**
Daniel Veillardd2298792003-02-14 16:54:11 +00003861 * xmlRelaxNGNormExtSpace:
3862 * @value: a value
3863 *
3864 * Removes the leading and ending spaces of the value
3865 * The string is modified "in situ"
3866 */
3867static void
3868xmlRelaxNGNormExtSpace(xmlChar *value) {
3869 xmlChar *start = value;
3870 xmlChar *cur = value;
3871 if (value == NULL)
3872 return;
3873
3874 while (IS_BLANK(*cur)) cur++;
3875 if (cur == start) {
3876 do {
3877 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
3878 if (*cur == 0)
3879 return;
3880 start = cur;
3881 while (IS_BLANK(*cur)) cur++;
3882 if (*cur == 0) {
3883 *start = 0;
3884 return;
3885 }
3886 } while (1);
3887 } else {
3888 do {
3889 while ((*cur != 0) && (!IS_BLANK(*cur)))
3890 *start++ = *cur++;
3891 if (*cur == 0) {
3892 *start = 0;
3893 return;
3894 }
3895 /* don't try to normalize the inner spaces */
3896 while (IS_BLANK(*cur)) cur++;
3897 *start++ = *cur++;
3898 if (*cur == 0) {
3899 *start = 0;
3900 return;
3901 }
3902 } while (1);
3903 }
3904}
3905
3906/**
3907 * xmlRelaxNGCheckAttributes:
3908 * @ctxt: a Relax-NG parser context
3909 * @node: a Relax-NG node
3910 *
3911 * Check all the attributes on the given node
3912 */
3913static void
3914xmlRelaxNGCleanupAttributes(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3915 xmlAttrPtr cur, next;
3916
3917 cur = node->properties;
3918 while (cur != NULL) {
3919 next = cur->next;
3920 if ((cur->ns == NULL) ||
3921 (xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
3922 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
3923 if ((!xmlStrEqual(node->name, BAD_CAST "element")) &&
3924 (!xmlStrEqual(node->name, BAD_CAST "attribute")) &&
3925 (!xmlStrEqual(node->name, BAD_CAST "ref")) &&
3926 (!xmlStrEqual(node->name, BAD_CAST "parentRef")) &&
Daniel Veillard2df2de22003-02-17 23:34:33 +00003927 (!xmlStrEqual(node->name, BAD_CAST "param")) &&
Daniel Veillardd2298792003-02-14 16:54:11 +00003928 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
3929 if (ctxt->error != NULL)
3930 ctxt->error(ctxt->userData,
3931 "Attribute %s is not allowed on %s\n",
3932 cur->name, node->name);
3933 ctxt->nbErrors++;
3934 }
3935 } else if (xmlStrEqual(cur->name, BAD_CAST "type")) {
3936 if ((!xmlStrEqual(node->name, BAD_CAST "value")) &&
3937 (!xmlStrEqual(node->name, BAD_CAST "data"))) {
3938 if (ctxt->error != NULL)
3939 ctxt->error(ctxt->userData,
3940 "Attribute %s is not allowed on %s\n",
3941 cur->name, node->name);
3942 ctxt->nbErrors++;
3943 }
3944 } else if (xmlStrEqual(cur->name, BAD_CAST "href")) {
3945 if ((!xmlStrEqual(node->name, BAD_CAST "externalRef")) &&
3946 (!xmlStrEqual(node->name, BAD_CAST "include"))) {
3947 if (ctxt->error != NULL)
3948 ctxt->error(ctxt->userData,
3949 "Attribute %s is not allowed on %s\n",
3950 cur->name, node->name);
3951 ctxt->nbErrors++;
3952 }
3953 } else if (xmlStrEqual(cur->name, BAD_CAST "combine")) {
3954 if ((!xmlStrEqual(node->name, BAD_CAST "start")) &&
3955 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
3956 if (ctxt->error != NULL)
3957 ctxt->error(ctxt->userData,
3958 "Attribute %s is not allowed on %s\n",
3959 cur->name, node->name);
3960 ctxt->nbErrors++;
3961 }
3962 } else if (xmlStrEqual(cur->name, BAD_CAST "datatypeLibrary")) {
3963 xmlChar *val;
3964 xmlURIPtr uri;
3965
3966 val = xmlNodeListGetString(node->doc, cur->children, 1);
3967 if (val != NULL) {
3968 if (val[0] != 0) {
3969 uri = xmlParseURI((const char *) val);
3970 if (uri == NULL) {
3971 if (ctxt->error != NULL)
3972 ctxt->error(ctxt->userData,
3973 "Attribute %s contains invalid URI %s\n",
3974 cur->name, val);
3975 ctxt->nbErrors++;
3976 } else {
3977 if (uri->scheme == NULL) {
3978 if (ctxt->error != NULL)
3979 ctxt->error(ctxt->userData,
3980 "Attribute %s URI %s is not absolute\n",
3981 cur->name, val);
3982 ctxt->nbErrors++;
3983 }
3984 if (uri->fragment != NULL) {
3985 if (ctxt->error != NULL)
3986 ctxt->error(ctxt->userData,
3987 "Attribute %s URI %s has a fragment ID\n",
3988 cur->name, val);
3989 ctxt->nbErrors++;
3990 }
3991 xmlFreeURI(uri);
3992 }
3993 }
3994 xmlFree(val);
3995 }
3996 } else if (!xmlStrEqual(cur->name, BAD_CAST "ns")) {
3997 if (ctxt->error != NULL)
3998 ctxt->error(ctxt->userData,
3999 "Unknown attribute %s on %s\n",
4000 cur->name, node->name);
4001 ctxt->nbErrors++;
4002 }
4003 }
4004 cur = next;
4005 }
4006}
4007
4008/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004009 * xmlRelaxNGCleanupDoc:
4010 * @ctxt: a Relax-NG parser context
4011 * @doc: an xmldocPtr document pointer
Daniel Veillard6eadf632003-01-23 18:29:16 +00004012 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004013 * Cleanup the document from unwanted nodes for parsing, resolve
4014 * Include and externalRef lookups.
Daniel Veillard6eadf632003-01-23 18:29:16 +00004015 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004016 * Returns the cleaned up document or NULL in case of error
Daniel Veillard6eadf632003-01-23 18:29:16 +00004017 */
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004018static xmlDocPtr
4019xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt, xmlDocPtr doc) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00004020 xmlNodePtr root, cur, delete;
4021
Daniel Veillard6eadf632003-01-23 18:29:16 +00004022 /*
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004023 * Extract the root
Daniel Veillard6eadf632003-01-23 18:29:16 +00004024 */
4025 root = xmlDocGetRootElement(doc);
4026 if (root == NULL) {
4027 if (ctxt->error != NULL)
4028 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
4029 ctxt->URL);
4030 ctxt->nbErrors++;
4031 return (NULL);
4032 }
4033
4034 /*
4035 * Remove all the blank text nodes
4036 */
4037 delete = NULL;
4038 cur = root;
4039 while (cur != NULL) {
4040 if (delete != NULL) {
4041 xmlUnlinkNode(delete);
4042 xmlFreeNode(delete);
4043 delete = NULL;
4044 }
4045 if (cur->type == XML_ELEMENT_NODE) {
4046 /*
4047 * Simplification 4.1. Annotations
4048 */
4049 if ((cur->ns == NULL) ||
4050 (!xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
Daniel Veillardd2298792003-02-14 16:54:11 +00004051 if ((cur->parent != NULL) &&
4052 (cur->parent->type == XML_ELEMENT_NODE) &&
4053 ((xmlStrEqual(cur->parent->name, BAD_CAST "name")) ||
4054 (xmlStrEqual(cur->parent->name, BAD_CAST "value")) ||
4055 (xmlStrEqual(cur->parent->name, BAD_CAST "param")))) {
4056 if (ctxt->error != NULL)
4057 ctxt->error(ctxt->userData,
4058 "element %s doesn't allow foreign elements\n",
4059 cur->parent->name);
4060 ctxt->nbErrors++;
4061 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004062 delete = cur;
4063 goto skip_children;
4064 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00004065 xmlRelaxNGCleanupAttributes(ctxt, cur);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004066 if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004067 xmlChar *href, *ns, *base, *URL;
4068 xmlRelaxNGDocumentPtr docu;
Daniel Veillard416589a2003-02-17 17:25:42 +00004069 xmlNodePtr tmp;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004070
4071 ns = xmlGetProp(cur, BAD_CAST "ns");
Daniel Veillard416589a2003-02-17 17:25:42 +00004072 if (ns == NULL) {
4073 tmp = cur->parent;
4074 while ((tmp != NULL) &&
4075 (tmp->type == XML_ELEMENT_NODE)) {
4076 ns = xmlGetProp(tmp, BAD_CAST "ns");
4077 if (ns != NULL)
4078 break;
4079 tmp = tmp->parent;
4080 }
4081 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004082 href = xmlGetProp(cur, BAD_CAST "href");
4083 if (href == NULL) {
4084 if (ctxt->error != NULL)
4085 ctxt->error(ctxt->userData,
4086 "xmlRelaxNGParse: externalRef has no href attribute\n");
4087 ctxt->nbErrors++;
4088 delete = cur;
4089 goto skip_children;
4090 }
4091 base = xmlNodeGetBase(cur->doc, cur);
4092 URL = xmlBuildURI(href, base);
4093 if (URL == NULL) {
4094 if (ctxt->error != NULL)
4095 ctxt->error(ctxt->userData,
4096 "Failed to compute URL for externalRef %s\n", href);
4097 ctxt->nbErrors++;
4098 if (href != NULL)
4099 xmlFree(href);
4100 if (base != NULL)
4101 xmlFree(base);
4102 delete = cur;
4103 goto skip_children;
4104 }
4105 if (href != NULL)
4106 xmlFree(href);
4107 if (base != NULL)
4108 xmlFree(base);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004109 docu = xmlRelaxNGLoadExternalRef(ctxt, URL, ns);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004110 if (docu == NULL) {
4111 if (ctxt->error != NULL)
4112 ctxt->error(ctxt->userData,
4113 "Failed to load externalRef %s\n", URL);
4114 ctxt->nbErrors++;
4115 xmlFree(URL);
4116 delete = cur;
4117 goto skip_children;
4118 }
4119 xmlFree(URL);
4120 cur->_private = docu;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004121 } else if (xmlStrEqual(cur->name, BAD_CAST "include")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00004122 xmlChar *href, *ns, *base, *URL;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004123 xmlRelaxNGIncludePtr incl;
Daniel Veillard416589a2003-02-17 17:25:42 +00004124 xmlNodePtr tmp;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004125
4126 href = xmlGetProp(cur, BAD_CAST "href");
4127 if (href == NULL) {
4128 if (ctxt->error != NULL)
4129 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004130 "xmlRelaxNGParse: include has no href attribute\n");
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004131 ctxt->nbErrors++;
4132 delete = cur;
4133 goto skip_children;
4134 }
4135 base = xmlNodeGetBase(cur->doc, cur);
4136 URL = xmlBuildURI(href, base);
4137 if (URL == NULL) {
4138 if (ctxt->error != NULL)
4139 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004140 "Failed to compute URL for include %s\n", href);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004141 ctxt->nbErrors++;
4142 if (href != NULL)
4143 xmlFree(href);
4144 if (base != NULL)
4145 xmlFree(base);
4146 delete = cur;
4147 goto skip_children;
4148 }
4149 if (href != NULL)
4150 xmlFree(href);
4151 if (base != NULL)
4152 xmlFree(base);
Daniel Veillard416589a2003-02-17 17:25:42 +00004153 ns = xmlGetProp(cur, BAD_CAST "ns");
4154 if (ns == NULL) {
4155 tmp = cur->parent;
4156 while ((tmp != NULL) &&
4157 (tmp->type == XML_ELEMENT_NODE)) {
4158 ns = xmlGetProp(tmp, BAD_CAST "ns");
4159 if (ns != NULL)
4160 break;
4161 tmp = tmp->parent;
4162 }
4163 }
4164 incl = xmlRelaxNGLoadInclude(ctxt, URL, cur, ns);
4165 if (ns != NULL)
4166 xmlFree(ns);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004167 if (incl == NULL) {
4168 if (ctxt->error != NULL)
4169 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004170 "Failed to load include %s\n", URL);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004171 ctxt->nbErrors++;
4172 xmlFree(URL);
4173 delete = cur;
4174 goto skip_children;
4175 }
4176 xmlFree(URL);
4177 cur->_private = incl;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004178 } else if ((xmlStrEqual(cur->name, BAD_CAST "element")) ||
4179 (xmlStrEqual(cur->name, BAD_CAST "attribute"))) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00004180 xmlChar *name, *ns;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004181 xmlNodePtr text = NULL;
4182
4183 /*
4184 * Simplification 4.8. name attribute of element
4185 * and attribute elements
4186 */
4187 name = xmlGetProp(cur, BAD_CAST "name");
4188 if (name != NULL) {
4189 if (cur->children == NULL) {
4190 text = xmlNewChild(cur, cur->ns, BAD_CAST "name",
4191 name);
4192 } else {
4193 xmlNodePtr node;
4194 node = xmlNewNode(cur->ns, BAD_CAST "name");
4195 if (node != NULL) {
4196 xmlAddPrevSibling(cur->children, node);
4197 text = xmlNewText(name);
4198 xmlAddChild(node, text);
4199 text = node;
4200 }
4201 }
Daniel Veillardfebcca42003-02-16 15:44:18 +00004202 if (text == NULL) {
4203 if (ctxt->error != NULL)
4204 ctxt->error(ctxt->userData,
4205 "Failed to create a name %s element\n", name);
4206 ctxt->nbErrors++;
4207 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004208 xmlUnsetProp(cur, BAD_CAST "name");
4209 xmlFree(name);
Daniel Veillardfebcca42003-02-16 15:44:18 +00004210 ns = xmlGetProp(cur, BAD_CAST "ns");
4211 if (ns != NULL) {
4212 if (text != NULL) {
4213 xmlSetProp(text, BAD_CAST "ns", ns);
4214 /* xmlUnsetProp(cur, BAD_CAST "ns"); */
Daniel Veillard6eadf632003-01-23 18:29:16 +00004215 }
Daniel Veillardfebcca42003-02-16 15:44:18 +00004216 xmlFree(ns);
4217 } else if (xmlStrEqual(cur->name,
4218 BAD_CAST "attribute")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00004219 xmlSetProp(text, BAD_CAST "ns", BAD_CAST "");
4220 }
4221 }
4222 } else if ((xmlStrEqual(cur->name, BAD_CAST "name")) ||
4223 (xmlStrEqual(cur->name, BAD_CAST "nsName")) ||
4224 (xmlStrEqual(cur->name, BAD_CAST "value"))) {
4225 /*
4226 * Simplification 4.8. name attribute of element
4227 * and attribute elements
4228 */
4229 if (xmlHasProp(cur, BAD_CAST "ns") == NULL) {
4230 xmlNodePtr node;
4231 xmlChar *ns = NULL;
4232
4233 node = cur->parent;
4234 while ((node != NULL) &&
4235 (node->type == XML_ELEMENT_NODE)) {
4236 ns = xmlGetProp(node, BAD_CAST "ns");
4237 if (ns != NULL) {
4238 break;
4239 }
4240 node = node->parent;
4241 }
4242 if (ns == NULL) {
4243 xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
4244 } else {
4245 xmlSetProp(cur, BAD_CAST "ns", ns);
4246 xmlFree(ns);
4247 }
4248 }
4249 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
4250 xmlChar *name, *local, *prefix;
4251
4252 /*
4253 * Simplification: 4.10. QNames
4254 */
4255 name = xmlNodeGetContent(cur);
4256 if (name != NULL) {
4257 local = xmlSplitQName2(name, &prefix);
4258 if (local != NULL) {
4259 xmlNsPtr ns;
4260
4261 ns = xmlSearchNs(cur->doc, cur, prefix);
4262 if (ns == NULL) {
4263 if (ctxt->error != NULL)
4264 ctxt->error(ctxt->userData,
4265 "xmlRelaxNGParse: no namespace for prefix %s\n", prefix);
4266 ctxt->nbErrors++;
4267 } else {
4268 xmlSetProp(cur, BAD_CAST "ns", ns->href);
4269 xmlNodeSetContent(cur, local);
4270 }
4271 xmlFree(local);
4272 xmlFree(prefix);
4273 }
4274 xmlFree(name);
4275 }
4276 }
4277 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004278 /*
4279 * Thisd is not an else since "include" is transformed
4280 * into a div
4281 */
4282 if (xmlStrEqual(cur->name, BAD_CAST "div")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00004283 xmlChar *ns;
4284 xmlNodePtr child, ins, tmp;
4285
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004286 /*
4287 * implements rule 4.11
4288 */
Daniel Veillard416589a2003-02-17 17:25:42 +00004289
4290 ns = xmlGetProp(cur, BAD_CAST "ns");
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004291
4292 child = cur->children;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004293 ins = cur;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004294 while (child != NULL) {
Daniel Veillard416589a2003-02-17 17:25:42 +00004295 if (ns != NULL) {
4296 if (!xmlHasProp(child, BAD_CAST "ns")) {
4297 xmlSetProp(child, BAD_CAST "ns", ns);
4298 }
4299 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004300 tmp = child->next;
4301 xmlUnlinkNode(child);
4302 ins = xmlAddNextSibling(ins, child);
4303 child = tmp;
4304 }
Daniel Veillard416589a2003-02-17 17:25:42 +00004305 if (ns != NULL)
4306 xmlFree(ns);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004307 delete = cur;
4308 goto skip_children;
4309 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004310 }
4311 }
4312 /*
4313 * Simplification 4.2 whitespaces
4314 */
4315 else if (cur->type == XML_TEXT_NODE) {
4316 if (IS_BLANK_NODE(cur)) {
4317 if (cur->parent->type == XML_ELEMENT_NODE) {
4318 if ((!xmlStrEqual(cur->parent->name, BAD_CAST "value")) &&
4319 (!xmlStrEqual(cur->parent->name, BAD_CAST "param")))
4320 delete = cur;
4321 } else {
4322 delete = cur;
4323 goto skip_children;
4324 }
4325 }
4326 } else if (cur->type != XML_CDATA_SECTION_NODE) {
4327 delete = cur;
4328 goto skip_children;
4329 }
4330
4331 /*
4332 * Skip to next node
4333 */
4334 if (cur->children != NULL) {
4335 if ((cur->children->type != XML_ENTITY_DECL) &&
4336 (cur->children->type != XML_ENTITY_REF_NODE) &&
4337 (cur->children->type != XML_ENTITY_NODE)) {
4338 cur = cur->children;
4339 continue;
4340 }
4341 }
4342skip_children:
4343 if (cur->next != NULL) {
4344 cur = cur->next;
4345 continue;
4346 }
4347
4348 do {
4349 cur = cur->parent;
4350 if (cur == NULL)
4351 break;
4352 if (cur == root) {
4353 cur = NULL;
4354 break;
4355 }
4356 if (cur->next != NULL) {
4357 cur = cur->next;
4358 break;
4359 }
4360 } while (cur != NULL);
4361 }
4362 if (delete != NULL) {
4363 xmlUnlinkNode(delete);
4364 xmlFreeNode(delete);
4365 delete = NULL;
4366 }
4367
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004368 return(doc);
4369}
4370
4371/**
4372 * xmlRelaxNGParse:
4373 * @ctxt: a Relax-NG parser context
4374 *
4375 * parse a schema definition resource and build an internal
4376 * XML Shema struture which can be used to validate instances.
4377 * *WARNING* this interface is highly subject to change
4378 *
4379 * Returns the internal XML RelaxNG structure built from the resource or
4380 * NULL in case of error
4381 */
4382xmlRelaxNGPtr
4383xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt)
4384{
4385 xmlRelaxNGPtr ret = NULL;
4386 xmlDocPtr doc;
4387 xmlNodePtr root;
4388
4389 xmlRelaxNGInitTypes();
4390
4391 if (ctxt == NULL)
4392 return (NULL);
4393
4394 /*
4395 * First step is to parse the input document into an DOM/Infoset
4396 */
4397 if (ctxt->URL != NULL) {
4398 doc = xmlParseFile((const char *) ctxt->URL);
4399 if (doc == NULL) {
4400 if (ctxt->error != NULL)
4401 ctxt->error(ctxt->userData,
4402 "xmlRelaxNGParse: could not load %s\n", ctxt->URL);
4403 ctxt->nbErrors++;
4404 return (NULL);
4405 }
4406 } else if (ctxt->buffer != NULL) {
4407 doc = xmlParseMemory(ctxt->buffer, ctxt->size);
4408 if (doc == NULL) {
4409 if (ctxt->error != NULL)
4410 ctxt->error(ctxt->userData,
4411 "xmlRelaxNGParse: could not parse schemas\n");
4412 ctxt->nbErrors++;
4413 return (NULL);
4414 }
4415 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
4416 ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
4417 } else {
4418 if (ctxt->error != NULL)
4419 ctxt->error(ctxt->userData,
4420 "xmlRelaxNGParse: nothing to parse\n");
4421 ctxt->nbErrors++;
4422 return (NULL);
4423 }
4424 ctxt->document = doc;
4425
4426 /*
4427 * Some preprocessing of the document content
4428 */
4429 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
4430 if (doc == NULL) {
4431 xmlFreeDoc(ctxt->document);
4432 ctxt->document = NULL;
4433 return(NULL);
4434 }
4435
Daniel Veillard6eadf632003-01-23 18:29:16 +00004436 /*
4437 * Then do the parsing for good
4438 */
4439 root = xmlDocGetRootElement(doc);
4440 if (root == NULL) {
4441 if (ctxt->error != NULL)
4442 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
4443 ctxt->URL);
4444 ctxt->nbErrors++;
4445 return (NULL);
4446 }
4447 ret = xmlRelaxNGParseDocument(ctxt, root);
4448 if (ret == NULL)
4449 return(NULL);
4450
4451 /*
4452 * Check the ref/defines links
4453 */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004454 /*
4455 * try to preprocess interleaves
4456 */
4457 if (ctxt->interleaves != NULL) {
4458 xmlHashScan(ctxt->interleaves,
4459 (xmlHashScanner)xmlRelaxNGComputeInterleaves, ctxt);
4460 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004461
4462 /*
4463 * if there was a parsing error return NULL
4464 */
4465 if (ctxt->nbErrors > 0) {
4466 xmlRelaxNGFree(ret);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004467 ctxt->document = NULL;
4468 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004469 return(NULL);
4470 }
4471
4472 /*
4473 * Transfer the pointer for cleanup at the schema level.
4474 */
4475 ret->doc = doc;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004476 ctxt->document = NULL;
4477 ret->documents = ctxt->documents;
4478 ctxt->documents = NULL;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004479 ret->includes = ctxt->includes;
4480 ctxt->includes = NULL;
Daniel Veillard419a7682003-02-03 23:22:49 +00004481 ret->defNr = ctxt->defNr;
4482 ret->defTab = ctxt->defTab;
4483 ctxt->defTab = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004484
4485 return (ret);
4486}
4487
4488/**
4489 * xmlRelaxNGSetParserErrors:
4490 * @ctxt: a Relax-NG validation context
4491 * @err: the error callback
4492 * @warn: the warning callback
4493 * @ctx: contextual data for the callbacks
4494 *
4495 * Set the callback functions used to handle errors for a validation context
4496 */
4497void
4498xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
4499 xmlRelaxNGValidityErrorFunc err,
4500 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
4501 if (ctxt == NULL)
4502 return;
4503 ctxt->error = err;
4504 ctxt->warning = warn;
4505 ctxt->userData = ctx;
4506}
4507/************************************************************************
4508 * *
4509 * Dump back a compiled form *
4510 * *
4511 ************************************************************************/
4512static void xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define);
4513
4514/**
4515 * xmlRelaxNGDumpDefines:
4516 * @output: the file output
4517 * @defines: a list of define structures
4518 *
4519 * Dump a RelaxNG structure back
4520 */
4521static void
4522xmlRelaxNGDumpDefines(FILE * output, xmlRelaxNGDefinePtr defines) {
4523 while (defines != NULL) {
4524 xmlRelaxNGDumpDefine(output, defines);
4525 defines = defines->next;
4526 }
4527}
4528
4529/**
4530 * xmlRelaxNGDumpDefine:
4531 * @output: the file output
4532 * @define: a define structure
4533 *
4534 * Dump a RelaxNG structure back
4535 */
4536static void
4537xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define) {
4538 if (define == NULL)
4539 return;
4540 switch(define->type) {
4541 case XML_RELAXNG_EMPTY:
4542 fprintf(output, "<empty/>\n");
4543 break;
4544 case XML_RELAXNG_NOT_ALLOWED:
4545 fprintf(output, "<notAllowed/>\n");
4546 break;
4547 case XML_RELAXNG_TEXT:
4548 fprintf(output, "<text/>\n");
4549 break;
4550 case XML_RELAXNG_ELEMENT:
4551 fprintf(output, "<element>\n");
4552 if (define->name != NULL) {
4553 fprintf(output, "<name");
4554 if (define->ns != NULL)
4555 fprintf(output, " ns=\"%s\"", define->ns);
4556 fprintf(output, ">%s</name>\n", define->name);
4557 }
4558 xmlRelaxNGDumpDefines(output, define->attrs);
4559 xmlRelaxNGDumpDefines(output, define->content);
4560 fprintf(output, "</element>\n");
4561 break;
4562 case XML_RELAXNG_LIST:
4563 fprintf(output, "<list>\n");
4564 xmlRelaxNGDumpDefines(output, define->content);
4565 fprintf(output, "</list>\n");
4566 break;
4567 case XML_RELAXNG_ONEORMORE:
4568 fprintf(output, "<oneOrMore>\n");
4569 xmlRelaxNGDumpDefines(output, define->content);
4570 fprintf(output, "</oneOrMore>\n");
4571 break;
4572 case XML_RELAXNG_ZEROORMORE:
4573 fprintf(output, "<zeroOrMore>\n");
4574 xmlRelaxNGDumpDefines(output, define->content);
4575 fprintf(output, "</zeroOrMore>\n");
4576 break;
4577 case XML_RELAXNG_CHOICE:
4578 fprintf(output, "<choice>\n");
4579 xmlRelaxNGDumpDefines(output, define->content);
4580 fprintf(output, "</choice>\n");
4581 break;
4582 case XML_RELAXNG_GROUP:
4583 fprintf(output, "<group>\n");
4584 xmlRelaxNGDumpDefines(output, define->content);
4585 fprintf(output, "</group>\n");
4586 break;
4587 case XML_RELAXNG_INTERLEAVE:
4588 fprintf(output, "<interleave>\n");
4589 xmlRelaxNGDumpDefines(output, define->content);
4590 fprintf(output, "</interleave>\n");
4591 break;
4592 case XML_RELAXNG_OPTIONAL:
4593 fprintf(output, "<optional>\n");
4594 xmlRelaxNGDumpDefines(output, define->content);
4595 fprintf(output, "</optional>\n");
4596 break;
4597 case XML_RELAXNG_ATTRIBUTE:
4598 fprintf(output, "<attribute>\n");
4599 xmlRelaxNGDumpDefines(output, define->content);
4600 fprintf(output, "</attribute>\n");
4601 break;
4602 case XML_RELAXNG_DEF:
4603 fprintf(output, "<define");
4604 if (define->name != NULL)
4605 fprintf(output, " name=\"%s\"", define->name);
4606 fprintf(output, ">\n");
4607 xmlRelaxNGDumpDefines(output, define->content);
4608 fprintf(output, "</define>\n");
4609 break;
4610 case XML_RELAXNG_REF:
4611 fprintf(output, "<ref");
4612 if (define->name != NULL)
4613 fprintf(output, " name=\"%s\"", define->name);
4614 fprintf(output, ">\n");
4615 xmlRelaxNGDumpDefines(output, define->content);
4616 fprintf(output, "</ref>\n");
4617 break;
Daniel Veillard419a7682003-02-03 23:22:49 +00004618 case XML_RELAXNG_PARENTREF:
4619 fprintf(output, "<parentRef");
4620 if (define->name != NULL)
4621 fprintf(output, " name=\"%s\"", define->name);
4622 fprintf(output, ">\n");
4623 xmlRelaxNGDumpDefines(output, define->content);
4624 fprintf(output, "</parentRef>\n");
4625 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004626 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard416589a2003-02-17 17:25:42 +00004627 fprintf(output, "<externalRef>");
Daniel Veillarde431a272003-01-29 23:02:33 +00004628 xmlRelaxNGDumpDefines(output, define->content);
4629 fprintf(output, "</externalRef>\n");
4630 break;
Daniel Veillard416589a2003-02-17 17:25:42 +00004631#if 0
4632 case XML_RELAXNG_MIXED:
4633 fprintf(output, "<mixed>");
4634 xmlRelaxNGDumpDefines(output, define->content);
4635 fprintf(output, "</mixed>\n");
4636 break;
4637#endif
Daniel Veillarde431a272003-01-29 23:02:33 +00004638 case XML_RELAXNG_DATATYPE:
Daniel Veillard6eadf632003-01-23 18:29:16 +00004639 case XML_RELAXNG_VALUE:
4640 TODO
4641 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004642 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00004643 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004644 TODO
4645 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004646 }
4647}
4648
4649/**
4650 * xmlRelaxNGDumpGrammar:
4651 * @output: the file output
4652 * @grammar: a grammar structure
4653 * @top: is this a top grammar
4654 *
4655 * Dump a RelaxNG structure back
4656 */
4657static void
4658xmlRelaxNGDumpGrammar(FILE * output, xmlRelaxNGGrammarPtr grammar, int top)
4659{
4660 if (grammar == NULL)
4661 return;
4662
4663 fprintf(output, "<grammar");
4664 if (top)
4665 fprintf(output,
4666 " xmlns=\"http://relaxng.org/ns/structure/1.0\"");
4667 switch(grammar->combine) {
4668 case XML_RELAXNG_COMBINE_UNDEFINED:
4669 break;
4670 case XML_RELAXNG_COMBINE_CHOICE:
4671 fprintf(output, " combine=\"choice\"");
4672 break;
4673 case XML_RELAXNG_COMBINE_INTERLEAVE:
4674 fprintf(output, " combine=\"interleave\"");
4675 break;
4676 default:
4677 fprintf(output, " <!-- invalid combine value -->");
4678 }
4679 fprintf(output, ">\n");
4680 if (grammar->start == NULL) {
4681 fprintf(output, " <!-- grammar had no start -->");
4682 } else {
4683 fprintf(output, "<start>\n");
4684 xmlRelaxNGDumpDefine(output, grammar->start);
4685 fprintf(output, "</start>\n");
4686 }
4687 /* TODO ? Dump the defines ? */
4688 fprintf(output, "</grammar>\n");
4689}
4690
4691/**
4692 * xmlRelaxNGDump:
4693 * @output: the file output
4694 * @schema: a schema structure
4695 *
4696 * Dump a RelaxNG structure back
4697 */
4698void
4699xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema)
4700{
4701 if (schema == NULL) {
4702 fprintf(output, "RelaxNG empty or failed to compile\n");
4703 return;
4704 }
4705 fprintf(output, "RelaxNG: ");
4706 if (schema->doc == NULL) {
4707 fprintf(output, "no document\n");
4708 } else if (schema->doc->URL != NULL) {
4709 fprintf(output, "%s\n", schema->doc->URL);
4710 } else {
4711 fprintf(output, "\n");
4712 }
4713 if (schema->topgrammar == NULL) {
4714 fprintf(output, "RelaxNG has no top grammar\n");
4715 return;
4716 }
4717 xmlRelaxNGDumpGrammar(output, schema->topgrammar, 1);
4718}
4719
Daniel Veillardfebcca42003-02-16 15:44:18 +00004720/**
4721 * xmlRelaxNGDumpTree:
4722 * @output: the file output
4723 * @schema: a schema structure
4724 *
4725 * Dump the transformed RelaxNG tree.
4726 */
4727void
4728xmlRelaxNGDumpTree(FILE * output, xmlRelaxNGPtr schema)
4729{
4730 if (schema == NULL) {
4731 fprintf(output, "RelaxNG empty or failed to compile\n");
4732 return;
4733 }
4734 if (schema->doc == NULL) {
4735 fprintf(output, "no document\n");
4736 } else {
4737 xmlDocDump(output, schema->doc);
4738 }
4739}
4740
Daniel Veillard6eadf632003-01-23 18:29:16 +00004741/************************************************************************
4742 * *
4743 * Validation implementation *
4744 * *
4745 ************************************************************************/
4746static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
4747 xmlRelaxNGDefinePtr define);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004748static int xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
4749 xmlRelaxNGDefinePtr define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004750
4751/**
4752 * xmlRelaxNGSkipIgnored:
4753 * @ctxt: a schema validation context
4754 * @node: the top node.
4755 *
4756 * Skip ignorable nodes in that context
4757 *
4758 * Returns the new sibling or NULL in case of error.
4759 */
4760static xmlNodePtr
4761xmlRelaxNGSkipIgnored(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
4762 xmlNodePtr node) {
4763 /*
4764 * TODO complete and handle entities
4765 */
4766 while ((node != NULL) &&
4767 ((node->type == XML_COMMENT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004768 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00004769 ((node->type == XML_TEXT_NODE) &&
4770 (IS_BLANK_NODE(node))))) {
4771 node = node->next;
4772 }
4773 return(node);
4774}
4775
4776/**
Daniel Veillardedc91922003-01-26 00:52:04 +00004777 * xmlRelaxNGNormalize:
4778 * @ctxt: a schema validation context
4779 * @str: the string to normalize
4780 *
4781 * Implements the normalizeWhiteSpace( s ) function from
4782 * section 6.2.9 of the spec
4783 *
4784 * Returns the new string or NULL in case of error.
4785 */
4786static xmlChar *
4787xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *str) {
4788 xmlChar *ret, *p;
4789 const xmlChar *tmp;
4790 int len;
4791
4792 if (str == NULL)
4793 return(NULL);
4794 tmp = str;
4795 while (*tmp != 0) tmp++;
4796 len = tmp - str;
4797
4798 ret = (xmlChar *) xmlMalloc((len + 1) * sizeof(xmlChar));
4799 if (ret == NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00004800 if (ctxt != NULL) {
4801 VALID_CTXT();
4802 VALID_ERROR("xmlRelaxNGNormalize: out of memory\n");
4803 } else {
4804 xmlGenericError(xmlGenericErrorContext,
4805 "xmlRelaxNGNormalize: out of memory\n");
4806 }
Daniel Veillardedc91922003-01-26 00:52:04 +00004807 return(NULL);
4808 }
4809 p = ret;
4810 while (IS_BLANK(*str)) str++;
4811 while (*str != 0) {
4812 if (IS_BLANK(*str)) {
4813 while (IS_BLANK(*str)) str++;
4814 if (*str == 0)
4815 break;
4816 *p++ = ' ';
4817 } else
4818 *p++ = *str++;
4819 }
4820 *p = 0;
4821 return(ret);
4822}
4823
4824/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004825 * xmlRelaxNGValidateDatatype:
4826 * @ctxt: a Relax-NG validation context
4827 * @value: the string value
4828 * @type: the datatype definition
4829 *
4830 * Validate the given value against the dataype
4831 *
4832 * Returns 0 if the validation succeeded or an error code.
4833 */
4834static int
4835xmlRelaxNGValidateDatatype(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *value,
4836 xmlRelaxNGDefinePtr define) {
4837 int ret;
4838 xmlRelaxNGTypeLibraryPtr lib;
4839
4840 if ((define == NULL) || (define->data == NULL)) {
4841 return(-1);
4842 }
4843 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
4844 if (lib->check != NULL)
4845 ret = lib->check(lib->data, define->name, value);
4846 else
4847 ret = -1;
4848 if (ret < 0) {
4849 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004850 VALID_ERROR2("Internal: failed to validate type %s\n", define->name);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004851 return(-1);
4852 } else if (ret == 1) {
4853 ret = 0;
4854 } else {
4855 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004856 VALID_ERROR3("Type %s doesn't allow value %s\n", define->name, value);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004857 return(-1);
4858 ret = -1;
4859 }
Daniel Veillard416589a2003-02-17 17:25:42 +00004860 if ((ret == 0) && (define->content != NULL)) {
4861 const xmlChar *oldvalue, *oldendvalue;
4862
4863 oldvalue = ctxt->state->value;
4864 oldendvalue = ctxt->state->endvalue;
4865 ctxt->state->value = (xmlChar *) value;
4866 ctxt->state->endvalue = NULL;
4867 ret = xmlRelaxNGValidateValue(ctxt, define->content);
4868 ctxt->state->value = (xmlChar *) oldvalue;
4869 ctxt->state->endvalue = (xmlChar *) oldendvalue;
4870 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004871 return(ret);
4872}
4873
4874/**
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004875 * xmlRelaxNGNextValue:
4876 * @ctxt: a Relax-NG validation context
4877 *
4878 * Skip to the next value when validating within a list
4879 *
4880 * Returns 0 if the operation succeeded or an error code.
4881 */
4882static int
4883xmlRelaxNGNextValue(xmlRelaxNGValidCtxtPtr ctxt) {
4884 xmlChar *cur;
4885
4886 cur = ctxt->state->value;
4887 if ((cur == NULL) || (ctxt->state->endvalue == NULL)) {
4888 ctxt->state->value = NULL;
Daniel Veillarde5b110b2003-02-04 14:43:39 +00004889 ctxt->state->endvalue = NULL;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004890 return(0);
4891 }
4892 while (*cur != 0) cur++;
4893 while ((cur != ctxt->state->endvalue) && (*cur == 0)) cur++;
4894 if (cur == ctxt->state->endvalue)
4895 ctxt->state->value = NULL;
4896 else
4897 ctxt->state->value = cur;
4898 return(0);
4899}
4900
4901/**
4902 * xmlRelaxNGValidateValueList:
4903 * @ctxt: a Relax-NG validation context
4904 * @defines: the list of definitions to verify
4905 *
4906 * Validate the given set of definitions for the current value
4907 *
4908 * Returns 0 if the validation succeeded or an error code.
4909 */
4910static int
4911xmlRelaxNGValidateValueList(xmlRelaxNGValidCtxtPtr ctxt,
4912 xmlRelaxNGDefinePtr defines) {
4913 int ret = 0;
4914
4915 while (defines != NULL) {
4916 ret = xmlRelaxNGValidateValue(ctxt, defines);
4917 if (ret != 0)
4918 break;
4919 defines = defines->next;
4920 }
4921 return(ret);
4922}
4923
4924/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004925 * xmlRelaxNGValidateValue:
4926 * @ctxt: a Relax-NG validation context
4927 * @define: the definition to verify
4928 *
4929 * Validate the given definition for the current value
4930 *
4931 * Returns 0 if the validation succeeded or an error code.
4932 */
4933static int
4934xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
4935 xmlRelaxNGDefinePtr define) {
Daniel Veillardedc91922003-01-26 00:52:04 +00004936 int ret = 0, oldflags;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004937 xmlChar *value;
4938
4939 value = ctxt->state->value;
4940 switch (define->type) {
4941 case XML_RELAXNG_EMPTY:
4942 if ((value != NULL) && (value[0] != '0'))
4943 ret = -1;
4944 break;
4945 case XML_RELAXNG_TEXT:
4946 break;
Daniel Veillardedc91922003-01-26 00:52:04 +00004947 case XML_RELAXNG_VALUE: {
4948 if (!xmlStrEqual(value, define->value)) {
4949 if (define->name != NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00004950 xmlRelaxNGTypeLibraryPtr lib;
4951
4952 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
4953 if ((lib != NULL) && (lib->comp != NULL))
4954 ret = lib->comp(lib->data, define->name, value,
4955 define->value);
4956 else
4957 ret = -1;
4958 if (ret < 0) {
4959 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004960 VALID_ERROR2("Internal: failed to compare type %s\n",
Daniel Veillardea3f3982003-01-26 19:45:18 +00004961 define->name);
4962 return(-1);
4963 } else if (ret == 1) {
4964 ret = 0;
4965 } else {
4966 ret = -1;
4967 }
Daniel Veillardedc91922003-01-26 00:52:04 +00004968 } else {
4969 xmlChar *nval, *nvalue;
4970
4971 /*
4972 * TODO: trivial optimizations are possible by
4973 * computing at compile-time
4974 */
4975 nval = xmlRelaxNGNormalize(ctxt, define->value);
4976 nvalue = xmlRelaxNGNormalize(ctxt, value);
4977
Daniel Veillardea3f3982003-01-26 19:45:18 +00004978 if ((nval == NULL) || (nvalue == NULL) ||
4979 (!xmlStrEqual(nval, nvalue)))
Daniel Veillardedc91922003-01-26 00:52:04 +00004980 ret = -1;
4981 if (nval != NULL)
4982 xmlFree(nval);
4983 if (nvalue != NULL)
4984 xmlFree(nvalue);
4985 }
4986 }
Daniel Veillard416589a2003-02-17 17:25:42 +00004987 if (ret == 0)
4988 xmlRelaxNGNextValue(ctxt);
Daniel Veillardedc91922003-01-26 00:52:04 +00004989 break;
4990 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004991 case XML_RELAXNG_DATATYPE: {
4992 ret = xmlRelaxNGValidateDatatype(ctxt, value, define);
4993 if (ret == 0)
4994 xmlRelaxNGNextValue(ctxt);
4995
4996 break;
4997 }
4998 case XML_RELAXNG_CHOICE: {
4999 xmlRelaxNGDefinePtr list = define->content;
5000 xmlChar *oldvalue;
5001
5002 oldflags = ctxt->flags;
5003 ctxt->flags |= FLAGS_IGNORABLE;
5004
5005 oldvalue = ctxt->state->value;
5006 while (list != NULL) {
5007 ret = xmlRelaxNGValidateValue(ctxt, list);
5008 if (ret == 0) {
5009 break;
5010 }
5011 ctxt->state->value = oldvalue;
5012 list = list->next;
5013 }
5014 ctxt->flags = oldflags;
Daniel Veillard416589a2003-02-17 17:25:42 +00005015 if (ret == 0)
5016 xmlRelaxNGNextValue(ctxt);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005017 break;
5018 }
5019 case XML_RELAXNG_LIST: {
5020 xmlRelaxNGDefinePtr list = define->content;
5021 xmlChar *oldvalue, *oldend, *val, *cur;
Daniel Veillard416589a2003-02-17 17:25:42 +00005022#ifdef DEBUG_LIST
5023 int nb_values = 0;
5024#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005025
5026 oldvalue = ctxt->state->value;
5027 oldend = ctxt->state->endvalue;
5028
5029 val = xmlStrdup(oldvalue);
5030 if (val == NULL) {
5031 VALID_CTXT();
5032 VALID_ERROR("Internal: no state\n");
5033 return(-1);
5034 }
5035 cur = val;
5036 while (*cur != 0) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005037 if (IS_BLANK(*cur)) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005038 *cur = 0;
Daniel Veillard416589a2003-02-17 17:25:42 +00005039 cur++;
5040#ifdef DEBUG_LIST
5041 nb_values++;
5042#endif
5043 while (IS_BLANK(*cur))
5044 *cur++ = 0;
5045 } else
5046 cur++;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005047 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005048#ifdef DEBUG_LIST
5049 xmlGenericError(xmlGenericErrorContext,
5050 "list value: '%s' found %d items\n", oldvalue, nb_values);
5051 nb_values = 0;
5052#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005053 ctxt->state->endvalue = cur;
5054 cur = val;
5055 while ((*cur == 0) && (cur != ctxt->state->endvalue)) cur++;
5056
5057 ctxt->state->value = cur;
5058
5059 while (list != NULL) {
5060 ret = xmlRelaxNGValidateValue(ctxt, list);
5061 if (ret != 0) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005062#ifdef DEBUG_LIST
5063 xmlGenericError(xmlGenericErrorContext,
5064 "Failed to validate value: '%s' with %d rule\n",
5065 ctxt->state->value, nb_values);
5066#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005067 break;
5068 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005069#ifdef DEBUG_LIST
5070 nb_values++;
5071#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005072 list = list->next;
5073 }
5074 if ((ret == 0) && (ctxt->state->value != NULL) &&
5075 (ctxt->state->value != ctxt->state->endvalue)) {
5076 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005077 VALID_ERROR2("Extra data in list: %s\n", ctxt->state->value);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005078 ret = -1;
5079 }
5080 xmlFree(val);
5081 ctxt->state->value = oldvalue;
5082 ctxt->state->endvalue = oldend;
5083 break;
5084 }
5085 case XML_RELAXNG_ONEORMORE:
5086 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
5087 if (ret != 0) {
5088 break;
5089 }
5090 /* no break on purpose */
5091 case XML_RELAXNG_ZEROORMORE: {
5092 xmlChar *cur, *temp;
5093
5094 oldflags = ctxt->flags;
5095 ctxt->flags |= FLAGS_IGNORABLE;
5096 cur = ctxt->state->value;
5097 temp = NULL;
5098 while ((cur != NULL) && (cur != ctxt->state->endvalue) &&
5099 (temp != cur)) {
5100 temp = cur;
5101 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
5102 if (ret != 0) {
5103 ctxt->state->value = temp;
5104 ret = 0;
5105 break;
5106 }
5107 cur = ctxt->state->value;
5108 }
5109 ctxt->flags = oldflags;
5110 break;
5111 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005112 case XML_RELAXNG_EXCEPT: {
5113 xmlRelaxNGDefinePtr list;
5114
5115 list = define->content;
5116 while (list != NULL) {
5117 ret = xmlRelaxNGValidateValue(ctxt, list);
5118 if (ret == 0) {
5119 ret = -1;
5120 break;
5121 } else
5122 ret = 0;
5123 list = list->next;
5124 }
5125 break;
5126 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005127 default:
5128 TODO
5129 ret = -1;
5130 }
5131 return(ret);
5132}
5133
5134/**
5135 * xmlRelaxNGValidateValueContent:
5136 * @ctxt: a Relax-NG validation context
5137 * @defines: the list of definitions to verify
5138 *
5139 * Validate the given definitions for the current value
5140 *
5141 * Returns 0 if the validation succeeded or an error code.
5142 */
5143static int
5144xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt,
5145 xmlRelaxNGDefinePtr defines) {
5146 int ret = 0;
5147
5148 while (defines != NULL) {
5149 ret = xmlRelaxNGValidateValue(ctxt, defines);
5150 if (ret != 0)
5151 break;
5152 defines = defines->next;
5153 }
5154 return(ret);
5155}
5156
5157/**
Daniel Veillard144fae12003-02-03 13:17:57 +00005158 * xmlRelaxNGAttributeMatch:
5159 * @ctxt: a Relax-NG validation context
5160 * @define: the definition to check
5161 * @prop: the attribute
5162 *
5163 * Check if the attribute matches the definition nameClass
5164 *
5165 * Returns 1 if the attribute matches, 0 if no, or -1 in case of error
5166 */
5167static int
5168xmlRelaxNGAttributeMatch(xmlRelaxNGValidCtxtPtr ctxt,
5169 xmlRelaxNGDefinePtr define,
5170 xmlAttrPtr prop) {
5171 int ret;
5172
5173 if (define->name != NULL) {
5174 if (!xmlStrEqual(define->name, prop->name))
5175 return(0);
5176 }
5177 if (define->ns != NULL) {
5178 if (define->ns[0] == 0) {
5179 if (prop->ns != NULL)
5180 return(0);
5181 } else {
5182 if ((prop->ns == NULL) ||
5183 (!xmlStrEqual(define->ns, prop->ns->href)))
5184 return(0);
5185 }
5186 }
5187 if (define->nameClass == NULL)
5188 return(1);
5189 define = define->nameClass;
5190 if (define->type == XML_RELAXNG_EXCEPT) {
5191 xmlRelaxNGDefinePtr list;
5192
5193 list = define->content;
5194 while (list != NULL) {
5195 ret = xmlRelaxNGAttributeMatch(ctxt, list, prop);
5196 if (ret == 1)
5197 return(0);
5198 if (ret < 0)
5199 return(ret);
5200 list = list->next;
5201 }
5202 } else {
5203 TODO
5204 }
5205 return(1);
5206}
5207
5208/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00005209 * xmlRelaxNGValidateAttribute:
5210 * @ctxt: a Relax-NG validation context
5211 * @define: the definition to verify
5212 *
5213 * Validate the given attribute definition for that node
5214 *
5215 * Returns 0 if the validation succeeded or an error code.
5216 */
5217static int
5218xmlRelaxNGValidateAttribute(xmlRelaxNGValidCtxtPtr ctxt,
5219 xmlRelaxNGDefinePtr define) {
5220 int ret = 0, i;
5221 xmlChar *value, *oldvalue;
5222 xmlAttrPtr prop = NULL, tmp;
5223
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005224 if (ctxt->state->nbAttrLeft <= 0)
5225 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005226 if (define->name != NULL) {
5227 for (i = 0;i < ctxt->state->nbAttrs;i++) {
5228 tmp = ctxt->state->attrs[i];
5229 if ((tmp != NULL) && (xmlStrEqual(define->name, tmp->name))) {
5230 if ((((define->ns == NULL) || (define->ns[0] == 0)) &&
5231 (tmp->ns == NULL)) ||
5232 ((tmp->ns != NULL) &&
5233 (xmlStrEqual(define->ns, tmp->ns->href)))) {
5234 prop = tmp;
5235 break;
5236 }
5237 }
5238 }
5239 if (prop != NULL) {
5240 value = xmlNodeListGetString(prop->doc, prop->children, 1);
5241 oldvalue = ctxt->state->value;
5242 ctxt->state->value = value;
Daniel Veillard231d7912003-02-09 14:22:17 +00005243 ctxt->state->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005244 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00005245 if (ctxt->state->value != NULL)
5246 value = ctxt->state->value;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005247 if (value != NULL)
5248 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00005249 ctxt->state->value = oldvalue;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005250 if (ret == 0) {
5251 /*
5252 * flag the attribute as processed
5253 */
5254 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005255 ctxt->state->nbAttrLeft--;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005256 }
5257 } else {
5258 ret = -1;
5259 }
5260#ifdef DEBUG
5261 xmlGenericError(xmlGenericErrorContext,
5262 "xmlRelaxNGValidateAttribute(%s): %d\n", define->name, ret);
5263#endif
5264 } else {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005265 for (i = 0;i < ctxt->state->nbAttrs;i++) {
5266 tmp = ctxt->state->attrs[i];
Daniel Veillard144fae12003-02-03 13:17:57 +00005267 if ((tmp != NULL) &&
5268 (xmlRelaxNGAttributeMatch(ctxt, define, tmp) == 1)) {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005269 prop = tmp;
5270 break;
5271 }
5272 }
5273 if (prop != NULL) {
5274 value = xmlNodeListGetString(prop->doc, prop->children, 1);
5275 oldvalue = ctxt->state->value;
5276 ctxt->state->value = value;
5277 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00005278 if (ctxt->state->value != NULL)
5279 value = ctxt->state->value;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005280 if (value != NULL)
5281 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00005282 ctxt->state->value = oldvalue;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005283 if (ret == 0) {
5284 /*
5285 * flag the attribute as processed
5286 */
5287 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005288 ctxt->state->nbAttrLeft--;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005289 }
5290 } else {
5291 ret = -1;
5292 }
5293#ifdef DEBUG
Daniel Veillard144fae12003-02-03 13:17:57 +00005294 if (define->ns != NULL) {
5295 xmlGenericError(xmlGenericErrorContext,
5296 "xmlRelaxNGValidateAttribute(nsName ns = %s): %d\n",
5297 define->ns, ret);
5298 } else {
5299 xmlGenericError(xmlGenericErrorContext,
5300 "xmlRelaxNGValidateAttribute(anyName): %d\n",
5301 ret);
5302 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005303#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00005304 }
5305
5306 return(ret);
5307}
5308
5309/**
5310 * xmlRelaxNGValidateAttributeList:
5311 * @ctxt: a Relax-NG validation context
5312 * @define: the list of definition to verify
5313 *
5314 * Validate the given node against the list of attribute definitions
5315 *
5316 * Returns 0 if the validation succeeded or an error code.
5317 */
5318static int
5319xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt,
5320 xmlRelaxNGDefinePtr defines) {
5321 int ret = 0;
5322 while (defines != NULL) {
5323 if (xmlRelaxNGValidateAttribute(ctxt, defines) != 0)
5324 ret = -1;
5325 defines = defines->next;
5326 }
5327 return(ret);
5328}
5329
5330/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005331 * xmlRelaxNGValidateTryPermutation:
5332 * @ctxt: a Relax-NG validation context
5333 * @groups: the array of groups
5334 * @nbgroups: the number of groups in the array
5335 * @array: the permutation to try
5336 * @len: the size of the set
5337 *
5338 * Try to validate a permutation for the group of definitions.
5339 *
5340 * Returns 0 if the validation succeeded or an error code.
5341 */
5342static int
5343xmlRelaxNGValidateTryPermutation(xmlRelaxNGValidCtxtPtr ctxt,
5344 xmlRelaxNGDefinePtr rule,
5345 xmlNodePtr *array, int len) {
5346 int i, ret;
5347
5348 if (len > 0) {
5349 /*
5350 * One only need the next pointer set-up to do the validation
5351 */
5352 for (i = 0;i < (len - 1);i++)
5353 array[i]->next = array[i + 1];
5354 array[i]->next = NULL;
5355
5356 /*
5357 * Now try to validate the sequence
5358 */
5359 ctxt->state->seq = array[0];
5360 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
5361 } else {
5362 ctxt->state->seq = NULL;
5363 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
5364 }
5365
5366 /*
5367 * the sequence must be fully consumed
5368 */
5369 if (ctxt->state->seq != NULL)
5370 return(-1);
5371
5372 return(ret);
5373}
5374
5375/**
5376 * xmlRelaxNGValidateWalkPermutations:
5377 * @ctxt: a Relax-NG validation context
5378 * @groups: the array of groups
5379 * @nbgroups: the number of groups in the array
5380 * @nodes: the set of nodes
5381 * @array: the current state of the parmutation
5382 * @len: the size of the set
5383 * @level: a pointer to the level variable
5384 * @k: the index in the array to fill
5385 *
5386 * Validate a set of nodes for a groups of definitions, will try the
5387 * full set of permutations
5388 *
5389 * Returns 0 if the validation succeeded or an error code.
5390 */
5391static int
5392xmlRelaxNGValidateWalkPermutations(xmlRelaxNGValidCtxtPtr ctxt,
5393 xmlRelaxNGDefinePtr rule, xmlNodePtr *nodes,
5394 xmlNodePtr *array, int len,
5395 int *level, int k) {
5396 int i, ret;
5397
5398 if ((k >= 0) && (k < len))
5399 array[k] = nodes[*level];
5400 *level = *level + 1;
5401 if (*level == len) {
5402 ret = xmlRelaxNGValidateTryPermutation(ctxt, rule, array, len);
5403 if (ret == 0)
5404 return(0);
5405 } else {
5406 for (i = 0;i < len;i++) {
5407 if (array[i] == NULL) {
5408 ret = xmlRelaxNGValidateWalkPermutations(ctxt, rule,
5409 nodes, array, len, level, i);
5410 if (ret == 0)
5411 return(0);
5412 }
5413 }
5414 }
5415 *level = *level - 1;
5416 array[k] = NULL;
5417 return(-1);
5418}
5419
5420/**
5421 * xmlRelaxNGNodeMatchesList:
5422 * @node: the node
5423 * @list: a NULL terminated array of definitions
5424 *
5425 * Check if a node can be matched by one of the definitions
5426 *
5427 * Returns 1 if matches 0 otherwise
5428 */
5429static int
5430xmlRelaxNGNodeMatchesList(xmlNodePtr node, xmlRelaxNGDefinePtr *list) {
5431 xmlRelaxNGDefinePtr cur;
5432 int i = 0;
5433
5434 if ((node == NULL) || (list == NULL))
5435 return(0);
5436
5437 cur = list[i++];
5438 while (cur != NULL) {
5439 if ((node->type == XML_ELEMENT_NODE) &&
5440 (cur->type == XML_RELAXNG_ELEMENT)) {
5441 if (cur->name == NULL) {
5442 if ((node->ns != NULL) &&
5443 (xmlStrEqual(node->ns->href, cur->ns)))
5444 return(1);
5445 } else if (xmlStrEqual(cur->name, node->name)) {
5446 if ((cur->ns == NULL) || (cur->ns[0] == 0)) {
5447 if (node->ns == NULL)
5448 return(1);
5449 } else {
5450 if ((node->ns != NULL) &&
5451 (xmlStrEqual(node->ns->href, cur->ns)))
5452 return(1);
5453 }
5454 }
5455 } else if ((node->type == XML_TEXT_NODE) &&
5456 (cur->type == XML_RELAXNG_TEXT)) {
5457 return(1);
5458 }
5459 cur = list[i++];
5460 }
5461 return(0);
5462}
5463
5464/**
5465 * xmlRelaxNGValidatePartGroup:
5466 * @ctxt: a Relax-NG validation context
5467 * @groups: the array of groups
5468 * @nbgroups: the number of groups in the array
5469 * @nodes: the set of nodes
5470 * @len: the size of the set of nodes
5471 *
5472 * Validate a set of nodes for a groups of definitions
5473 *
5474 * Returns 0 if the validation succeeded or an error code.
5475 */
5476static int
5477xmlRelaxNGValidatePartGroup(xmlRelaxNGValidCtxtPtr ctxt,
5478 xmlRelaxNGInterleaveGroupPtr *groups,
5479 int nbgroups, xmlNodePtr *nodes, int len) {
Daniel Veillard231d7912003-02-09 14:22:17 +00005480 int level, ret = -1, i, j, k, top_j, max_j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005481 xmlNodePtr *array = NULL, *list, oldseq;
5482 xmlRelaxNGInterleaveGroupPtr group;
5483
5484 list = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
5485 if (list == NULL) {
5486 return(-1);
5487 }
5488 array = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
5489 if (array == NULL) {
5490 xmlFree(list);
5491 return(-1);
5492 }
5493 memset(array, 0, len * sizeof(xmlNodePtr));
5494
5495 /*
5496 * Partition the elements and validate the subsets.
5497 */
5498 oldseq = ctxt->state->seq;
Daniel Veillard231d7912003-02-09 14:22:17 +00005499 max_j = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005500 for (i = 0;i < nbgroups;i++) {
5501 group = groups[i];
5502 if (group == NULL)
5503 continue;
5504 k = 0;
Daniel Veillard231d7912003-02-09 14:22:17 +00005505 top_j = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005506 for (j = 0;j < len;j++) {
5507 if (nodes[j] == NULL)
5508 continue;
5509 if (xmlRelaxNGNodeMatchesList(nodes[j], group->defs)) {
5510 list[k++] = nodes[j];
5511 nodes[j] = NULL;
Daniel Veillard231d7912003-02-09 14:22:17 +00005512 top_j = j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005513 }
5514 }
Daniel Veillard231d7912003-02-09 14:22:17 +00005515 if (top_j > max_j)
5516 max_j = top_j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005517 ctxt->state->seq = oldseq;
5518 if (k > 1) {
5519 memset(array, 0, k * sizeof(xmlNodePtr));
Daniel Veillardb08c9812003-01-28 23:09:49 +00005520 level = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005521 ret = xmlRelaxNGValidateWalkPermutations(ctxt, group->rule,
5522 list, array, k, &level, -1);
5523 } else {
5524 ret = xmlRelaxNGValidateTryPermutation(ctxt, group->rule, list, k);
5525 }
5526 if (ret != 0) {
5527 ctxt->state->seq = oldseq;
5528 break;
5529 }
5530 }
5531
Daniel Veillard231d7912003-02-09 14:22:17 +00005532 for (j = 0;j < max_j;j++) {
5533 if (nodes[j] != NULL) {
5534 TODO /* problem, one of the nodes didn't got a match */
5535 }
5536 }
5537 if (ret == 0) {
5538 if (max_j + 1 < len)
5539 ctxt->state->seq = nodes[max_j + 1];
5540 else
5541 ctxt->state->seq = NULL;
5542 }
5543
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005544 xmlFree(list);
5545 xmlFree(array);
5546 return(ret);
5547}
5548
5549/**
5550 * xmlRelaxNGValidateInterleave:
5551 * @ctxt: a Relax-NG validation context
5552 * @define: the definition to verify
5553 *
5554 * Validate an interleave definition for a node.
5555 *
5556 * Returns 0 if the validation succeeded or an error code.
5557 */
5558static int
5559xmlRelaxNGValidateInterleave(xmlRelaxNGValidCtxtPtr ctxt,
5560 xmlRelaxNGDefinePtr define) {
5561 int ret = 0, nbchildren, nbtot, i, j;
5562 xmlRelaxNGPartitionPtr partitions;
5563 xmlNodePtr *children = NULL;
5564 xmlNodePtr *order = NULL;
Daniel Veillard231d7912003-02-09 14:22:17 +00005565 xmlNodePtr cur, oldseq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005566
5567 if (define->data != NULL) {
5568 partitions = (xmlRelaxNGPartitionPtr) define->data;
5569 } else {
5570 VALID_CTXT();
5571 VALID_ERROR("Internal: interleave block has no data\n");
5572 return(-1);
5573 }
5574
5575 /*
5576 * Build the sequence of child and an array preserving the children
5577 * initial order.
5578 */
5579 cur = ctxt->state->seq;
Daniel Veillard231d7912003-02-09 14:22:17 +00005580 oldseq = ctxt->state->seq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005581 nbchildren = 0;
5582 nbtot = 0;
5583 while (cur != NULL) {
5584 if ((cur->type == XML_COMMENT_NODE) ||
5585 (cur->type == XML_PI_NODE) ||
5586 ((cur->type == XML_TEXT_NODE) &&
5587 (IS_BLANK_NODE(cur)))) {
5588 nbtot++;
5589 } else {
5590 nbchildren++;
5591 nbtot++;
5592 }
5593 cur = cur->next;
5594 }
5595 children = (xmlNodePtr *) xmlMalloc(nbchildren * sizeof(xmlNodePtr));
5596 if (children == NULL)
5597 goto error;
5598 order = (xmlNodePtr *) xmlMalloc(nbtot * sizeof(xmlNodePtr));
5599 if (order == NULL)
5600 goto error;
5601 cur = ctxt->state->seq;
5602 i = 0;
5603 j = 0;
5604 while (cur != NULL) {
5605 if ((cur->type == XML_COMMENT_NODE) ||
5606 (cur->type == XML_PI_NODE) ||
5607 ((cur->type == XML_TEXT_NODE) &&
5608 (IS_BLANK_NODE(cur)))) {
5609 order[j++] = cur;
5610 } else {
5611 order[j++] = cur;
5612 children[i++] = cur;
5613 }
5614 cur = cur->next;
5615 }
5616
5617 /* TODO: retry with a maller set of child if there is a next... */
5618 ret = xmlRelaxNGValidatePartGroup(ctxt, partitions->groups,
5619 partitions->nbgroups, children, nbchildren);
Daniel Veillard231d7912003-02-09 14:22:17 +00005620 if (ret != 0)
5621 ctxt->state->seq = oldseq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005622
5623 /*
5624 * Cleanup: rebuid the child sequence and free the structure
5625 */
5626 if (order != NULL) {
5627 for (i = 0;i < nbtot;i++) {
5628 if (i == 0)
5629 order[i]->prev = NULL;
5630 else
5631 order[i]->prev = order[i - 1];
5632 if (i == nbtot - 1)
5633 order[i]->next = NULL;
5634 else
5635 order[i]->next = order[i + 1];
5636 }
5637 xmlFree(order);
5638 }
5639 if (children != NULL)
5640 xmlFree(children);
5641
5642 return(ret);
5643
5644error:
5645 if (order != NULL) {
5646 for (i = 0;i < nbtot;i++) {
5647 if (i == 0)
5648 order[i]->prev = NULL;
5649 else
5650 order[i]->prev = order[i - 1];
5651 if (i == nbtot - 1)
5652 order[i]->next = NULL;
5653 else
5654 order[i]->next = order[i + 1];
5655 }
5656 xmlFree(order);
5657 }
5658 if (children != NULL)
5659 xmlFree(children);
5660 return(-1);
5661}
5662
5663/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00005664 * xmlRelaxNGValidateElementContent:
5665 * @ctxt: a Relax-NG validation context
5666 * @define: the list of definition to verify
5667 *
5668 * Validate the given node content against the (list) of definitions
5669 *
5670 * Returns 0 if the validation succeeded or an error code.
5671 */
5672static int
5673xmlRelaxNGValidateElementContent(xmlRelaxNGValidCtxtPtr ctxt,
5674 xmlRelaxNGDefinePtr defines) {
5675 int ret = 0, res;
5676
5677 if (ctxt->state == NULL) {
5678 VALID_CTXT();
5679 VALID_ERROR("Internal: no state\n");
5680 return(-1);
5681 }
5682 while (defines != NULL) {
5683 res = xmlRelaxNGValidateDefinition(ctxt, defines);
5684 if (res < 0)
5685 ret = -1;
5686 defines = defines->next;
5687 }
5688
5689 return(ret);
5690}
5691
5692/**
Daniel Veillard416589a2003-02-17 17:25:42 +00005693 * xmlRelaxNGElementMatch:
5694 * @ctxt: a Relax-NG validation context
5695 * @define: the definition to check
5696 * @elem: the element
5697 *
5698 * Check if the element matches the definition nameClass
5699 *
5700 * Returns 1 if the element matches, 0 if no, or -1 in case of error
5701 */
5702static int
5703xmlRelaxNGElementMatch(xmlRelaxNGValidCtxtPtr ctxt,
5704 xmlRelaxNGDefinePtr define,
5705 xmlNodePtr elem) {
5706 int ret, oldflags;
5707
5708 if (define->name != NULL) {
5709 if (!xmlStrEqual(elem->name, define->name)) {
5710 VALID_CTXT();
5711 VALID_ERROR3("Expecting element %s, got %s\n",
5712 define->name, elem->name);
5713 return(0);
5714 }
5715 }
5716 if ((define->ns != NULL) && (define->ns[0] != 0)) {
5717 if (elem->ns == NULL) {
5718 VALID_CTXT();
5719 VALID_ERROR2("Expecting a namespace for element %s\n",
5720 elem->name);
5721 return(0);
5722 } else if (!xmlStrEqual(elem->ns->href, define->ns)) {
5723 VALID_CTXT();
5724 VALID_ERROR3("Expecting element %s has wrong namespace: expecting %s\n",
5725 elem->name, define->ns);
5726 return(0);
5727 }
5728 } else if (define->name != NULL) {
5729 if (elem->ns != NULL) {
5730 VALID_CTXT();
5731 VALID_ERROR2("Expecting no namespace for element %s\n",
5732 define->name);
5733 return(0);
5734 }
5735 }
5736
5737 if (define->nameClass == NULL)
5738 return(1);
5739
5740 define = define->nameClass;
5741 if (define->type == XML_RELAXNG_EXCEPT) {
5742 xmlRelaxNGDefinePtr list;
5743 oldflags = ctxt->flags;
5744 ctxt->flags |= FLAGS_IGNORABLE;
5745
5746 list = define->content;
5747 while (list != NULL) {
5748 ret = xmlRelaxNGElementMatch(ctxt, list, elem);
5749 if (ret == 1) {
5750 ctxt->flags = oldflags;
5751 return(0);
5752 }
5753 if (ret < 0) {
5754 ctxt->flags = oldflags;
5755 return(ret);
5756 }
5757 list = list->next;
5758 }
5759 ctxt->flags = oldflags;
5760 } else {
5761 TODO
5762 }
5763 return(1);
5764}
5765
5766/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00005767 * xmlRelaxNGValidateDefinition:
5768 * @ctxt: a Relax-NG validation context
5769 * @define: the definition to verify
5770 *
5771 * Validate the current node against the definition
5772 *
5773 * Returns 0 if the validation succeeded or an error code.
5774 */
5775static int
5776xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
5777 xmlRelaxNGDefinePtr define) {
5778 xmlNodePtr node;
5779 int ret = 0, i, tmp, oldflags;
5780 xmlRelaxNGValidStatePtr oldstate, state;
5781
5782 if (define == NULL) {
5783 VALID_CTXT();
5784 VALID_ERROR("internal error: define == NULL\n");
5785 return(-1);
5786 }
5787 if (ctxt->state != NULL) {
5788 node = ctxt->state->seq;
5789 } else {
5790 node = NULL;
5791 }
Daniel Veillard231d7912003-02-09 14:22:17 +00005792#ifdef DEBUG
5793 for (i = 0;i < ctxt->depth;i++)
5794 xmlGenericError(xmlGenericErrorContext, " ");
5795 xmlGenericError(xmlGenericErrorContext,
5796 "Start validating %s ", xmlRelaxNGDefName(define));
5797 if (define->name != NULL)
5798 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
5799 if ((node != NULL) && (node->name != NULL))
5800 xmlGenericError(xmlGenericErrorContext, "on %s\n", node->name);
5801 else
5802 xmlGenericError(xmlGenericErrorContext, "\n");
5803#endif
5804 ctxt->depth++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005805 switch (define->type) {
5806 case XML_RELAXNG_EMPTY:
5807 if (node != NULL) {
5808 VALID_CTXT();
5809 VALID_ERROR("Expecting an empty element\n");
Daniel Veillard231d7912003-02-09 14:22:17 +00005810 ret = -1;
5811 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005812 }
Daniel Veillard231d7912003-02-09 14:22:17 +00005813 ret = 0;
5814 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005815 case XML_RELAXNG_NOT_ALLOWED:
Daniel Veillard231d7912003-02-09 14:22:17 +00005816 ret = -1;
5817 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005818 case XML_RELAXNG_TEXT:
Daniel Veillard231d7912003-02-09 14:22:17 +00005819 if (node == NULL) {
5820 ret = 0;
5821 break;
5822 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005823 while ((node != NULL) &&
5824 ((node->type == XML_TEXT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005825 (node->type == XML_COMMENT_NODE) ||
5826 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00005827 (node->type == XML_CDATA_SECTION_NODE)))
5828 node = node->next;
Daniel Veillard276be4a2003-01-24 01:03:34 +00005829 if (node == ctxt->state->seq) {
5830 VALID_CTXT();
5831 VALID_ERROR("Expecting text content\n");
5832 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005833 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00005834 ctxt->state->seq = node;
5835 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005836 case XML_RELAXNG_ELEMENT:
5837 node = xmlRelaxNGSkipIgnored(ctxt, node);
Daniel Veillard231d7912003-02-09 14:22:17 +00005838 if (node == NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005839 VALID_CTXT();
Daniel Veillard231d7912003-02-09 14:22:17 +00005840 VALID_ERROR("Expecting an element, got empty\n");
5841 ret = -1;
5842 break;
5843 }
5844 if (node->type != XML_ELEMENT_NODE) {
5845 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005846 VALID_ERROR2("Expecting an element got %d type\n", node->type);
Daniel Veillard231d7912003-02-09 14:22:17 +00005847 ret = -1;
5848 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005849 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00005850 /*
5851 * This node was already validated successfully against
5852 * this definition.
5853 */
5854 if (node->_private == define)
5855 break;
Daniel Veillard416589a2003-02-17 17:25:42 +00005856
5857 ret = xmlRelaxNGElementMatch(ctxt, define, node);
5858 if (ret <= 0) {
5859 ret = -1;
5860 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005861 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005862 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005863
5864 state = xmlRelaxNGNewValidState(ctxt, node);
5865 if (state == NULL) {
Daniel Veillard231d7912003-02-09 14:22:17 +00005866 ret = -1;
5867 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005868 }
5869
5870 oldstate = ctxt->state;
5871 ctxt->state = state;
5872 if (define->attrs != NULL) {
5873 tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
Daniel Veillard231d7912003-02-09 14:22:17 +00005874 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005875 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00005876#ifdef DEBUG
5877 xmlGenericError(xmlGenericErrorContext,
5878 "E: Element %s failed to validate attributes\n",
5879 node->name);
5880#endif
5881 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005882 }
5883 if (define->content != NULL) {
5884 tmp = xmlRelaxNGValidateElementContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00005885 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005886 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00005887#ifdef DEBUG
5888 xmlGenericError(xmlGenericErrorContext,
5889 "E: Element %s failed to validate element content\n",
5890 node->name);
5891#endif
5892 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005893 }
5894 state = ctxt->state;
5895 if (state->seq != NULL) {
5896 state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq);
5897 if (state->seq != NULL) {
5898 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005899 VALID_ERROR3("Extra content for element %s: %s\n",
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005900 node->name, state->seq->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005901 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00005902#ifdef DEBUG
5903 xmlGenericError(xmlGenericErrorContext,
5904 "E: Element %s has extra content: %s\n",
5905 node->name, state->seq->name);
5906#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00005907 }
5908 }
5909 for (i = 0;i < state->nbAttrs;i++) {
5910 if (state->attrs[i] != NULL) {
5911 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005912 VALID_ERROR3("Invalid attribute %s for element %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00005913 state->attrs[i]->name, node->name);
5914 ret = -1;
5915 }
5916 }
5917 ctxt->state = oldstate;
5918 xmlRelaxNGFreeValidState(state);
5919 if (oldstate != NULL)
Daniel Veillarde5b110b2003-02-04 14:43:39 +00005920 oldstate->seq = xmlRelaxNGSkipIgnored(ctxt, node->next);
5921 if (ret == 0)
5922 node->_private = define;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005923
5924
5925#ifdef DEBUG
5926 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard231d7912003-02-09 14:22:17 +00005927 "xmlRelaxNGValidateDefinition(): validated %s : %d",
Daniel Veillard6eadf632003-01-23 18:29:16 +00005928 node->name, ret);
Daniel Veillard231d7912003-02-09 14:22:17 +00005929 if (oldstate == NULL)
5930 xmlGenericError(xmlGenericErrorContext, ": no state\n");
5931 else if (oldstate->seq == NULL)
5932 xmlGenericError(xmlGenericErrorContext, ": done\n");
5933 else if (oldstate->seq->type == XML_ELEMENT_NODE)
5934 xmlGenericError(xmlGenericErrorContext, ": next elem %s\n",
5935 oldstate->seq->name);
5936 else
5937 xmlGenericError(xmlGenericErrorContext, ": next %s %d\n",
5938 oldstate->seq->name, oldstate->seq->type);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005939#endif
5940 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005941 case XML_RELAXNG_OPTIONAL:
5942 oldflags = ctxt->flags;
5943 ctxt->flags |= FLAGS_IGNORABLE;
5944 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5945 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5946 if (ret != 0) {
5947 xmlRelaxNGFreeValidState(ctxt->state);
5948 ctxt->state = oldstate;
5949 ret = 0;
5950 break;
5951 }
5952 xmlRelaxNGFreeValidState(oldstate);
5953 ctxt->flags = oldflags;
5954 ret = 0;
5955 break;
5956 case XML_RELAXNG_ONEORMORE:
5957 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5958 if (ret != 0) {
5959 break;
5960 }
5961 /* no break on purpose */
Daniel Veillard276be4a2003-01-24 01:03:34 +00005962 case XML_RELAXNG_ZEROORMORE: {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005963 oldflags = ctxt->flags;
5964 ctxt->flags |= FLAGS_IGNORABLE;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005965 while (ctxt->state->nbAttrLeft != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005966 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5967 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5968 if (ret != 0) {
5969 xmlRelaxNGFreeValidState(ctxt->state);
5970 ctxt->state = oldstate;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005971 break;
5972 }
5973 xmlRelaxNGFreeValidState(oldstate);
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005974 }
5975 if (ret == 0) {
5976 /*
5977 * There is no attribute left to be consumed,
5978 * we can check the closure by looking at ctxt->state->seq
5979 */
5980 xmlNodePtr cur, temp;
5981
Daniel Veillard276be4a2003-01-24 01:03:34 +00005982 cur = ctxt->state->seq;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005983 temp = NULL;
5984 while ((cur != NULL) && (temp != cur)) {
5985 temp = cur;
5986 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5987 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5988 if (ret != 0) {
5989 xmlRelaxNGFreeValidState(ctxt->state);
5990 ctxt->state = oldstate;
5991 break;
5992 }
5993 xmlRelaxNGFreeValidState(oldstate);
5994 cur = ctxt->state->seq;
5995 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005996 }
5997 ctxt->flags = oldflags;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005998 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005999 break;
Daniel Veillard276be4a2003-01-24 01:03:34 +00006000 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006001 case XML_RELAXNG_CHOICE: {
6002 xmlRelaxNGDefinePtr list = define->content;
6003
6004 oldflags = ctxt->flags;
6005 ctxt->flags |= FLAGS_IGNORABLE;
6006
6007 while (list != NULL) {
6008 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6009 ret = xmlRelaxNGValidateDefinition(ctxt, list);
6010 if (ret == 0) {
6011 xmlRelaxNGFreeValidState(oldstate);
6012 break;
6013 }
6014 xmlRelaxNGFreeValidState(ctxt->state);
6015 ctxt->state = oldstate;
6016 list = list->next;
6017 }
6018 ctxt->flags = oldflags;
6019 break;
6020 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00006021 case XML_RELAXNG_DEF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00006022 case XML_RELAXNG_GROUP: {
6023 xmlRelaxNGDefinePtr list = define->content;
6024
6025 while (list != NULL) {
6026 ret = xmlRelaxNGValidateDefinition(ctxt, list);
6027 if (ret != 0)
6028 break;
6029 list = list->next;
6030 }
6031 break;
6032 }
6033 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006034 ret = xmlRelaxNGValidateInterleave(ctxt, define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006035 break;
6036 case XML_RELAXNG_ATTRIBUTE:
6037 ret = xmlRelaxNGValidateAttribute(ctxt, define);
6038 break;
6039 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00006040 case XML_RELAXNG_PARENTREF:
6041 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00006042 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6043 break;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006044 case XML_RELAXNG_DATATYPE: {
6045 xmlChar *content;
6046
6047 content = xmlNodeGetContent(node);
6048 ret = xmlRelaxNGValidateDatatype(ctxt, content, define);
6049 if (ret == -1) {
6050 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006051 VALID_ERROR2("internal error validating %s\n", define->name);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006052 } else if (ret == 0) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006053 if (node != NULL)
6054 ctxt->state->seq = node->next;
6055 else
6056 ctxt->state->seq = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006057 }
6058 /*
6059 * TODO cover the problems with
6060 * <p>12<!-- comment -->34</p>
6061 * TODO detect full element coverage at compilation time.
6062 */
6063 if ((node != NULL) && (node->next != NULL)) {
6064 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006065 VALID_ERROR2("The data does not cover the full element %s\n",
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006066 node->parent->name);
6067 ret = -1;
6068 }
6069 if (content != NULL)
6070 xmlFree(content);
6071 break;
6072 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00006073 case XML_RELAXNG_VALUE: {
6074 xmlChar *content;
6075 xmlChar *oldvalue;
6076
6077 content = xmlNodeGetContent(node);
6078 oldvalue = ctxt->state->value;
6079 ctxt->state->value = content;
6080 ret = xmlRelaxNGValidateValue(ctxt, define);
6081 ctxt->state->value = oldvalue;
6082 if (ret == -1) {
6083 VALID_CTXT();
Daniel Veillardd2298792003-02-14 16:54:11 +00006084 if (define->name != NULL) {
6085 VALID_ERROR2("error validating value %s\n", define->name);
6086 } else {
6087 VALID_ERROR("error validating value\n");
6088 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00006089 } else if (ret == 0) {
6090 ctxt->state->seq = node->next;
6091 }
6092 /*
6093 * TODO cover the problems with
6094 * <p>12<!-- comment -->34</p>
6095 * TODO detect full element coverage at compilation time.
6096 */
6097 if ((node != NULL) && (node->next != NULL)) {
6098 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006099 VALID_ERROR2("The value does not cover the full element %s\n",
Daniel Veillardea3f3982003-01-26 19:45:18 +00006100 node->parent->name);
6101 ret = -1;
6102 }
6103 if (content != NULL)
6104 xmlFree(content);
6105 break;
6106 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006107 case XML_RELAXNG_LIST: {
6108 xmlChar *content;
6109 xmlChar *oldvalue, *oldendvalue;
6110 int len;
Daniel Veillardea3f3982003-01-26 19:45:18 +00006111
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006112 content = xmlNodeGetContent(node);
6113 len = xmlStrlen(content);
6114 oldvalue = ctxt->state->value;
6115 oldendvalue = ctxt->state->endvalue;
6116 ctxt->state->value = content;
6117 ctxt->state->endvalue = content + len;
6118 ret = xmlRelaxNGValidateValue(ctxt, define);
6119 ctxt->state->value = oldvalue;
6120 ctxt->state->endvalue = oldendvalue;
6121 if (ret == -1) {
6122 VALID_CTXT();
6123 VALID_ERROR("internal error validating list\n");
6124 } else if (ret == 0) {
6125 ctxt->state->seq = node->next;
6126 }
6127 /*
6128 * TODO cover the problems with
6129 * <p>12<!-- comment -->34</p>
6130 * TODO detect full element coverage at compilation time.
6131 */
6132 if ((node != NULL) && (node->next != NULL)) {
6133 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006134 VALID_ERROR2("The list does not cover the full element %s\n",
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006135 node->parent->name);
6136 ret = -1;
6137 }
6138 if (content != NULL)
6139 xmlFree(content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006140 break;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006141 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006142#if 0
6143 case XML_RELAXNG_MIXED:
6144 TODO
6145 ret = -1;
6146 break;
6147#endif
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006148 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00006149 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006150 TODO
Daniel Veillard416589a2003-02-17 17:25:42 +00006151 ret = -1;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006152 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006153 }
Daniel Veillard231d7912003-02-09 14:22:17 +00006154 ctxt->depth--;
6155#ifdef DEBUG
6156 for (i = 0;i < ctxt->depth;i++)
6157 xmlGenericError(xmlGenericErrorContext, " ");
6158 xmlGenericError(xmlGenericErrorContext,
6159 "Validating %s ", xmlRelaxNGDefName(define));
6160 if (define->name != NULL)
6161 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
6162 if (ret == 0)
6163 xmlGenericError(xmlGenericErrorContext, "suceeded\n");
6164 else
6165 xmlGenericError(xmlGenericErrorContext, "failed\n");
6166#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00006167 return(ret);
6168}
6169
6170/**
6171 * xmlRelaxNGValidateDocument:
6172 * @ctxt: a Relax-NG validation context
6173 * @doc: the document
6174 *
6175 * Validate the given document
6176 *
6177 * Returns 0 if the validation succeeded or an error code.
6178 */
6179static int
6180xmlRelaxNGValidateDocument(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
6181 int ret;
6182 xmlRelaxNGPtr schema;
6183 xmlRelaxNGGrammarPtr grammar;
6184 xmlRelaxNGValidStatePtr state;
6185
6186 if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL))
6187 return(-1);
6188
6189 schema = ctxt->schema;
6190 grammar = schema->topgrammar;
6191 if (grammar == NULL) {
6192 VALID_CTXT();
6193 VALID_ERROR("No top grammar defined\n");
6194 return(-1);
6195 }
6196 state = xmlRelaxNGNewValidState(ctxt, NULL);
6197 ctxt->state = state;
6198 ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start);
6199 state = ctxt->state;
6200 if ((state != NULL) && (state->seq != NULL)) {
6201 xmlNodePtr node;
6202
6203 node = state->seq;
6204 node = xmlRelaxNGSkipIgnored(ctxt, node);
6205 if (node != NULL) {
6206 VALID_CTXT();
6207 VALID_ERROR("extra data on the document\n");
6208 ret = -1;
6209 }
6210 }
6211 xmlRelaxNGFreeValidState(state);
6212
6213 return(ret);
6214}
6215
6216/************************************************************************
6217 * *
6218 * Validation interfaces *
6219 * *
6220 ************************************************************************/
6221/**
6222 * xmlRelaxNGNewValidCtxt:
6223 * @schema: a precompiled XML RelaxNGs
6224 *
6225 * Create an XML RelaxNGs validation context based on the given schema
6226 *
6227 * Returns the validation context or NULL in case of error
6228 */
6229xmlRelaxNGValidCtxtPtr
6230xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema) {
6231 xmlRelaxNGValidCtxtPtr ret;
6232
6233 ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt));
6234 if (ret == NULL) {
6235 xmlGenericError(xmlGenericErrorContext,
6236 "Failed to allocate new schama validation context\n");
6237 return (NULL);
6238 }
6239 memset(ret, 0, sizeof(xmlRelaxNGValidCtxt));
6240 ret->schema = schema;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006241 ret->error = xmlGenericError;
6242 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006243 return (ret);
6244}
6245
6246/**
6247 * xmlRelaxNGFreeValidCtxt:
6248 * @ctxt: the schema validation context
6249 *
6250 * Free the resources associated to the schema validation context
6251 */
6252void
6253xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt) {
6254 if (ctxt == NULL)
6255 return;
6256 xmlFree(ctxt);
6257}
6258
6259/**
6260 * xmlRelaxNGSetValidErrors:
6261 * @ctxt: a Relax-NG validation context
6262 * @err: the error function
6263 * @warn: the warning function
6264 * @ctx: the functions context
6265 *
6266 * Set the error and warning callback informations
6267 */
6268void
6269xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
6270 xmlRelaxNGValidityErrorFunc err,
6271 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
6272 if (ctxt == NULL)
6273 return;
6274 ctxt->error = err;
6275 ctxt->warning = warn;
6276 ctxt->userData = ctx;
6277}
6278
6279/**
6280 * xmlRelaxNGValidateDoc:
6281 * @ctxt: a Relax-NG validation context
6282 * @doc: a parsed document tree
6283 *
6284 * Validate a document tree in memory.
6285 *
6286 * Returns 0 if the document is valid, a positive error code
6287 * number otherwise and -1 in case of internal or API error.
6288 */
6289int
6290xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
6291 int ret;
6292
6293 if ((ctxt == NULL) || (doc == NULL))
6294 return(-1);
6295
6296 ctxt->doc = doc;
6297
6298 ret = xmlRelaxNGValidateDocument(ctxt, doc);
Daniel Veillard71531f32003-02-05 13:19:53 +00006299 /*
6300 * TODO: build error codes
6301 */
6302 if (ret == -1)
6303 return(1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006304 return(ret);
6305}
6306
6307#endif /* LIBXML_SCHEMAS_ENABLED */
6308