blob: 5b0efab958b9ed8a621c59472144a1fe2943233b [file] [log] [blame]
Daniel Veillard6eadf632003-01-23 18:29:16 +00001/*
2 * relaxng.c : implementation of the Relax-NG handling and validity checking
3 *
4 * See Copyright for the status of this software.
5 *
6 * Daniel Veillard <veillard@redhat.com>
7 */
8
Daniel Veillardd41f4f42003-01-29 21:07:52 +00009/**
10 * TODO:
Daniel Veillardd41f4f42003-01-29 21:07:52 +000011 * - error reporting
Daniel Veillarde2a5a082003-02-02 14:35:17 +000012 * - simplification of the resulting compiled trees:
13 * - NOT_ALLOWED
14 * - EMPTY
Daniel Veillard1ed7f362003-02-03 10:57:45 +000015 * - handle namespace declarations as attributes.
Daniel Veillardf4b4f982003-02-13 11:02:08 +000016 * - add support for DTD compatibility spec
17 * http://www.oasis-open.org/committees/relax-ng/compatibility-20011203.html
Daniel Veillardd41f4f42003-01-29 21:07:52 +000018 */
19
Daniel Veillard6eadf632003-01-23 18:29:16 +000020#define IN_LIBXML
21#include "libxml.h"
22
23#ifdef LIBXML_SCHEMAS_ENABLED
24
25#include <string.h>
26#include <stdio.h>
27#include <libxml/xmlmemory.h>
28#include <libxml/parser.h>
29#include <libxml/parserInternals.h>
30#include <libxml/hash.h>
31#include <libxml/uri.h>
32
33#include <libxml/relaxng.h>
34
35#include <libxml/xmlschemastypes.h>
36#include <libxml/xmlautomata.h>
37#include <libxml/xmlregexp.h>
Daniel Veillardc6e997c2003-01-27 12:35:42 +000038#include <libxml/xmlschemastypes.h>
Daniel Veillard6eadf632003-01-23 18:29:16 +000039
40/*
41 * The Relax-NG namespace
42 */
43static const xmlChar *xmlRelaxNGNs = (const xmlChar *)
44 "http://relaxng.org/ns/structure/1.0";
45
46#define IS_RELAXNG(node, type) \
47 ((node != NULL) && (node->ns != NULL) && \
48 (xmlStrEqual(node->name, (const xmlChar *) type)) && \
49 (xmlStrEqual(node->ns->href, xmlRelaxNGNs)))
50
51
Daniel Veillard71531f32003-02-05 13:19:53 +000052/* #define DEBUG 1 */ /* very verbose output */
53/* #define DEBUG_CONTENT 1 */
54/* #define DEBUG_TYPE 1 */
55/* #define DEBUG_VALID 1 */
Daniel Veillarde5b110b2003-02-04 14:43:39 +000056/* #define DEBUG_INTERLEAVE 1 */
Daniel Veillard416589a2003-02-17 17:25:42 +000057/* #define DEBUG_LIST 1 */
Daniel Veillard6eadf632003-01-23 18:29:16 +000058
59#define UNBOUNDED (1 << 30)
60#define TODO \
61 xmlGenericError(xmlGenericErrorContext, \
62 "Unimplemented block at %s:%d\n", \
63 __FILE__, __LINE__);
64
65typedef struct _xmlRelaxNGSchema xmlRelaxNGSchema;
66typedef xmlRelaxNGSchema *xmlRelaxNGSchemaPtr;
67
68typedef struct _xmlRelaxNGDefine xmlRelaxNGDefine;
69typedef xmlRelaxNGDefine *xmlRelaxNGDefinePtr;
70
Daniel Veillardd41f4f42003-01-29 21:07:52 +000071typedef struct _xmlRelaxNGDocument xmlRelaxNGDocument;
72typedef xmlRelaxNGDocument *xmlRelaxNGDocumentPtr;
73
Daniel Veillarda9d912d2003-02-01 17:43:10 +000074typedef struct _xmlRelaxNGInclude xmlRelaxNGInclude;
75typedef xmlRelaxNGInclude *xmlRelaxNGIncludePtr;
76
Daniel Veillard6eadf632003-01-23 18:29:16 +000077typedef enum {
78 XML_RELAXNG_COMBINE_UNDEFINED = 0, /* undefined */
79 XML_RELAXNG_COMBINE_CHOICE, /* choice */
80 XML_RELAXNG_COMBINE_INTERLEAVE /* interleave */
81} xmlRelaxNGCombine;
82
83typedef struct _xmlRelaxNGGrammar xmlRelaxNGGrammar;
84typedef xmlRelaxNGGrammar *xmlRelaxNGGrammarPtr;
85
86struct _xmlRelaxNGGrammar {
87 xmlRelaxNGGrammarPtr parent;/* the parent grammar if any */
88 xmlRelaxNGGrammarPtr children;/* the children grammar if any */
89 xmlRelaxNGGrammarPtr next; /* the next grammar if any */
90 xmlRelaxNGDefinePtr start; /* <start> content */
91 xmlRelaxNGCombine combine; /* the default combine value */
Daniel Veillardd41f4f42003-01-29 21:07:52 +000092 xmlRelaxNGDefinePtr startList;/* list of <start> definitions */
Daniel Veillard6eadf632003-01-23 18:29:16 +000093 xmlHashTablePtr defs; /* define* */
94 xmlHashTablePtr refs; /* references */
95};
96
97
Daniel Veillard6eadf632003-01-23 18:29:16 +000098typedef enum {
99 XML_RELAXNG_EMPTY = 0, /* an empty pattern */
100 XML_RELAXNG_NOT_ALLOWED, /* not allowed top */
Daniel Veillard144fae12003-02-03 13:17:57 +0000101 XML_RELAXNG_EXCEPT, /* except present in nameclass defs */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000102 XML_RELAXNG_TEXT, /* textual content */
103 XML_RELAXNG_ELEMENT, /* an element */
104 XML_RELAXNG_DATATYPE, /* extenal data type definition */
105 XML_RELAXNG_VALUE, /* value from an extenal data type definition */
106 XML_RELAXNG_LIST, /* a list of patterns */
107 XML_RELAXNG_ATTRIBUTE, /* an attrbute following a pattern */
108 XML_RELAXNG_DEF, /* a definition */
109 XML_RELAXNG_REF, /* reference to a definition */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000110 XML_RELAXNG_EXTERNALREF, /* reference to an external def */
Daniel Veillard419a7682003-02-03 23:22:49 +0000111 XML_RELAXNG_PARENTREF, /* reference to a def in the parent grammar */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000112 XML_RELAXNG_OPTIONAL, /* optional patterns */
113 XML_RELAXNG_ZEROORMORE, /* zero or more non empty patterns */
114 XML_RELAXNG_ONEORMORE, /* one or more non empty patterns */
115 XML_RELAXNG_CHOICE, /* a choice between non empty patterns */
116 XML_RELAXNG_GROUP, /* a pair/group of non empty patterns */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000117 XML_RELAXNG_INTERLEAVE, /* interleaving choice of non-empty patterns */
118 XML_RELAXNG_START /* Used to keep track of starts on grammars */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000119} xmlRelaxNGType;
120
121struct _xmlRelaxNGDefine {
122 xmlRelaxNGType type; /* the type of definition */
123 xmlNodePtr node; /* the node in the source */
124 xmlChar *name; /* the element local name if present */
125 xmlChar *ns; /* the namespace local name if present */
Daniel Veillardedc91922003-01-26 00:52:04 +0000126 xmlChar *value; /* value when available */
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000127 void *data; /* data lib or specific pointer */
Daniel Veillardd4310742003-02-18 21:12:46 +0000128 int depth; /* used for the cycle detection */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000129 xmlRelaxNGDefinePtr content;/* the expected content */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000130 xmlRelaxNGDefinePtr parent; /* the parent definition, if any */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000131 xmlRelaxNGDefinePtr next; /* list within grouping sequences */
132 xmlRelaxNGDefinePtr attrs; /* list of attributes for elements */
Daniel Veillard3b2e4e12003-02-03 08:52:58 +0000133 xmlRelaxNGDefinePtr nameClass;/* the nameClass definition if any */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000134 xmlRelaxNGDefinePtr nextHash;/* next define in defs/refs hash tables */
135};
136
137/**
138 * _xmlRelaxNG:
139 *
140 * A RelaxNGs definition
141 */
142struct _xmlRelaxNG {
143 xmlRelaxNGGrammarPtr topgrammar;
144 xmlDocPtr doc;
145
146 xmlHashTablePtr defs; /* define */
147 xmlHashTablePtr refs; /* references */
Daniel Veillard419a7682003-02-03 23:22:49 +0000148 xmlHashTablePtr documents; /* all the documents loaded */
149 xmlHashTablePtr includes; /* all the includes loaded */
150 int defNr; /* number of defines used */
151 xmlRelaxNGDefinePtr *defTab;/* pointer to the allocated definitions */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000152 void *_private; /* unused by the library for users or bindings */
153};
154
155typedef enum {
156 XML_RELAXNG_ERR_OK = 0,
157 XML_RELAXNG_ERR_NOROOT = 1,
158 XML_RELAXNG_ERR_
159} xmlRelaxNGValidError;
160
161#define XML_RELAXNG_IN_ATTRIBUTE 1
162
163struct _xmlRelaxNGParserCtxt {
164 void *userData; /* user specific data block */
165 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
166 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
167 xmlRelaxNGValidError err;
168
169 xmlRelaxNGPtr schema; /* The schema in use */
170 xmlRelaxNGGrammarPtr grammar; /* the current grammar */
Daniel Veillard419a7682003-02-03 23:22:49 +0000171 xmlRelaxNGGrammarPtr parentgrammar;/* the parent grammar */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000172 int flags; /* parser flags */
173 int nbErrors; /* number of errors at parse time */
174 int nbWarnings; /* number of warnings at parse time */
Daniel Veillard276be4a2003-01-24 01:03:34 +0000175 const xmlChar *define; /* the current define scope */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000176 xmlRelaxNGDefinePtr def; /* the current define */
177
178 int nbInterleaves;
179 xmlHashTablePtr interleaves; /* keep track of all the interleaves */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000180
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000181 xmlHashTablePtr documents; /* all the documents loaded */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000182 xmlHashTablePtr includes; /* all the includes loaded */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000183 xmlChar *URL;
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000184 xmlDocPtr document;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000185
Daniel Veillard419a7682003-02-03 23:22:49 +0000186 int defNr; /* number of defines used */
187 int defMax; /* number of defines aloocated */
188 xmlRelaxNGDefinePtr *defTab; /* pointer to the allocated definitions */
189
Daniel Veillard6eadf632003-01-23 18:29:16 +0000190 const char *buffer;
191 int size;
192
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000193 /* the document stack */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000194 xmlRelaxNGDocumentPtr doc; /* Current parsed external ref */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000195 int docNr; /* Depth of the parsing stack */
196 int docMax; /* Max depth of the parsing stack */
197 xmlRelaxNGDocumentPtr *docTab; /* array of docs */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000198
199 /* the include stack */
Daniel Veillardd4310742003-02-18 21:12:46 +0000200 xmlRelaxNGIncludePtr inc; /* Current parsed include */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000201 int incNr; /* Depth of the include parsing stack */
202 int incMax; /* Max depth of the parsing stack */
203 xmlRelaxNGIncludePtr *incTab; /* array of incs */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000204};
205
206#define FLAGS_IGNORABLE 1
207#define FLAGS_NEGATIVE 2
208
209/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000210 * xmlRelaxNGInterleaveGroup:
211 *
212 * A RelaxNGs partition set associated to lists of definitions
213 */
214typedef struct _xmlRelaxNGInterleaveGroup xmlRelaxNGInterleaveGroup;
215typedef xmlRelaxNGInterleaveGroup *xmlRelaxNGInterleaveGroupPtr;
216struct _xmlRelaxNGInterleaveGroup {
217 xmlRelaxNGDefinePtr rule; /* the rule to satisfy */
218 xmlRelaxNGDefinePtr *defs; /* the array of element definitions */
219};
220
221/**
222 * xmlRelaxNGPartitions:
223 *
224 * A RelaxNGs partition associated to an interleave group
225 */
226typedef struct _xmlRelaxNGPartition xmlRelaxNGPartition;
227typedef xmlRelaxNGPartition *xmlRelaxNGPartitionPtr;
228struct _xmlRelaxNGPartition {
229 int nbgroups; /* number of groups in the partitions */
230 xmlRelaxNGInterleaveGroupPtr *groups;
231};
232
233/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000234 * xmlRelaxNGValidState:
235 *
236 * A RelaxNGs validation state
237 */
238#define MAX_ATTR 20
239typedef struct _xmlRelaxNGValidState xmlRelaxNGValidState;
240typedef xmlRelaxNGValidState *xmlRelaxNGValidStatePtr;
241struct _xmlRelaxNGValidState {
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000242 xmlNodePtr node; /* the current node */
243 xmlNodePtr seq; /* the sequence of children left to validate */
244 int nbAttrs; /* the number of attributes */
Daniel Veillard1ed7f362003-02-03 10:57:45 +0000245 int nbAttrLeft; /* the number of attributes left to validate */
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000246 xmlChar *value; /* the value when operating on string */
247 xmlChar *endvalue; /* the end value when operating on string */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000248 xmlAttrPtr attrs[1]; /* the array of attributes */
249};
250
251/**
252 * xmlRelaxNGValidCtxt:
253 *
254 * A RelaxNGs validation context
255 */
256
257struct _xmlRelaxNGValidCtxt {
258 void *userData; /* user specific data block */
259 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
260 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
261
262 xmlRelaxNGPtr schema; /* The schema in use */
263 xmlDocPtr doc; /* the document being validated */
264 xmlRelaxNGValidStatePtr state; /* the current validation state */
265 int flags; /* validation flags */
Daniel Veillard231d7912003-02-09 14:22:17 +0000266 int depth; /* validation depth */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000267};
268
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000269/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000270 * xmlRelaxNGInclude:
271 *
272 * Structure associated to a RelaxNGs document element
273 */
274struct _xmlRelaxNGInclude {
275 xmlChar *href; /* the normalized href value */
276 xmlDocPtr doc; /* the associated XML document */
277 xmlRelaxNGDefinePtr content;/* the definitions */
278 xmlRelaxNGPtr schema; /* the schema */
279};
280
281/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000282 * xmlRelaxNGDocument:
283 *
284 * Structure associated to a RelaxNGs document element
285 */
286struct _xmlRelaxNGDocument {
287 xmlChar *href; /* the normalized href value */
288 xmlDocPtr doc; /* the associated XML document */
289 xmlRelaxNGDefinePtr content;/* the definitions */
290 xmlRelaxNGPtr schema; /* the schema */
291};
292
Daniel Veillard6eadf632003-01-23 18:29:16 +0000293/************************************************************************
294 * *
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000295 * Preliminary type checking interfaces *
296 * *
297 ************************************************************************/
298/**
299 * xmlRelaxNGTypeHave:
300 * @data: data needed for the library
301 * @type: the type name
302 * @value: the value to check
303 *
304 * Function provided by a type library to check if a type is exported
305 *
306 * Returns 1 if yes, 0 if no and -1 in case of error.
307 */
308typedef int (*xmlRelaxNGTypeHave) (void *data, const xmlChar *type);
309
310/**
311 * xmlRelaxNGTypeCheck:
312 * @data: data needed for the library
313 * @type: the type name
314 * @value: the value to check
315 *
316 * Function provided by a type library to check if a value match a type
317 *
318 * Returns 1 if yes, 0 if no and -1 in case of error.
319 */
320typedef int (*xmlRelaxNGTypeCheck) (void *data, const xmlChar *type,
321 const xmlChar *value);
322
323/**
324 * xmlRelaxNGTypeCompare:
325 * @data: data needed for the library
326 * @type: the type name
327 * @value1: the first value
328 * @value2: the second value
329 *
330 * Function provided by a type library to compare two values accordingly
331 * to a type.
332 *
333 * Returns 1 if yes, 0 if no and -1 in case of error.
334 */
335typedef int (*xmlRelaxNGTypeCompare) (void *data, const xmlChar *type,
336 const xmlChar *value1,
337 const xmlChar *value2);
338typedef struct _xmlRelaxNGTypeLibrary xmlRelaxNGTypeLibrary;
339typedef xmlRelaxNGTypeLibrary *xmlRelaxNGTypeLibraryPtr;
340struct _xmlRelaxNGTypeLibrary {
341 const xmlChar *namespace; /* the datatypeLibrary value */
342 void *data; /* data needed for the library */
343 xmlRelaxNGTypeHave have; /* the export function */
344 xmlRelaxNGTypeCheck check; /* the checking function */
345 xmlRelaxNGTypeCompare comp; /* the compare function */
346};
347
348/************************************************************************
349 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +0000350 * Allocation functions *
351 * *
352 ************************************************************************/
Daniel Veillard6eadf632003-01-23 18:29:16 +0000353static void xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar);
354static void xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define);
Daniel Veillardd2298792003-02-14 16:54:11 +0000355static void xmlRelaxNGNormExtSpace(xmlChar *value);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000356
357/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000358 * xmlRelaxNGFreeDocument:
359 * @docu: a document structure
360 *
361 * Deallocate a RelaxNG document structure.
362 */
363static void
364xmlRelaxNGFreeDocument(xmlRelaxNGDocumentPtr docu)
365{
366 if (docu == NULL)
367 return;
368
369 if (docu->href != NULL)
370 xmlFree(docu->href);
371 if (docu->doc != NULL)
372 xmlFreeDoc(docu->doc);
373 if (docu->schema != NULL)
374 xmlRelaxNGFree(docu->schema);
375 xmlFree(docu);
376}
377
378/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000379 * xmlRelaxNGFreeInclude:
380 * @incl: a include structure
381 *
382 * Deallocate a RelaxNG include structure.
383 */
384static void
385xmlRelaxNGFreeInclude(xmlRelaxNGIncludePtr incl)
386{
387 if (incl == NULL)
388 return;
389
390 if (incl->href != NULL)
391 xmlFree(incl->href);
392 if (incl->doc != NULL)
393 xmlFreeDoc(incl->doc);
394 if (incl->schema != NULL)
395 xmlRelaxNGFree(incl->schema);
396 xmlFree(incl);
397}
398
399/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000400 * xmlRelaxNGNewRelaxNG:
401 * @ctxt: a Relax-NG validation context (optional)
402 *
403 * Allocate a new RelaxNG structure.
404 *
405 * Returns the newly allocated structure or NULL in case or error
406 */
407static xmlRelaxNGPtr
408xmlRelaxNGNewRelaxNG(xmlRelaxNGParserCtxtPtr ctxt)
409{
410 xmlRelaxNGPtr ret;
411
412 ret = (xmlRelaxNGPtr) xmlMalloc(sizeof(xmlRelaxNG));
413 if (ret == NULL) {
414 if ((ctxt != NULL) && (ctxt->error != NULL))
415 ctxt->error(ctxt->userData, "Out of memory\n");
416 ctxt->nbErrors++;
417 return (NULL);
418 }
419 memset(ret, 0, sizeof(xmlRelaxNG));
420
421 return (ret);
422}
423
424/**
425 * xmlRelaxNGFree:
426 * @schema: a schema structure
427 *
428 * Deallocate a RelaxNG structure.
429 */
430void
431xmlRelaxNGFree(xmlRelaxNGPtr schema)
432{
433 if (schema == NULL)
434 return;
435
Daniel Veillard6eadf632003-01-23 18:29:16 +0000436 if (schema->topgrammar != NULL)
437 xmlRelaxNGFreeGrammar(schema->topgrammar);
438 if (schema->doc != NULL)
439 xmlFreeDoc(schema->doc);
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000440 if (schema->documents != NULL)
441 xmlHashFree(schema->documents, (xmlHashDeallocator)
442 xmlRelaxNGFreeDocument);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000443 if (schema->includes != NULL)
444 xmlHashFree(schema->includes, (xmlHashDeallocator)
445 xmlRelaxNGFreeInclude);
Daniel Veillard419a7682003-02-03 23:22:49 +0000446 if (schema->defTab != NULL) {
447 int i;
448
449 for (i = 0;i < schema->defNr;i++)
450 xmlRelaxNGFreeDefine(schema->defTab[i]);
451 xmlFree(schema->defTab);
452 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000453
454 xmlFree(schema);
455}
456
457/**
458 * xmlRelaxNGNewGrammar:
459 * @ctxt: a Relax-NG validation context (optional)
460 *
461 * Allocate a new RelaxNG grammar.
462 *
463 * Returns the newly allocated structure or NULL in case or error
464 */
465static xmlRelaxNGGrammarPtr
466xmlRelaxNGNewGrammar(xmlRelaxNGParserCtxtPtr ctxt)
467{
468 xmlRelaxNGGrammarPtr ret;
469
470 ret = (xmlRelaxNGGrammarPtr) xmlMalloc(sizeof(xmlRelaxNGGrammar));
471 if (ret == NULL) {
472 if ((ctxt != NULL) && (ctxt->error != NULL))
473 ctxt->error(ctxt->userData, "Out of memory\n");
474 ctxt->nbErrors++;
475 return (NULL);
476 }
477 memset(ret, 0, sizeof(xmlRelaxNGGrammar));
478
479 return (ret);
480}
481
482/**
483 * xmlRelaxNGFreeGrammar:
484 * @grammar: a grammar structure
485 *
486 * Deallocate a RelaxNG grammar structure.
487 */
488static void
489xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar)
490{
491 if (grammar == NULL)
492 return;
493
Daniel Veillard419a7682003-02-03 23:22:49 +0000494 if (grammar->next != NULL) {
495 xmlRelaxNGFreeGrammar(grammar->next);
496 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000497 if (grammar->refs != NULL) {
498 xmlHashFree(grammar->refs, NULL);
499 }
500 if (grammar->defs != NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000501 xmlHashFree(grammar->defs, NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000502 }
503
504 xmlFree(grammar);
505}
506
507/**
508 * xmlRelaxNGNewDefine:
509 * @ctxt: a Relax-NG validation context
510 * @node: the node in the input document.
511 *
512 * Allocate a new RelaxNG define.
513 *
514 * Returns the newly allocated structure or NULL in case or error
515 */
516static xmlRelaxNGDefinePtr
517xmlRelaxNGNewDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
518{
519 xmlRelaxNGDefinePtr ret;
520
Daniel Veillard419a7682003-02-03 23:22:49 +0000521 if (ctxt->defMax == 0) {
522 ctxt->defMax = 16;
523 ctxt->defNr = 0;
524 ctxt->defTab = (xmlRelaxNGDefinePtr *)
525 xmlMalloc(ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
526 if (ctxt->defTab == NULL) {
527 if ((ctxt != NULL) && (ctxt->error != NULL))
528 ctxt->error(ctxt->userData, "Out of memory\n");
529 ctxt->nbErrors++;
530 return (NULL);
531 }
532 } else if (ctxt->defMax <= ctxt->defNr) {
533 xmlRelaxNGDefinePtr *tmp;
534 ctxt->defMax *= 2;
535 tmp = (xmlRelaxNGDefinePtr *) xmlRealloc(ctxt->defTab,
536 ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
537 if (tmp == NULL) {
538 if ((ctxt != NULL) && (ctxt->error != NULL))
539 ctxt->error(ctxt->userData, "Out of memory\n");
540 ctxt->nbErrors++;
541 return (NULL);
542 }
543 ctxt->defTab = tmp;
544 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000545 ret = (xmlRelaxNGDefinePtr) xmlMalloc(sizeof(xmlRelaxNGDefine));
546 if (ret == NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000547 if ((ctxt != NULL) && (ctxt->error != NULL))
548 ctxt->error(ctxt->userData, "Out of memory\n");
Daniel Veillard6eadf632003-01-23 18:29:16 +0000549 ctxt->nbErrors++;
Daniel Veillard419a7682003-02-03 23:22:49 +0000550 return(NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000551 }
552 memset(ret, 0, sizeof(xmlRelaxNGDefine));
Daniel Veillard419a7682003-02-03 23:22:49 +0000553 ctxt->defTab[ctxt->defNr++] = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000554 ret->node = node;
Daniel Veillardd4310742003-02-18 21:12:46 +0000555 ret->depth = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000556 return (ret);
557}
558
559/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000560 * xmlRelaxNGFreePartition:
561 * @partitions: a partition set structure
562 *
563 * Deallocate RelaxNG partition set structures.
564 */
565static void
566xmlRelaxNGFreePartition(xmlRelaxNGPartitionPtr partitions) {
567 xmlRelaxNGInterleaveGroupPtr group;
568 int j;
569
570 if (partitions != NULL) {
571 if (partitions->groups != NULL) {
572 for (j = 0;j < partitions->nbgroups;j++) {
573 group = partitions->groups[j];
574 if (group != NULL) {
575 if (group->defs != NULL)
576 xmlFree(group->defs);
577 xmlFree(group);
578 }
579 }
580 xmlFree(partitions->groups);
581 }
582 xmlFree(partitions);
583 }
584}
585/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000586 * xmlRelaxNGFreeDefine:
587 * @define: a define structure
588 *
589 * Deallocate a RelaxNG define structure.
590 */
591static void
592xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define)
593{
594 if (define == NULL)
595 return;
596
Daniel Veillard419a7682003-02-03 23:22:49 +0000597 if ((define->data != NULL) &&
598 (define->type == XML_RELAXNG_INTERLEAVE))
599 xmlRelaxNGFreePartition((xmlRelaxNGPartitionPtr) define->data);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000600 if (define->name != NULL)
601 xmlFree(define->name);
602 if (define->ns != NULL)
603 xmlFree(define->ns);
Daniel Veillardedc91922003-01-26 00:52:04 +0000604 if (define->value != NULL)
605 xmlFree(define->value);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000606 xmlFree(define);
607}
608
609/**
610 * xmlRelaxNGNewValidState:
611 * @ctxt: a Relax-NG validation context
612 * @node: the current node or NULL for the document
613 *
614 * Allocate a new RelaxNG validation state
615 *
616 * Returns the newly allocated structure or NULL in case or error
617 */
618static xmlRelaxNGValidStatePtr
619xmlRelaxNGNewValidState(xmlRelaxNGValidCtxtPtr ctxt, xmlNodePtr node)
620{
621 xmlRelaxNGValidStatePtr ret;
622 xmlAttrPtr attr;
623 xmlAttrPtr attrs[MAX_ATTR];
624 int nbAttrs = 0;
625 xmlNodePtr root = NULL;
626
627 if (node == NULL) {
628 root = xmlDocGetRootElement(ctxt->doc);
629 if (root == NULL)
630 return(NULL);
631 } else {
632 attr = node->properties;
633 while (attr != NULL) {
634 if (nbAttrs < MAX_ATTR)
635 attrs[nbAttrs++] = attr;
636 else
637 nbAttrs++;
638 attr = attr->next;
639 }
640 }
641
642 if (nbAttrs < MAX_ATTR)
643 attrs[nbAttrs] = NULL;
644 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(sizeof(xmlRelaxNGValidState) +
645 nbAttrs * sizeof(xmlAttrPtr));
646 if (ret == NULL) {
647 if ((ctxt != NULL) && (ctxt->error != NULL))
648 ctxt->error(ctxt->userData, "Out of memory\n");
649 return (NULL);
650 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +0000651 ret->value = NULL;
652 ret->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000653 if (node == NULL) {
654 ret->node = (xmlNodePtr) ctxt->doc;
655 ret->seq = root;
656 ret->nbAttrs = 0;
657 } else {
658 ret->node = node;
659 ret->seq = node->children;
660 ret->nbAttrs = nbAttrs;
661 if (nbAttrs > 0) {
662 if (nbAttrs < MAX_ATTR) {
663 memcpy(&(ret->attrs[0]), attrs,
664 sizeof(xmlAttrPtr) * (nbAttrs + 1));
665 } else {
666 attr = node->properties;
667 nbAttrs = 0;
668 while (attr != NULL) {
669 ret->attrs[nbAttrs++] = attr;
670 attr = attr->next;
671 }
672 ret->attrs[nbAttrs] = NULL;
673 }
674 }
675 }
Daniel Veillard1ed7f362003-02-03 10:57:45 +0000676 ret->nbAttrLeft = ret->nbAttrs;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000677 return (ret);
678}
679
680/**
681 * xmlRelaxNGCopyValidState:
682 * @ctxt: a Relax-NG validation context
683 * @state: a validation state
684 *
685 * Copy the validation state
686 *
687 * Returns the newly allocated structure or NULL in case or error
688 */
689static xmlRelaxNGValidStatePtr
690xmlRelaxNGCopyValidState(xmlRelaxNGValidCtxtPtr ctxt,
691 xmlRelaxNGValidStatePtr state)
692{
693 xmlRelaxNGValidStatePtr ret;
694 unsigned int size;
695
696 if (state == NULL)
697 return(NULL);
698
699 size = sizeof(xmlRelaxNGValidState) +
700 state->nbAttrs * sizeof(xmlAttrPtr);
701 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(size);
702 if (ret == NULL) {
703 if ((ctxt != NULL) && (ctxt->error != NULL))
704 ctxt->error(ctxt->userData, "Out of memory\n");
705 return (NULL);
706 }
707 memcpy(ret, state, size);
708 return(ret);
709}
710
711/**
712 * xmlRelaxNGFreeValidState:
713 * @state: a validation state structure
714 *
715 * Deallocate a RelaxNG validation state structure.
716 */
717static void
718xmlRelaxNGFreeValidState(xmlRelaxNGValidStatePtr state)
719{
720 if (state == NULL)
721 return;
722
723 xmlFree(state);
724}
725
726/************************************************************************
727 * *
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000728 * Document functions *
729 * *
730 ************************************************************************/
731static xmlDocPtr xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt,
732 xmlDocPtr doc);
733
734/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000735 * xmlRelaxNGIncludePush:
736 * @ctxt: the parser context
737 * @value: the element doc
738 *
739 * Pushes a new include on top of the include stack
740 *
741 * Returns 0 in case of error, the index in the stack otherwise
742 */
743static int
744xmlRelaxNGIncludePush(xmlRelaxNGParserCtxtPtr ctxt,
745 xmlRelaxNGIncludePtr value)
746{
747 if (ctxt->incTab == NULL) {
748 ctxt->incMax = 4;
749 ctxt->incNr = 0;
750 ctxt->incTab = (xmlRelaxNGIncludePtr *) xmlMalloc(
751 ctxt->incMax * sizeof(ctxt->incTab[0]));
752 if (ctxt->incTab == NULL) {
753 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
754 return (0);
755 }
756 }
757 if (ctxt->incNr >= ctxt->incMax) {
758 ctxt->incMax *= 2;
759 ctxt->incTab =
760 (xmlRelaxNGIncludePtr *) xmlRealloc(ctxt->incTab,
761 ctxt->incMax *
762 sizeof(ctxt->incTab[0]));
763 if (ctxt->incTab == NULL) {
764 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
765 return (0);
766 }
767 }
768 ctxt->incTab[ctxt->incNr] = value;
769 ctxt->inc = value;
770 return (ctxt->incNr++);
771}
772
773/**
774 * xmlRelaxNGIncludePop:
775 * @ctxt: the parser context
776 *
777 * Pops the top include from the include stack
778 *
779 * Returns the include just removed
780 */
781static xmlRelaxNGIncludePtr
782xmlRelaxNGIncludePop(xmlRelaxNGParserCtxtPtr ctxt)
783{
784 xmlRelaxNGIncludePtr ret;
785
786 if (ctxt->incNr <= 0)
787 return (0);
788 ctxt->incNr--;
789 if (ctxt->incNr > 0)
790 ctxt->inc = ctxt->incTab[ctxt->incNr - 1];
791 else
792 ctxt->inc = NULL;
793 ret = ctxt->incTab[ctxt->incNr];
794 ctxt->incTab[ctxt->incNr] = 0;
795 return (ret);
796}
797
798/**
799 * xmlRelaxNGLoadInclude:
800 * @ctxt: the parser context
801 * @URL: the normalized URL
802 * @node: the include node.
Daniel Veillard416589a2003-02-17 17:25:42 +0000803 * @ns: the namespace passed from the context.
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000804 *
805 * First lookup if the document is already loaded into the parser context,
806 * check against recursion. If not found the resource is loaded and
807 * the content is preprocessed before being returned back to the caller.
808 *
809 * Returns the xmlRelaxNGIncludePtr or NULL in case of error
810 */
811static xmlRelaxNGIncludePtr
812xmlRelaxNGLoadInclude(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillard416589a2003-02-17 17:25:42 +0000813 xmlNodePtr node, const xmlChar *ns) {
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000814 xmlRelaxNGIncludePtr ret = NULL;
815 xmlDocPtr doc;
816 int i;
817 xmlNodePtr root, tmp, tmp2, cur;
818
819 /*
820 * check against recursion in the stack
821 */
822 for (i = 0;i < ctxt->incNr;i++) {
823 if (xmlStrEqual(ctxt->incTab[i]->href, URL)) {
824 if (ctxt->error != NULL)
825 ctxt->error(ctxt->userData,
826 "Detected an externalRef recursion for %s\n",
827 URL);
828 ctxt->nbErrors++;
829 return(NULL);
830 }
831 }
832
833 /*
834 * Lookup in the hash table
835 */
836 if (ctxt->includes == NULL) {
837 ctxt->includes = xmlHashCreate(10);
838 if (ctxt->includes == NULL) {
839 if (ctxt->error != NULL)
840 ctxt->error(ctxt->userData,
841 "Failed to allocate hash table for document\n");
842 ctxt->nbErrors++;
843 return(NULL);
844 }
845 } else {
Daniel Veillard416589a2003-02-17 17:25:42 +0000846 if (ns == NULL)
847 ret = xmlHashLookup2(ctxt->includes, BAD_CAST "", URL);
848 else
849 ret = xmlHashLookup2(ctxt->includes, ns, URL);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000850 if (ret != NULL)
851 return(ret);
852 }
853
854
855 /*
856 * load the document
857 */
858 doc = xmlParseFile((const char *) URL);
859 if (doc == NULL) {
860 if (ctxt->error != NULL)
861 ctxt->error(ctxt->userData,
862 "xmlRelaxNG: could not load %s\n", URL);
863 ctxt->nbErrors++;
864 return (NULL);
865 }
866
867 /*
868 * Allocate the document structures and register it first.
869 */
870 ret = (xmlRelaxNGIncludePtr) xmlMalloc(sizeof(xmlRelaxNGInclude));
871 if (ret == NULL) {
872 if (ctxt->error != NULL)
873 ctxt->error(ctxt->userData,
874 "xmlRelaxNG: allocate memory for doc %s\n", URL);
875 ctxt->nbErrors++;
876 xmlFreeDoc(doc);
877 return (NULL);
878 }
879 memset(ret, 0, sizeof(xmlRelaxNGInclude));
880 ret->doc = doc;
881 ret->href = xmlStrdup(URL);
882
883 /*
Daniel Veillard416589a2003-02-17 17:25:42 +0000884 * transmit the ns if needed
885 */
886 if (ns != NULL) {
887 root = xmlDocGetRootElement(doc);
888 if (root != NULL) {
889 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
890 xmlSetProp(root, BAD_CAST"ns", ns);
891 }
892 }
893 }
894
895 /*
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000896 * push it on the stack and register it in the hash table
897 */
Daniel Veillard416589a2003-02-17 17:25:42 +0000898 if (ns == NULL)
899 xmlHashAddEntry2(ctxt->includes, BAD_CAST "", URL, ret);
900 else
901 xmlHashAddEntry2(ctxt->includes, ns, URL, ret);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000902 xmlRelaxNGIncludePush(ctxt, ret);
903
904 /*
905 * Some preprocessing of the document content, this include recursing
906 * in the include stack.
907 */
908 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
909 if (doc == NULL) {
910 /* xmlFreeDoc(ctxt->include); */
911 ctxt->inc = NULL;
912 return(NULL);
913 }
914
915 /*
916 * Pop up the include from the stack
917 */
918 xmlRelaxNGIncludePop(ctxt);
919
920 /*
921 * Check that the top element is a grammar
922 */
923 root = xmlDocGetRootElement(doc);
924 if (root == NULL) {
925 if (ctxt->error != NULL)
926 ctxt->error(ctxt->userData,
927 "xmlRelaxNG: included document is empty %s\n", URL);
928 ctxt->nbErrors++;
929 xmlFreeDoc(doc);
930 return (NULL);
931 }
932 if (!IS_RELAXNG(root, "grammar")) {
933 if (ctxt->error != NULL)
934 ctxt->error(ctxt->userData,
935 "xmlRelaxNG: included document %s root is not a grammar\n",
936 URL);
937 ctxt->nbErrors++;
938 xmlFreeDoc(doc);
939 return (NULL);
940 }
941
942 /*
943 * Elimination of redefined rules in the include.
944 */
945 cur = node->children;
946 while (cur != NULL) {
947 if (IS_RELAXNG(cur, "start")) {
948 int found = 0;
949
950 tmp = root->children;
951 while (tmp != NULL) {
952 tmp2 = tmp->next;
953 if (IS_RELAXNG(tmp, "start")) {
954 found = 1;
955 xmlUnlinkNode(tmp);
956 xmlFreeNode(tmp);
957 }
958 tmp = tmp2;
959 }
960 if (!found) {
961 if (ctxt->error != NULL)
962 ctxt->error(ctxt->userData,
963 "xmlRelaxNG: include %s has a start but not the included grammar\n",
964 URL);
965 ctxt->nbErrors++;
966 }
967 } else if (IS_RELAXNG(cur, "define")) {
968 xmlChar *name, *name2;
969
970 name = xmlGetProp(cur, BAD_CAST "name");
971 if (name == NULL) {
972 if (ctxt->error != NULL)
973 ctxt->error(ctxt->userData,
974 "xmlRelaxNG: include %s has define without name\n",
975 URL);
976 ctxt->nbErrors++;
977 } else {
978 int found = 0;
979
Daniel Veillardd2298792003-02-14 16:54:11 +0000980 xmlRelaxNGNormExtSpace(name);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000981 tmp = root->children;
982 while (tmp != NULL) {
983 tmp2 = tmp->next;
984 if (IS_RELAXNG(tmp, "define")) {
985 name2 = xmlGetProp(tmp, BAD_CAST "name");
Daniel Veillardd2298792003-02-14 16:54:11 +0000986 xmlRelaxNGNormExtSpace(name2);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000987 if (name2 != NULL) {
988 if (xmlStrEqual(name, name2)) {
989 found = 1;
990 xmlUnlinkNode(tmp);
991 xmlFreeNode(tmp);
992 }
993 xmlFree(name2);
994 }
995 }
996 tmp = tmp2;
997 }
998 if (!found) {
999 if (ctxt->error != NULL)
1000 ctxt->error(ctxt->userData,
1001 "xmlRelaxNG: include %s has a define %s but not the included grammar\n",
1002 URL, name);
1003 ctxt->nbErrors++;
1004 }
1005 xmlFree(name);
1006 }
1007 }
1008 cur = cur->next;
1009 }
1010
1011
1012 return(ret);
1013}
1014
1015/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001016 * xmlRelaxNGDocumentPush:
1017 * @ctxt: the parser context
1018 * @value: the element doc
1019 *
1020 * Pushes a new doc on top of the doc stack
1021 *
1022 * Returns 0 in case of error, the index in the stack otherwise
1023 */
1024static int
1025xmlRelaxNGDocumentPush(xmlRelaxNGParserCtxtPtr ctxt,
1026 xmlRelaxNGDocumentPtr value)
1027{
1028 if (ctxt->docTab == NULL) {
1029 ctxt->docMax = 4;
1030 ctxt->docNr = 0;
1031 ctxt->docTab = (xmlRelaxNGDocumentPtr *) xmlMalloc(
1032 ctxt->docMax * sizeof(ctxt->docTab[0]));
1033 if (ctxt->docTab == NULL) {
1034 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
1035 return (0);
1036 }
1037 }
1038 if (ctxt->docNr >= ctxt->docMax) {
1039 ctxt->docMax *= 2;
1040 ctxt->docTab =
1041 (xmlRelaxNGDocumentPtr *) xmlRealloc(ctxt->docTab,
1042 ctxt->docMax *
1043 sizeof(ctxt->docTab[0]));
1044 if (ctxt->docTab == NULL) {
1045 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
1046 return (0);
1047 }
1048 }
1049 ctxt->docTab[ctxt->docNr] = value;
1050 ctxt->doc = value;
1051 return (ctxt->docNr++);
1052}
1053
1054/**
1055 * xmlRelaxNGDocumentPop:
1056 * @ctxt: the parser context
1057 *
1058 * Pops the top doc from the doc stack
1059 *
1060 * Returns the doc just removed
1061 */
1062static xmlRelaxNGDocumentPtr
1063xmlRelaxNGDocumentPop(xmlRelaxNGParserCtxtPtr ctxt)
1064{
1065 xmlRelaxNGDocumentPtr ret;
1066
1067 if (ctxt->docNr <= 0)
1068 return (0);
1069 ctxt->docNr--;
1070 if (ctxt->docNr > 0)
1071 ctxt->doc = ctxt->docTab[ctxt->docNr - 1];
1072 else
1073 ctxt->doc = NULL;
1074 ret = ctxt->docTab[ctxt->docNr];
1075 ctxt->docTab[ctxt->docNr] = 0;
1076 return (ret);
1077}
1078
1079/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001080 * xmlRelaxNGLoadExternalRef:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001081 * @ctxt: the parser context
1082 * @URL: the normalized URL
1083 * @ns: the inherited ns if any
1084 *
1085 * First lookup if the document is already loaded into the parser context,
1086 * check against recursion. If not found the resource is loaded and
1087 * the content is preprocessed before being returned back to the caller.
1088 *
1089 * Returns the xmlRelaxNGDocumentPtr or NULL in case of error
1090 */
1091static xmlRelaxNGDocumentPtr
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001092xmlRelaxNGLoadExternalRef(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001093 const xmlChar *ns) {
1094 xmlRelaxNGDocumentPtr ret = NULL;
1095 xmlDocPtr doc;
1096 xmlNodePtr root;
1097 int i;
1098
1099 /*
1100 * check against recursion in the stack
1101 */
1102 for (i = 0;i < ctxt->docNr;i++) {
1103 if (xmlStrEqual(ctxt->docTab[i]->href, URL)) {
1104 if (ctxt->error != NULL)
1105 ctxt->error(ctxt->userData,
1106 "Detected an externalRef recursion for %s\n",
1107 URL);
1108 ctxt->nbErrors++;
1109 return(NULL);
1110 }
1111 }
1112
1113 /*
1114 * Lookup in the hash table
1115 */
1116 if (ctxt->documents == NULL) {
1117 ctxt->documents = xmlHashCreate(10);
1118 if (ctxt->documents == NULL) {
1119 if (ctxt->error != NULL)
1120 ctxt->error(ctxt->userData,
1121 "Failed to allocate hash table for document\n");
1122 ctxt->nbErrors++;
1123 return(NULL);
1124 }
1125 } else {
1126 if (ns == NULL)
1127 ret = xmlHashLookup2(ctxt->documents, BAD_CAST "", URL);
1128 else
1129 ret = xmlHashLookup2(ctxt->documents, ns, URL);
1130 if (ret != NULL)
1131 return(ret);
1132 }
1133
1134
1135 /*
1136 * load the document
1137 */
1138 doc = xmlParseFile((const char *) URL);
1139 if (doc == NULL) {
1140 if (ctxt->error != NULL)
1141 ctxt->error(ctxt->userData,
1142 "xmlRelaxNG: could not load %s\n", URL);
1143 ctxt->nbErrors++;
1144 return (NULL);
1145 }
1146
1147 /*
1148 * Allocate the document structures and register it first.
1149 */
1150 ret = (xmlRelaxNGDocumentPtr) xmlMalloc(sizeof(xmlRelaxNGDocument));
1151 if (ret == NULL) {
1152 if (ctxt->error != NULL)
1153 ctxt->error(ctxt->userData,
1154 "xmlRelaxNG: allocate memory for doc %s\n", URL);
1155 ctxt->nbErrors++;
1156 xmlFreeDoc(doc);
1157 return (NULL);
1158 }
1159 memset(ret, 0, sizeof(xmlRelaxNGDocument));
1160 ret->doc = doc;
1161 ret->href = xmlStrdup(URL);
1162
1163 /*
1164 * transmit the ns if needed
1165 */
1166 if (ns != NULL) {
1167 root = xmlDocGetRootElement(doc);
1168 if (root != NULL) {
1169 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
1170 xmlSetProp(root, BAD_CAST"ns", ns);
1171 }
1172 }
1173 }
1174
1175 /*
1176 * push it on the stack and register it in the hash table
1177 */
1178 if (ns == NULL)
1179 xmlHashAddEntry2(ctxt->documents, BAD_CAST "", URL, ret);
1180 else
1181 xmlHashAddEntry2(ctxt->documents, ns, URL, ret);
1182 xmlRelaxNGDocumentPush(ctxt, ret);
1183
1184 /*
1185 * Some preprocessing of the document content
1186 */
1187 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
1188 if (doc == NULL) {
1189 xmlFreeDoc(ctxt->document);
1190 ctxt->doc = NULL;
1191 return(NULL);
1192 }
1193
1194 xmlRelaxNGDocumentPop(ctxt);
1195
1196 return(ret);
1197}
1198
1199/************************************************************************
1200 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +00001201 * Error functions *
1202 * *
1203 ************************************************************************/
1204
1205#define VALID_CTXT() \
Daniel Veillard231d7912003-02-09 14:22:17 +00001206 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1207 xmlGenericError(xmlGenericErrorContext, \
Daniel Veillard6eadf632003-01-23 18:29:16 +00001208 "error detected at %s:%d\n", \
1209 __FILE__, __LINE__);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001210
1211#define VALID_ERROR(a) \
Daniel Veillard231d7912003-02-09 14:22:17 +00001212 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001213 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a)
1214#define VALID_ERROR2(a, b) \
1215 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1216 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a, b)
1217#define VALID_ERROR3(a, b, c) \
1218 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1219 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a, b, c)
Daniel Veillard6eadf632003-01-23 18:29:16 +00001220
Daniel Veillardd2298792003-02-14 16:54:11 +00001221#ifdef DEBUG
Daniel Veillard231d7912003-02-09 14:22:17 +00001222static const char *
1223xmlRelaxNGDefName(xmlRelaxNGDefinePtr def) {
1224 if (def == NULL)
1225 return("none");
1226 switch(def->type) {
1227 case XML_RELAXNG_EMPTY: return("empty");
1228 case XML_RELAXNG_NOT_ALLOWED: return("notAllowed");
1229 case XML_RELAXNG_EXCEPT: return("except");
1230 case XML_RELAXNG_TEXT: return("text");
1231 case XML_RELAXNG_ELEMENT: return("element");
1232 case XML_RELAXNG_DATATYPE: return("datatype");
1233 case XML_RELAXNG_VALUE: return("value");
1234 case XML_RELAXNG_LIST: return("list");
1235 case XML_RELAXNG_ATTRIBUTE: return("attribute");
1236 case XML_RELAXNG_DEF: return("def");
1237 case XML_RELAXNG_REF: return("ref");
1238 case XML_RELAXNG_EXTERNALREF: return("externalRef");
1239 case XML_RELAXNG_PARENTREF: return("parentRef");
1240 case XML_RELAXNG_OPTIONAL: return("optional");
1241 case XML_RELAXNG_ZEROORMORE: return("zeroOrMore");
1242 case XML_RELAXNG_ONEORMORE: return("oneOrMore");
1243 case XML_RELAXNG_CHOICE: return("choice");
1244 case XML_RELAXNG_GROUP: return("group");
1245 case XML_RELAXNG_INTERLEAVE: return("interleave");
1246 case XML_RELAXNG_START: return("start");
1247 }
1248 return("unknown");
1249}
Daniel Veillardd2298792003-02-14 16:54:11 +00001250#endif
1251
Daniel Veillard6eadf632003-01-23 18:29:16 +00001252#if 0
1253/**
1254 * xmlRelaxNGErrorContext:
1255 * @ctxt: the parsing context
1256 * @schema: the schema being built
1257 * @node: the node being processed
1258 * @child: the child being processed
1259 *
1260 * Dump a RelaxNGType structure
1261 */
1262static void
1263xmlRelaxNGErrorContext(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGPtr schema,
1264 xmlNodePtr node, xmlNodePtr child)
1265{
1266 int line = 0;
1267 const xmlChar *file = NULL;
1268 const xmlChar *name = NULL;
1269 const char *type = "error";
1270
1271 if ((ctxt == NULL) || (ctxt->error == NULL))
1272 return;
1273
1274 if (child != NULL)
1275 node = child;
1276
1277 if (node != NULL) {
1278 if ((node->type == XML_DOCUMENT_NODE) ||
1279 (node->type == XML_HTML_DOCUMENT_NODE)) {
1280 xmlDocPtr doc = (xmlDocPtr) node;
1281
1282 file = doc->URL;
1283 } else {
1284 /*
1285 * Try to find contextual informations to report
1286 */
1287 if (node->type == XML_ELEMENT_NODE) {
1288 line = (int) node->content;
1289 } else if ((node->prev != NULL) &&
1290 (node->prev->type == XML_ELEMENT_NODE)) {
1291 line = (int) node->prev->content;
1292 } else if ((node->parent != NULL) &&
1293 (node->parent->type == XML_ELEMENT_NODE)) {
1294 line = (int) node->parent->content;
1295 }
1296 if ((node->doc != NULL) && (node->doc->URL != NULL))
1297 file = node->doc->URL;
1298 if (node->name != NULL)
1299 name = node->name;
1300 }
1301 }
1302
1303 if (ctxt != NULL)
1304 type = "compilation error";
1305 else if (schema != NULL)
1306 type = "runtime error";
1307
1308 if ((file != NULL) && (line != 0) && (name != NULL))
1309 ctxt->error(ctxt->userData, "%s: file %s line %d element %s\n",
1310 type, file, line, name);
1311 else if ((file != NULL) && (name != NULL))
1312 ctxt->error(ctxt->userData, "%s: file %s element %s\n",
1313 type, file, name);
1314 else if ((file != NULL) && (line != 0))
1315 ctxt->error(ctxt->userData, "%s: file %s line %d\n", type, file, line);
1316 else if (file != NULL)
1317 ctxt->error(ctxt->userData, "%s: file %s\n", type, file);
1318 else if (name != NULL)
1319 ctxt->error(ctxt->userData, "%s: element %s\n", type, name);
1320 else
1321 ctxt->error(ctxt->userData, "%s\n", type);
1322}
1323#endif
1324
1325/************************************************************************
1326 * *
1327 * Type library hooks *
1328 * *
1329 ************************************************************************/
Daniel Veillardea3f3982003-01-26 19:45:18 +00001330static xmlChar *xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt,
1331 const xmlChar *str);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001332
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001333/**
1334 * xmlRelaxNGSchemaTypeHave:
1335 * @data: data needed for the library
1336 * @type: the type name
1337 *
1338 * Check if the given type is provided by
1339 * the W3C XMLSchema Datatype library.
1340 *
1341 * Returns 1 if yes, 0 if no and -1 in case of error.
1342 */
1343static int
1344xmlRelaxNGSchemaTypeHave(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001345 const xmlChar *type) {
1346 xmlSchemaTypePtr typ;
1347
1348 if (type == NULL)
1349 return(-1);
1350 typ = xmlSchemaGetPredefinedType(type,
1351 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1352 if (typ == NULL)
1353 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001354 return(1);
1355}
1356
1357/**
1358 * xmlRelaxNGSchemaTypeCheck:
1359 * @data: data needed for the library
1360 * @type: the type name
1361 * @value: the value to check
1362 *
1363 * Check if the given type and value are validated by
1364 * the W3C XMLSchema Datatype library.
1365 *
1366 * Returns 1 if yes, 0 if no and -1 in case of error.
1367 */
1368static int
1369xmlRelaxNGSchemaTypeCheck(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001370 const xmlChar *type,
1371 const xmlChar *value) {
1372 xmlSchemaTypePtr typ;
1373 int ret;
1374
1375 /*
1376 * TODO: the type should be cached ab provided back, interface subject
1377 * to changes.
1378 * TODO: handle facets, may require an additional interface and keep
1379 * the value returned from the validation.
1380 */
1381 if ((type == NULL) || (value == NULL))
1382 return(-1);
1383 typ = xmlSchemaGetPredefinedType(type,
1384 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1385 if (typ == NULL)
1386 return(-1);
1387 ret = xmlSchemaValidatePredefinedType(typ, value, NULL);
1388 if (ret == 0)
1389 return(1);
1390 if (ret > 0)
1391 return(0);
1392 return(-1);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001393}
1394
1395/**
1396 * xmlRelaxNGSchemaTypeCompare:
1397 * @data: data needed for the library
1398 * @type: the type name
1399 * @value1: the first value
1400 * @value2: the second value
1401 *
1402 * Compare two values accordingly a type from the W3C XMLSchema
1403 * Datatype library.
1404 *
1405 * Returns 1 if yes, 0 if no and -1 in case of error.
1406 */
1407static int
1408xmlRelaxNGSchemaTypeCompare(void *data ATTRIBUTE_UNUSED,
1409 const xmlChar *type ATTRIBUTE_UNUSED,
1410 const xmlChar *value1 ATTRIBUTE_UNUSED,
1411 const xmlChar *value2 ATTRIBUTE_UNUSED) {
1412 TODO
1413 return(1);
1414}
1415
1416/**
1417 * xmlRelaxNGDefaultTypeHave:
1418 * @data: data needed for the library
1419 * @type: the type name
1420 *
1421 * Check if the given type is provided by
1422 * the default datatype library.
1423 *
1424 * Returns 1 if yes, 0 if no and -1 in case of error.
1425 */
1426static int
1427xmlRelaxNGDefaultTypeHave(void *data ATTRIBUTE_UNUSED, const xmlChar *type) {
1428 if (type == NULL)
1429 return(-1);
1430 if (xmlStrEqual(type, BAD_CAST "string"))
1431 return(1);
1432 if (xmlStrEqual(type, BAD_CAST "token"))
1433 return(1);
1434 return(0);
1435}
1436
1437/**
1438 * xmlRelaxNGDefaultTypeCheck:
1439 * @data: data needed for the library
1440 * @type: the type name
1441 * @value: the value to check
1442 *
1443 * Check if the given type and value are validated by
1444 * the default datatype library.
1445 *
1446 * Returns 1 if yes, 0 if no and -1 in case of error.
1447 */
1448static int
1449xmlRelaxNGDefaultTypeCheck(void *data ATTRIBUTE_UNUSED,
1450 const xmlChar *type ATTRIBUTE_UNUSED,
1451 const xmlChar *value ATTRIBUTE_UNUSED) {
Daniel Veillardd4310742003-02-18 21:12:46 +00001452 if (value == NULL)
1453 return(-1);
1454 if (xmlStrEqual(type, BAD_CAST "string"))
1455 return(1);
1456 if (xmlStrEqual(type, BAD_CAST "token")) {
1457#if 0
1458 const xmlChar *cur = value;
1459
1460 while (*cur != 0) {
1461 if (!IS_BLANK(*cur))
1462 return(1);
1463 cur++;
1464 }
1465#endif
1466 return(1);
1467 }
1468
1469 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001470}
1471
1472/**
1473 * xmlRelaxNGDefaultTypeCompare:
1474 * @data: data needed for the library
1475 * @type: the type name
1476 * @value1: the first value
1477 * @value2: the second value
1478 *
1479 * Compare two values accordingly a type from the default
1480 * datatype library.
1481 *
1482 * Returns 1 if yes, 0 if no and -1 in case of error.
1483 */
1484static int
1485xmlRelaxNGDefaultTypeCompare(void *data ATTRIBUTE_UNUSED,
1486 const xmlChar *type ATTRIBUTE_UNUSED,
1487 const xmlChar *value1 ATTRIBUTE_UNUSED,
1488 const xmlChar *value2 ATTRIBUTE_UNUSED) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00001489 int ret = -1;
1490
1491 if (xmlStrEqual(type, BAD_CAST "string")) {
1492 ret = xmlStrEqual(value1, value2);
1493 } else if (xmlStrEqual(type, BAD_CAST "token")) {
1494 if (!xmlStrEqual(value1, value2)) {
1495 xmlChar *nval, *nvalue;
1496
1497 /*
1498 * TODO: trivial optimizations are possible by
1499 * computing at compile-time
1500 */
1501 nval = xmlRelaxNGNormalize(NULL, value1);
1502 nvalue = xmlRelaxNGNormalize(NULL, value2);
1503
Daniel Veillardd4310742003-02-18 21:12:46 +00001504 if ((nval == NULL) || (nvalue == NULL))
Daniel Veillardea3f3982003-01-26 19:45:18 +00001505 ret = -1;
Daniel Veillardd4310742003-02-18 21:12:46 +00001506 else if (xmlStrEqual(nval, nvalue))
1507 ret = 1;
1508 else
1509 ret = 0;
Daniel Veillardea3f3982003-01-26 19:45:18 +00001510 if (nval != NULL)
1511 xmlFree(nval);
1512 if (nvalue != NULL)
1513 xmlFree(nvalue);
Daniel Veillardd4310742003-02-18 21:12:46 +00001514 } else
1515 ret = 1;
Daniel Veillardea3f3982003-01-26 19:45:18 +00001516 }
1517 return(ret);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001518}
1519
1520static int xmlRelaxNGTypeInitialized = 0;
1521static xmlHashTablePtr xmlRelaxNGRegisteredTypes = NULL;
1522
1523/**
1524 * xmlRelaxNGFreeTypeLibrary:
1525 * @lib: the type library structure
1526 * @namespace: the URI bound to the library
1527 *
1528 * Free the structure associated to the type library
1529 */
Daniel Veillard6eadf632003-01-23 18:29:16 +00001530static void
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001531xmlRelaxNGFreeTypeLibrary(xmlRelaxNGTypeLibraryPtr lib,
1532 const xmlChar *namespace ATTRIBUTE_UNUSED) {
1533 if (lib == NULL)
1534 return;
1535 if (lib->namespace != NULL)
1536 xmlFree((xmlChar *)lib->namespace);
1537 xmlFree(lib);
1538}
1539
1540/**
1541 * xmlRelaxNGRegisterTypeLibrary:
1542 * @namespace: the URI bound to the library
1543 * @data: data associated to the library
1544 * @have: the provide function
1545 * @check: the checking function
1546 * @comp: the comparison function
1547 *
1548 * Register a new type library
1549 *
1550 * Returns 0 in case of success and -1 in case of error.
1551 */
1552static int
1553xmlRelaxNGRegisterTypeLibrary(const xmlChar *namespace, void *data,
1554 xmlRelaxNGTypeHave have, xmlRelaxNGTypeCheck check,
1555 xmlRelaxNGTypeCompare comp) {
1556 xmlRelaxNGTypeLibraryPtr lib;
1557 int ret;
1558
1559 if ((xmlRelaxNGRegisteredTypes == NULL) || (namespace == NULL) ||
1560 (check == NULL) || (comp == NULL))
1561 return(-1);
1562 if (xmlHashLookup(xmlRelaxNGRegisteredTypes, namespace) != NULL) {
1563 xmlGenericError(xmlGenericErrorContext,
1564 "Relax-NG types library '%s' already registered\n",
1565 namespace);
1566 return(-1);
1567 }
1568 lib = (xmlRelaxNGTypeLibraryPtr) xmlMalloc(sizeof(xmlRelaxNGTypeLibrary));
1569 if (lib == NULL) {
1570 xmlGenericError(xmlGenericErrorContext,
1571 "Relax-NG types library '%s' malloc() failed\n",
1572 namespace);
1573 return (-1);
1574 }
1575 memset(lib, 0, sizeof(xmlRelaxNGTypeLibrary));
1576 lib->namespace = xmlStrdup(namespace);
1577 lib->data = data;
1578 lib->have = have;
1579 lib->comp = comp;
1580 lib->check = check;
1581 ret = xmlHashAddEntry(xmlRelaxNGRegisteredTypes, namespace, lib);
1582 if (ret < 0) {
1583 xmlGenericError(xmlGenericErrorContext,
1584 "Relax-NG types library failed to register '%s'\n",
1585 namespace);
1586 xmlRelaxNGFreeTypeLibrary(lib, namespace);
1587 return(-1);
1588 }
1589 return(0);
1590}
1591
1592/**
1593 * xmlRelaxNGInitTypes:
1594 *
1595 * Initilize the default type libraries.
1596 *
1597 * Returns 0 in case of success and -1 in case of error.
1598 */
1599static int
Daniel Veillard6eadf632003-01-23 18:29:16 +00001600xmlRelaxNGInitTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001601 if (xmlRelaxNGTypeInitialized != 0)
1602 return(0);
1603 xmlRelaxNGRegisteredTypes = xmlHashCreate(10);
1604 if (xmlRelaxNGRegisteredTypes == NULL) {
1605 xmlGenericError(xmlGenericErrorContext,
1606 "Failed to allocate sh table for Relax-NG types\n");
1607 return(-1);
1608 }
1609 xmlRelaxNGRegisterTypeLibrary(
1610 BAD_CAST "http://www.w3.org/2001/XMLSchema-datatypes",
1611 NULL,
1612 xmlRelaxNGSchemaTypeHave,
1613 xmlRelaxNGSchemaTypeCheck,
1614 xmlRelaxNGSchemaTypeCompare);
1615 xmlRelaxNGRegisterTypeLibrary(
1616 xmlRelaxNGNs,
1617 NULL,
1618 xmlRelaxNGDefaultTypeHave,
1619 xmlRelaxNGDefaultTypeCheck,
1620 xmlRelaxNGDefaultTypeCompare);
1621 xmlRelaxNGTypeInitialized = 1;
1622 return(0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001623}
1624
1625/**
1626 * xmlRelaxNGCleanupTypes:
1627 *
1628 * Cleanup the default Schemas type library associated to RelaxNG
1629 */
1630void
1631xmlRelaxNGCleanupTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001632 if (xmlRelaxNGTypeInitialized == 0)
1633 return;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001634 xmlSchemaCleanupTypes();
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001635 xmlHashFree(xmlRelaxNGRegisteredTypes, (xmlHashDeallocator)
1636 xmlRelaxNGFreeTypeLibrary);
1637 xmlRelaxNGTypeInitialized = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001638}
1639
1640/************************************************************************
1641 * *
1642 * Parsing functions *
1643 * *
1644 ************************************************************************/
1645
1646static xmlRelaxNGDefinePtr xmlRelaxNGParseAttribute(
1647 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1648static xmlRelaxNGDefinePtr xmlRelaxNGParseElement(
1649 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1650static xmlRelaxNGDefinePtr xmlRelaxNGParsePatterns(
Daniel Veillard154877e2003-01-30 12:17:05 +00001651 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes, int group);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001652static xmlRelaxNGDefinePtr xmlRelaxNGParsePattern(
1653 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001654static xmlRelaxNGPtr xmlRelaxNGParseDocument(
1655 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00001656static int xmlRelaxNGParseGrammarContent(
1657 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00001658static xmlRelaxNGDefinePtr xmlRelaxNGParseNameClass(
1659 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
1660 xmlRelaxNGDefinePtr def);
Daniel Veillard419a7682003-02-03 23:22:49 +00001661static xmlRelaxNGGrammarPtr xmlRelaxNGParseGrammar(
1662 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001663
1664
1665#define IS_BLANK_NODE(n) \
1666 (((n)->type == XML_TEXT_NODE) && (xmlRelaxNGIsBlank((n)->content)))
1667
1668/**
1669 * xmlRelaxNGIsBlank:
1670 * @str: a string
1671 *
1672 * Check if a string is ignorable c.f. 4.2. Whitespace
1673 *
1674 * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
1675 */
1676static int
1677xmlRelaxNGIsBlank(xmlChar *str) {
1678 if (str == NULL)
1679 return(1);
1680 while (*str != 0) {
1681 if (!(IS_BLANK(*str))) return(0);
1682 str++;
1683 }
1684 return(1);
1685}
1686
Daniel Veillard6eadf632003-01-23 18:29:16 +00001687/**
1688 * xmlRelaxNGGetDataTypeLibrary:
1689 * @ctxt: a Relax-NG parser context
1690 * @node: the current data or value element
1691 *
1692 * Applies algorithm from 4.3. datatypeLibrary attribute
1693 *
1694 * Returns the datatypeLibary value or NULL if not found
1695 */
1696static xmlChar *
1697xmlRelaxNGGetDataTypeLibrary(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1698 xmlNodePtr node) {
1699 xmlChar *ret, *escape;
1700
Daniel Veillard6eadf632003-01-23 18:29:16 +00001701 if ((IS_RELAXNG(node, "data")) || (IS_RELAXNG(node, "value"))) {
1702 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1703 if (ret != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001704 if (ret[0] == 0) {
1705 xmlFree(ret);
1706 return(NULL);
1707 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001708 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +00001709 if (escape == NULL) {
1710 return(ret);
1711 }
1712 xmlFree(ret);
1713 return(escape);
1714 }
1715 }
1716 node = node->parent;
1717 while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) {
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001718 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1719 if (ret != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001720 if (ret[0] == 0) {
1721 xmlFree(ret);
1722 return(NULL);
1723 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001724 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
1725 if (escape == NULL) {
1726 return(ret);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001727 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001728 xmlFree(ret);
1729 return(escape);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001730 }
1731 node = node->parent;
1732 }
1733 return(NULL);
1734}
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001735
1736/**
Daniel Veillardedc91922003-01-26 00:52:04 +00001737 * xmlRelaxNGParseValue:
1738 * @ctxt: a Relax-NG parser context
1739 * @node: the data node.
1740 *
1741 * parse the content of a RelaxNG value node.
1742 *
1743 * Returns the definition pointer or NULL in case of error
1744 */
1745static xmlRelaxNGDefinePtr
1746xmlRelaxNGParseValue(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1747 xmlRelaxNGDefinePtr def = NULL;
1748 xmlRelaxNGTypeLibraryPtr lib;
1749 xmlChar *type;
1750 xmlChar *library;
1751 int tmp;
1752
1753 def = xmlRelaxNGNewDefine(ctxt, node);
1754 if (def == NULL)
1755 return(NULL);
1756 def->type = XML_RELAXNG_VALUE;
1757
1758 type = xmlGetProp(node, BAD_CAST "type");
1759 if (type != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001760 xmlRelaxNGNormExtSpace(type);
1761 if (xmlValidateNCName(type, 0)) {
1762 if (ctxt->error != NULL)
1763 ctxt->error(ctxt->userData,
1764 "value type '%s' is not an NCName\n",
1765 type);
1766 ctxt->nbErrors++;
1767 }
Daniel Veillardedc91922003-01-26 00:52:04 +00001768 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1769 if (library == NULL)
1770 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1771
1772 def->name = type;
1773 def->ns = library;
1774
1775 lib = (xmlRelaxNGTypeLibraryPtr)
1776 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1777 if (lib == NULL) {
1778 if (ctxt->error != NULL)
1779 ctxt->error(ctxt->userData,
1780 "Use of unregistered type library '%s'\n",
1781 library);
1782 ctxt->nbErrors++;
1783 def->data = NULL;
1784 } else {
1785 def->data = lib;
1786 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001787 if (ctxt->error != NULL)
1788 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00001789 "Internal error with type library '%s': no 'have'\n",
1790 library);
1791 ctxt->nbErrors++;
1792 } else {
1793 tmp = lib->have(lib->data, def->name);
1794 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001795 if (ctxt->error != NULL)
1796 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00001797 "Error type '%s' is not exported by type library '%s'\n",
1798 def->name, library);
1799 ctxt->nbErrors++;
1800 }
1801 }
1802 }
1803 }
1804 if (node->children == NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00001805 def->value = xmlStrdup(BAD_CAST "");
Daniel Veillardedc91922003-01-26 00:52:04 +00001806 } else if ((node->children->type != XML_TEXT_NODE) ||
1807 (node->children->next != NULL)) {
1808 if (ctxt->error != NULL)
1809 ctxt->error(ctxt->userData,
1810 "Expecting a single text value for <value>content\n");
1811 ctxt->nbErrors++;
1812 } else {
1813 def->value = xmlNodeGetContent(node);
1814 if (def->value == NULL) {
1815 if (ctxt->error != NULL)
1816 ctxt->error(ctxt->userData,
1817 "Element <value> has no content\n");
1818 ctxt->nbErrors++;
1819 }
1820 }
1821 /* TODO check ahead of time that the value is okay per the type */
1822 return(def);
1823}
1824
1825/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001826 * xmlRelaxNGParseData:
1827 * @ctxt: a Relax-NG parser context
1828 * @node: the data node.
1829 *
1830 * parse the content of a RelaxNG data node.
1831 *
1832 * Returns the definition pointer or NULL in case of error
1833 */
1834static xmlRelaxNGDefinePtr
1835xmlRelaxNGParseData(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
Daniel Veillard416589a2003-02-17 17:25:42 +00001836 xmlRelaxNGDefinePtr def = NULL, except, last = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001837 xmlRelaxNGTypeLibraryPtr lib;
1838 xmlChar *type;
1839 xmlChar *library;
1840 xmlNodePtr content;
1841 int tmp;
1842
1843 type = xmlGetProp(node, BAD_CAST "type");
1844 if (type == NULL) {
1845 if (ctxt->error != NULL)
1846 ctxt->error(ctxt->userData,
1847 "data has no type\n");
1848 ctxt->nbErrors++;
1849 return(NULL);
1850 }
Daniel Veillardd2298792003-02-14 16:54:11 +00001851 xmlRelaxNGNormExtSpace(type);
1852 if (xmlValidateNCName(type, 0)) {
1853 if (ctxt->error != NULL)
1854 ctxt->error(ctxt->userData,
1855 "data type '%s' is not an NCName\n",
1856 type);
1857 ctxt->nbErrors++;
1858 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001859 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1860 if (library == NULL)
1861 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1862
1863 def = xmlRelaxNGNewDefine(ctxt, node);
1864 if (def == NULL) {
1865 xmlFree(type);
1866 return(NULL);
1867 }
1868 def->type = XML_RELAXNG_DATATYPE;
1869 def->name = type;
1870 def->ns = library;
1871
1872 lib = (xmlRelaxNGTypeLibraryPtr)
1873 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1874 if (lib == NULL) {
1875 if (ctxt->error != NULL)
1876 ctxt->error(ctxt->userData,
1877 "Use of unregistered type library '%s'\n",
1878 library);
1879 ctxt->nbErrors++;
1880 def->data = NULL;
1881 } else {
1882 def->data = lib;
1883 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001884 if (ctxt->error != NULL)
1885 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001886 "Internal error with type library '%s': no 'have'\n",
1887 library);
1888 ctxt->nbErrors++;
1889 } else {
1890 tmp = lib->have(lib->data, def->name);
1891 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001892 if (ctxt->error != NULL)
1893 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001894 "Error type '%s' is not exported by type library '%s'\n",
1895 def->name, library);
1896 ctxt->nbErrors++;
1897 }
1898 }
1899 }
1900 content = node->children;
Daniel Veillard416589a2003-02-17 17:25:42 +00001901
1902 /*
1903 * Handle optional params
1904 */
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001905 while (content != NULL) {
Daniel Veillard416589a2003-02-17 17:25:42 +00001906 if (!xmlStrEqual(content->name, BAD_CAST "param"))
1907 break;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001908 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001909 ctxt->nbErrors++;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001910 content = content->next;
1911 }
Daniel Veillard416589a2003-02-17 17:25:42 +00001912 /*
1913 * Handle optional except
1914 */
1915 if ((content != NULL) && (xmlStrEqual(content->name, BAD_CAST "except"))) {
1916 xmlNodePtr child;
1917 xmlRelaxNGDefinePtr tmp2, last2 = NULL;
1918
1919 except = xmlRelaxNGNewDefine(ctxt, node);
1920 if (except == NULL) {
1921 return(def);
1922 }
1923 except->type = XML_RELAXNG_EXCEPT;
1924 child = content->children;
1925 if (last == NULL) {
1926 def->content = except;
1927 } else {
1928 last->next = except;
1929 }
1930 if (child == NULL) {
1931 if (ctxt->error != NULL)
1932 ctxt->error(ctxt->userData,
1933 "except has no content\n");
1934 ctxt->nbErrors++;
1935 }
1936 while (child != NULL) {
1937 tmp2 = xmlRelaxNGParsePattern(ctxt, child);
1938 if (tmp2 != NULL) {
1939 if (last2 == NULL) {
1940 except->content = last2 = tmp2;
1941 } else {
1942 last2->next = tmp2;
1943 last2 = tmp2;
1944 }
1945 }
1946 child = child->next;
1947 }
1948 content = content->next;
1949 }
1950 /*
1951 * Check there is no unhandled data
1952 */
1953 if (content != NULL) {
1954 if (ctxt->error != NULL)
1955 ctxt->error(ctxt->userData,
1956 "Element data has unexpected content %s\n", content->name);
1957 ctxt->nbErrors++;
1958 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001959
1960 return(def);
1961}
1962
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001963/**
1964 * xmlRelaxNGCompareElemDefLists:
1965 * @ctxt: a Relax-NG parser context
1966 * @defs1: the first list of element defs
1967 * @defs2: the second list of element defs
1968 *
1969 * Compare the 2 lists of element definitions. The comparison is
1970 * that if both lists do not accept the same QNames, it returns 1
1971 * If the 2 lists can accept the same QName the comparison returns 0
1972 *
1973 * Returns 1 disttinct, 0 if equal
1974 */
1975static int
1976xmlRelaxNGCompareElemDefLists(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1977 xmlRelaxNGDefinePtr *def1,
1978 xmlRelaxNGDefinePtr *def2) {
1979 xmlRelaxNGDefinePtr *basedef2 = def2;
1980
Daniel Veillard154877e2003-01-30 12:17:05 +00001981 if ((def1 == NULL) || (def2 == NULL))
1982 return(1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001983 if ((*def1 == NULL) || (*def2 == NULL))
1984 return(1);
1985 while (*def1 != NULL) {
1986 while ((*def2) != NULL) {
1987 if ((*def1)->name == NULL) {
1988 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1989 return(0);
1990 } else if ((*def2)->name == NULL) {
1991 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1992 return(0);
1993 } else if (xmlStrEqual((*def1)->name, (*def2)->name)) {
1994 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1995 return(0);
1996 }
1997 def2++;
1998 }
1999 def2 = basedef2;
2000 def1++;
2001 }
2002 return(1);
2003}
2004
2005/**
2006 * xmlRelaxNGGetElements:
2007 * @ctxt: a Relax-NG parser context
2008 * @def: the interleave definition
2009 *
2010 * Compute the list of top elements a definition can generate
2011 *
2012 * Returns a list of elements or NULL if none was found.
2013 */
2014static xmlRelaxNGDefinePtr *
2015xmlRelaxNGGetElements(xmlRelaxNGParserCtxtPtr ctxt,
2016 xmlRelaxNGDefinePtr def) {
2017 xmlRelaxNGDefinePtr *ret = NULL, parent, cur, tmp;
2018 int len = 0;
2019 int max = 0;
2020
2021 parent = NULL;
2022 cur = def;
2023 while (cur != NULL) {
Daniel Veillardb08c9812003-01-28 23:09:49 +00002024 if ((cur->type == XML_RELAXNG_ELEMENT) ||
2025 (cur->type == XML_RELAXNG_TEXT)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002026 if (ret == NULL) {
2027 max = 10;
2028 ret = (xmlRelaxNGDefinePtr *)
2029 xmlMalloc((max + 1) * sizeof(xmlRelaxNGDefinePtr));
2030 if (ret == NULL) {
2031 if (ctxt->error != NULL)
2032 ctxt->error(ctxt->userData,
2033 "Out of memory in element search\n");
2034 ctxt->nbErrors++;
2035 return(NULL);
2036 }
2037 } else if (max <= len) {
2038 max *= 2;
2039 ret = xmlRealloc(ret, (max + 1) * sizeof(xmlRelaxNGDefinePtr));
2040 if (ret == NULL) {
2041 if (ctxt->error != NULL)
2042 ctxt->error(ctxt->userData,
2043 "Out of memory in element search\n");
2044 ctxt->nbErrors++;
2045 return(NULL);
2046 }
2047 }
Daniel Veillardb08c9812003-01-28 23:09:49 +00002048 ret[len++] = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002049 ret[len] = NULL;
2050 } else if ((cur->type == XML_RELAXNG_CHOICE) ||
2051 (cur->type == XML_RELAXNG_INTERLEAVE) ||
2052 (cur->type == XML_RELAXNG_GROUP) ||
2053 (cur->type == XML_RELAXNG_ONEORMORE) ||
Daniel Veillardb08c9812003-01-28 23:09:49 +00002054 (cur->type == XML_RELAXNG_ZEROORMORE) ||
2055 (cur->type == XML_RELAXNG_OPTIONAL) ||
2056 (cur->type == XML_RELAXNG_REF) ||
2057 (cur->type == XML_RELAXNG_DEF)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002058 /*
2059 * Don't go within elements or attributes or string values.
2060 * Just gather the element top list
2061 */
2062 if (cur->content != NULL) {
2063 parent = cur;
2064 cur = cur->content;
2065 tmp = cur;
2066 while (tmp != NULL) {
2067 tmp->parent = parent;
2068 tmp = tmp->next;
2069 }
2070 continue;
2071 }
2072 }
Daniel Veillard154877e2003-01-30 12:17:05 +00002073 if (cur == def)
2074 return(ret);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002075 if (cur->next != NULL) {
2076 cur = cur->next;
2077 continue;
2078 }
2079 do {
2080 cur = cur->parent;
2081 if (cur == NULL) break;
2082 if (cur == def) return(ret);
2083 if (cur->next != NULL) {
2084 cur = cur->next;
2085 break;
2086 }
2087 } while (cur != NULL);
2088 }
2089 return(ret);
2090}
2091
2092/**
2093 * xmlRelaxNGComputeInterleaves:
2094 * @def: the interleave definition
2095 * @ctxt: a Relax-NG parser context
2096 * @node: the data node.
2097 *
2098 * A lot of work for preprocessing interleave definitions
2099 * is potentially needed to get a decent execution speed at runtime
2100 * - trying to get a total order on the element nodes generated
2101 * by the interleaves, order the list of interleave definitions
2102 * following that order.
2103 * - if <text/> is used to handle mixed content, it is better to
2104 * flag this in the define and simplify the runtime checking
2105 * algorithm
2106 */
2107static void
2108xmlRelaxNGComputeInterleaves(xmlRelaxNGDefinePtr def,
2109 xmlRelaxNGParserCtxtPtr ctxt,
2110 xmlChar *name ATTRIBUTE_UNUSED) {
2111 xmlRelaxNGDefinePtr cur;
2112
2113 xmlRelaxNGDefinePtr *list = NULL;
2114 xmlRelaxNGPartitionPtr partitions = NULL;
2115 xmlRelaxNGInterleaveGroupPtr *groups = NULL;
2116 xmlRelaxNGInterleaveGroupPtr group;
2117 int i,j,ret;
2118 int nbgroups = 0;
2119 int nbchild = 0;
2120
2121#ifdef DEBUG_INTERLEAVE
2122 xmlGenericError(xmlGenericErrorContext,
2123 "xmlRelaxNGComputeInterleaves(%s)\n",
2124 name);
2125#endif
2126 cur = def->content;
2127 while (cur != NULL) {
2128 nbchild++;
2129 cur = cur->next;
2130 }
2131
2132#ifdef DEBUG_INTERLEAVE
2133 xmlGenericError(xmlGenericErrorContext, " %d child\n", nbchild);
2134#endif
2135 groups = (xmlRelaxNGInterleaveGroupPtr *)
2136 xmlMalloc(nbchild * sizeof(xmlRelaxNGInterleaveGroupPtr));
2137 if (groups == NULL)
2138 goto error;
2139 cur = def->content;
2140 while (cur != NULL) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002141 groups[nbgroups] = (xmlRelaxNGInterleaveGroupPtr)
2142 xmlMalloc(sizeof(xmlRelaxNGInterleaveGroup));
2143 if (groups[nbgroups] == NULL)
2144 goto error;
2145 groups[nbgroups]->rule = cur;
2146 groups[nbgroups]->defs = xmlRelaxNGGetElements(ctxt, cur);
2147 nbgroups++;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002148 cur = cur->next;
2149 }
2150 list = NULL;
2151#ifdef DEBUG_INTERLEAVE
2152 xmlGenericError(xmlGenericErrorContext, " %d groups\n", nbgroups);
2153#endif
2154
2155 /*
2156 * Let's check that all rules makes a partitions according to 7.4
2157 */
2158 partitions = (xmlRelaxNGPartitionPtr)
2159 xmlMalloc(sizeof(xmlRelaxNGPartition));
2160 if (partitions == NULL)
2161 goto error;
2162 partitions->nbgroups = nbgroups;
2163 for (i = 0;i < nbgroups;i++) {
2164 group = groups[i];
2165 for (j = i+1;j < nbgroups;j++) {
2166 if (groups[j] == NULL)
2167 continue;
2168 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->defs,
2169 groups[j]->defs);
2170 if (ret == 0) {
2171 if (ctxt->error != NULL)
2172 ctxt->error(ctxt->userData,
2173 "Element or text conflicts in interleave\n");
2174 ctxt->nbErrors++;
2175 }
2176 }
2177 }
2178 partitions->groups = groups;
2179
2180 /*
2181 * Free Up the child list, and save the partition list back in the def
2182 */
2183 def->data = partitions;
2184 return;
2185
2186error:
2187 if (ctxt->error != NULL)
2188 ctxt->error(ctxt->userData,
2189 "Out of memory in interleave computation\n");
2190 ctxt->nbErrors++;
2191 if (list == NULL)
2192 xmlFree(list);
2193 if (groups != NULL) {
2194 for (i = 0;i < nbgroups;i++)
2195 if (groups[i] != NULL) {
2196 if (groups[i]->defs != NULL)
2197 xmlFree(groups[i]->defs);
2198 xmlFree(groups[i]);
2199 }
2200 xmlFree(groups);
2201 }
2202 xmlRelaxNGFreePartition(partitions);
2203}
2204
2205/**
2206 * xmlRelaxNGParseInterleave:
2207 * @ctxt: a Relax-NG parser context
2208 * @node: the data node.
2209 *
2210 * parse the content of a RelaxNG interleave node.
2211 *
2212 * Returns the definition pointer or NULL in case of error
2213 */
2214static xmlRelaxNGDefinePtr
2215xmlRelaxNGParseInterleave(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2216 xmlRelaxNGDefinePtr def = NULL;
2217 xmlRelaxNGDefinePtr last = NULL, cur;
2218 xmlNodePtr child;
2219
2220 def = xmlRelaxNGNewDefine(ctxt, node);
2221 if (def == NULL) {
2222 return(NULL);
2223 }
2224 def->type = XML_RELAXNG_INTERLEAVE;
2225
2226 if (ctxt->interleaves == NULL)
2227 ctxt->interleaves = xmlHashCreate(10);
2228 if (ctxt->interleaves == NULL) {
2229 if (ctxt->error != NULL)
2230 ctxt->error(ctxt->userData,
2231 "Failed to create interleaves hash table\n");
2232 ctxt->nbErrors++;
2233 } else {
2234 char name[32];
2235
2236 snprintf(name, 32, "interleave%d", ctxt->nbInterleaves++);
2237 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST name, def) < 0) {
2238 if (ctxt->error != NULL)
2239 ctxt->error(ctxt->userData,
2240 "Failed to add %s to hash table\n", name);
2241 ctxt->nbErrors++;
2242 }
2243 }
2244 child = node->children;
Daniel Veillardd2298792003-02-14 16:54:11 +00002245 if (child == NULL) {
2246 if (ctxt->error != NULL)
2247 ctxt->error(ctxt->userData, "Element interleave is empty\n");
2248 ctxt->nbErrors++;
2249 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002250 while (child != NULL) {
2251 if (IS_RELAXNG(child, "element")) {
2252 cur = xmlRelaxNGParseElement(ctxt, child);
2253 } else {
2254 cur = xmlRelaxNGParsePattern(ctxt, child);
2255 }
2256 if (cur != NULL) {
2257 cur->parent = def;
2258 if (last == NULL) {
2259 def->content = last = cur;
2260 } else {
2261 last->next = cur;
2262 last = cur;
2263 }
2264 }
2265 child = child->next;
2266 }
2267
2268 return(def);
2269}
Daniel Veillard6eadf632003-01-23 18:29:16 +00002270
2271/**
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002272 * xmlRelaxNGParseInclude:
2273 * @ctxt: a Relax-NG parser context
2274 * @node: the include node
2275 *
2276 * Integrate the content of an include node in the current grammar
2277 *
2278 * Returns 0 in case of success or -1 in case of error
2279 */
2280static int
2281xmlRelaxNGParseInclude(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2282 xmlRelaxNGIncludePtr incl;
2283 xmlNodePtr root;
2284 int ret = 0, tmp;
2285
2286 incl = node->_private;
2287 if (incl == NULL) {
2288 if (ctxt->error != NULL)
2289 ctxt->error(ctxt->userData,
2290 "Include node has no data\n");
2291 ctxt->nbErrors++;
2292 return(-1);
2293 }
2294 root = xmlDocGetRootElement(incl->doc);
2295 if (root == NULL) {
2296 if (ctxt->error != NULL)
2297 ctxt->error(ctxt->userData,
2298 "Include document is empty\n");
2299 ctxt->nbErrors++;
2300 return(-1);
2301 }
2302 if (!xmlStrEqual(root->name, BAD_CAST "grammar")) {
2303 if (ctxt->error != NULL)
2304 ctxt->error(ctxt->userData,
2305 "Include document root is not a grammar\n");
2306 ctxt->nbErrors++;
2307 return(-1);
2308 }
2309
2310 /*
2311 * Merge the definition from both the include and the internal list
2312 */
2313 if (root->children != NULL) {
2314 tmp = xmlRelaxNGParseGrammarContent(ctxt, root->children);
2315 if (tmp != 0)
2316 ret = -1;
2317 }
2318 if (node->children != NULL) {
2319 tmp = xmlRelaxNGParseGrammarContent(ctxt, node->children);
2320 if (tmp != 0)
2321 ret = -1;
2322 }
2323 return(ret);
2324}
2325
2326/**
Daniel Veillard276be4a2003-01-24 01:03:34 +00002327 * xmlRelaxNGParseDefine:
2328 * @ctxt: a Relax-NG parser context
2329 * @node: the define node
2330 *
2331 * parse the content of a RelaxNG define element node.
2332 *
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002333 * Returns 0 in case of success or -1 in case of error
Daniel Veillard276be4a2003-01-24 01:03:34 +00002334 */
2335static int
2336xmlRelaxNGParseDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2337 xmlChar *name;
2338 int ret = 0, tmp;
2339 xmlRelaxNGDefinePtr def;
2340 const xmlChar *olddefine;
2341
2342 name = xmlGetProp(node, BAD_CAST "name");
2343 if (name == NULL) {
2344 if (ctxt->error != NULL)
2345 ctxt->error(ctxt->userData,
2346 "define has no name\n");
2347 ctxt->nbErrors++;
2348 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00002349 xmlRelaxNGNormExtSpace(name);
2350 if (xmlValidateNCName(name, 0)) {
2351 if (ctxt->error != NULL)
2352 ctxt->error(ctxt->userData,
2353 "define name '%s' is not an NCName\n",
2354 name);
2355 ctxt->nbErrors++;
2356 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002357 def = xmlRelaxNGNewDefine(ctxt, node);
2358 if (def == NULL) {
2359 xmlFree(name);
2360 return(-1);
2361 }
2362 def->type = XML_RELAXNG_DEF;
2363 def->name = name;
2364 if (node->children == NULL) {
2365 if (ctxt->error != NULL)
2366 ctxt->error(ctxt->userData,
2367 "define has no children\n");
2368 ctxt->nbErrors++;
2369 } else {
2370 olddefine = ctxt->define;
2371 ctxt->define = name;
Daniel Veillard154877e2003-01-30 12:17:05 +00002372 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard276be4a2003-01-24 01:03:34 +00002373 ctxt->define = olddefine;
2374 }
2375 if (ctxt->grammar->defs == NULL)
2376 ctxt->grammar->defs = xmlHashCreate(10);
2377 if (ctxt->grammar->defs == NULL) {
2378 if (ctxt->error != NULL)
2379 ctxt->error(ctxt->userData,
2380 "Could not create definition hash\n");
2381 ctxt->nbErrors++;
2382 ret = -1;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002383 } else {
2384 tmp = xmlHashAddEntry(ctxt->grammar->defs, name, def);
2385 if (tmp < 0) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002386 xmlRelaxNGDefinePtr prev;
2387
2388 prev = xmlHashLookup(ctxt->grammar->defs, name);
2389 if (prev == NULL) {
2390 if (ctxt->error != NULL)
2391 ctxt->error(ctxt->userData,
2392 "Internal error on define aggregation of %s\n",
2393 name);
2394 ctxt->nbErrors++;
2395 ret = -1;
Daniel Veillard154877e2003-01-30 12:17:05 +00002396 } else {
2397 while (prev->nextHash != NULL)
2398 prev = prev->nextHash;
2399 prev->nextHash = def;
2400 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002401 }
2402 }
2403 }
2404 return(ret);
2405}
2406
2407/**
Daniel Veillardfebcca42003-02-16 15:44:18 +00002408 * xmlRelaxNGProcessExternalRef:
2409 * @ctxt: the parser context
2410 * @node: the externlRef node
2411 *
2412 * Process and compile an externlRef node
2413 *
2414 * Returns the xmlRelaxNGDefinePtr or NULL in case of error
2415 */
2416static xmlRelaxNGDefinePtr
2417xmlRelaxNGProcessExternalRef(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2418 xmlRelaxNGDocumentPtr docu;
2419 xmlNodePtr root, tmp;
2420 xmlChar *ns;
2421 int newNs = 0;
2422 xmlRelaxNGDefinePtr def;
2423
2424 docu = node->_private;
2425 if (docu != NULL) {
2426 def = xmlRelaxNGNewDefine(ctxt, node);
2427 if (def == NULL)
2428 return(NULL);
2429 def->type = XML_RELAXNG_EXTERNALREF;
2430
2431 if (docu->content == NULL) {
2432 /*
2433 * Then do the parsing for good
2434 */
2435 root = xmlDocGetRootElement(docu->doc);
2436 if (root == NULL) {
2437 if (ctxt->error != NULL)
2438 ctxt->error(ctxt->userData,
2439 "xmlRelaxNGParse: %s is empty\n",
2440 ctxt->URL);
2441 ctxt->nbErrors++;
2442 return (NULL);
2443 }
2444 /*
2445 * ns transmission rules
2446 */
2447 ns = xmlGetProp(root, BAD_CAST "ns");
2448 if (ns == NULL) {
2449 tmp = node;
2450 while ((tmp != NULL) &&
2451 (tmp->type == XML_ELEMENT_NODE)) {
2452 ns = xmlGetProp(tmp, BAD_CAST "ns");
2453 if (ns != NULL) {
2454 break;
2455 }
2456 tmp = tmp->parent;
2457 }
2458 if (ns != NULL) {
2459 xmlSetProp(root, BAD_CAST "ns", ns);
2460 newNs = 1;
2461 xmlFree(ns);
2462 }
2463 } else {
2464 xmlFree(ns);
2465 }
2466
2467 /*
2468 * Parsing to get a precompiled schemas.
2469 */
2470 docu->schema = xmlRelaxNGParseDocument(ctxt, root);
2471 if ((docu->schema != NULL) &&
2472 (docu->schema->topgrammar != NULL)) {
2473 docu->content = docu->schema->topgrammar->start;
2474 }
2475
2476 /*
2477 * the externalRef may be reused in a different ns context
2478 */
2479 if (newNs == 1) {
2480 xmlUnsetProp(root, BAD_CAST "ns");
2481 }
2482 }
2483 def->content = docu->content;
2484 } else {
2485 def = NULL;
2486 }
2487 return(def);
2488}
2489
2490/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00002491 * xmlRelaxNGParsePattern:
2492 * @ctxt: a Relax-NG parser context
2493 * @node: the pattern node.
2494 *
2495 * parse the content of a RelaxNG pattern node.
2496 *
Daniel Veillard276be4a2003-01-24 01:03:34 +00002497 * Returns the definition pointer or NULL in case of error or if no
2498 * pattern is generated.
Daniel Veillard6eadf632003-01-23 18:29:16 +00002499 */
2500static xmlRelaxNGDefinePtr
2501xmlRelaxNGParsePattern(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2502 xmlRelaxNGDefinePtr def = NULL;
2503
Daniel Veillardd2298792003-02-14 16:54:11 +00002504 if (node == NULL) {
2505 return(NULL);
2506 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002507 if (IS_RELAXNG(node, "element")) {
2508 def = xmlRelaxNGParseElement(ctxt, node);
2509 } else if (IS_RELAXNG(node, "attribute")) {
2510 def = xmlRelaxNGParseAttribute(ctxt, node);
2511 } else if (IS_RELAXNG(node, "empty")) {
2512 def = xmlRelaxNGNewDefine(ctxt, node);
2513 if (def == NULL)
2514 return(NULL);
2515 def->type = XML_RELAXNG_EMPTY;
Daniel Veillardd2298792003-02-14 16:54:11 +00002516 if (node->children != NULL) {
2517 if (ctxt->error != NULL)
2518 ctxt->error(ctxt->userData, "empty: had a child node\n");
2519 ctxt->nbErrors++;
2520 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002521 } else if (IS_RELAXNG(node, "text")) {
2522 def = xmlRelaxNGNewDefine(ctxt, node);
2523 if (def == NULL)
2524 return(NULL);
2525 def->type = XML_RELAXNG_TEXT;
2526 if (node->children != NULL) {
2527 if (ctxt->error != NULL)
2528 ctxt->error(ctxt->userData, "text: had a child node\n");
2529 ctxt->nbErrors++;
2530 }
2531 } else if (IS_RELAXNG(node, "zeroOrMore")) {
2532 def = xmlRelaxNGNewDefine(ctxt, node);
2533 if (def == NULL)
2534 return(NULL);
2535 def->type = XML_RELAXNG_ZEROORMORE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002536 if (node->children == NULL) {
2537 if (ctxt->error != NULL)
2538 ctxt->error(ctxt->userData,
2539 "Element %s is empty\n", node->name);
2540 ctxt->nbErrors++;
2541 } else {
2542 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2543 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002544 } else if (IS_RELAXNG(node, "oneOrMore")) {
2545 def = xmlRelaxNGNewDefine(ctxt, node);
2546 if (def == NULL)
2547 return(NULL);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002548 def->type = XML_RELAXNG_ONEORMORE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002549 if (node->children == NULL) {
2550 if (ctxt->error != NULL)
2551 ctxt->error(ctxt->userData,
2552 "Element %s is empty\n", node->name);
2553 ctxt->nbErrors++;
2554 } else {
2555 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2556 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002557 } else if (IS_RELAXNG(node, "optional")) {
2558 def = xmlRelaxNGNewDefine(ctxt, node);
2559 if (def == NULL)
2560 return(NULL);
2561 def->type = XML_RELAXNG_OPTIONAL;
Daniel Veillardd2298792003-02-14 16:54:11 +00002562 if (node->children == NULL) {
2563 if (ctxt->error != NULL)
2564 ctxt->error(ctxt->userData,
2565 "Element %s is empty\n", node->name);
2566 ctxt->nbErrors++;
2567 } else {
2568 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2569 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002570 } else if (IS_RELAXNG(node, "choice")) {
2571 def = xmlRelaxNGNewDefine(ctxt, node);
2572 if (def == NULL)
2573 return(NULL);
2574 def->type = XML_RELAXNG_CHOICE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002575 if (node->children == NULL) {
2576 if (ctxt->error != NULL)
2577 ctxt->error(ctxt->userData,
2578 "Element %s is empty\n", node->name);
2579 ctxt->nbErrors++;
2580 } else {
2581 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2582 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002583 } else if (IS_RELAXNG(node, "group")) {
2584 def = xmlRelaxNGNewDefine(ctxt, node);
2585 if (def == NULL)
2586 return(NULL);
2587 def->type = XML_RELAXNG_GROUP;
Daniel Veillardd2298792003-02-14 16:54:11 +00002588 if (node->children == NULL) {
2589 if (ctxt->error != NULL)
2590 ctxt->error(ctxt->userData,
2591 "Element %s is empty\n", node->name);
2592 ctxt->nbErrors++;
2593 } else {
2594 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2595 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002596 } else if (IS_RELAXNG(node, "ref")) {
2597 def = xmlRelaxNGNewDefine(ctxt, node);
2598 if (def == NULL)
2599 return(NULL);
2600 def->type = XML_RELAXNG_REF;
2601 def->name = xmlGetProp(node, BAD_CAST "name");
2602 if (def->name == NULL) {
2603 if (ctxt->error != NULL)
2604 ctxt->error(ctxt->userData,
2605 "ref has no name\n");
2606 ctxt->nbErrors++;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002607 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00002608 xmlRelaxNGNormExtSpace(def->name);
2609 if (xmlValidateNCName(def->name, 0)) {
2610 if (ctxt->error != NULL)
2611 ctxt->error(ctxt->userData,
2612 "ref name '%s' is not an NCName\n",
2613 def->name);
2614 ctxt->nbErrors++;
2615 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002616 }
2617 if (node->children != NULL) {
2618 if (ctxt->error != NULL)
2619 ctxt->error(ctxt->userData,
2620 "ref is not empty\n");
2621 ctxt->nbErrors++;
2622 }
2623 if (ctxt->grammar->refs == NULL)
2624 ctxt->grammar->refs = xmlHashCreate(10);
2625 if (ctxt->grammar->refs == NULL) {
2626 if (ctxt->error != NULL)
2627 ctxt->error(ctxt->userData,
2628 "Could not create references hash\n");
2629 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002630 def = NULL;
2631 } else {
2632 int tmp;
2633
2634 tmp = xmlHashAddEntry(ctxt->grammar->refs, def->name, def);
2635 if (tmp < 0) {
2636 xmlRelaxNGDefinePtr prev;
2637
2638 prev = (xmlRelaxNGDefinePtr)
2639 xmlHashLookup(ctxt->grammar->refs, def->name);
2640 if (prev == NULL) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00002641 if (def->name != NULL) {
2642 if (ctxt->error != NULL)
2643 ctxt->error(ctxt->userData,
2644 "Error refs definitions '%s'\n",
2645 def->name);
2646 } else {
2647 if (ctxt->error != NULL)
2648 ctxt->error(ctxt->userData,
2649 "Error refs definitions\n");
2650 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002651 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002652 def = NULL;
2653 } else {
2654 def->nextHash = prev->nextHash;
2655 prev->nextHash = def;
2656 }
2657 }
2658 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002659 } else if (IS_RELAXNG(node, "data")) {
2660 def = xmlRelaxNGParseData(ctxt, node);
Daniel Veillardd2298792003-02-14 16:54:11 +00002661#if 0
Daniel Veillard276be4a2003-01-24 01:03:34 +00002662 } else if (IS_RELAXNG(node, "define")) {
2663 xmlRelaxNGParseDefine(ctxt, node);
2664 def = NULL;
Daniel Veillardd2298792003-02-14 16:54:11 +00002665#endif
Daniel Veillardedc91922003-01-26 00:52:04 +00002666 } else if (IS_RELAXNG(node, "value")) {
2667 def = xmlRelaxNGParseValue(ctxt, node);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002668 } else if (IS_RELAXNG(node, "list")) {
2669 def = xmlRelaxNGNewDefine(ctxt, node);
2670 if (def == NULL)
2671 return(NULL);
2672 def->type = XML_RELAXNG_LIST;
Daniel Veillardd2298792003-02-14 16:54:11 +00002673 if (node->children == NULL) {
2674 if (ctxt->error != NULL)
2675 ctxt->error(ctxt->userData,
2676 "Element %s is empty\n", node->name);
2677 ctxt->nbErrors++;
2678 } else {
2679 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2680 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002681 } else if (IS_RELAXNG(node, "interleave")) {
2682 def = xmlRelaxNGParseInterleave(ctxt, node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002683 } else if (IS_RELAXNG(node, "externalRef")) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00002684 def = xmlRelaxNGProcessExternalRef(ctxt, node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002685 } else if (IS_RELAXNG(node, "notAllowed")) {
2686 def = xmlRelaxNGNewDefine(ctxt, node);
2687 if (def == NULL)
2688 return(NULL);
2689 def->type = XML_RELAXNG_NOT_ALLOWED;
2690 if (node->children != NULL) {
2691 if (ctxt->error != NULL)
2692 ctxt->error(ctxt->userData,
2693 "xmlRelaxNGParse: notAllowed element is not empty\n");
2694 ctxt->nbErrors++;
2695 }
Daniel Veillard419a7682003-02-03 23:22:49 +00002696 } else if (IS_RELAXNG(node, "grammar")) {
2697 xmlRelaxNGGrammarPtr grammar, old;
2698 xmlRelaxNGGrammarPtr oldparent;
2699
2700 oldparent = ctxt->parentgrammar;
2701 old = ctxt->grammar;
2702 ctxt->parentgrammar = old;
2703 grammar = xmlRelaxNGParseGrammar(ctxt, node->children);
2704 if (old != NULL) {
2705 ctxt->grammar = old;
2706 ctxt->parentgrammar = oldparent;
2707 if (grammar != NULL) {
2708 grammar->next = old->next;
2709 old->next = grammar;
2710 }
2711 }
2712 if (grammar != NULL)
2713 def = grammar->start;
2714 else
2715 def = NULL;
2716 } else if (IS_RELAXNG(node, "parentRef")) {
2717 if (ctxt->parentgrammar == NULL) {
2718 if (ctxt->error != NULL)
2719 ctxt->error(ctxt->userData,
2720 "Use of parentRef without a parent grammar\n");
2721 ctxt->nbErrors++;
2722 return(NULL);
2723 }
2724 def = xmlRelaxNGNewDefine(ctxt, node);
2725 if (def == NULL)
2726 return(NULL);
2727 def->type = XML_RELAXNG_PARENTREF;
2728 def->name = xmlGetProp(node, BAD_CAST "name");
2729 if (def->name == NULL) {
2730 if (ctxt->error != NULL)
2731 ctxt->error(ctxt->userData,
2732 "parentRef has no name\n");
2733 ctxt->nbErrors++;
Daniel Veillardd2298792003-02-14 16:54:11 +00002734 } else {
2735 xmlRelaxNGNormExtSpace(def->name);
2736 if (xmlValidateNCName(def->name, 0)) {
2737 if (ctxt->error != NULL)
2738 ctxt->error(ctxt->userData,
2739 "parentRef name '%s' is not an NCName\n",
2740 def->name);
2741 ctxt->nbErrors++;
2742 }
Daniel Veillard419a7682003-02-03 23:22:49 +00002743 }
2744 if (node->children != NULL) {
2745 if (ctxt->error != NULL)
2746 ctxt->error(ctxt->userData,
2747 "parentRef is not empty\n");
2748 ctxt->nbErrors++;
2749 }
2750 if (ctxt->parentgrammar->refs == NULL)
2751 ctxt->parentgrammar->refs = xmlHashCreate(10);
2752 if (ctxt->parentgrammar->refs == NULL) {
2753 if (ctxt->error != NULL)
2754 ctxt->error(ctxt->userData,
2755 "Could not create references hash\n");
2756 ctxt->nbErrors++;
2757 def = NULL;
Daniel Veillardd2298792003-02-14 16:54:11 +00002758 } else if (def->name != NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +00002759 int tmp;
2760
2761 tmp = xmlHashAddEntry(ctxt->parentgrammar->refs, def->name, def);
2762 if (tmp < 0) {
2763 xmlRelaxNGDefinePtr prev;
2764
2765 prev = (xmlRelaxNGDefinePtr)
2766 xmlHashLookup(ctxt->parentgrammar->refs, def->name);
2767 if (prev == NULL) {
2768 if (ctxt->error != NULL)
2769 ctxt->error(ctxt->userData,
2770 "Internal error parentRef definitions '%s'\n",
2771 def->name);
2772 ctxt->nbErrors++;
2773 def = NULL;
2774 } else {
2775 def->nextHash = prev->nextHash;
2776 prev->nextHash = def;
2777 }
2778 }
2779 }
Daniel Veillardd2298792003-02-14 16:54:11 +00002780 } else if (IS_RELAXNG(node, "mixed")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00002781 if (node->children == NULL) {
2782 if (ctxt->error != NULL)
2783 ctxt->error(ctxt->userData,
2784 "Mixed is empty\n");
2785 ctxt->nbErrors++;
2786 def = NULL;
Daniel Veillard416589a2003-02-17 17:25:42 +00002787 } else {
2788 def = xmlRelaxNGParseInterleave(ctxt, node);
2789 if (def != NULL) {
2790 xmlRelaxNGDefinePtr tmp;
Daniel Veillard2df2de22003-02-17 23:34:33 +00002791
2792 if ((def->content != NULL) && (def->content->next != NULL)) {
2793 tmp = xmlRelaxNGNewDefine(ctxt, node);
2794 if (tmp != NULL) {
2795 tmp->type = XML_RELAXNG_GROUP;
2796 tmp->content = def->content;
2797 def->content = tmp;
2798 }
2799 }
2800
Daniel Veillard416589a2003-02-17 17:25:42 +00002801 tmp = xmlRelaxNGNewDefine(ctxt, node);
2802 if (tmp == NULL)
2803 return(def);
2804 tmp->type = XML_RELAXNG_TEXT;
2805 tmp->next = def->content;
2806 def->content = tmp;
2807 }
2808 }
Daniel Veillardd2298792003-02-14 16:54:11 +00002809 } else {
2810 if (ctxt->error != NULL)
2811 ctxt->error(ctxt->userData,
2812 "Unexpected node %s is not a pattern\n",
2813 node->name);
2814 ctxt->nbErrors++;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002815 def = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002816 }
2817 return(def);
2818}
2819
2820/**
2821 * xmlRelaxNGParseAttribute:
2822 * @ctxt: a Relax-NG parser context
2823 * @node: the element node
2824 *
2825 * parse the content of a RelaxNG attribute node.
2826 *
2827 * Returns the definition pointer or NULL in case of error.
2828 */
2829static xmlRelaxNGDefinePtr
2830xmlRelaxNGParseAttribute(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
Daniel Veillardd2298792003-02-14 16:54:11 +00002831 xmlRelaxNGDefinePtr ret, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002832 xmlNodePtr child;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002833 int old_flags;
2834
2835 ret = xmlRelaxNGNewDefine(ctxt, node);
2836 if (ret == NULL)
2837 return(NULL);
2838 ret->type = XML_RELAXNG_ATTRIBUTE;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002839 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002840 child = node->children;
2841 if (child == NULL) {
2842 if (ctxt->error != NULL)
2843 ctxt->error(ctxt->userData,
2844 "xmlRelaxNGParseattribute: attribute has no children\n");
2845 ctxt->nbErrors++;
2846 return(ret);
2847 }
2848 old_flags = ctxt->flags;
2849 ctxt->flags |= XML_RELAXNG_IN_ATTRIBUTE;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002850 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
2851 if (cur != NULL)
2852 child = child->next;
2853
Daniel Veillardd2298792003-02-14 16:54:11 +00002854 if (child != NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00002855 cur = xmlRelaxNGParsePattern(ctxt, child);
2856 if (cur != NULL) {
2857 switch (cur->type) {
2858 case XML_RELAXNG_EMPTY:
2859 case XML_RELAXNG_NOT_ALLOWED:
2860 case XML_RELAXNG_TEXT:
2861 case XML_RELAXNG_ELEMENT:
2862 case XML_RELAXNG_DATATYPE:
2863 case XML_RELAXNG_VALUE:
2864 case XML_RELAXNG_LIST:
2865 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00002866 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002867 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00002868 case XML_RELAXNG_DEF:
2869 case XML_RELAXNG_ONEORMORE:
2870 case XML_RELAXNG_ZEROORMORE:
2871 case XML_RELAXNG_OPTIONAL:
2872 case XML_RELAXNG_CHOICE:
2873 case XML_RELAXNG_GROUP:
2874 case XML_RELAXNG_INTERLEAVE:
Daniel Veillardd2298792003-02-14 16:54:11 +00002875 ret->content = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002876 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002877 break;
2878 case XML_RELAXNG_ATTRIBUTE:
Daniel Veillardd2298792003-02-14 16:54:11 +00002879 if (ctxt->error != NULL)
2880 ctxt->error(ctxt->userData,
2881 "attribute has an attribute child\n");
2882 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002883 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002884 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00002885 case XML_RELAXNG_EXCEPT:
Daniel Veillardd2298792003-02-14 16:54:11 +00002886 if (ctxt->error != NULL)
2887 ctxt->error(ctxt->userData,
2888 "attribute has invalid content\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002889 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002890 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002891 }
2892 }
2893 child = child->next;
2894 }
Daniel Veillardd2298792003-02-14 16:54:11 +00002895 if (child != NULL) {
2896 if (ctxt->error != NULL)
2897 ctxt->error(ctxt->userData, "attribute has multiple children\n");
2898 ctxt->nbErrors++;
2899 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002900 ctxt->flags = old_flags;
2901 return(ret);
2902}
2903
2904/**
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002905 * xmlRelaxNGParseExceptNameClass:
2906 * @ctxt: a Relax-NG parser context
2907 * @node: the except node
Daniel Veillard144fae12003-02-03 13:17:57 +00002908 * @attr: 1 if within an attribute, 0 if within an element
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002909 *
2910 * parse the content of a RelaxNG nameClass node.
2911 *
2912 * Returns the definition pointer or NULL in case of error.
2913 */
2914static xmlRelaxNGDefinePtr
Daniel Veillard144fae12003-02-03 13:17:57 +00002915xmlRelaxNGParseExceptNameClass(xmlRelaxNGParserCtxtPtr ctxt,
2916 xmlNodePtr node, int attr) {
2917 xmlRelaxNGDefinePtr ret, cur, last = NULL;
2918 xmlNodePtr child;
2919
Daniel Veillardd2298792003-02-14 16:54:11 +00002920 if (!IS_RELAXNG(node, "except")) {
2921 if (ctxt->error != NULL)
2922 ctxt->error(ctxt->userData,
2923 "Expecting an except node\n");
2924 ctxt->nbErrors++;
Daniel Veillard144fae12003-02-03 13:17:57 +00002925 return(NULL);
Daniel Veillardd2298792003-02-14 16:54:11 +00002926 }
2927 if (node->next != NULL) {
2928 if (ctxt->error != NULL)
2929 ctxt->error(ctxt->userData,
2930 "exceptNameClass allows only a single except node\n");
2931 ctxt->nbErrors++;
2932 }
Daniel Veillard144fae12003-02-03 13:17:57 +00002933 if (node->children == NULL) {
2934 if (ctxt->error != NULL)
2935 ctxt->error(ctxt->userData,
2936 "except has no content\n");
2937 ctxt->nbErrors++;
2938 return(NULL);
2939 }
2940
2941 ret = xmlRelaxNGNewDefine(ctxt, node);
2942 if (ret == NULL)
2943 return(NULL);
2944 ret->type = XML_RELAXNG_EXCEPT;
2945 child = node->children;
2946 while (child != NULL) {
2947 cur = xmlRelaxNGNewDefine(ctxt, child);
2948 if (cur == NULL)
2949 break;
2950 if (attr)
2951 cur->type = XML_RELAXNG_ATTRIBUTE;
2952 else
2953 cur->type = XML_RELAXNG_ELEMENT;
2954
Daniel Veillard419a7682003-02-03 23:22:49 +00002955 if (xmlRelaxNGParseNameClass(ctxt, child, cur) != NULL) {
Daniel Veillard144fae12003-02-03 13:17:57 +00002956 if (last == NULL) {
2957 ret->content = cur;
2958 } else {
2959 last->next = cur;
2960 }
2961 last = cur;
2962 }
2963 child = child->next;
2964 }
2965
2966 return(ret);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002967}
2968
2969/**
2970 * xmlRelaxNGParseNameClass:
2971 * @ctxt: a Relax-NG parser context
2972 * @node: the nameClass node
2973 * @def: the current definition
2974 *
2975 * parse the content of a RelaxNG nameClass node.
2976 *
2977 * Returns the definition pointer or NULL in case of error.
2978 */
2979static xmlRelaxNGDefinePtr
2980xmlRelaxNGParseNameClass(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
2981 xmlRelaxNGDefinePtr def) {
2982 xmlRelaxNGDefinePtr ret = def;
2983 xmlChar *val;
2984
2985 if (IS_RELAXNG(node, "name")) {
2986 val = xmlNodeGetContent(node);
Daniel Veillardd2298792003-02-14 16:54:11 +00002987 xmlRelaxNGNormExtSpace(val);
2988 if (xmlValidateNCName(val, 0)) {
2989 if (ctxt->error != NULL) {
2990 if (node->parent != NULL)
2991 ctxt->error(ctxt->userData,
2992 "Element %s name '%s' is not an NCName\n",
2993 node->parent->name, val);
2994 else
2995 ctxt->error(ctxt->userData,
2996 "name '%s' is not an NCName\n",
2997 val);
2998 }
2999 ctxt->nbErrors++;
3000 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003001 ret->name = val;
3002 val = xmlGetProp(node, BAD_CAST "ns");
3003 ret->ns = val;
Daniel Veillard416589a2003-02-17 17:25:42 +00003004 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3005 (val != NULL) &&
3006 (xmlStrEqual(val, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
3007 ctxt->error(ctxt->userData,
3008 "Attribute with namespace '%s' is not allowed\n",
3009 val);
3010 ctxt->nbErrors++;
3011 }
3012 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3013 (val != NULL) &&
3014 (val[0] == 0) &&
3015 (xmlStrEqual(ret->name, BAD_CAST "xmlns"))) {
3016 ctxt->error(ctxt->userData,
3017 "Attribute with QName 'xmlns' is not allowed\n",
3018 val);
3019 ctxt->nbErrors++;
3020 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003021 } else if (IS_RELAXNG(node, "anyName")) {
3022 ret->name = NULL;
3023 ret->ns = NULL;
3024 if (node->children != NULL) {
3025 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00003026 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
3027 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003028 }
3029 } else if (IS_RELAXNG(node, "nsName")) {
3030 ret->name = NULL;
3031 ret->ns = xmlGetProp(node, BAD_CAST "ns");
3032 if (ret->ns == NULL) {
3033 if (ctxt->error != NULL)
3034 ctxt->error(ctxt->userData,
3035 "nsName has no ns attribute\n");
3036 ctxt->nbErrors++;
3037 }
Daniel Veillard416589a2003-02-17 17:25:42 +00003038 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3039 (ret->ns != NULL) &&
3040 (xmlStrEqual(ret->ns, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
3041 ctxt->error(ctxt->userData,
3042 "Attribute with namespace '%s' is not allowed\n",
3043 ret->ns);
3044 ctxt->nbErrors++;
3045 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003046 if (node->children != NULL) {
3047 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00003048 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
3049 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003050 }
3051 } else if (IS_RELAXNG(node, "choice")) {
Daniel Veillardd4310742003-02-18 21:12:46 +00003052 ret = xmlRelaxNGNewDefine(ctxt, node);
3053 if (ret == NULL)
3054 return(NULL);
3055 ret->parent = def;
3056 ret->type = XML_RELAXNG_CHOICE;
3057
Daniel Veillardd2298792003-02-14 16:54:11 +00003058 if (node->children == NULL) {
3059 if (ctxt->error != NULL)
3060 ctxt->error(ctxt->userData,
3061 "Element choice is empty\n");
3062 ctxt->nbErrors++;
3063 } else {
3064 xmlNodePtr child;
3065 xmlRelaxNGDefinePtr last = NULL, tmp;
3066
3067 child = node->children;
3068 while (child != NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00003069 tmp = xmlRelaxNGParseNameClass(ctxt, child, ret);
Daniel Veillardd2298792003-02-14 16:54:11 +00003070 if (tmp != NULL) {
3071 if (last == NULL) {
3072 last = ret->nameClass = tmp;
3073 } else {
3074 last->next = tmp;
3075 last = tmp;
3076 }
3077 }
3078 child = child->next;
3079 }
3080 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003081 } else {
3082 if (ctxt->error != NULL)
3083 ctxt->error(ctxt->userData,
3084 "expecting name, anyName, nsName or choice : got %s\n",
3085 node->name);
3086 ctxt->nbErrors++;
3087 return(NULL);
3088 }
3089 return(ret);
3090}
3091
3092/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00003093 * xmlRelaxNGParseElement:
3094 * @ctxt: a Relax-NG parser context
3095 * @node: the element node
3096 *
3097 * parse the content of a RelaxNG element node.
3098 *
3099 * Returns the definition pointer or NULL in case of error.
3100 */
3101static xmlRelaxNGDefinePtr
3102xmlRelaxNGParseElement(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3103 xmlRelaxNGDefinePtr ret, cur, last;
3104 xmlNodePtr child;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003105 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003106
3107 ret = xmlRelaxNGNewDefine(ctxt, node);
3108 if (ret == NULL)
3109 return(NULL);
3110 ret->type = XML_RELAXNG_ELEMENT;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003111 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003112 child = node->children;
3113 if (child == NULL) {
3114 if (ctxt->error != NULL)
3115 ctxt->error(ctxt->userData,
3116 "xmlRelaxNGParseElement: element has no children\n");
3117 ctxt->nbErrors++;
3118 return(ret);
3119 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003120 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
3121 if (cur != NULL)
3122 child = child->next;
3123
Daniel Veillard6eadf632003-01-23 18:29:16 +00003124 if (child == NULL) {
3125 if (ctxt->error != NULL)
3126 ctxt->error(ctxt->userData,
3127 "xmlRelaxNGParseElement: element has no content\n");
3128 ctxt->nbErrors++;
3129 return(ret);
3130 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003131 olddefine = ctxt->define;
3132 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003133 last = NULL;
3134 while (child != NULL) {
3135 cur = xmlRelaxNGParsePattern(ctxt, child);
3136 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003137 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003138 switch (cur->type) {
3139 case XML_RELAXNG_EMPTY:
3140 case XML_RELAXNG_NOT_ALLOWED:
3141 case XML_RELAXNG_TEXT:
3142 case XML_RELAXNG_ELEMENT:
3143 case XML_RELAXNG_DATATYPE:
3144 case XML_RELAXNG_VALUE:
3145 case XML_RELAXNG_LIST:
3146 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00003147 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003148 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00003149 case XML_RELAXNG_DEF:
3150 case XML_RELAXNG_ZEROORMORE:
3151 case XML_RELAXNG_ONEORMORE:
3152 case XML_RELAXNG_OPTIONAL:
3153 case XML_RELAXNG_CHOICE:
3154 case XML_RELAXNG_GROUP:
3155 case XML_RELAXNG_INTERLEAVE:
3156 if (last == NULL) {
3157 ret->content = last = cur;
3158 } else {
3159 if ((last->type == XML_RELAXNG_ELEMENT) &&
3160 (ret->content == last)) {
3161 ret->content = xmlRelaxNGNewDefine(ctxt, node);
3162 if (ret->content != NULL) {
3163 ret->content->type = XML_RELAXNG_GROUP;
3164 ret->content->content = last;
3165 } else {
3166 ret->content = last;
3167 }
3168 }
3169 last->next = cur;
3170 last = cur;
3171 }
3172 break;
3173 case XML_RELAXNG_ATTRIBUTE:
3174 cur->next = ret->attrs;
3175 ret->attrs = cur;
3176 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003177 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00003178 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003179 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003180 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003181 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003182 }
3183 }
3184 child = child->next;
3185 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003186 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003187 return(ret);
3188}
3189
3190/**
3191 * xmlRelaxNGParsePatterns:
3192 * @ctxt: a Relax-NG parser context
3193 * @nodes: list of nodes
Daniel Veillard154877e2003-01-30 12:17:05 +00003194 * @group: use an implicit <group> for elements
Daniel Veillard6eadf632003-01-23 18:29:16 +00003195 *
3196 * parse the content of a RelaxNG start node.
3197 *
3198 * Returns the definition pointer or NULL in case of error.
3199 */
3200static xmlRelaxNGDefinePtr
Daniel Veillard154877e2003-01-30 12:17:05 +00003201xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes,
3202 int group) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003203 xmlRelaxNGDefinePtr def = NULL, last = NULL, cur, parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003204
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003205 parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003206 while (nodes != NULL) {
3207 if (IS_RELAXNG(nodes, "element")) {
3208 cur = xmlRelaxNGParseElement(ctxt, nodes);
3209 if (def == NULL) {
3210 def = last = cur;
3211 } else {
Daniel Veillard154877e2003-01-30 12:17:05 +00003212 if ((group == 1) && (def->type == XML_RELAXNG_ELEMENT) &&
3213 (def == last)) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003214 def = xmlRelaxNGNewDefine(ctxt, nodes);
3215 def->type = XML_RELAXNG_GROUP;
3216 def->content = last;
3217 }
3218 last->next = cur;
3219 last = cur;
3220 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003221 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003222 } else {
3223 cur = xmlRelaxNGParsePattern(ctxt, nodes);
Daniel Veillard419a7682003-02-03 23:22:49 +00003224 if (cur != NULL) {
3225 if (def == NULL) {
3226 def = last = cur;
3227 } else {
3228 last->next = cur;
3229 last = cur;
3230 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003231 }
3232 }
3233 nodes = nodes->next;
3234 }
3235 return(def);
3236}
3237
3238/**
3239 * xmlRelaxNGParseStart:
3240 * @ctxt: a Relax-NG parser context
3241 * @nodes: start children nodes
3242 *
3243 * parse the content of a RelaxNG start node.
3244 *
3245 * Returns 0 in case of success, -1 in case of error
3246 */
3247static int
3248xmlRelaxNGParseStart(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
3249 int ret = 0;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003250 xmlRelaxNGDefinePtr def = NULL, last;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003251
Daniel Veillardd2298792003-02-14 16:54:11 +00003252 if (nodes == NULL) {
3253 if (ctxt->error != NULL)
3254 ctxt->error(ctxt->userData,
3255 "start has no children\n");
3256 ctxt->nbErrors++;
3257 return(-1);
3258 }
3259 if (IS_RELAXNG(nodes, "empty")) {
3260 def = xmlRelaxNGNewDefine(ctxt, nodes);
3261 if (def == NULL)
3262 return(-1);
3263 def->type = XML_RELAXNG_EMPTY;
3264 if (nodes->children != NULL) {
3265 if (ctxt->error != NULL)
3266 ctxt->error(ctxt->userData, "element empty is not empty\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003267 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003268 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003269 } else if (IS_RELAXNG(nodes, "notAllowed")) {
3270 def = xmlRelaxNGNewDefine(ctxt, nodes);
3271 if (def == NULL)
3272 return(-1);
3273 def->type = XML_RELAXNG_NOT_ALLOWED;
3274 if (nodes->children != NULL) {
3275 if (ctxt->error != NULL)
3276 ctxt->error(ctxt->userData,
3277 "element notAllowed is not empty\n");
3278 ctxt->nbErrors++;
3279 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003280 } else {
3281 def = xmlRelaxNGParsePatterns(ctxt, nodes, 1);
Daniel Veillard2df2de22003-02-17 23:34:33 +00003282 }
3283 if (ctxt->grammar->start != NULL) {
3284 last = ctxt->grammar->start;
3285 while (last->next != NULL)
3286 last = last->next;
3287 last->next = def;
3288 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00003289 ctxt->grammar->start = def;
3290 }
3291 nodes = nodes->next;
3292 if (nodes != NULL) {
3293 if (ctxt->error != NULL)
3294 ctxt->error(ctxt->userData,
3295 "start more than one children\n");
3296 ctxt->nbErrors++;
3297 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003298 }
3299 return(ret);
3300}
3301
3302/**
3303 * xmlRelaxNGParseGrammarContent:
3304 * @ctxt: a Relax-NG parser context
3305 * @nodes: grammar children nodes
3306 *
3307 * parse the content of a RelaxNG grammar node.
3308 *
3309 * Returns 0 in case of success, -1 in case of error
3310 */
3311static int
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003312xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003313{
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003314 int ret = 0, tmp;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003315
3316 if (nodes == NULL) {
3317 if (ctxt->error != NULL)
3318 ctxt->error(ctxt->userData,
3319 "grammar has no children\n");
3320 ctxt->nbErrors++;
3321 return(-1);
3322 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003323 while (nodes != NULL) {
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003324 if (IS_RELAXNG(nodes, "start")) {
3325 if (nodes->children == NULL) {
3326 if (ctxt->error != NULL)
3327 ctxt->error(ctxt->userData,
Daniel Veillardd2298792003-02-14 16:54:11 +00003328 "start has no children\n");
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003329 ctxt->nbErrors++;
3330 } else {
3331 tmp = xmlRelaxNGParseStart(ctxt, nodes->children);
3332 if (tmp != 0)
3333 ret = -1;
3334 }
3335 } else if (IS_RELAXNG(nodes, "define")) {
3336 tmp = xmlRelaxNGParseDefine(ctxt, nodes);
3337 if (tmp != 0)
3338 ret = -1;
3339 } else if (IS_RELAXNG(nodes, "include")) {
3340 tmp = xmlRelaxNGParseInclude(ctxt, nodes);
3341 if (tmp != 0)
3342 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003343 } else {
3344 if (ctxt->error != NULL)
3345 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003346 "grammar has unexpected child %s\n", nodes->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003347 ctxt->nbErrors++;
3348 ret = -1;
3349 }
3350 nodes = nodes->next;
3351 }
3352 return (ret);
3353}
3354
3355/**
3356 * xmlRelaxNGCheckReference:
3357 * @ref: the ref
3358 * @ctxt: a Relax-NG parser context
3359 * @name: the name associated to the defines
3360 *
3361 * Applies the 4.17. combine attribute rule for all the define
3362 * element of a given grammar using the same name.
3363 */
3364static void
3365xmlRelaxNGCheckReference(xmlRelaxNGDefinePtr ref,
3366 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
3367 xmlRelaxNGGrammarPtr grammar;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003368 xmlRelaxNGDefinePtr def, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003369
3370 grammar = ctxt->grammar;
3371 if (grammar == NULL) {
3372 if (ctxt->error != NULL)
3373 ctxt->error(ctxt->userData,
3374 "Internal error: no grammar in CheckReference %s\n",
3375 name);
3376 ctxt->nbErrors++;
3377 return;
3378 }
3379 if (ref->content != NULL) {
3380 if (ctxt->error != NULL)
3381 ctxt->error(ctxt->userData,
3382 "Internal error: reference has content in CheckReference %s\n",
3383 name);
3384 ctxt->nbErrors++;
3385 return;
3386 }
3387 if (grammar->defs != NULL) {
3388 def = xmlHashLookup(grammar->defs, name);
3389 if (def != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00003390 cur = ref;
3391 while (cur != NULL) {
3392 cur->content = def;
3393 cur = cur->nextHash;
3394 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003395 } else {
Daniel Veillardd4310742003-02-18 21:12:46 +00003396 if (ctxt->error != NULL)
3397 ctxt->error(ctxt->userData,
3398 "Reference %s has no matching definition\n",
3399 name);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003400 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003401 }
Daniel Veillardd4310742003-02-18 21:12:46 +00003402 } else {
3403 if (ctxt->error != NULL)
3404 ctxt->error(ctxt->userData,
3405 "Reference %s has no matching definition\n",
3406 name);
3407 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003408 }
3409 /*
3410 * TODO: make a closure and verify there is no loop !
3411 */
3412}
3413
3414/**
3415 * xmlRelaxNGCheckCombine:
3416 * @define: the define(s) list
3417 * @ctxt: a Relax-NG parser context
3418 * @name: the name associated to the defines
3419 *
3420 * Applies the 4.17. combine attribute rule for all the define
3421 * element of a given grammar using the same name.
3422 */
3423static void
3424xmlRelaxNGCheckCombine(xmlRelaxNGDefinePtr define,
3425 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
3426 xmlChar *combine;
3427 int choiceOrInterleave = -1;
3428 int missing = 0;
3429 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
3430
3431 if (define->nextHash == NULL)
3432 return;
3433 cur = define;
3434 while (cur != NULL) {
3435 combine = xmlGetProp(cur->node, BAD_CAST "combine");
3436 if (combine != NULL) {
3437 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3438 if (choiceOrInterleave == -1)
3439 choiceOrInterleave = 1;
3440 else if (choiceOrInterleave == 0) {
3441 if (ctxt->error != NULL)
3442 ctxt->error(ctxt->userData,
3443 "Defines for %s use both 'choice' and 'interleave'\n",
3444 name);
3445 ctxt->nbErrors++;
3446 }
Daniel Veillard154877e2003-01-30 12:17:05 +00003447 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003448 if (choiceOrInterleave == -1)
3449 choiceOrInterleave = 0;
3450 else if (choiceOrInterleave == 1) {
3451 if (ctxt->error != NULL)
3452 ctxt->error(ctxt->userData,
3453 "Defines for %s use both 'choice' and 'interleave'\n",
3454 name);
3455 ctxt->nbErrors++;
3456 }
3457 } else {
3458 if (ctxt->error != NULL)
3459 ctxt->error(ctxt->userData,
3460 "Defines for %s use unknown combine value '%s''\n",
3461 name, combine);
3462 ctxt->nbErrors++;
3463 }
3464 xmlFree(combine);
3465 } else {
3466 if (missing == 0)
3467 missing = 1;
3468 else {
3469 if (ctxt->error != NULL)
3470 ctxt->error(ctxt->userData,
3471 "Some defines for %s lacks the combine attribute\n",
3472 name);
3473 ctxt->nbErrors++;
3474 }
3475 }
3476
3477 cur = cur->nextHash;
3478 }
3479#ifdef DEBUG
3480 xmlGenericError(xmlGenericErrorContext,
3481 "xmlRelaxNGCheckCombine(): merging %s defines: %d\n",
3482 name, choiceOrInterleave);
3483#endif
3484 if (choiceOrInterleave == -1)
3485 choiceOrInterleave = 0;
3486 cur = xmlRelaxNGNewDefine(ctxt, define->node);
3487 if (cur == NULL)
3488 return;
3489 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003490 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard154877e2003-01-30 12:17:05 +00003491 else
3492 cur->type = XML_RELAXNG_CHOICE;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003493 tmp = define;
3494 last = NULL;
3495 while (tmp != NULL) {
3496 if (tmp->content != NULL) {
3497 if (tmp->content->next != NULL) {
3498 /*
3499 * we need first to create a wrapper.
3500 */
3501 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
3502 if (tmp2 == NULL)
3503 break;
3504 tmp2->type = XML_RELAXNG_GROUP;
3505 tmp2->content = tmp->content;
3506 } else {
3507 tmp2 = tmp->content;
3508 }
3509 if (last == NULL) {
3510 cur->content = tmp2;
3511 } else {
3512 last->next = tmp2;
3513 }
3514 last = tmp2;
3515 tmp->content = NULL;
3516 }
3517 tmp = tmp->nextHash;
3518 }
3519 define->content = cur;
Daniel Veillard154877e2003-01-30 12:17:05 +00003520 if (choiceOrInterleave == 0) {
3521 if (ctxt->interleaves == NULL)
3522 ctxt->interleaves = xmlHashCreate(10);
3523 if (ctxt->interleaves == NULL) {
3524 if (ctxt->error != NULL)
3525 ctxt->error(ctxt->userData,
3526 "Failed to create interleaves hash table\n");
3527 ctxt->nbErrors++;
3528 } else {
3529 char tmpname[32];
3530
3531 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
3532 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
3533 if (ctxt->error != NULL)
3534 ctxt->error(ctxt->userData,
3535 "Failed to add %s to hash table\n", tmpname);
3536 ctxt->nbErrors++;
3537 }
3538 }
3539 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003540}
3541
3542/**
3543 * xmlRelaxNGCombineStart:
3544 * @ctxt: a Relax-NG parser context
3545 * @grammar: the grammar
3546 *
3547 * Applies the 4.17. combine rule for all the start
3548 * element of a given grammar.
3549 */
3550static void
3551xmlRelaxNGCombineStart(xmlRelaxNGParserCtxtPtr ctxt,
3552 xmlRelaxNGGrammarPtr grammar) {
3553 xmlRelaxNGDefinePtr starts;
3554 xmlChar *combine;
3555 int choiceOrInterleave = -1;
3556 int missing = 0;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003557 xmlRelaxNGDefinePtr cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003558
Daniel Veillard2df2de22003-02-17 23:34:33 +00003559 starts = grammar->start;
3560 if ((starts == NULL) || (starts->next == NULL))
Daniel Veillard6eadf632003-01-23 18:29:16 +00003561 return;
3562 cur = starts;
3563 while (cur != NULL) {
Daniel Veillard2df2de22003-02-17 23:34:33 +00003564 if ((cur->node == NULL) || (cur->node->parent == NULL) ||
3565 (!xmlStrEqual(cur->node->parent->name, BAD_CAST "start"))) {
3566 combine = NULL;
3567 if (ctxt->error != NULL)
3568 ctxt->error(ctxt->userData,
3569 "Internal error: start element not found\n");
3570 ctxt->nbErrors++;
3571 } else {
3572 combine = xmlGetProp(cur->node->parent, BAD_CAST "combine");
3573 }
3574
Daniel Veillard6eadf632003-01-23 18:29:16 +00003575 if (combine != NULL) {
3576 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3577 if (choiceOrInterleave == -1)
3578 choiceOrInterleave = 1;
3579 else if (choiceOrInterleave == 0) {
3580 if (ctxt->error != NULL)
3581 ctxt->error(ctxt->userData,
3582 "<start> use both 'choice' and 'interleave'\n");
3583 ctxt->nbErrors++;
3584 }
Daniel Veillard2df2de22003-02-17 23:34:33 +00003585 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003586 if (choiceOrInterleave == -1)
3587 choiceOrInterleave = 0;
3588 else if (choiceOrInterleave == 1) {
3589 if (ctxt->error != NULL)
3590 ctxt->error(ctxt->userData,
3591 "<start> use both 'choice' and 'interleave'\n");
3592 ctxt->nbErrors++;
3593 }
3594 } else {
3595 if (ctxt->error != NULL)
3596 ctxt->error(ctxt->userData,
3597 "<start> uses unknown combine value '%s''\n", combine);
3598 ctxt->nbErrors++;
3599 }
3600 xmlFree(combine);
3601 } else {
3602 if (missing == 0)
3603 missing = 1;
3604 else {
3605 if (ctxt->error != NULL)
3606 ctxt->error(ctxt->userData,
3607 "Some <start> elements lacks the combine attribute\n");
3608 ctxt->nbErrors++;
3609 }
3610 }
3611
Daniel Veillard2df2de22003-02-17 23:34:33 +00003612 cur = cur->next;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003613 }
3614#ifdef DEBUG
3615 xmlGenericError(xmlGenericErrorContext,
3616 "xmlRelaxNGCombineStart(): merging <start>: %d\n",
3617 choiceOrInterleave);
3618#endif
3619 if (choiceOrInterleave == -1)
3620 choiceOrInterleave = 0;
3621 cur = xmlRelaxNGNewDefine(ctxt, starts->node);
3622 if (cur == NULL)
3623 return;
3624 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003625 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003626 else
3627 cur->type = XML_RELAXNG_CHOICE;
3628 cur->content = grammar->start;
3629 grammar->start = cur;
3630 if (choiceOrInterleave == 0) {
3631 if (ctxt->interleaves == NULL)
3632 ctxt->interleaves = xmlHashCreate(10);
3633 if (ctxt->interleaves == NULL) {
3634 if (ctxt->error != NULL)
3635 ctxt->error(ctxt->userData,
3636 "Failed to create interleaves hash table\n");
3637 ctxt->nbErrors++;
3638 } else {
3639 char tmpname[32];
3640
3641 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
3642 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
3643 if (ctxt->error != NULL)
3644 ctxt->error(ctxt->userData,
3645 "Failed to add %s to hash table\n", tmpname);
3646 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003647 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003648 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003649 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003650}
3651
3652/**
Daniel Veillardd4310742003-02-18 21:12:46 +00003653 * xmlRelaxNGCheckCycles:
3654 * @ctxt: a Relax-NG parser context
3655 * @nodes: grammar children nodes
3656 * @depth: the counter
3657 *
3658 * Check for cycles.
3659 *
3660 * Returns 0 if check passed, and -1 in case of error
3661 */
3662static int
3663xmlRelaxNGCheckCycles(xmlRelaxNGParserCtxtPtr ctxt,
3664 xmlRelaxNGDefinePtr cur, int depth) {
3665 int ret = 0;
3666
3667 while ((ret == 0) && (cur != NULL)) {
3668 if ((cur->type == XML_RELAXNG_REF) ||
3669 (cur->type == XML_RELAXNG_PARENTREF)) {
3670 if (cur->depth == -1) {
3671 cur->depth = depth;
3672 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth);
3673 cur->depth = -2;
3674 } else if (depth == cur->depth) {
3675 if (ctxt->error != NULL)
3676 ctxt->error(ctxt->userData,
3677 "Detected a cycle in %s references\n", cur->name);
3678 ctxt->nbErrors++;
3679 return(-1);
3680 }
3681 } else if (cur->type == XML_RELAXNG_ELEMENT) {
3682 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth + 1);
3683 } else {
3684 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth);
3685 }
3686 cur = cur->next;
3687 }
3688 return(ret);
3689}
3690
3691/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00003692 * xmlRelaxNGParseGrammar:
3693 * @ctxt: a Relax-NG parser context
3694 * @nodes: grammar children nodes
3695 *
3696 * parse a Relax-NG <grammar> node
3697 *
3698 * Returns the internal xmlRelaxNGGrammarPtr built or
3699 * NULL in case of error
3700 */
3701static xmlRelaxNGGrammarPtr
3702xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
3703 xmlRelaxNGGrammarPtr ret, tmp, old;
3704
Daniel Veillard6eadf632003-01-23 18:29:16 +00003705 ret = xmlRelaxNGNewGrammar(ctxt);
3706 if (ret == NULL)
3707 return(NULL);
3708
3709 /*
3710 * Link the new grammar in the tree
3711 */
3712 ret->parent = ctxt->grammar;
3713 if (ctxt->grammar != NULL) {
3714 tmp = ctxt->grammar->children;
3715 if (tmp == NULL) {
3716 ctxt->grammar->children = ret;
3717 } else {
3718 while (tmp->next != NULL)
3719 tmp = tmp->next;
3720 tmp->next = ret;
3721 }
3722 }
3723
3724 old = ctxt->grammar;
3725 ctxt->grammar = ret;
3726 xmlRelaxNGParseGrammarContent(ctxt, nodes);
3727 ctxt->grammar = ret;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003728 if (ctxt->grammar == NULL) {
3729 if (ctxt->error != NULL)
3730 ctxt->error(ctxt->userData,
3731 "Failed to parse <grammar> content\n");
3732 ctxt->nbErrors++;
3733 } else if (ctxt->grammar->start == NULL) {
3734 if (ctxt->error != NULL)
3735 ctxt->error(ctxt->userData,
3736 "Element <grammar> has no <start>\n");
3737 ctxt->nbErrors++;
3738 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003739
3740 /*
3741 * Apply 4.17 mergingd rules to defines and starts
3742 */
3743 xmlRelaxNGCombineStart(ctxt, ret);
3744 if (ret->defs != NULL) {
3745 xmlHashScan(ret->defs, (xmlHashScanner) xmlRelaxNGCheckCombine,
3746 ctxt);
3747 }
3748
3749 /*
3750 * link together defines and refs in this grammar
3751 */
3752 if (ret->refs != NULL) {
3753 xmlHashScan(ret->refs, (xmlHashScanner) xmlRelaxNGCheckReference,
3754 ctxt);
3755 }
3756 ctxt->grammar = old;
3757 return(ret);
3758}
3759
3760/**
3761 * xmlRelaxNGParseDocument:
3762 * @ctxt: a Relax-NG parser context
3763 * @node: the root node of the RelaxNG schema
3764 *
3765 * parse a Relax-NG definition resource and build an internal
3766 * xmlRelaxNG struture which can be used to validate instances.
3767 *
3768 * Returns the internal XML RelaxNG structure built or
3769 * NULL in case of error
3770 */
3771static xmlRelaxNGPtr
3772xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3773 xmlRelaxNGPtr schema = NULL;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003774 const xmlChar *olddefine;
Daniel Veillarde431a272003-01-29 23:02:33 +00003775 xmlRelaxNGGrammarPtr old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003776
3777 if ((ctxt == NULL) || (node == NULL))
3778 return (NULL);
3779
3780 schema = xmlRelaxNGNewRelaxNG(ctxt);
3781 if (schema == NULL)
3782 return(NULL);
3783
Daniel Veillard276be4a2003-01-24 01:03:34 +00003784 olddefine = ctxt->define;
3785 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003786 if (IS_RELAXNG(node, "grammar")) {
3787 schema->topgrammar = xmlRelaxNGParseGrammar(ctxt, node->children);
3788 } else {
3789 schema->topgrammar = xmlRelaxNGNewGrammar(ctxt);
3790 if (schema->topgrammar == NULL) {
3791 return(schema);
3792 }
3793 schema->topgrammar->parent = NULL;
Daniel Veillarde431a272003-01-29 23:02:33 +00003794 old = ctxt->grammar;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003795 ctxt->grammar = schema->topgrammar;
3796 xmlRelaxNGParseStart(ctxt, node);
Daniel Veillarde431a272003-01-29 23:02:33 +00003797 if (old != NULL)
3798 ctxt->grammar = old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003799 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003800 ctxt->define = olddefine;
Daniel Veillardd4310742003-02-18 21:12:46 +00003801 if (schema->topgrammar->start != NULL) {
3802 xmlRelaxNGCheckCycles(ctxt, schema->topgrammar->start, 0);
3803 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003804
3805#ifdef DEBUG
3806 if (schema == NULL)
3807 xmlGenericError(xmlGenericErrorContext,
3808 "xmlRelaxNGParseDocument() failed\n");
3809#endif
3810
3811 return (schema);
3812}
3813
3814/************************************************************************
3815 * *
3816 * Reading RelaxNGs *
3817 * *
3818 ************************************************************************/
3819
3820/**
3821 * xmlRelaxNGNewParserCtxt:
3822 * @URL: the location of the schema
3823 *
3824 * Create an XML RelaxNGs parse context for that file/resource expected
3825 * to contain an XML RelaxNGs file.
3826 *
3827 * Returns the parser context or NULL in case of error
3828 */
3829xmlRelaxNGParserCtxtPtr
3830xmlRelaxNGNewParserCtxt(const char *URL) {
3831 xmlRelaxNGParserCtxtPtr ret;
3832
3833 if (URL == NULL)
3834 return(NULL);
3835
3836 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
3837 if (ret == NULL) {
3838 xmlGenericError(xmlGenericErrorContext,
3839 "Failed to allocate new schama parser context for %s\n", URL);
3840 return (NULL);
3841 }
3842 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
3843 ret->URL = xmlStrdup((const xmlChar *)URL);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003844 ret->error = xmlGenericError;
3845 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003846 return (ret);
3847}
3848
3849/**
3850 * xmlRelaxNGNewMemParserCtxt:
3851 * @buffer: a pointer to a char array containing the schemas
3852 * @size: the size of the array
3853 *
3854 * Create an XML RelaxNGs parse context for that memory buffer expected
3855 * to contain an XML RelaxNGs file.
3856 *
3857 * Returns the parser context or NULL in case of error
3858 */
3859xmlRelaxNGParserCtxtPtr
3860xmlRelaxNGNewMemParserCtxt(const char *buffer, int size) {
3861 xmlRelaxNGParserCtxtPtr ret;
3862
3863 if ((buffer == NULL) || (size <= 0))
3864 return(NULL);
3865
3866 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
3867 if (ret == NULL) {
3868 xmlGenericError(xmlGenericErrorContext,
3869 "Failed to allocate new schama parser context\n");
3870 return (NULL);
3871 }
3872 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
3873 ret->buffer = buffer;
3874 ret->size = size;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003875 ret->error = xmlGenericError;
3876 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003877 return (ret);
3878}
3879
3880/**
3881 * xmlRelaxNGFreeParserCtxt:
3882 * @ctxt: the schema parser context
3883 *
3884 * Free the resources associated to the schema parser context
3885 */
3886void
3887xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxtPtr ctxt) {
3888 if (ctxt == NULL)
3889 return;
3890 if (ctxt->URL != NULL)
3891 xmlFree(ctxt->URL);
3892 if (ctxt->doc != NULL)
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003893 xmlFreeDoc(ctxt->document);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003894 if (ctxt->interleaves != NULL)
3895 xmlHashFree(ctxt->interleaves, NULL);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003896 if (ctxt->documents != NULL)
3897 xmlHashFree(ctxt->documents, (xmlHashDeallocator)
3898 xmlRelaxNGFreeDocument);
3899 if (ctxt->docTab != NULL)
3900 xmlFree(ctxt->docTab);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003901 if (ctxt->incTab != NULL)
3902 xmlFree(ctxt->incTab);
Daniel Veillard419a7682003-02-03 23:22:49 +00003903 if (ctxt->defTab != NULL) {
3904 int i;
3905
3906 for (i = 0;i < ctxt->defNr;i++)
3907 xmlRelaxNGFreeDefine(ctxt->defTab[i]);
3908 xmlFree(ctxt->defTab);
3909 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003910 xmlFree(ctxt);
3911}
3912
Daniel Veillard6eadf632003-01-23 18:29:16 +00003913/**
Daniel Veillardd2298792003-02-14 16:54:11 +00003914 * xmlRelaxNGNormExtSpace:
3915 * @value: a value
3916 *
3917 * Removes the leading and ending spaces of the value
3918 * The string is modified "in situ"
3919 */
3920static void
3921xmlRelaxNGNormExtSpace(xmlChar *value) {
3922 xmlChar *start = value;
3923 xmlChar *cur = value;
3924 if (value == NULL)
3925 return;
3926
3927 while (IS_BLANK(*cur)) cur++;
3928 if (cur == start) {
3929 do {
3930 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
3931 if (*cur == 0)
3932 return;
3933 start = cur;
3934 while (IS_BLANK(*cur)) cur++;
3935 if (*cur == 0) {
3936 *start = 0;
3937 return;
3938 }
3939 } while (1);
3940 } else {
3941 do {
3942 while ((*cur != 0) && (!IS_BLANK(*cur)))
3943 *start++ = *cur++;
3944 if (*cur == 0) {
3945 *start = 0;
3946 return;
3947 }
3948 /* don't try to normalize the inner spaces */
3949 while (IS_BLANK(*cur)) cur++;
3950 *start++ = *cur++;
3951 if (*cur == 0) {
3952 *start = 0;
3953 return;
3954 }
3955 } while (1);
3956 }
3957}
3958
3959/**
3960 * xmlRelaxNGCheckAttributes:
3961 * @ctxt: a Relax-NG parser context
3962 * @node: a Relax-NG node
3963 *
3964 * Check all the attributes on the given node
3965 */
3966static void
3967xmlRelaxNGCleanupAttributes(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3968 xmlAttrPtr cur, next;
3969
3970 cur = node->properties;
3971 while (cur != NULL) {
3972 next = cur->next;
3973 if ((cur->ns == NULL) ||
3974 (xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
3975 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
3976 if ((!xmlStrEqual(node->name, BAD_CAST "element")) &&
3977 (!xmlStrEqual(node->name, BAD_CAST "attribute")) &&
3978 (!xmlStrEqual(node->name, BAD_CAST "ref")) &&
3979 (!xmlStrEqual(node->name, BAD_CAST "parentRef")) &&
Daniel Veillard2df2de22003-02-17 23:34:33 +00003980 (!xmlStrEqual(node->name, BAD_CAST "param")) &&
Daniel Veillardd2298792003-02-14 16:54:11 +00003981 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
3982 if (ctxt->error != NULL)
3983 ctxt->error(ctxt->userData,
3984 "Attribute %s is not allowed on %s\n",
3985 cur->name, node->name);
3986 ctxt->nbErrors++;
3987 }
3988 } else if (xmlStrEqual(cur->name, BAD_CAST "type")) {
3989 if ((!xmlStrEqual(node->name, BAD_CAST "value")) &&
3990 (!xmlStrEqual(node->name, BAD_CAST "data"))) {
3991 if (ctxt->error != NULL)
3992 ctxt->error(ctxt->userData,
3993 "Attribute %s is not allowed on %s\n",
3994 cur->name, node->name);
3995 ctxt->nbErrors++;
3996 }
3997 } else if (xmlStrEqual(cur->name, BAD_CAST "href")) {
3998 if ((!xmlStrEqual(node->name, BAD_CAST "externalRef")) &&
3999 (!xmlStrEqual(node->name, BAD_CAST "include"))) {
4000 if (ctxt->error != NULL)
4001 ctxt->error(ctxt->userData,
4002 "Attribute %s is not allowed on %s\n",
4003 cur->name, node->name);
4004 ctxt->nbErrors++;
4005 }
4006 } else if (xmlStrEqual(cur->name, BAD_CAST "combine")) {
4007 if ((!xmlStrEqual(node->name, BAD_CAST "start")) &&
4008 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
4009 if (ctxt->error != NULL)
4010 ctxt->error(ctxt->userData,
4011 "Attribute %s is not allowed on %s\n",
4012 cur->name, node->name);
4013 ctxt->nbErrors++;
4014 }
4015 } else if (xmlStrEqual(cur->name, BAD_CAST "datatypeLibrary")) {
4016 xmlChar *val;
4017 xmlURIPtr uri;
4018
4019 val = xmlNodeListGetString(node->doc, cur->children, 1);
4020 if (val != NULL) {
4021 if (val[0] != 0) {
4022 uri = xmlParseURI((const char *) val);
4023 if (uri == NULL) {
4024 if (ctxt->error != NULL)
4025 ctxt->error(ctxt->userData,
4026 "Attribute %s contains invalid URI %s\n",
4027 cur->name, val);
4028 ctxt->nbErrors++;
4029 } else {
4030 if (uri->scheme == NULL) {
4031 if (ctxt->error != NULL)
4032 ctxt->error(ctxt->userData,
4033 "Attribute %s URI %s is not absolute\n",
4034 cur->name, val);
4035 ctxt->nbErrors++;
4036 }
4037 if (uri->fragment != NULL) {
4038 if (ctxt->error != NULL)
4039 ctxt->error(ctxt->userData,
4040 "Attribute %s URI %s has a fragment ID\n",
4041 cur->name, val);
4042 ctxt->nbErrors++;
4043 }
4044 xmlFreeURI(uri);
4045 }
4046 }
4047 xmlFree(val);
4048 }
4049 } else if (!xmlStrEqual(cur->name, BAD_CAST "ns")) {
4050 if (ctxt->error != NULL)
4051 ctxt->error(ctxt->userData,
4052 "Unknown attribute %s on %s\n",
4053 cur->name, node->name);
4054 ctxt->nbErrors++;
4055 }
4056 }
4057 cur = next;
4058 }
4059}
4060
4061/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004062 * xmlRelaxNGCleanupDoc:
4063 * @ctxt: a Relax-NG parser context
4064 * @doc: an xmldocPtr document pointer
Daniel Veillard6eadf632003-01-23 18:29:16 +00004065 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004066 * Cleanup the document from unwanted nodes for parsing, resolve
4067 * Include and externalRef lookups.
Daniel Veillard6eadf632003-01-23 18:29:16 +00004068 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004069 * Returns the cleaned up document or NULL in case of error
Daniel Veillard6eadf632003-01-23 18:29:16 +00004070 */
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004071static xmlDocPtr
4072xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt, xmlDocPtr doc) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00004073 xmlNodePtr root, cur, delete;
4074
Daniel Veillard6eadf632003-01-23 18:29:16 +00004075 /*
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004076 * Extract the root
Daniel Veillard6eadf632003-01-23 18:29:16 +00004077 */
4078 root = xmlDocGetRootElement(doc);
4079 if (root == NULL) {
4080 if (ctxt->error != NULL)
4081 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
4082 ctxt->URL);
4083 ctxt->nbErrors++;
4084 return (NULL);
4085 }
4086
4087 /*
4088 * Remove all the blank text nodes
4089 */
4090 delete = NULL;
4091 cur = root;
4092 while (cur != NULL) {
4093 if (delete != NULL) {
4094 xmlUnlinkNode(delete);
4095 xmlFreeNode(delete);
4096 delete = NULL;
4097 }
4098 if (cur->type == XML_ELEMENT_NODE) {
4099 /*
4100 * Simplification 4.1. Annotations
4101 */
4102 if ((cur->ns == NULL) ||
4103 (!xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
Daniel Veillardd2298792003-02-14 16:54:11 +00004104 if ((cur->parent != NULL) &&
4105 (cur->parent->type == XML_ELEMENT_NODE) &&
4106 ((xmlStrEqual(cur->parent->name, BAD_CAST "name")) ||
4107 (xmlStrEqual(cur->parent->name, BAD_CAST "value")) ||
4108 (xmlStrEqual(cur->parent->name, BAD_CAST "param")))) {
4109 if (ctxt->error != NULL)
4110 ctxt->error(ctxt->userData,
4111 "element %s doesn't allow foreign elements\n",
4112 cur->parent->name);
4113 ctxt->nbErrors++;
4114 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004115 delete = cur;
4116 goto skip_children;
4117 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00004118 xmlRelaxNGCleanupAttributes(ctxt, cur);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004119 if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004120 xmlChar *href, *ns, *base, *URL;
4121 xmlRelaxNGDocumentPtr docu;
Daniel Veillard416589a2003-02-17 17:25:42 +00004122 xmlNodePtr tmp;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004123
4124 ns = xmlGetProp(cur, BAD_CAST "ns");
Daniel Veillard416589a2003-02-17 17:25:42 +00004125 if (ns == NULL) {
4126 tmp = cur->parent;
4127 while ((tmp != NULL) &&
4128 (tmp->type == XML_ELEMENT_NODE)) {
4129 ns = xmlGetProp(tmp, BAD_CAST "ns");
4130 if (ns != NULL)
4131 break;
4132 tmp = tmp->parent;
4133 }
4134 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004135 href = xmlGetProp(cur, BAD_CAST "href");
4136 if (href == NULL) {
4137 if (ctxt->error != NULL)
4138 ctxt->error(ctxt->userData,
4139 "xmlRelaxNGParse: externalRef has no href attribute\n");
4140 ctxt->nbErrors++;
4141 delete = cur;
4142 goto skip_children;
4143 }
4144 base = xmlNodeGetBase(cur->doc, cur);
4145 URL = xmlBuildURI(href, base);
4146 if (URL == NULL) {
4147 if (ctxt->error != NULL)
4148 ctxt->error(ctxt->userData,
4149 "Failed to compute URL for externalRef %s\n", href);
4150 ctxt->nbErrors++;
4151 if (href != NULL)
4152 xmlFree(href);
4153 if (base != NULL)
4154 xmlFree(base);
4155 delete = cur;
4156 goto skip_children;
4157 }
4158 if (href != NULL)
4159 xmlFree(href);
4160 if (base != NULL)
4161 xmlFree(base);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004162 docu = xmlRelaxNGLoadExternalRef(ctxt, URL, ns);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004163 if (docu == NULL) {
4164 if (ctxt->error != NULL)
4165 ctxt->error(ctxt->userData,
4166 "Failed to load externalRef %s\n", URL);
4167 ctxt->nbErrors++;
4168 xmlFree(URL);
4169 delete = cur;
4170 goto skip_children;
4171 }
4172 xmlFree(URL);
4173 cur->_private = docu;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004174 } else if (xmlStrEqual(cur->name, BAD_CAST "include")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00004175 xmlChar *href, *ns, *base, *URL;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004176 xmlRelaxNGIncludePtr incl;
Daniel Veillard416589a2003-02-17 17:25:42 +00004177 xmlNodePtr tmp;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004178
4179 href = xmlGetProp(cur, BAD_CAST "href");
4180 if (href == NULL) {
4181 if (ctxt->error != NULL)
4182 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004183 "xmlRelaxNGParse: include has no href attribute\n");
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004184 ctxt->nbErrors++;
4185 delete = cur;
4186 goto skip_children;
4187 }
4188 base = xmlNodeGetBase(cur->doc, cur);
4189 URL = xmlBuildURI(href, base);
4190 if (URL == NULL) {
4191 if (ctxt->error != NULL)
4192 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004193 "Failed to compute URL for include %s\n", href);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004194 ctxt->nbErrors++;
4195 if (href != NULL)
4196 xmlFree(href);
4197 if (base != NULL)
4198 xmlFree(base);
4199 delete = cur;
4200 goto skip_children;
4201 }
4202 if (href != NULL)
4203 xmlFree(href);
4204 if (base != NULL)
4205 xmlFree(base);
Daniel Veillard416589a2003-02-17 17:25:42 +00004206 ns = xmlGetProp(cur, BAD_CAST "ns");
4207 if (ns == NULL) {
4208 tmp = cur->parent;
4209 while ((tmp != NULL) &&
4210 (tmp->type == XML_ELEMENT_NODE)) {
4211 ns = xmlGetProp(tmp, BAD_CAST "ns");
4212 if (ns != NULL)
4213 break;
4214 tmp = tmp->parent;
4215 }
4216 }
4217 incl = xmlRelaxNGLoadInclude(ctxt, URL, cur, ns);
4218 if (ns != NULL)
4219 xmlFree(ns);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004220 if (incl == NULL) {
4221 if (ctxt->error != NULL)
4222 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004223 "Failed to load include %s\n", URL);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004224 ctxt->nbErrors++;
4225 xmlFree(URL);
4226 delete = cur;
4227 goto skip_children;
4228 }
4229 xmlFree(URL);
4230 cur->_private = incl;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004231 } else if ((xmlStrEqual(cur->name, BAD_CAST "element")) ||
4232 (xmlStrEqual(cur->name, BAD_CAST "attribute"))) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00004233 xmlChar *name, *ns;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004234 xmlNodePtr text = NULL;
4235
4236 /*
4237 * Simplification 4.8. name attribute of element
4238 * and attribute elements
4239 */
4240 name = xmlGetProp(cur, BAD_CAST "name");
4241 if (name != NULL) {
4242 if (cur->children == NULL) {
4243 text = xmlNewChild(cur, cur->ns, BAD_CAST "name",
4244 name);
4245 } else {
4246 xmlNodePtr node;
4247 node = xmlNewNode(cur->ns, BAD_CAST "name");
4248 if (node != NULL) {
4249 xmlAddPrevSibling(cur->children, node);
4250 text = xmlNewText(name);
4251 xmlAddChild(node, text);
4252 text = node;
4253 }
4254 }
Daniel Veillardfebcca42003-02-16 15:44:18 +00004255 if (text == NULL) {
4256 if (ctxt->error != NULL)
4257 ctxt->error(ctxt->userData,
4258 "Failed to create a name %s element\n", name);
4259 ctxt->nbErrors++;
4260 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004261 xmlUnsetProp(cur, BAD_CAST "name");
4262 xmlFree(name);
Daniel Veillardfebcca42003-02-16 15:44:18 +00004263 ns = xmlGetProp(cur, BAD_CAST "ns");
4264 if (ns != NULL) {
4265 if (text != NULL) {
4266 xmlSetProp(text, BAD_CAST "ns", ns);
4267 /* xmlUnsetProp(cur, BAD_CAST "ns"); */
Daniel Veillard6eadf632003-01-23 18:29:16 +00004268 }
Daniel Veillardfebcca42003-02-16 15:44:18 +00004269 xmlFree(ns);
4270 } else if (xmlStrEqual(cur->name,
4271 BAD_CAST "attribute")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00004272 xmlSetProp(text, BAD_CAST "ns", BAD_CAST "");
4273 }
4274 }
4275 } else if ((xmlStrEqual(cur->name, BAD_CAST "name")) ||
4276 (xmlStrEqual(cur->name, BAD_CAST "nsName")) ||
4277 (xmlStrEqual(cur->name, BAD_CAST "value"))) {
4278 /*
4279 * Simplification 4.8. name attribute of element
4280 * and attribute elements
4281 */
4282 if (xmlHasProp(cur, BAD_CAST "ns") == NULL) {
4283 xmlNodePtr node;
4284 xmlChar *ns = NULL;
4285
4286 node = cur->parent;
4287 while ((node != NULL) &&
4288 (node->type == XML_ELEMENT_NODE)) {
4289 ns = xmlGetProp(node, BAD_CAST "ns");
4290 if (ns != NULL) {
4291 break;
4292 }
4293 node = node->parent;
4294 }
4295 if (ns == NULL) {
4296 xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
4297 } else {
4298 xmlSetProp(cur, BAD_CAST "ns", ns);
4299 xmlFree(ns);
4300 }
4301 }
4302 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
4303 xmlChar *name, *local, *prefix;
4304
4305 /*
4306 * Simplification: 4.10. QNames
4307 */
4308 name = xmlNodeGetContent(cur);
4309 if (name != NULL) {
4310 local = xmlSplitQName2(name, &prefix);
4311 if (local != NULL) {
4312 xmlNsPtr ns;
4313
4314 ns = xmlSearchNs(cur->doc, cur, prefix);
4315 if (ns == NULL) {
4316 if (ctxt->error != NULL)
4317 ctxt->error(ctxt->userData,
4318 "xmlRelaxNGParse: no namespace for prefix %s\n", prefix);
4319 ctxt->nbErrors++;
4320 } else {
4321 xmlSetProp(cur, BAD_CAST "ns", ns->href);
4322 xmlNodeSetContent(cur, local);
4323 }
4324 xmlFree(local);
4325 xmlFree(prefix);
4326 }
4327 xmlFree(name);
4328 }
4329 }
4330 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004331 /*
4332 * Thisd is not an else since "include" is transformed
4333 * into a div
4334 */
4335 if (xmlStrEqual(cur->name, BAD_CAST "div")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00004336 xmlChar *ns;
4337 xmlNodePtr child, ins, tmp;
4338
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004339 /*
4340 * implements rule 4.11
4341 */
Daniel Veillard416589a2003-02-17 17:25:42 +00004342
4343 ns = xmlGetProp(cur, BAD_CAST "ns");
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004344
4345 child = cur->children;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004346 ins = cur;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004347 while (child != NULL) {
Daniel Veillard416589a2003-02-17 17:25:42 +00004348 if (ns != NULL) {
4349 if (!xmlHasProp(child, BAD_CAST "ns")) {
4350 xmlSetProp(child, BAD_CAST "ns", ns);
4351 }
4352 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004353 tmp = child->next;
4354 xmlUnlinkNode(child);
4355 ins = xmlAddNextSibling(ins, child);
4356 child = tmp;
4357 }
Daniel Veillard416589a2003-02-17 17:25:42 +00004358 if (ns != NULL)
4359 xmlFree(ns);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004360 delete = cur;
4361 goto skip_children;
4362 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004363 }
4364 }
4365 /*
4366 * Simplification 4.2 whitespaces
4367 */
4368 else if (cur->type == XML_TEXT_NODE) {
4369 if (IS_BLANK_NODE(cur)) {
4370 if (cur->parent->type == XML_ELEMENT_NODE) {
4371 if ((!xmlStrEqual(cur->parent->name, BAD_CAST "value")) &&
4372 (!xmlStrEqual(cur->parent->name, BAD_CAST "param")))
4373 delete = cur;
4374 } else {
4375 delete = cur;
4376 goto skip_children;
4377 }
4378 }
4379 } else if (cur->type != XML_CDATA_SECTION_NODE) {
4380 delete = cur;
4381 goto skip_children;
4382 }
4383
4384 /*
4385 * Skip to next node
4386 */
4387 if (cur->children != NULL) {
4388 if ((cur->children->type != XML_ENTITY_DECL) &&
4389 (cur->children->type != XML_ENTITY_REF_NODE) &&
4390 (cur->children->type != XML_ENTITY_NODE)) {
4391 cur = cur->children;
4392 continue;
4393 }
4394 }
4395skip_children:
4396 if (cur->next != NULL) {
4397 cur = cur->next;
4398 continue;
4399 }
4400
4401 do {
4402 cur = cur->parent;
4403 if (cur == NULL)
4404 break;
4405 if (cur == root) {
4406 cur = NULL;
4407 break;
4408 }
4409 if (cur->next != NULL) {
4410 cur = cur->next;
4411 break;
4412 }
4413 } while (cur != NULL);
4414 }
4415 if (delete != NULL) {
4416 xmlUnlinkNode(delete);
4417 xmlFreeNode(delete);
4418 delete = NULL;
4419 }
4420
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004421 return(doc);
4422}
4423
4424/**
4425 * xmlRelaxNGParse:
4426 * @ctxt: a Relax-NG parser context
4427 *
4428 * parse a schema definition resource and build an internal
4429 * XML Shema struture which can be used to validate instances.
4430 * *WARNING* this interface is highly subject to change
4431 *
4432 * Returns the internal XML RelaxNG structure built from the resource or
4433 * NULL in case of error
4434 */
4435xmlRelaxNGPtr
4436xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt)
4437{
4438 xmlRelaxNGPtr ret = NULL;
4439 xmlDocPtr doc;
4440 xmlNodePtr root;
4441
4442 xmlRelaxNGInitTypes();
4443
4444 if (ctxt == NULL)
4445 return (NULL);
4446
4447 /*
4448 * First step is to parse the input document into an DOM/Infoset
4449 */
4450 if (ctxt->URL != NULL) {
4451 doc = xmlParseFile((const char *) ctxt->URL);
4452 if (doc == NULL) {
4453 if (ctxt->error != NULL)
4454 ctxt->error(ctxt->userData,
4455 "xmlRelaxNGParse: could not load %s\n", ctxt->URL);
4456 ctxt->nbErrors++;
4457 return (NULL);
4458 }
4459 } else if (ctxt->buffer != NULL) {
4460 doc = xmlParseMemory(ctxt->buffer, ctxt->size);
4461 if (doc == NULL) {
4462 if (ctxt->error != NULL)
4463 ctxt->error(ctxt->userData,
4464 "xmlRelaxNGParse: could not parse schemas\n");
4465 ctxt->nbErrors++;
4466 return (NULL);
4467 }
4468 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
4469 ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
4470 } else {
4471 if (ctxt->error != NULL)
4472 ctxt->error(ctxt->userData,
4473 "xmlRelaxNGParse: nothing to parse\n");
4474 ctxt->nbErrors++;
4475 return (NULL);
4476 }
4477 ctxt->document = doc;
4478
4479 /*
4480 * Some preprocessing of the document content
4481 */
4482 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
4483 if (doc == NULL) {
4484 xmlFreeDoc(ctxt->document);
4485 ctxt->document = NULL;
4486 return(NULL);
4487 }
4488
Daniel Veillard6eadf632003-01-23 18:29:16 +00004489 /*
4490 * Then do the parsing for good
4491 */
4492 root = xmlDocGetRootElement(doc);
4493 if (root == NULL) {
4494 if (ctxt->error != NULL)
4495 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
4496 ctxt->URL);
4497 ctxt->nbErrors++;
4498 return (NULL);
4499 }
4500 ret = xmlRelaxNGParseDocument(ctxt, root);
4501 if (ret == NULL)
4502 return(NULL);
4503
4504 /*
4505 * Check the ref/defines links
4506 */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004507 /*
4508 * try to preprocess interleaves
4509 */
4510 if (ctxt->interleaves != NULL) {
4511 xmlHashScan(ctxt->interleaves,
4512 (xmlHashScanner)xmlRelaxNGComputeInterleaves, ctxt);
4513 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004514
4515 /*
4516 * if there was a parsing error return NULL
4517 */
4518 if (ctxt->nbErrors > 0) {
4519 xmlRelaxNGFree(ret);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004520 ctxt->document = NULL;
4521 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004522 return(NULL);
4523 }
4524
4525 /*
4526 * Transfer the pointer for cleanup at the schema level.
4527 */
4528 ret->doc = doc;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004529 ctxt->document = NULL;
4530 ret->documents = ctxt->documents;
4531 ctxt->documents = NULL;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004532 ret->includes = ctxt->includes;
4533 ctxt->includes = NULL;
Daniel Veillard419a7682003-02-03 23:22:49 +00004534 ret->defNr = ctxt->defNr;
4535 ret->defTab = ctxt->defTab;
4536 ctxt->defTab = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004537
4538 return (ret);
4539}
4540
4541/**
4542 * xmlRelaxNGSetParserErrors:
4543 * @ctxt: a Relax-NG validation context
4544 * @err: the error callback
4545 * @warn: the warning callback
4546 * @ctx: contextual data for the callbacks
4547 *
4548 * Set the callback functions used to handle errors for a validation context
4549 */
4550void
4551xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
4552 xmlRelaxNGValidityErrorFunc err,
4553 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
4554 if (ctxt == NULL)
4555 return;
4556 ctxt->error = err;
4557 ctxt->warning = warn;
4558 ctxt->userData = ctx;
4559}
4560/************************************************************************
4561 * *
4562 * Dump back a compiled form *
4563 * *
4564 ************************************************************************/
4565static void xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define);
4566
4567/**
4568 * xmlRelaxNGDumpDefines:
4569 * @output: the file output
4570 * @defines: a list of define structures
4571 *
4572 * Dump a RelaxNG structure back
4573 */
4574static void
4575xmlRelaxNGDumpDefines(FILE * output, xmlRelaxNGDefinePtr defines) {
4576 while (defines != NULL) {
4577 xmlRelaxNGDumpDefine(output, defines);
4578 defines = defines->next;
4579 }
4580}
4581
4582/**
4583 * xmlRelaxNGDumpDefine:
4584 * @output: the file output
4585 * @define: a define structure
4586 *
4587 * Dump a RelaxNG structure back
4588 */
4589static void
4590xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define) {
4591 if (define == NULL)
4592 return;
4593 switch(define->type) {
4594 case XML_RELAXNG_EMPTY:
4595 fprintf(output, "<empty/>\n");
4596 break;
4597 case XML_RELAXNG_NOT_ALLOWED:
4598 fprintf(output, "<notAllowed/>\n");
4599 break;
4600 case XML_RELAXNG_TEXT:
4601 fprintf(output, "<text/>\n");
4602 break;
4603 case XML_RELAXNG_ELEMENT:
4604 fprintf(output, "<element>\n");
4605 if (define->name != NULL) {
4606 fprintf(output, "<name");
4607 if (define->ns != NULL)
4608 fprintf(output, " ns=\"%s\"", define->ns);
4609 fprintf(output, ">%s</name>\n", define->name);
4610 }
4611 xmlRelaxNGDumpDefines(output, define->attrs);
4612 xmlRelaxNGDumpDefines(output, define->content);
4613 fprintf(output, "</element>\n");
4614 break;
4615 case XML_RELAXNG_LIST:
4616 fprintf(output, "<list>\n");
4617 xmlRelaxNGDumpDefines(output, define->content);
4618 fprintf(output, "</list>\n");
4619 break;
4620 case XML_RELAXNG_ONEORMORE:
4621 fprintf(output, "<oneOrMore>\n");
4622 xmlRelaxNGDumpDefines(output, define->content);
4623 fprintf(output, "</oneOrMore>\n");
4624 break;
4625 case XML_RELAXNG_ZEROORMORE:
4626 fprintf(output, "<zeroOrMore>\n");
4627 xmlRelaxNGDumpDefines(output, define->content);
4628 fprintf(output, "</zeroOrMore>\n");
4629 break;
4630 case XML_RELAXNG_CHOICE:
4631 fprintf(output, "<choice>\n");
4632 xmlRelaxNGDumpDefines(output, define->content);
4633 fprintf(output, "</choice>\n");
4634 break;
4635 case XML_RELAXNG_GROUP:
4636 fprintf(output, "<group>\n");
4637 xmlRelaxNGDumpDefines(output, define->content);
4638 fprintf(output, "</group>\n");
4639 break;
4640 case XML_RELAXNG_INTERLEAVE:
4641 fprintf(output, "<interleave>\n");
4642 xmlRelaxNGDumpDefines(output, define->content);
4643 fprintf(output, "</interleave>\n");
4644 break;
4645 case XML_RELAXNG_OPTIONAL:
4646 fprintf(output, "<optional>\n");
4647 xmlRelaxNGDumpDefines(output, define->content);
4648 fprintf(output, "</optional>\n");
4649 break;
4650 case XML_RELAXNG_ATTRIBUTE:
4651 fprintf(output, "<attribute>\n");
4652 xmlRelaxNGDumpDefines(output, define->content);
4653 fprintf(output, "</attribute>\n");
4654 break;
4655 case XML_RELAXNG_DEF:
4656 fprintf(output, "<define");
4657 if (define->name != NULL)
4658 fprintf(output, " name=\"%s\"", define->name);
4659 fprintf(output, ">\n");
4660 xmlRelaxNGDumpDefines(output, define->content);
4661 fprintf(output, "</define>\n");
4662 break;
4663 case XML_RELAXNG_REF:
4664 fprintf(output, "<ref");
4665 if (define->name != NULL)
4666 fprintf(output, " name=\"%s\"", define->name);
4667 fprintf(output, ">\n");
4668 xmlRelaxNGDumpDefines(output, define->content);
4669 fprintf(output, "</ref>\n");
4670 break;
Daniel Veillard419a7682003-02-03 23:22:49 +00004671 case XML_RELAXNG_PARENTREF:
4672 fprintf(output, "<parentRef");
4673 if (define->name != NULL)
4674 fprintf(output, " name=\"%s\"", define->name);
4675 fprintf(output, ">\n");
4676 xmlRelaxNGDumpDefines(output, define->content);
4677 fprintf(output, "</parentRef>\n");
4678 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004679 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard416589a2003-02-17 17:25:42 +00004680 fprintf(output, "<externalRef>");
Daniel Veillarde431a272003-01-29 23:02:33 +00004681 xmlRelaxNGDumpDefines(output, define->content);
4682 fprintf(output, "</externalRef>\n");
4683 break;
4684 case XML_RELAXNG_DATATYPE:
Daniel Veillard6eadf632003-01-23 18:29:16 +00004685 case XML_RELAXNG_VALUE:
4686 TODO
4687 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004688 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00004689 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004690 TODO
4691 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004692 }
4693}
4694
4695/**
4696 * xmlRelaxNGDumpGrammar:
4697 * @output: the file output
4698 * @grammar: a grammar structure
4699 * @top: is this a top grammar
4700 *
4701 * Dump a RelaxNG structure back
4702 */
4703static void
4704xmlRelaxNGDumpGrammar(FILE * output, xmlRelaxNGGrammarPtr grammar, int top)
4705{
4706 if (grammar == NULL)
4707 return;
4708
4709 fprintf(output, "<grammar");
4710 if (top)
4711 fprintf(output,
4712 " xmlns=\"http://relaxng.org/ns/structure/1.0\"");
4713 switch(grammar->combine) {
4714 case XML_RELAXNG_COMBINE_UNDEFINED:
4715 break;
4716 case XML_RELAXNG_COMBINE_CHOICE:
4717 fprintf(output, " combine=\"choice\"");
4718 break;
4719 case XML_RELAXNG_COMBINE_INTERLEAVE:
4720 fprintf(output, " combine=\"interleave\"");
4721 break;
4722 default:
4723 fprintf(output, " <!-- invalid combine value -->");
4724 }
4725 fprintf(output, ">\n");
4726 if (grammar->start == NULL) {
4727 fprintf(output, " <!-- grammar had no start -->");
4728 } else {
4729 fprintf(output, "<start>\n");
4730 xmlRelaxNGDumpDefine(output, grammar->start);
4731 fprintf(output, "</start>\n");
4732 }
4733 /* TODO ? Dump the defines ? */
4734 fprintf(output, "</grammar>\n");
4735}
4736
4737/**
4738 * xmlRelaxNGDump:
4739 * @output: the file output
4740 * @schema: a schema structure
4741 *
4742 * Dump a RelaxNG structure back
4743 */
4744void
4745xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema)
4746{
4747 if (schema == NULL) {
4748 fprintf(output, "RelaxNG empty or failed to compile\n");
4749 return;
4750 }
4751 fprintf(output, "RelaxNG: ");
4752 if (schema->doc == NULL) {
4753 fprintf(output, "no document\n");
4754 } else if (schema->doc->URL != NULL) {
4755 fprintf(output, "%s\n", schema->doc->URL);
4756 } else {
4757 fprintf(output, "\n");
4758 }
4759 if (schema->topgrammar == NULL) {
4760 fprintf(output, "RelaxNG has no top grammar\n");
4761 return;
4762 }
4763 xmlRelaxNGDumpGrammar(output, schema->topgrammar, 1);
4764}
4765
Daniel Veillardfebcca42003-02-16 15:44:18 +00004766/**
4767 * xmlRelaxNGDumpTree:
4768 * @output: the file output
4769 * @schema: a schema structure
4770 *
4771 * Dump the transformed RelaxNG tree.
4772 */
4773void
4774xmlRelaxNGDumpTree(FILE * output, xmlRelaxNGPtr schema)
4775{
4776 if (schema == NULL) {
4777 fprintf(output, "RelaxNG empty or failed to compile\n");
4778 return;
4779 }
4780 if (schema->doc == NULL) {
4781 fprintf(output, "no document\n");
4782 } else {
4783 xmlDocDump(output, schema->doc);
4784 }
4785}
4786
Daniel Veillard6eadf632003-01-23 18:29:16 +00004787/************************************************************************
4788 * *
4789 * Validation implementation *
4790 * *
4791 ************************************************************************/
4792static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
4793 xmlRelaxNGDefinePtr define);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004794static int xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
4795 xmlRelaxNGDefinePtr define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004796
4797/**
4798 * xmlRelaxNGSkipIgnored:
4799 * @ctxt: a schema validation context
4800 * @node: the top node.
4801 *
4802 * Skip ignorable nodes in that context
4803 *
4804 * Returns the new sibling or NULL in case of error.
4805 */
4806static xmlNodePtr
4807xmlRelaxNGSkipIgnored(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
4808 xmlNodePtr node) {
4809 /*
4810 * TODO complete and handle entities
4811 */
4812 while ((node != NULL) &&
4813 ((node->type == XML_COMMENT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004814 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00004815 ((node->type == XML_TEXT_NODE) &&
4816 (IS_BLANK_NODE(node))))) {
4817 node = node->next;
4818 }
4819 return(node);
4820}
4821
4822/**
Daniel Veillardedc91922003-01-26 00:52:04 +00004823 * xmlRelaxNGNormalize:
4824 * @ctxt: a schema validation context
4825 * @str: the string to normalize
4826 *
4827 * Implements the normalizeWhiteSpace( s ) function from
4828 * section 6.2.9 of the spec
4829 *
4830 * Returns the new string or NULL in case of error.
4831 */
4832static xmlChar *
4833xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *str) {
4834 xmlChar *ret, *p;
4835 const xmlChar *tmp;
4836 int len;
4837
4838 if (str == NULL)
4839 return(NULL);
4840 tmp = str;
4841 while (*tmp != 0) tmp++;
4842 len = tmp - str;
4843
4844 ret = (xmlChar *) xmlMalloc((len + 1) * sizeof(xmlChar));
4845 if (ret == NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00004846 if (ctxt != NULL) {
4847 VALID_CTXT();
4848 VALID_ERROR("xmlRelaxNGNormalize: out of memory\n");
4849 } else {
4850 xmlGenericError(xmlGenericErrorContext,
4851 "xmlRelaxNGNormalize: out of memory\n");
4852 }
Daniel Veillardedc91922003-01-26 00:52:04 +00004853 return(NULL);
4854 }
4855 p = ret;
4856 while (IS_BLANK(*str)) str++;
4857 while (*str != 0) {
4858 if (IS_BLANK(*str)) {
4859 while (IS_BLANK(*str)) str++;
4860 if (*str == 0)
4861 break;
4862 *p++ = ' ';
4863 } else
4864 *p++ = *str++;
4865 }
4866 *p = 0;
4867 return(ret);
4868}
4869
4870/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004871 * xmlRelaxNGValidateDatatype:
4872 * @ctxt: a Relax-NG validation context
4873 * @value: the string value
4874 * @type: the datatype definition
4875 *
4876 * Validate the given value against the dataype
4877 *
4878 * Returns 0 if the validation succeeded or an error code.
4879 */
4880static int
4881xmlRelaxNGValidateDatatype(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *value,
4882 xmlRelaxNGDefinePtr define) {
4883 int ret;
4884 xmlRelaxNGTypeLibraryPtr lib;
4885
4886 if ((define == NULL) || (define->data == NULL)) {
4887 return(-1);
4888 }
4889 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
4890 if (lib->check != NULL)
4891 ret = lib->check(lib->data, define->name, value);
4892 else
4893 ret = -1;
4894 if (ret < 0) {
4895 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004896 VALID_ERROR2("Internal: failed to validate type %s\n", define->name);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004897 return(-1);
4898 } else if (ret == 1) {
4899 ret = 0;
4900 } else {
4901 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004902 VALID_ERROR3("Type %s doesn't allow value %s\n", define->name, value);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004903 return(-1);
4904 ret = -1;
4905 }
Daniel Veillard416589a2003-02-17 17:25:42 +00004906 if ((ret == 0) && (define->content != NULL)) {
4907 const xmlChar *oldvalue, *oldendvalue;
4908
4909 oldvalue = ctxt->state->value;
4910 oldendvalue = ctxt->state->endvalue;
4911 ctxt->state->value = (xmlChar *) value;
4912 ctxt->state->endvalue = NULL;
4913 ret = xmlRelaxNGValidateValue(ctxt, define->content);
4914 ctxt->state->value = (xmlChar *) oldvalue;
4915 ctxt->state->endvalue = (xmlChar *) oldendvalue;
4916 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004917 return(ret);
4918}
4919
4920/**
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004921 * xmlRelaxNGNextValue:
4922 * @ctxt: a Relax-NG validation context
4923 *
4924 * Skip to the next value when validating within a list
4925 *
4926 * Returns 0 if the operation succeeded or an error code.
4927 */
4928static int
4929xmlRelaxNGNextValue(xmlRelaxNGValidCtxtPtr ctxt) {
4930 xmlChar *cur;
4931
4932 cur = ctxt->state->value;
4933 if ((cur == NULL) || (ctxt->state->endvalue == NULL)) {
4934 ctxt->state->value = NULL;
Daniel Veillarde5b110b2003-02-04 14:43:39 +00004935 ctxt->state->endvalue = NULL;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004936 return(0);
4937 }
4938 while (*cur != 0) cur++;
4939 while ((cur != ctxt->state->endvalue) && (*cur == 0)) cur++;
4940 if (cur == ctxt->state->endvalue)
4941 ctxt->state->value = NULL;
4942 else
4943 ctxt->state->value = cur;
4944 return(0);
4945}
4946
4947/**
4948 * xmlRelaxNGValidateValueList:
4949 * @ctxt: a Relax-NG validation context
4950 * @defines: the list of definitions to verify
4951 *
4952 * Validate the given set of definitions for the current value
4953 *
4954 * Returns 0 if the validation succeeded or an error code.
4955 */
4956static int
4957xmlRelaxNGValidateValueList(xmlRelaxNGValidCtxtPtr ctxt,
4958 xmlRelaxNGDefinePtr defines) {
4959 int ret = 0;
4960
4961 while (defines != NULL) {
4962 ret = xmlRelaxNGValidateValue(ctxt, defines);
4963 if (ret != 0)
4964 break;
4965 defines = defines->next;
4966 }
4967 return(ret);
4968}
4969
4970/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004971 * xmlRelaxNGValidateValue:
4972 * @ctxt: a Relax-NG validation context
4973 * @define: the definition to verify
4974 *
4975 * Validate the given definition for the current value
4976 *
4977 * Returns 0 if the validation succeeded or an error code.
4978 */
4979static int
4980xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
4981 xmlRelaxNGDefinePtr define) {
Daniel Veillardedc91922003-01-26 00:52:04 +00004982 int ret = 0, oldflags;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004983 xmlChar *value;
4984
4985 value = ctxt->state->value;
4986 switch (define->type) {
Daniel Veillardd4310742003-02-18 21:12:46 +00004987 case XML_RELAXNG_EMPTY: {
4988 if ((value != NULL) && (value[0] != 0)) {
4989 int idx = 0;
4990
4991 while (IS_BLANK(value[idx]))
4992 idx++;
4993 if (value[idx] != 0)
4994 ret = -1;
4995 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004996 break;
Daniel Veillardd4310742003-02-18 21:12:46 +00004997 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004998 case XML_RELAXNG_TEXT:
4999 break;
Daniel Veillardedc91922003-01-26 00:52:04 +00005000 case XML_RELAXNG_VALUE: {
5001 if (!xmlStrEqual(value, define->value)) {
5002 if (define->name != NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00005003 xmlRelaxNGTypeLibraryPtr lib;
5004
5005 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
5006 if ((lib != NULL) && (lib->comp != NULL))
5007 ret = lib->comp(lib->data, define->name, value,
5008 define->value);
5009 else
5010 ret = -1;
5011 if (ret < 0) {
5012 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005013 VALID_ERROR2("Internal: failed to compare type %s\n",
Daniel Veillardea3f3982003-01-26 19:45:18 +00005014 define->name);
5015 return(-1);
5016 } else if (ret == 1) {
5017 ret = 0;
5018 } else {
5019 ret = -1;
5020 }
Daniel Veillardedc91922003-01-26 00:52:04 +00005021 } else {
5022 xmlChar *nval, *nvalue;
5023
5024 /*
5025 * TODO: trivial optimizations are possible by
5026 * computing at compile-time
5027 */
5028 nval = xmlRelaxNGNormalize(ctxt, define->value);
5029 nvalue = xmlRelaxNGNormalize(ctxt, value);
5030
Daniel Veillardea3f3982003-01-26 19:45:18 +00005031 if ((nval == NULL) || (nvalue == NULL) ||
5032 (!xmlStrEqual(nval, nvalue)))
Daniel Veillardedc91922003-01-26 00:52:04 +00005033 ret = -1;
5034 if (nval != NULL)
5035 xmlFree(nval);
5036 if (nvalue != NULL)
5037 xmlFree(nvalue);
5038 }
5039 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005040 if (ret == 0)
5041 xmlRelaxNGNextValue(ctxt);
Daniel Veillardedc91922003-01-26 00:52:04 +00005042 break;
5043 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005044 case XML_RELAXNG_DATATYPE: {
5045 ret = xmlRelaxNGValidateDatatype(ctxt, value, define);
5046 if (ret == 0)
5047 xmlRelaxNGNextValue(ctxt);
5048
5049 break;
5050 }
5051 case XML_RELAXNG_CHOICE: {
5052 xmlRelaxNGDefinePtr list = define->content;
5053 xmlChar *oldvalue;
5054
5055 oldflags = ctxt->flags;
5056 ctxt->flags |= FLAGS_IGNORABLE;
5057
5058 oldvalue = ctxt->state->value;
5059 while (list != NULL) {
5060 ret = xmlRelaxNGValidateValue(ctxt, list);
5061 if (ret == 0) {
5062 break;
5063 }
5064 ctxt->state->value = oldvalue;
5065 list = list->next;
5066 }
5067 ctxt->flags = oldflags;
Daniel Veillard416589a2003-02-17 17:25:42 +00005068 if (ret == 0)
5069 xmlRelaxNGNextValue(ctxt);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005070 break;
5071 }
5072 case XML_RELAXNG_LIST: {
5073 xmlRelaxNGDefinePtr list = define->content;
5074 xmlChar *oldvalue, *oldend, *val, *cur;
Daniel Veillard416589a2003-02-17 17:25:42 +00005075#ifdef DEBUG_LIST
5076 int nb_values = 0;
5077#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005078
5079 oldvalue = ctxt->state->value;
5080 oldend = ctxt->state->endvalue;
5081
5082 val = xmlStrdup(oldvalue);
5083 if (val == NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00005084 val = xmlStrdup(BAD_CAST "");
5085 }
5086 if (val == NULL) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005087 VALID_CTXT();
5088 VALID_ERROR("Internal: no state\n");
5089 return(-1);
5090 }
5091 cur = val;
5092 while (*cur != 0) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005093 if (IS_BLANK(*cur)) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005094 *cur = 0;
Daniel Veillard416589a2003-02-17 17:25:42 +00005095 cur++;
5096#ifdef DEBUG_LIST
5097 nb_values++;
5098#endif
5099 while (IS_BLANK(*cur))
5100 *cur++ = 0;
5101 } else
5102 cur++;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005103 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005104#ifdef DEBUG_LIST
5105 xmlGenericError(xmlGenericErrorContext,
5106 "list value: '%s' found %d items\n", oldvalue, nb_values);
5107 nb_values = 0;
5108#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005109 ctxt->state->endvalue = cur;
5110 cur = val;
5111 while ((*cur == 0) && (cur != ctxt->state->endvalue)) cur++;
5112
5113 ctxt->state->value = cur;
5114
5115 while (list != NULL) {
5116 ret = xmlRelaxNGValidateValue(ctxt, list);
5117 if (ret != 0) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005118#ifdef DEBUG_LIST
5119 xmlGenericError(xmlGenericErrorContext,
5120 "Failed to validate value: '%s' with %d rule\n",
5121 ctxt->state->value, nb_values);
5122#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005123 break;
5124 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005125#ifdef DEBUG_LIST
5126 nb_values++;
5127#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005128 list = list->next;
5129 }
5130 if ((ret == 0) && (ctxt->state->value != NULL) &&
5131 (ctxt->state->value != ctxt->state->endvalue)) {
5132 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005133 VALID_ERROR2("Extra data in list: %s\n", ctxt->state->value);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005134 ret = -1;
5135 }
5136 xmlFree(val);
5137 ctxt->state->value = oldvalue;
5138 ctxt->state->endvalue = oldend;
5139 break;
5140 }
5141 case XML_RELAXNG_ONEORMORE:
5142 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
5143 if (ret != 0) {
5144 break;
5145 }
5146 /* no break on purpose */
5147 case XML_RELAXNG_ZEROORMORE: {
5148 xmlChar *cur, *temp;
5149
5150 oldflags = ctxt->flags;
5151 ctxt->flags |= FLAGS_IGNORABLE;
5152 cur = ctxt->state->value;
5153 temp = NULL;
5154 while ((cur != NULL) && (cur != ctxt->state->endvalue) &&
5155 (temp != cur)) {
5156 temp = cur;
5157 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
5158 if (ret != 0) {
5159 ctxt->state->value = temp;
5160 ret = 0;
5161 break;
5162 }
5163 cur = ctxt->state->value;
5164 }
5165 ctxt->flags = oldflags;
5166 break;
5167 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005168 case XML_RELAXNG_EXCEPT: {
5169 xmlRelaxNGDefinePtr list;
5170
5171 list = define->content;
5172 while (list != NULL) {
5173 ret = xmlRelaxNGValidateValue(ctxt, list);
5174 if (ret == 0) {
5175 ret = -1;
5176 break;
5177 } else
5178 ret = 0;
5179 list = list->next;
5180 }
5181 break;
5182 }
Daniel Veillardd4310742003-02-18 21:12:46 +00005183 case XML_RELAXNG_GROUP: {
5184 xmlRelaxNGDefinePtr list;
5185
5186 list = define->content;
5187 while (list != NULL) {
5188 ret = xmlRelaxNGValidateValue(ctxt, list);
5189 if (ret != 0) {
5190 ret = -1;
5191 break;
5192 } else
5193 ret = 0;
5194 list = list->next;
5195 }
5196 break;
5197 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005198 default:
5199 TODO
5200 ret = -1;
5201 }
5202 return(ret);
5203}
5204
5205/**
5206 * xmlRelaxNGValidateValueContent:
5207 * @ctxt: a Relax-NG validation context
5208 * @defines: the list of definitions to verify
5209 *
5210 * Validate the given definitions for the current value
5211 *
5212 * Returns 0 if the validation succeeded or an error code.
5213 */
5214static int
5215xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt,
5216 xmlRelaxNGDefinePtr defines) {
5217 int ret = 0;
5218
5219 while (defines != NULL) {
5220 ret = xmlRelaxNGValidateValue(ctxt, defines);
5221 if (ret != 0)
5222 break;
5223 defines = defines->next;
5224 }
5225 return(ret);
5226}
5227
5228/**
Daniel Veillard144fae12003-02-03 13:17:57 +00005229 * xmlRelaxNGAttributeMatch:
5230 * @ctxt: a Relax-NG validation context
5231 * @define: the definition to check
5232 * @prop: the attribute
5233 *
5234 * Check if the attribute matches the definition nameClass
5235 *
5236 * Returns 1 if the attribute matches, 0 if no, or -1 in case of error
5237 */
5238static int
5239xmlRelaxNGAttributeMatch(xmlRelaxNGValidCtxtPtr ctxt,
5240 xmlRelaxNGDefinePtr define,
5241 xmlAttrPtr prop) {
5242 int ret;
5243
5244 if (define->name != NULL) {
5245 if (!xmlStrEqual(define->name, prop->name))
5246 return(0);
5247 }
5248 if (define->ns != NULL) {
5249 if (define->ns[0] == 0) {
5250 if (prop->ns != NULL)
5251 return(0);
5252 } else {
5253 if ((prop->ns == NULL) ||
5254 (!xmlStrEqual(define->ns, prop->ns->href)))
5255 return(0);
5256 }
5257 }
5258 if (define->nameClass == NULL)
5259 return(1);
5260 define = define->nameClass;
5261 if (define->type == XML_RELAXNG_EXCEPT) {
5262 xmlRelaxNGDefinePtr list;
5263
5264 list = define->content;
5265 while (list != NULL) {
5266 ret = xmlRelaxNGAttributeMatch(ctxt, list, prop);
5267 if (ret == 1)
5268 return(0);
5269 if (ret < 0)
5270 return(ret);
5271 list = list->next;
5272 }
5273 } else {
5274 TODO
5275 }
5276 return(1);
5277}
5278
5279/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00005280 * xmlRelaxNGValidateAttribute:
5281 * @ctxt: a Relax-NG validation context
5282 * @define: the definition to verify
5283 *
5284 * Validate the given attribute definition for that node
5285 *
5286 * Returns 0 if the validation succeeded or an error code.
5287 */
5288static int
5289xmlRelaxNGValidateAttribute(xmlRelaxNGValidCtxtPtr ctxt,
5290 xmlRelaxNGDefinePtr define) {
5291 int ret = 0, i;
5292 xmlChar *value, *oldvalue;
5293 xmlAttrPtr prop = NULL, tmp;
5294
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005295 if (ctxt->state->nbAttrLeft <= 0)
5296 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005297 if (define->name != NULL) {
5298 for (i = 0;i < ctxt->state->nbAttrs;i++) {
5299 tmp = ctxt->state->attrs[i];
5300 if ((tmp != NULL) && (xmlStrEqual(define->name, tmp->name))) {
5301 if ((((define->ns == NULL) || (define->ns[0] == 0)) &&
5302 (tmp->ns == NULL)) ||
5303 ((tmp->ns != NULL) &&
5304 (xmlStrEqual(define->ns, tmp->ns->href)))) {
5305 prop = tmp;
5306 break;
5307 }
5308 }
5309 }
5310 if (prop != NULL) {
5311 value = xmlNodeListGetString(prop->doc, prop->children, 1);
5312 oldvalue = ctxt->state->value;
5313 ctxt->state->value = value;
Daniel Veillard231d7912003-02-09 14:22:17 +00005314 ctxt->state->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005315 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00005316 if (ctxt->state->value != NULL)
5317 value = ctxt->state->value;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005318 if (value != NULL)
5319 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00005320 ctxt->state->value = oldvalue;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005321 if (ret == 0) {
5322 /*
5323 * flag the attribute as processed
5324 */
5325 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005326 ctxt->state->nbAttrLeft--;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005327 }
5328 } else {
5329 ret = -1;
5330 }
5331#ifdef DEBUG
5332 xmlGenericError(xmlGenericErrorContext,
5333 "xmlRelaxNGValidateAttribute(%s): %d\n", define->name, ret);
5334#endif
5335 } else {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005336 for (i = 0;i < ctxt->state->nbAttrs;i++) {
5337 tmp = ctxt->state->attrs[i];
Daniel Veillard144fae12003-02-03 13:17:57 +00005338 if ((tmp != NULL) &&
5339 (xmlRelaxNGAttributeMatch(ctxt, define, tmp) == 1)) {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005340 prop = tmp;
5341 break;
5342 }
5343 }
5344 if (prop != NULL) {
5345 value = xmlNodeListGetString(prop->doc, prop->children, 1);
5346 oldvalue = ctxt->state->value;
5347 ctxt->state->value = value;
5348 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00005349 if (ctxt->state->value != NULL)
5350 value = ctxt->state->value;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005351 if (value != NULL)
5352 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00005353 ctxt->state->value = oldvalue;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005354 if (ret == 0) {
5355 /*
5356 * flag the attribute as processed
5357 */
5358 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005359 ctxt->state->nbAttrLeft--;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005360 }
5361 } else {
5362 ret = -1;
5363 }
5364#ifdef DEBUG
Daniel Veillard144fae12003-02-03 13:17:57 +00005365 if (define->ns != NULL) {
5366 xmlGenericError(xmlGenericErrorContext,
5367 "xmlRelaxNGValidateAttribute(nsName ns = %s): %d\n",
5368 define->ns, ret);
5369 } else {
5370 xmlGenericError(xmlGenericErrorContext,
5371 "xmlRelaxNGValidateAttribute(anyName): %d\n",
5372 ret);
5373 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005374#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00005375 }
5376
5377 return(ret);
5378}
5379
5380/**
5381 * xmlRelaxNGValidateAttributeList:
5382 * @ctxt: a Relax-NG validation context
5383 * @define: the list of definition to verify
5384 *
5385 * Validate the given node against the list of attribute definitions
5386 *
5387 * Returns 0 if the validation succeeded or an error code.
5388 */
5389static int
5390xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt,
5391 xmlRelaxNGDefinePtr defines) {
5392 int ret = 0;
5393 while (defines != NULL) {
5394 if (xmlRelaxNGValidateAttribute(ctxt, defines) != 0)
5395 ret = -1;
5396 defines = defines->next;
5397 }
5398 return(ret);
5399}
5400
5401/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005402 * xmlRelaxNGValidateTryPermutation:
5403 * @ctxt: a Relax-NG validation context
5404 * @groups: the array of groups
5405 * @nbgroups: the number of groups in the array
5406 * @array: the permutation to try
5407 * @len: the size of the set
5408 *
5409 * Try to validate a permutation for the group of definitions.
5410 *
5411 * Returns 0 if the validation succeeded or an error code.
5412 */
5413static int
5414xmlRelaxNGValidateTryPermutation(xmlRelaxNGValidCtxtPtr ctxt,
5415 xmlRelaxNGDefinePtr rule,
5416 xmlNodePtr *array, int len) {
5417 int i, ret;
5418
5419 if (len > 0) {
5420 /*
5421 * One only need the next pointer set-up to do the validation
5422 */
5423 for (i = 0;i < (len - 1);i++)
5424 array[i]->next = array[i + 1];
5425 array[i]->next = NULL;
5426
5427 /*
5428 * Now try to validate the sequence
5429 */
5430 ctxt->state->seq = array[0];
5431 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
5432 } else {
5433 ctxt->state->seq = NULL;
5434 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
5435 }
5436
5437 /*
5438 * the sequence must be fully consumed
5439 */
5440 if (ctxt->state->seq != NULL)
5441 return(-1);
5442
5443 return(ret);
5444}
5445
5446/**
5447 * xmlRelaxNGValidateWalkPermutations:
5448 * @ctxt: a Relax-NG validation context
5449 * @groups: the array of groups
5450 * @nbgroups: the number of groups in the array
5451 * @nodes: the set of nodes
5452 * @array: the current state of the parmutation
5453 * @len: the size of the set
5454 * @level: a pointer to the level variable
5455 * @k: the index in the array to fill
5456 *
5457 * Validate a set of nodes for a groups of definitions, will try the
5458 * full set of permutations
5459 *
5460 * Returns 0 if the validation succeeded or an error code.
5461 */
5462static int
5463xmlRelaxNGValidateWalkPermutations(xmlRelaxNGValidCtxtPtr ctxt,
5464 xmlRelaxNGDefinePtr rule, xmlNodePtr *nodes,
5465 xmlNodePtr *array, int len,
5466 int *level, int k) {
5467 int i, ret;
5468
5469 if ((k >= 0) && (k < len))
5470 array[k] = nodes[*level];
5471 *level = *level + 1;
5472 if (*level == len) {
5473 ret = xmlRelaxNGValidateTryPermutation(ctxt, rule, array, len);
5474 if (ret == 0)
5475 return(0);
5476 } else {
5477 for (i = 0;i < len;i++) {
5478 if (array[i] == NULL) {
5479 ret = xmlRelaxNGValidateWalkPermutations(ctxt, rule,
5480 nodes, array, len, level, i);
5481 if (ret == 0)
5482 return(0);
5483 }
5484 }
5485 }
5486 *level = *level - 1;
5487 array[k] = NULL;
5488 return(-1);
5489}
5490
5491/**
5492 * xmlRelaxNGNodeMatchesList:
5493 * @node: the node
5494 * @list: a NULL terminated array of definitions
5495 *
5496 * Check if a node can be matched by one of the definitions
5497 *
5498 * Returns 1 if matches 0 otherwise
5499 */
5500static int
5501xmlRelaxNGNodeMatchesList(xmlNodePtr node, xmlRelaxNGDefinePtr *list) {
5502 xmlRelaxNGDefinePtr cur;
5503 int i = 0;
5504
5505 if ((node == NULL) || (list == NULL))
5506 return(0);
5507
5508 cur = list[i++];
5509 while (cur != NULL) {
5510 if ((node->type == XML_ELEMENT_NODE) &&
5511 (cur->type == XML_RELAXNG_ELEMENT)) {
5512 if (cur->name == NULL) {
5513 if ((node->ns != NULL) &&
5514 (xmlStrEqual(node->ns->href, cur->ns)))
5515 return(1);
5516 } else if (xmlStrEqual(cur->name, node->name)) {
5517 if ((cur->ns == NULL) || (cur->ns[0] == 0)) {
5518 if (node->ns == NULL)
5519 return(1);
5520 } else {
5521 if ((node->ns != NULL) &&
5522 (xmlStrEqual(node->ns->href, cur->ns)))
5523 return(1);
5524 }
5525 }
5526 } else if ((node->type == XML_TEXT_NODE) &&
5527 (cur->type == XML_RELAXNG_TEXT)) {
5528 return(1);
5529 }
5530 cur = list[i++];
5531 }
5532 return(0);
5533}
5534
5535/**
5536 * xmlRelaxNGValidatePartGroup:
5537 * @ctxt: a Relax-NG validation context
5538 * @groups: the array of groups
5539 * @nbgroups: the number of groups in the array
5540 * @nodes: the set of nodes
5541 * @len: the size of the set of nodes
5542 *
5543 * Validate a set of nodes for a groups of definitions
5544 *
5545 * Returns 0 if the validation succeeded or an error code.
5546 */
5547static int
5548xmlRelaxNGValidatePartGroup(xmlRelaxNGValidCtxtPtr ctxt,
5549 xmlRelaxNGInterleaveGroupPtr *groups,
5550 int nbgroups, xmlNodePtr *nodes, int len) {
Daniel Veillard231d7912003-02-09 14:22:17 +00005551 int level, ret = -1, i, j, k, top_j, max_j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005552 xmlNodePtr *array = NULL, *list, oldseq;
5553 xmlRelaxNGInterleaveGroupPtr group;
5554
5555 list = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
5556 if (list == NULL) {
5557 return(-1);
5558 }
5559 array = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
5560 if (array == NULL) {
5561 xmlFree(list);
5562 return(-1);
5563 }
5564 memset(array, 0, len * sizeof(xmlNodePtr));
5565
5566 /*
5567 * Partition the elements and validate the subsets.
5568 */
5569 oldseq = ctxt->state->seq;
Daniel Veillard231d7912003-02-09 14:22:17 +00005570 max_j = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005571 for (i = 0;i < nbgroups;i++) {
5572 group = groups[i];
5573 if (group == NULL)
5574 continue;
5575 k = 0;
Daniel Veillard231d7912003-02-09 14:22:17 +00005576 top_j = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005577 for (j = 0;j < len;j++) {
5578 if (nodes[j] == NULL)
5579 continue;
5580 if (xmlRelaxNGNodeMatchesList(nodes[j], group->defs)) {
5581 list[k++] = nodes[j];
5582 nodes[j] = NULL;
Daniel Veillard231d7912003-02-09 14:22:17 +00005583 top_j = j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005584 }
5585 }
Daniel Veillard231d7912003-02-09 14:22:17 +00005586 if (top_j > max_j)
5587 max_j = top_j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005588 ctxt->state->seq = oldseq;
5589 if (k > 1) {
5590 memset(array, 0, k * sizeof(xmlNodePtr));
Daniel Veillardb08c9812003-01-28 23:09:49 +00005591 level = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005592 ret = xmlRelaxNGValidateWalkPermutations(ctxt, group->rule,
5593 list, array, k, &level, -1);
5594 } else {
5595 ret = xmlRelaxNGValidateTryPermutation(ctxt, group->rule, list, k);
5596 }
5597 if (ret != 0) {
5598 ctxt->state->seq = oldseq;
5599 break;
5600 }
5601 }
5602
Daniel Veillard231d7912003-02-09 14:22:17 +00005603 for (j = 0;j < max_j;j++) {
5604 if (nodes[j] != NULL) {
5605 TODO /* problem, one of the nodes didn't got a match */
5606 }
5607 }
5608 if (ret == 0) {
5609 if (max_j + 1 < len)
5610 ctxt->state->seq = nodes[max_j + 1];
5611 else
5612 ctxt->state->seq = NULL;
5613 }
5614
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005615 xmlFree(list);
5616 xmlFree(array);
5617 return(ret);
5618}
5619
5620/**
5621 * xmlRelaxNGValidateInterleave:
5622 * @ctxt: a Relax-NG validation context
5623 * @define: the definition to verify
5624 *
5625 * Validate an interleave definition for a node.
5626 *
5627 * Returns 0 if the validation succeeded or an error code.
5628 */
5629static int
5630xmlRelaxNGValidateInterleave(xmlRelaxNGValidCtxtPtr ctxt,
5631 xmlRelaxNGDefinePtr define) {
5632 int ret = 0, nbchildren, nbtot, i, j;
5633 xmlRelaxNGPartitionPtr partitions;
5634 xmlNodePtr *children = NULL;
5635 xmlNodePtr *order = NULL;
Daniel Veillard231d7912003-02-09 14:22:17 +00005636 xmlNodePtr cur, oldseq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005637
5638 if (define->data != NULL) {
5639 partitions = (xmlRelaxNGPartitionPtr) define->data;
5640 } else {
5641 VALID_CTXT();
5642 VALID_ERROR("Internal: interleave block has no data\n");
5643 return(-1);
5644 }
5645
5646 /*
5647 * Build the sequence of child and an array preserving the children
5648 * initial order.
5649 */
5650 cur = ctxt->state->seq;
Daniel Veillard231d7912003-02-09 14:22:17 +00005651 oldseq = ctxt->state->seq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005652 nbchildren = 0;
5653 nbtot = 0;
5654 while (cur != NULL) {
5655 if ((cur->type == XML_COMMENT_NODE) ||
5656 (cur->type == XML_PI_NODE) ||
5657 ((cur->type == XML_TEXT_NODE) &&
5658 (IS_BLANK_NODE(cur)))) {
5659 nbtot++;
5660 } else {
5661 nbchildren++;
5662 nbtot++;
5663 }
5664 cur = cur->next;
5665 }
5666 children = (xmlNodePtr *) xmlMalloc(nbchildren * sizeof(xmlNodePtr));
5667 if (children == NULL)
5668 goto error;
5669 order = (xmlNodePtr *) xmlMalloc(nbtot * sizeof(xmlNodePtr));
5670 if (order == NULL)
5671 goto error;
5672 cur = ctxt->state->seq;
5673 i = 0;
5674 j = 0;
5675 while (cur != NULL) {
5676 if ((cur->type == XML_COMMENT_NODE) ||
5677 (cur->type == XML_PI_NODE) ||
5678 ((cur->type == XML_TEXT_NODE) &&
5679 (IS_BLANK_NODE(cur)))) {
5680 order[j++] = cur;
5681 } else {
5682 order[j++] = cur;
5683 children[i++] = cur;
5684 }
5685 cur = cur->next;
5686 }
5687
5688 /* TODO: retry with a maller set of child if there is a next... */
5689 ret = xmlRelaxNGValidatePartGroup(ctxt, partitions->groups,
5690 partitions->nbgroups, children, nbchildren);
Daniel Veillard231d7912003-02-09 14:22:17 +00005691 if (ret != 0)
5692 ctxt->state->seq = oldseq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005693
5694 /*
5695 * Cleanup: rebuid the child sequence and free the structure
5696 */
5697 if (order != NULL) {
5698 for (i = 0;i < nbtot;i++) {
5699 if (i == 0)
5700 order[i]->prev = NULL;
5701 else
5702 order[i]->prev = order[i - 1];
5703 if (i == nbtot - 1)
5704 order[i]->next = NULL;
5705 else
5706 order[i]->next = order[i + 1];
5707 }
5708 xmlFree(order);
5709 }
5710 if (children != NULL)
5711 xmlFree(children);
5712
5713 return(ret);
5714
5715error:
5716 if (order != NULL) {
5717 for (i = 0;i < nbtot;i++) {
5718 if (i == 0)
5719 order[i]->prev = NULL;
5720 else
5721 order[i]->prev = order[i - 1];
5722 if (i == nbtot - 1)
5723 order[i]->next = NULL;
5724 else
5725 order[i]->next = order[i + 1];
5726 }
5727 xmlFree(order);
5728 }
5729 if (children != NULL)
5730 xmlFree(children);
5731 return(-1);
5732}
5733
5734/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00005735 * xmlRelaxNGValidateElementContent:
5736 * @ctxt: a Relax-NG validation context
5737 * @define: the list of definition to verify
5738 *
5739 * Validate the given node content against the (list) of definitions
5740 *
5741 * Returns 0 if the validation succeeded or an error code.
5742 */
5743static int
5744xmlRelaxNGValidateElementContent(xmlRelaxNGValidCtxtPtr ctxt,
5745 xmlRelaxNGDefinePtr defines) {
5746 int ret = 0, res;
5747
5748 if (ctxt->state == NULL) {
5749 VALID_CTXT();
5750 VALID_ERROR("Internal: no state\n");
5751 return(-1);
5752 }
5753 while (defines != NULL) {
5754 res = xmlRelaxNGValidateDefinition(ctxt, defines);
5755 if (res < 0)
5756 ret = -1;
5757 defines = defines->next;
5758 }
5759
5760 return(ret);
5761}
5762
5763/**
Daniel Veillard416589a2003-02-17 17:25:42 +00005764 * xmlRelaxNGElementMatch:
5765 * @ctxt: a Relax-NG validation context
5766 * @define: the definition to check
5767 * @elem: the element
5768 *
5769 * Check if the element matches the definition nameClass
5770 *
5771 * Returns 1 if the element matches, 0 if no, or -1 in case of error
5772 */
5773static int
5774xmlRelaxNGElementMatch(xmlRelaxNGValidCtxtPtr ctxt,
5775 xmlRelaxNGDefinePtr define,
5776 xmlNodePtr elem) {
5777 int ret, oldflags;
5778
5779 if (define->name != NULL) {
5780 if (!xmlStrEqual(elem->name, define->name)) {
5781 VALID_CTXT();
5782 VALID_ERROR3("Expecting element %s, got %s\n",
5783 define->name, elem->name);
5784 return(0);
5785 }
5786 }
5787 if ((define->ns != NULL) && (define->ns[0] != 0)) {
5788 if (elem->ns == NULL) {
5789 VALID_CTXT();
5790 VALID_ERROR2("Expecting a namespace for element %s\n",
5791 elem->name);
5792 return(0);
5793 } else if (!xmlStrEqual(elem->ns->href, define->ns)) {
5794 VALID_CTXT();
5795 VALID_ERROR3("Expecting element %s has wrong namespace: expecting %s\n",
5796 elem->name, define->ns);
5797 return(0);
5798 }
5799 } else if (define->name != NULL) {
5800 if (elem->ns != NULL) {
5801 VALID_CTXT();
5802 VALID_ERROR2("Expecting no namespace for element %s\n",
5803 define->name);
5804 return(0);
5805 }
5806 }
5807
5808 if (define->nameClass == NULL)
5809 return(1);
5810
5811 define = define->nameClass;
5812 if (define->type == XML_RELAXNG_EXCEPT) {
5813 xmlRelaxNGDefinePtr list;
5814 oldflags = ctxt->flags;
5815 ctxt->flags |= FLAGS_IGNORABLE;
5816
5817 list = define->content;
5818 while (list != NULL) {
5819 ret = xmlRelaxNGElementMatch(ctxt, list, elem);
5820 if (ret == 1) {
5821 ctxt->flags = oldflags;
5822 return(0);
5823 }
5824 if (ret < 0) {
5825 ctxt->flags = oldflags;
5826 return(ret);
5827 }
5828 list = list->next;
5829 }
5830 ctxt->flags = oldflags;
5831 } else {
5832 TODO
5833 }
5834 return(1);
5835}
5836
5837/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00005838 * xmlRelaxNGValidateDefinition:
5839 * @ctxt: a Relax-NG validation context
5840 * @define: the definition to verify
5841 *
5842 * Validate the current node against the definition
5843 *
5844 * Returns 0 if the validation succeeded or an error code.
5845 */
5846static int
5847xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
5848 xmlRelaxNGDefinePtr define) {
5849 xmlNodePtr node;
5850 int ret = 0, i, tmp, oldflags;
5851 xmlRelaxNGValidStatePtr oldstate, state;
5852
5853 if (define == NULL) {
5854 VALID_CTXT();
5855 VALID_ERROR("internal error: define == NULL\n");
5856 return(-1);
5857 }
5858 if (ctxt->state != NULL) {
5859 node = ctxt->state->seq;
5860 } else {
5861 node = NULL;
5862 }
Daniel Veillard231d7912003-02-09 14:22:17 +00005863#ifdef DEBUG
5864 for (i = 0;i < ctxt->depth;i++)
5865 xmlGenericError(xmlGenericErrorContext, " ");
5866 xmlGenericError(xmlGenericErrorContext,
5867 "Start validating %s ", xmlRelaxNGDefName(define));
5868 if (define->name != NULL)
5869 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
5870 if ((node != NULL) && (node->name != NULL))
5871 xmlGenericError(xmlGenericErrorContext, "on %s\n", node->name);
5872 else
5873 xmlGenericError(xmlGenericErrorContext, "\n");
5874#endif
5875 ctxt->depth++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005876 switch (define->type) {
5877 case XML_RELAXNG_EMPTY:
Daniel Veillardd4310742003-02-18 21:12:46 +00005878 node = xmlRelaxNGSkipIgnored(ctxt, node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005879 if (node != NULL) {
5880 VALID_CTXT();
5881 VALID_ERROR("Expecting an empty element\n");
Daniel Veillard231d7912003-02-09 14:22:17 +00005882 ret = -1;
5883 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005884 }
Daniel Veillard231d7912003-02-09 14:22:17 +00005885 ret = 0;
5886 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005887 case XML_RELAXNG_NOT_ALLOWED:
Daniel Veillard231d7912003-02-09 14:22:17 +00005888 ret = -1;
5889 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005890 case XML_RELAXNG_TEXT:
Daniel Veillard231d7912003-02-09 14:22:17 +00005891 if (node == NULL) {
5892 ret = 0;
5893 break;
5894 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005895 while ((node != NULL) &&
5896 ((node->type == XML_TEXT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005897 (node->type == XML_COMMENT_NODE) ||
5898 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00005899 (node->type == XML_CDATA_SECTION_NODE)))
5900 node = node->next;
Daniel Veillard276be4a2003-01-24 01:03:34 +00005901 if (node == ctxt->state->seq) {
5902 VALID_CTXT();
5903 VALID_ERROR("Expecting text content\n");
5904 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005905 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00005906 ctxt->state->seq = node;
5907 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005908 case XML_RELAXNG_ELEMENT:
5909 node = xmlRelaxNGSkipIgnored(ctxt, node);
Daniel Veillard231d7912003-02-09 14:22:17 +00005910 if (node == NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005911 VALID_CTXT();
Daniel Veillard231d7912003-02-09 14:22:17 +00005912 VALID_ERROR("Expecting an element, got empty\n");
5913 ret = -1;
5914 break;
5915 }
5916 if (node->type != XML_ELEMENT_NODE) {
5917 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005918 VALID_ERROR2("Expecting an element got %d type\n", node->type);
Daniel Veillard231d7912003-02-09 14:22:17 +00005919 ret = -1;
5920 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005921 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00005922 /*
5923 * This node was already validated successfully against
5924 * this definition.
5925 */
5926 if (node->_private == define)
5927 break;
Daniel Veillard416589a2003-02-17 17:25:42 +00005928
5929 ret = xmlRelaxNGElementMatch(ctxt, define, node);
5930 if (ret <= 0) {
5931 ret = -1;
5932 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005933 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005934 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005935
5936 state = xmlRelaxNGNewValidState(ctxt, node);
5937 if (state == NULL) {
Daniel Veillard231d7912003-02-09 14:22:17 +00005938 ret = -1;
5939 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005940 }
5941
5942 oldstate = ctxt->state;
5943 ctxt->state = state;
5944 if (define->attrs != NULL) {
5945 tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
Daniel Veillard231d7912003-02-09 14:22:17 +00005946 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005947 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00005948#ifdef DEBUG
5949 xmlGenericError(xmlGenericErrorContext,
5950 "E: Element %s failed to validate attributes\n",
5951 node->name);
5952#endif
5953 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005954 }
5955 if (define->content != NULL) {
5956 tmp = xmlRelaxNGValidateElementContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00005957 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005958 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00005959#ifdef DEBUG
5960 xmlGenericError(xmlGenericErrorContext,
5961 "E: Element %s failed to validate element content\n",
5962 node->name);
5963#endif
5964 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005965 }
5966 state = ctxt->state;
5967 if (state->seq != NULL) {
5968 state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq);
5969 if (state->seq != NULL) {
5970 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005971 VALID_ERROR3("Extra content for element %s: %s\n",
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005972 node->name, state->seq->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005973 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00005974#ifdef DEBUG
5975 xmlGenericError(xmlGenericErrorContext,
5976 "E: Element %s has extra content: %s\n",
5977 node->name, state->seq->name);
5978#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00005979 }
5980 }
5981 for (i = 0;i < state->nbAttrs;i++) {
5982 if (state->attrs[i] != NULL) {
5983 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005984 VALID_ERROR3("Invalid attribute %s for element %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00005985 state->attrs[i]->name, node->name);
5986 ret = -1;
5987 }
5988 }
5989 ctxt->state = oldstate;
5990 xmlRelaxNGFreeValidState(state);
5991 if (oldstate != NULL)
Daniel Veillarde5b110b2003-02-04 14:43:39 +00005992 oldstate->seq = xmlRelaxNGSkipIgnored(ctxt, node->next);
5993 if (ret == 0)
5994 node->_private = define;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005995
5996
5997#ifdef DEBUG
5998 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard231d7912003-02-09 14:22:17 +00005999 "xmlRelaxNGValidateDefinition(): validated %s : %d",
Daniel Veillard6eadf632003-01-23 18:29:16 +00006000 node->name, ret);
Daniel Veillard231d7912003-02-09 14:22:17 +00006001 if (oldstate == NULL)
6002 xmlGenericError(xmlGenericErrorContext, ": no state\n");
6003 else if (oldstate->seq == NULL)
6004 xmlGenericError(xmlGenericErrorContext, ": done\n");
6005 else if (oldstate->seq->type == XML_ELEMENT_NODE)
6006 xmlGenericError(xmlGenericErrorContext, ": next elem %s\n",
6007 oldstate->seq->name);
6008 else
6009 xmlGenericError(xmlGenericErrorContext, ": next %s %d\n",
6010 oldstate->seq->name, oldstate->seq->type);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006011#endif
6012 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006013 case XML_RELAXNG_OPTIONAL:
6014 oldflags = ctxt->flags;
6015 ctxt->flags |= FLAGS_IGNORABLE;
6016 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6017 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6018 if (ret != 0) {
6019 xmlRelaxNGFreeValidState(ctxt->state);
6020 ctxt->state = oldstate;
6021 ret = 0;
6022 break;
6023 }
6024 xmlRelaxNGFreeValidState(oldstate);
6025 ctxt->flags = oldflags;
6026 ret = 0;
6027 break;
6028 case XML_RELAXNG_ONEORMORE:
6029 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6030 if (ret != 0) {
6031 break;
6032 }
6033 /* no break on purpose */
Daniel Veillard276be4a2003-01-24 01:03:34 +00006034 case XML_RELAXNG_ZEROORMORE: {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006035 oldflags = ctxt->flags;
6036 ctxt->flags |= FLAGS_IGNORABLE;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006037 while (ctxt->state->nbAttrLeft != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006038 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6039 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6040 if (ret != 0) {
6041 xmlRelaxNGFreeValidState(ctxt->state);
6042 ctxt->state = oldstate;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006043 break;
6044 }
6045 xmlRelaxNGFreeValidState(oldstate);
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006046 }
6047 if (ret == 0) {
6048 /*
6049 * There is no attribute left to be consumed,
6050 * we can check the closure by looking at ctxt->state->seq
6051 */
6052 xmlNodePtr cur, temp;
6053
Daniel Veillard276be4a2003-01-24 01:03:34 +00006054 cur = ctxt->state->seq;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006055 temp = NULL;
6056 while ((cur != NULL) && (temp != cur)) {
6057 temp = cur;
6058 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6059 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6060 if (ret != 0) {
6061 xmlRelaxNGFreeValidState(ctxt->state);
6062 ctxt->state = oldstate;
6063 break;
6064 }
6065 xmlRelaxNGFreeValidState(oldstate);
6066 cur = ctxt->state->seq;
6067 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006068 }
6069 ctxt->flags = oldflags;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006070 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006071 break;
Daniel Veillard276be4a2003-01-24 01:03:34 +00006072 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006073 case XML_RELAXNG_CHOICE: {
6074 xmlRelaxNGDefinePtr list = define->content;
6075
6076 oldflags = ctxt->flags;
6077 ctxt->flags |= FLAGS_IGNORABLE;
6078
6079 while (list != NULL) {
6080 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6081 ret = xmlRelaxNGValidateDefinition(ctxt, list);
6082 if (ret == 0) {
6083 xmlRelaxNGFreeValidState(oldstate);
6084 break;
6085 }
6086 xmlRelaxNGFreeValidState(ctxt->state);
6087 ctxt->state = oldstate;
6088 list = list->next;
6089 }
6090 ctxt->flags = oldflags;
6091 break;
6092 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00006093 case XML_RELAXNG_DEF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00006094 case XML_RELAXNG_GROUP: {
6095 xmlRelaxNGDefinePtr list = define->content;
6096
6097 while (list != NULL) {
6098 ret = xmlRelaxNGValidateDefinition(ctxt, list);
6099 if (ret != 0)
6100 break;
6101 list = list->next;
6102 }
6103 break;
6104 }
6105 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006106 ret = xmlRelaxNGValidateInterleave(ctxt, define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006107 break;
6108 case XML_RELAXNG_ATTRIBUTE:
6109 ret = xmlRelaxNGValidateAttribute(ctxt, define);
6110 break;
6111 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00006112 case XML_RELAXNG_PARENTREF:
6113 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00006114 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6115 break;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006116 case XML_RELAXNG_DATATYPE: {
Daniel Veillardd4310742003-02-18 21:12:46 +00006117 xmlNodePtr child;
6118 xmlChar *content = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006119
Daniel Veillardd4310742003-02-18 21:12:46 +00006120 child = node;
6121 while (child != NULL) {
6122 if (child->type == XML_ELEMENT_NODE) {
6123 VALID_CTXT();
6124 VALID_ERROR2("Element %s has child elements\n",
6125 node->parent->name);
6126 ret = -1;
6127 break;
6128 } else if ((child->type == XML_TEXT_NODE) ||
6129 (child->type == XML_CDATA_SECTION_NODE)) {
6130 content = xmlStrcat(content, child->content);
6131 }
6132 /* TODO: handle entities ... */
6133 child = child->next;
6134 }
6135 if (ret == -1) {
6136 if (content != NULL)
6137 xmlFree(content);
6138 break;
6139 }
6140 if (content == NULL) {
6141 content = xmlStrdup(BAD_CAST "");
6142 if (content == NULL) {
6143 VALID_CTXT();
6144 VALID_ERROR("Allocation failure\n");
6145 ret = -1;
6146 break;
6147 }
6148 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006149 ret = xmlRelaxNGValidateDatatype(ctxt, content, define);
6150 if (ret == -1) {
6151 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006152 VALID_ERROR2("internal error validating %s\n", define->name);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006153 } else if (ret == 0) {
Daniel Veillardd4310742003-02-18 21:12:46 +00006154 ctxt->state->seq = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006155 }
6156 if (content != NULL)
6157 xmlFree(content);
6158 break;
6159 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00006160 case XML_RELAXNG_VALUE: {
Daniel Veillardd4310742003-02-18 21:12:46 +00006161 xmlChar *content = NULL;
Daniel Veillardea3f3982003-01-26 19:45:18 +00006162 xmlChar *oldvalue;
Daniel Veillardd4310742003-02-18 21:12:46 +00006163 xmlNodePtr child;
Daniel Veillardea3f3982003-01-26 19:45:18 +00006164
Daniel Veillardd4310742003-02-18 21:12:46 +00006165 child = node;
6166 while (child != NULL) {
6167 if (child->type == XML_ELEMENT_NODE) {
6168 VALID_CTXT();
6169 VALID_ERROR2("Element %s has child elements\n",
6170 node->parent->name);
6171 ret = -1;
6172 break;
6173 } else if ((child->type == XML_TEXT_NODE) ||
6174 (child->type == XML_CDATA_SECTION_NODE)) {
6175 content = xmlStrcat(content, child->content);
6176 }
6177 /* TODO: handle entities ... */
6178 child = child->next;
6179 }
6180 if (ret == -1) {
6181 if (content != NULL)
6182 xmlFree(content);
6183 break;
6184 }
6185 if (content == NULL) {
6186 content = xmlStrdup(BAD_CAST "");
6187 if (content == NULL) {
6188 VALID_CTXT();
6189 VALID_ERROR("Allocation failure\n");
6190 ret = -1;
6191 break;
6192 }
6193 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00006194 oldvalue = ctxt->state->value;
6195 ctxt->state->value = content;
6196 ret = xmlRelaxNGValidateValue(ctxt, define);
6197 ctxt->state->value = oldvalue;
6198 if (ret == -1) {
6199 VALID_CTXT();
Daniel Veillardd2298792003-02-14 16:54:11 +00006200 if (define->name != NULL) {
6201 VALID_ERROR2("error validating value %s\n", define->name);
6202 } else {
6203 VALID_ERROR("error validating value\n");
6204 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00006205 } else if (ret == 0) {
Daniel Veillardd4310742003-02-18 21:12:46 +00006206 ctxt->state->seq = NULL;
Daniel Veillardea3f3982003-01-26 19:45:18 +00006207 }
6208 if (content != NULL)
6209 xmlFree(content);
6210 break;
6211 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006212 case XML_RELAXNG_LIST: {
6213 xmlChar *content;
Daniel Veillardd4310742003-02-18 21:12:46 +00006214 xmlNodePtr child;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006215 xmlChar *oldvalue, *oldendvalue;
6216 int len;
Daniel Veillardea3f3982003-01-26 19:45:18 +00006217
Daniel Veillardd4310742003-02-18 21:12:46 +00006218 /*
6219 * Make sure it's only text nodes
6220 */
6221
6222 content = NULL;
6223 child = node;
6224 while (child != NULL) {
6225 if (child->type == XML_ELEMENT_NODE) {
6226 VALID_CTXT();
6227 VALID_ERROR2("Element %s has child elements\n",
6228 node->parent->name);
6229 ret = -1;
6230 break;
6231 } else if ((child->type == XML_TEXT_NODE) ||
6232 (child->type == XML_CDATA_SECTION_NODE)) {
6233 content = xmlStrcat(content, child->content);
6234 }
6235 /* TODO: handle entities ... */
6236 child = child->next;
6237 }
6238 if (ret == -1) {
6239 if (content != NULL)
6240 xmlFree(content);
6241 break;
6242 }
6243 if (content == NULL) {
6244 content = xmlStrdup(BAD_CAST "");
6245 if (content == NULL) {
6246 VALID_CTXT();
6247 VALID_ERROR("Allocation failure\n");
6248 ret = -1;
6249 break;
6250 }
6251 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006252 len = xmlStrlen(content);
6253 oldvalue = ctxt->state->value;
6254 oldendvalue = ctxt->state->endvalue;
6255 ctxt->state->value = content;
6256 ctxt->state->endvalue = content + len;
6257 ret = xmlRelaxNGValidateValue(ctxt, define);
6258 ctxt->state->value = oldvalue;
6259 ctxt->state->endvalue = oldendvalue;
6260 if (ret == -1) {
6261 VALID_CTXT();
6262 VALID_ERROR("internal error validating list\n");
Daniel Veillardd4310742003-02-18 21:12:46 +00006263 } else if ((ret == 0) && (node != NULL)) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006264 ctxt->state->seq = node->next;
6265 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006266 if (content != NULL)
6267 xmlFree(content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006268 break;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006269 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006270 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00006271 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006272 TODO
Daniel Veillard416589a2003-02-17 17:25:42 +00006273 ret = -1;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006274 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006275 }
Daniel Veillard231d7912003-02-09 14:22:17 +00006276 ctxt->depth--;
6277#ifdef DEBUG
6278 for (i = 0;i < ctxt->depth;i++)
6279 xmlGenericError(xmlGenericErrorContext, " ");
6280 xmlGenericError(xmlGenericErrorContext,
6281 "Validating %s ", xmlRelaxNGDefName(define));
6282 if (define->name != NULL)
6283 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
6284 if (ret == 0)
6285 xmlGenericError(xmlGenericErrorContext, "suceeded\n");
6286 else
6287 xmlGenericError(xmlGenericErrorContext, "failed\n");
6288#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00006289 return(ret);
6290}
6291
6292/**
6293 * xmlRelaxNGValidateDocument:
6294 * @ctxt: a Relax-NG validation context
6295 * @doc: the document
6296 *
6297 * Validate the given document
6298 *
6299 * Returns 0 if the validation succeeded or an error code.
6300 */
6301static int
6302xmlRelaxNGValidateDocument(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
6303 int ret;
6304 xmlRelaxNGPtr schema;
6305 xmlRelaxNGGrammarPtr grammar;
6306 xmlRelaxNGValidStatePtr state;
6307
6308 if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL))
6309 return(-1);
6310
6311 schema = ctxt->schema;
6312 grammar = schema->topgrammar;
6313 if (grammar == NULL) {
6314 VALID_CTXT();
6315 VALID_ERROR("No top grammar defined\n");
6316 return(-1);
6317 }
6318 state = xmlRelaxNGNewValidState(ctxt, NULL);
6319 ctxt->state = state;
6320 ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start);
6321 state = ctxt->state;
6322 if ((state != NULL) && (state->seq != NULL)) {
6323 xmlNodePtr node;
6324
6325 node = state->seq;
6326 node = xmlRelaxNGSkipIgnored(ctxt, node);
6327 if (node != NULL) {
6328 VALID_CTXT();
6329 VALID_ERROR("extra data on the document\n");
6330 ret = -1;
6331 }
6332 }
6333 xmlRelaxNGFreeValidState(state);
6334
6335 return(ret);
6336}
6337
6338/************************************************************************
6339 * *
6340 * Validation interfaces *
6341 * *
6342 ************************************************************************/
6343/**
6344 * xmlRelaxNGNewValidCtxt:
6345 * @schema: a precompiled XML RelaxNGs
6346 *
6347 * Create an XML RelaxNGs validation context based on the given schema
6348 *
6349 * Returns the validation context or NULL in case of error
6350 */
6351xmlRelaxNGValidCtxtPtr
6352xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema) {
6353 xmlRelaxNGValidCtxtPtr ret;
6354
6355 ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt));
6356 if (ret == NULL) {
6357 xmlGenericError(xmlGenericErrorContext,
6358 "Failed to allocate new schama validation context\n");
6359 return (NULL);
6360 }
6361 memset(ret, 0, sizeof(xmlRelaxNGValidCtxt));
6362 ret->schema = schema;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006363 ret->error = xmlGenericError;
6364 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006365 return (ret);
6366}
6367
6368/**
6369 * xmlRelaxNGFreeValidCtxt:
6370 * @ctxt: the schema validation context
6371 *
6372 * Free the resources associated to the schema validation context
6373 */
6374void
6375xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt) {
6376 if (ctxt == NULL)
6377 return;
6378 xmlFree(ctxt);
6379}
6380
6381/**
6382 * xmlRelaxNGSetValidErrors:
6383 * @ctxt: a Relax-NG validation context
6384 * @err: the error function
6385 * @warn: the warning function
6386 * @ctx: the functions context
6387 *
6388 * Set the error and warning callback informations
6389 */
6390void
6391xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
6392 xmlRelaxNGValidityErrorFunc err,
6393 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
6394 if (ctxt == NULL)
6395 return;
6396 ctxt->error = err;
6397 ctxt->warning = warn;
6398 ctxt->userData = ctx;
6399}
6400
6401/**
6402 * xmlRelaxNGValidateDoc:
6403 * @ctxt: a Relax-NG validation context
6404 * @doc: a parsed document tree
6405 *
6406 * Validate a document tree in memory.
6407 *
6408 * Returns 0 if the document is valid, a positive error code
6409 * number otherwise and -1 in case of internal or API error.
6410 */
6411int
6412xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
6413 int ret;
6414
6415 if ((ctxt == NULL) || (doc == NULL))
6416 return(-1);
6417
6418 ctxt->doc = doc;
6419
6420 ret = xmlRelaxNGValidateDocument(ctxt, doc);
Daniel Veillard71531f32003-02-05 13:19:53 +00006421 /*
6422 * TODO: build error codes
6423 */
6424 if (ret == -1)
6425 return(1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006426 return(ret);
6427}
6428
6429#endif /* LIBXML_SCHEMAS_ENABLED */
6430