blob: a2dbe61b59e65888b51f7117e57b9a14d4f17c81 [file] [log] [blame]
Daniel Veillard6eadf632003-01-23 18:29:16 +00001/*
2 * relaxng.c : implementation of the Relax-NG handling and validity checking
3 *
4 * See Copyright for the status of this software.
5 *
6 * Daniel Veillard <veillard@redhat.com>
7 */
8
Daniel Veillardd41f4f42003-01-29 21:07:52 +00009/**
10 * TODO:
Daniel Veillardd41f4f42003-01-29 21:07:52 +000011 * - error reporting
Daniel Veillarde2a5a082003-02-02 14:35:17 +000012 * - simplification of the resulting compiled trees:
13 * - NOT_ALLOWED
14 * - EMPTY
Daniel Veillard1ed7f362003-02-03 10:57:45 +000015 * - handle namespace declarations as attributes.
Daniel Veillardf4b4f982003-02-13 11:02:08 +000016 * - add support for DTD compatibility spec
17 * http://www.oasis-open.org/committees/relax-ng/compatibility-20011203.html
Daniel Veillardd41f4f42003-01-29 21:07:52 +000018 */
19
Daniel Veillard6eadf632003-01-23 18:29:16 +000020#define IN_LIBXML
21#include "libxml.h"
22
23#ifdef LIBXML_SCHEMAS_ENABLED
24
25#include <string.h>
26#include <stdio.h>
27#include <libxml/xmlmemory.h>
28#include <libxml/parser.h>
29#include <libxml/parserInternals.h>
30#include <libxml/hash.h>
31#include <libxml/uri.h>
32
33#include <libxml/relaxng.h>
34
35#include <libxml/xmlschemastypes.h>
36#include <libxml/xmlautomata.h>
37#include <libxml/xmlregexp.h>
Daniel Veillardc6e997c2003-01-27 12:35:42 +000038#include <libxml/xmlschemastypes.h>
Daniel Veillard6eadf632003-01-23 18:29:16 +000039
40/*
41 * The Relax-NG namespace
42 */
43static const xmlChar *xmlRelaxNGNs = (const xmlChar *)
44 "http://relaxng.org/ns/structure/1.0";
45
46#define IS_RELAXNG(node, type) \
47 ((node != NULL) && (node->ns != NULL) && \
48 (xmlStrEqual(node->name, (const xmlChar *) type)) && \
49 (xmlStrEqual(node->ns->href, xmlRelaxNGNs)))
50
51
Daniel Veillard71531f32003-02-05 13:19:53 +000052/* #define DEBUG 1 */ /* very verbose output */
53/* #define DEBUG_CONTENT 1 */
54/* #define DEBUG_TYPE 1 */
55/* #define DEBUG_VALID 1 */
Daniel Veillarde5b110b2003-02-04 14:43:39 +000056/* #define DEBUG_INTERLEAVE 1 */
Daniel Veillard416589a2003-02-17 17:25:42 +000057/* #define DEBUG_LIST 1 */
Daniel Veillard6eadf632003-01-23 18:29:16 +000058
59#define UNBOUNDED (1 << 30)
60#define TODO \
61 xmlGenericError(xmlGenericErrorContext, \
62 "Unimplemented block at %s:%d\n", \
63 __FILE__, __LINE__);
64
65typedef struct _xmlRelaxNGSchema xmlRelaxNGSchema;
66typedef xmlRelaxNGSchema *xmlRelaxNGSchemaPtr;
67
68typedef struct _xmlRelaxNGDefine xmlRelaxNGDefine;
69typedef xmlRelaxNGDefine *xmlRelaxNGDefinePtr;
70
Daniel Veillardd41f4f42003-01-29 21:07:52 +000071typedef struct _xmlRelaxNGDocument xmlRelaxNGDocument;
72typedef xmlRelaxNGDocument *xmlRelaxNGDocumentPtr;
73
Daniel Veillarda9d912d2003-02-01 17:43:10 +000074typedef struct _xmlRelaxNGInclude xmlRelaxNGInclude;
75typedef xmlRelaxNGInclude *xmlRelaxNGIncludePtr;
76
Daniel Veillard6eadf632003-01-23 18:29:16 +000077typedef enum {
78 XML_RELAXNG_COMBINE_UNDEFINED = 0, /* undefined */
79 XML_RELAXNG_COMBINE_CHOICE, /* choice */
80 XML_RELAXNG_COMBINE_INTERLEAVE /* interleave */
81} xmlRelaxNGCombine;
82
83typedef struct _xmlRelaxNGGrammar xmlRelaxNGGrammar;
84typedef xmlRelaxNGGrammar *xmlRelaxNGGrammarPtr;
85
86struct _xmlRelaxNGGrammar {
87 xmlRelaxNGGrammarPtr parent;/* the parent grammar if any */
88 xmlRelaxNGGrammarPtr children;/* the children grammar if any */
89 xmlRelaxNGGrammarPtr next; /* the next grammar if any */
90 xmlRelaxNGDefinePtr start; /* <start> content */
91 xmlRelaxNGCombine combine; /* the default combine value */
Daniel Veillardd41f4f42003-01-29 21:07:52 +000092 xmlRelaxNGDefinePtr startList;/* list of <start> definitions */
Daniel Veillard6eadf632003-01-23 18:29:16 +000093 xmlHashTablePtr defs; /* define* */
94 xmlHashTablePtr refs; /* references */
95};
96
97
Daniel Veillard6eadf632003-01-23 18:29:16 +000098typedef enum {
99 XML_RELAXNG_EMPTY = 0, /* an empty pattern */
100 XML_RELAXNG_NOT_ALLOWED, /* not allowed top */
Daniel Veillard144fae12003-02-03 13:17:57 +0000101 XML_RELAXNG_EXCEPT, /* except present in nameclass defs */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000102 XML_RELAXNG_TEXT, /* textual content */
103 XML_RELAXNG_ELEMENT, /* an element */
104 XML_RELAXNG_DATATYPE, /* extenal data type definition */
Daniel Veillard8fe98712003-02-19 00:19:14 +0000105 XML_RELAXNG_PARAM, /* extenal data type parameter */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000106 XML_RELAXNG_VALUE, /* value from an extenal data type definition */
107 XML_RELAXNG_LIST, /* a list of patterns */
108 XML_RELAXNG_ATTRIBUTE, /* an attrbute following a pattern */
109 XML_RELAXNG_DEF, /* a definition */
110 XML_RELAXNG_REF, /* reference to a definition */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000111 XML_RELAXNG_EXTERNALREF, /* reference to an external def */
Daniel Veillard419a7682003-02-03 23:22:49 +0000112 XML_RELAXNG_PARENTREF, /* reference to a def in the parent grammar */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000113 XML_RELAXNG_OPTIONAL, /* optional patterns */
114 XML_RELAXNG_ZEROORMORE, /* zero or more non empty patterns */
115 XML_RELAXNG_ONEORMORE, /* one or more non empty patterns */
116 XML_RELAXNG_CHOICE, /* a choice between non empty patterns */
117 XML_RELAXNG_GROUP, /* a pair/group of non empty patterns */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000118 XML_RELAXNG_INTERLEAVE, /* interleaving choice of non-empty patterns */
119 XML_RELAXNG_START /* Used to keep track of starts on grammars */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000120} xmlRelaxNGType;
121
122struct _xmlRelaxNGDefine {
123 xmlRelaxNGType type; /* the type of definition */
124 xmlNodePtr node; /* the node in the source */
125 xmlChar *name; /* the element local name if present */
126 xmlChar *ns; /* the namespace local name if present */
Daniel Veillardedc91922003-01-26 00:52:04 +0000127 xmlChar *value; /* value when available */
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000128 void *data; /* data lib or specific pointer */
Daniel Veillardd4310742003-02-18 21:12:46 +0000129 int depth; /* used for the cycle detection */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000130 xmlRelaxNGDefinePtr content;/* the expected content */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000131 xmlRelaxNGDefinePtr parent; /* the parent definition, if any */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000132 xmlRelaxNGDefinePtr next; /* list within grouping sequences */
133 xmlRelaxNGDefinePtr attrs; /* list of attributes for elements */
Daniel Veillard3b2e4e12003-02-03 08:52:58 +0000134 xmlRelaxNGDefinePtr nameClass;/* the nameClass definition if any */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000135 xmlRelaxNGDefinePtr nextHash;/* next define in defs/refs hash tables */
136};
137
138/**
139 * _xmlRelaxNG:
140 *
141 * A RelaxNGs definition
142 */
143struct _xmlRelaxNG {
144 xmlRelaxNGGrammarPtr topgrammar;
145 xmlDocPtr doc;
146
147 xmlHashTablePtr defs; /* define */
148 xmlHashTablePtr refs; /* references */
Daniel Veillard419a7682003-02-03 23:22:49 +0000149 xmlHashTablePtr documents; /* all the documents loaded */
150 xmlHashTablePtr includes; /* all the includes loaded */
151 int defNr; /* number of defines used */
152 xmlRelaxNGDefinePtr *defTab;/* pointer to the allocated definitions */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000153 void *_private; /* unused by the library for users or bindings */
154};
155
156typedef enum {
157 XML_RELAXNG_ERR_OK = 0,
158 XML_RELAXNG_ERR_NOROOT = 1,
159 XML_RELAXNG_ERR_
160} xmlRelaxNGValidError;
161
162#define XML_RELAXNG_IN_ATTRIBUTE 1
163
164struct _xmlRelaxNGParserCtxt {
165 void *userData; /* user specific data block */
166 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
167 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
168 xmlRelaxNGValidError err;
169
170 xmlRelaxNGPtr schema; /* The schema in use */
171 xmlRelaxNGGrammarPtr grammar; /* the current grammar */
Daniel Veillard419a7682003-02-03 23:22:49 +0000172 xmlRelaxNGGrammarPtr parentgrammar;/* the parent grammar */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000173 int flags; /* parser flags */
174 int nbErrors; /* number of errors at parse time */
175 int nbWarnings; /* number of warnings at parse time */
Daniel Veillard276be4a2003-01-24 01:03:34 +0000176 const xmlChar *define; /* the current define scope */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000177 xmlRelaxNGDefinePtr def; /* the current define */
178
179 int nbInterleaves;
180 xmlHashTablePtr interleaves; /* keep track of all the interleaves */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000181
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000182 xmlHashTablePtr documents; /* all the documents loaded */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000183 xmlHashTablePtr includes; /* all the includes loaded */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000184 xmlChar *URL;
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000185 xmlDocPtr document;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000186
Daniel Veillard419a7682003-02-03 23:22:49 +0000187 int defNr; /* number of defines used */
188 int defMax; /* number of defines aloocated */
189 xmlRelaxNGDefinePtr *defTab; /* pointer to the allocated definitions */
190
Daniel Veillard6eadf632003-01-23 18:29:16 +0000191 const char *buffer;
192 int size;
193
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000194 /* the document stack */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000195 xmlRelaxNGDocumentPtr doc; /* Current parsed external ref */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000196 int docNr; /* Depth of the parsing stack */
197 int docMax; /* Max depth of the parsing stack */
198 xmlRelaxNGDocumentPtr *docTab; /* array of docs */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000199
200 /* the include stack */
Daniel Veillardd4310742003-02-18 21:12:46 +0000201 xmlRelaxNGIncludePtr inc; /* Current parsed include */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000202 int incNr; /* Depth of the include parsing stack */
203 int incMax; /* Max depth of the parsing stack */
204 xmlRelaxNGIncludePtr *incTab; /* array of incs */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000205};
206
207#define FLAGS_IGNORABLE 1
208#define FLAGS_NEGATIVE 2
209
210/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000211 * xmlRelaxNGInterleaveGroup:
212 *
213 * A RelaxNGs partition set associated to lists of definitions
214 */
215typedef struct _xmlRelaxNGInterleaveGroup xmlRelaxNGInterleaveGroup;
216typedef xmlRelaxNGInterleaveGroup *xmlRelaxNGInterleaveGroupPtr;
217struct _xmlRelaxNGInterleaveGroup {
218 xmlRelaxNGDefinePtr rule; /* the rule to satisfy */
219 xmlRelaxNGDefinePtr *defs; /* the array of element definitions */
220};
221
222/**
223 * xmlRelaxNGPartitions:
224 *
225 * A RelaxNGs partition associated to an interleave group
226 */
227typedef struct _xmlRelaxNGPartition xmlRelaxNGPartition;
228typedef xmlRelaxNGPartition *xmlRelaxNGPartitionPtr;
229struct _xmlRelaxNGPartition {
230 int nbgroups; /* number of groups in the partitions */
231 xmlRelaxNGInterleaveGroupPtr *groups;
232};
233
234/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000235 * xmlRelaxNGValidState:
236 *
237 * A RelaxNGs validation state
238 */
239#define MAX_ATTR 20
240typedef struct _xmlRelaxNGValidState xmlRelaxNGValidState;
241typedef xmlRelaxNGValidState *xmlRelaxNGValidStatePtr;
242struct _xmlRelaxNGValidState {
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000243 xmlNodePtr node; /* the current node */
244 xmlNodePtr seq; /* the sequence of children left to validate */
245 int nbAttrs; /* the number of attributes */
Daniel Veillard1ed7f362003-02-03 10:57:45 +0000246 int nbAttrLeft; /* the number of attributes left to validate */
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000247 xmlChar *value; /* the value when operating on string */
248 xmlChar *endvalue; /* the end value when operating on string */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000249 xmlAttrPtr attrs[1]; /* the array of attributes */
250};
251
252/**
253 * xmlRelaxNGValidCtxt:
254 *
255 * A RelaxNGs validation context
256 */
257
258struct _xmlRelaxNGValidCtxt {
259 void *userData; /* user specific data block */
260 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
261 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
262
263 xmlRelaxNGPtr schema; /* The schema in use */
264 xmlDocPtr doc; /* the document being validated */
265 xmlRelaxNGValidStatePtr state; /* the current validation state */
266 int flags; /* validation flags */
Daniel Veillard231d7912003-02-09 14:22:17 +0000267 int depth; /* validation depth */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000268};
269
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000270/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000271 * xmlRelaxNGInclude:
272 *
273 * Structure associated to a RelaxNGs document element
274 */
275struct _xmlRelaxNGInclude {
276 xmlChar *href; /* the normalized href value */
277 xmlDocPtr doc; /* the associated XML document */
278 xmlRelaxNGDefinePtr content;/* the definitions */
279 xmlRelaxNGPtr schema; /* the schema */
280};
281
282/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000283 * xmlRelaxNGDocument:
284 *
285 * Structure associated to a RelaxNGs document element
286 */
287struct _xmlRelaxNGDocument {
288 xmlChar *href; /* the normalized href value */
289 xmlDocPtr doc; /* the associated XML document */
290 xmlRelaxNGDefinePtr content;/* the definitions */
291 xmlRelaxNGPtr schema; /* the schema */
292};
293
Daniel Veillard6eadf632003-01-23 18:29:16 +0000294/************************************************************************
295 * *
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000296 * Preliminary type checking interfaces *
297 * *
298 ************************************************************************/
299/**
300 * xmlRelaxNGTypeHave:
301 * @data: data needed for the library
302 * @type: the type name
303 * @value: the value to check
304 *
305 * Function provided by a type library to check if a type is exported
306 *
307 * Returns 1 if yes, 0 if no and -1 in case of error.
308 */
309typedef int (*xmlRelaxNGTypeHave) (void *data, const xmlChar *type);
310
311/**
312 * xmlRelaxNGTypeCheck:
313 * @data: data needed for the library
314 * @type: the type name
315 * @value: the value to check
316 *
317 * Function provided by a type library to check if a value match a type
318 *
319 * Returns 1 if yes, 0 if no and -1 in case of error.
320 */
321typedef int (*xmlRelaxNGTypeCheck) (void *data, const xmlChar *type,
322 const xmlChar *value);
323
324/**
325 * xmlRelaxNGTypeCompare:
326 * @data: data needed for the library
327 * @type: the type name
328 * @value1: the first value
329 * @value2: the second value
330 *
331 * Function provided by a type library to compare two values accordingly
332 * to a type.
333 *
334 * Returns 1 if yes, 0 if no and -1 in case of error.
335 */
336typedef int (*xmlRelaxNGTypeCompare) (void *data, const xmlChar *type,
337 const xmlChar *value1,
338 const xmlChar *value2);
339typedef struct _xmlRelaxNGTypeLibrary xmlRelaxNGTypeLibrary;
340typedef xmlRelaxNGTypeLibrary *xmlRelaxNGTypeLibraryPtr;
341struct _xmlRelaxNGTypeLibrary {
342 const xmlChar *namespace; /* the datatypeLibrary value */
343 void *data; /* data needed for the library */
344 xmlRelaxNGTypeHave have; /* the export function */
345 xmlRelaxNGTypeCheck check; /* the checking function */
346 xmlRelaxNGTypeCompare comp; /* the compare function */
347};
348
349/************************************************************************
350 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +0000351 * Allocation functions *
352 * *
353 ************************************************************************/
Daniel Veillard6eadf632003-01-23 18:29:16 +0000354static void xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar);
355static void xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define);
Daniel Veillardd2298792003-02-14 16:54:11 +0000356static void xmlRelaxNGNormExtSpace(xmlChar *value);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000357
358/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000359 * xmlRelaxNGFreeDocument:
360 * @docu: a document structure
361 *
362 * Deallocate a RelaxNG document structure.
363 */
364static void
365xmlRelaxNGFreeDocument(xmlRelaxNGDocumentPtr docu)
366{
367 if (docu == NULL)
368 return;
369
370 if (docu->href != NULL)
371 xmlFree(docu->href);
372 if (docu->doc != NULL)
373 xmlFreeDoc(docu->doc);
374 if (docu->schema != NULL)
375 xmlRelaxNGFree(docu->schema);
376 xmlFree(docu);
377}
378
379/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000380 * xmlRelaxNGFreeInclude:
381 * @incl: a include structure
382 *
383 * Deallocate a RelaxNG include structure.
384 */
385static void
386xmlRelaxNGFreeInclude(xmlRelaxNGIncludePtr incl)
387{
388 if (incl == NULL)
389 return;
390
391 if (incl->href != NULL)
392 xmlFree(incl->href);
393 if (incl->doc != NULL)
394 xmlFreeDoc(incl->doc);
395 if (incl->schema != NULL)
396 xmlRelaxNGFree(incl->schema);
397 xmlFree(incl);
398}
399
400/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000401 * xmlRelaxNGNewRelaxNG:
402 * @ctxt: a Relax-NG validation context (optional)
403 *
404 * Allocate a new RelaxNG structure.
405 *
406 * Returns the newly allocated structure or NULL in case or error
407 */
408static xmlRelaxNGPtr
409xmlRelaxNGNewRelaxNG(xmlRelaxNGParserCtxtPtr ctxt)
410{
411 xmlRelaxNGPtr ret;
412
413 ret = (xmlRelaxNGPtr) xmlMalloc(sizeof(xmlRelaxNG));
414 if (ret == NULL) {
415 if ((ctxt != NULL) && (ctxt->error != NULL))
416 ctxt->error(ctxt->userData, "Out of memory\n");
417 ctxt->nbErrors++;
418 return (NULL);
419 }
420 memset(ret, 0, sizeof(xmlRelaxNG));
421
422 return (ret);
423}
424
425/**
426 * xmlRelaxNGFree:
427 * @schema: a schema structure
428 *
429 * Deallocate a RelaxNG structure.
430 */
431void
432xmlRelaxNGFree(xmlRelaxNGPtr schema)
433{
434 if (schema == NULL)
435 return;
436
Daniel Veillard6eadf632003-01-23 18:29:16 +0000437 if (schema->topgrammar != NULL)
438 xmlRelaxNGFreeGrammar(schema->topgrammar);
439 if (schema->doc != NULL)
440 xmlFreeDoc(schema->doc);
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000441 if (schema->documents != NULL)
442 xmlHashFree(schema->documents, (xmlHashDeallocator)
443 xmlRelaxNGFreeDocument);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000444 if (schema->includes != NULL)
445 xmlHashFree(schema->includes, (xmlHashDeallocator)
446 xmlRelaxNGFreeInclude);
Daniel Veillard419a7682003-02-03 23:22:49 +0000447 if (schema->defTab != NULL) {
448 int i;
449
450 for (i = 0;i < schema->defNr;i++)
451 xmlRelaxNGFreeDefine(schema->defTab[i]);
452 xmlFree(schema->defTab);
453 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000454
455 xmlFree(schema);
456}
457
458/**
459 * xmlRelaxNGNewGrammar:
460 * @ctxt: a Relax-NG validation context (optional)
461 *
462 * Allocate a new RelaxNG grammar.
463 *
464 * Returns the newly allocated structure or NULL in case or error
465 */
466static xmlRelaxNGGrammarPtr
467xmlRelaxNGNewGrammar(xmlRelaxNGParserCtxtPtr ctxt)
468{
469 xmlRelaxNGGrammarPtr ret;
470
471 ret = (xmlRelaxNGGrammarPtr) xmlMalloc(sizeof(xmlRelaxNGGrammar));
472 if (ret == NULL) {
473 if ((ctxt != NULL) && (ctxt->error != NULL))
474 ctxt->error(ctxt->userData, "Out of memory\n");
475 ctxt->nbErrors++;
476 return (NULL);
477 }
478 memset(ret, 0, sizeof(xmlRelaxNGGrammar));
479
480 return (ret);
481}
482
483/**
484 * xmlRelaxNGFreeGrammar:
485 * @grammar: a grammar structure
486 *
487 * Deallocate a RelaxNG grammar structure.
488 */
489static void
490xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar)
491{
492 if (grammar == NULL)
493 return;
494
Daniel Veillard419a7682003-02-03 23:22:49 +0000495 if (grammar->next != NULL) {
496 xmlRelaxNGFreeGrammar(grammar->next);
497 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000498 if (grammar->refs != NULL) {
499 xmlHashFree(grammar->refs, NULL);
500 }
501 if (grammar->defs != NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000502 xmlHashFree(grammar->defs, NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000503 }
504
505 xmlFree(grammar);
506}
507
508/**
509 * xmlRelaxNGNewDefine:
510 * @ctxt: a Relax-NG validation context
511 * @node: the node in the input document.
512 *
513 * Allocate a new RelaxNG define.
514 *
515 * Returns the newly allocated structure or NULL in case or error
516 */
517static xmlRelaxNGDefinePtr
518xmlRelaxNGNewDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
519{
520 xmlRelaxNGDefinePtr ret;
521
Daniel Veillard419a7682003-02-03 23:22:49 +0000522 if (ctxt->defMax == 0) {
523 ctxt->defMax = 16;
524 ctxt->defNr = 0;
525 ctxt->defTab = (xmlRelaxNGDefinePtr *)
526 xmlMalloc(ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
527 if (ctxt->defTab == NULL) {
528 if ((ctxt != NULL) && (ctxt->error != NULL))
529 ctxt->error(ctxt->userData, "Out of memory\n");
530 ctxt->nbErrors++;
531 return (NULL);
532 }
533 } else if (ctxt->defMax <= ctxt->defNr) {
534 xmlRelaxNGDefinePtr *tmp;
535 ctxt->defMax *= 2;
536 tmp = (xmlRelaxNGDefinePtr *) xmlRealloc(ctxt->defTab,
537 ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
538 if (tmp == NULL) {
539 if ((ctxt != NULL) && (ctxt->error != NULL))
540 ctxt->error(ctxt->userData, "Out of memory\n");
541 ctxt->nbErrors++;
542 return (NULL);
543 }
544 ctxt->defTab = tmp;
545 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000546 ret = (xmlRelaxNGDefinePtr) xmlMalloc(sizeof(xmlRelaxNGDefine));
547 if (ret == NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000548 if ((ctxt != NULL) && (ctxt->error != NULL))
549 ctxt->error(ctxt->userData, "Out of memory\n");
Daniel Veillard6eadf632003-01-23 18:29:16 +0000550 ctxt->nbErrors++;
Daniel Veillard419a7682003-02-03 23:22:49 +0000551 return(NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000552 }
553 memset(ret, 0, sizeof(xmlRelaxNGDefine));
Daniel Veillard419a7682003-02-03 23:22:49 +0000554 ctxt->defTab[ctxt->defNr++] = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000555 ret->node = node;
Daniel Veillardd4310742003-02-18 21:12:46 +0000556 ret->depth = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000557 return (ret);
558}
559
560/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000561 * xmlRelaxNGFreePartition:
562 * @partitions: a partition set structure
563 *
564 * Deallocate RelaxNG partition set structures.
565 */
566static void
567xmlRelaxNGFreePartition(xmlRelaxNGPartitionPtr partitions) {
568 xmlRelaxNGInterleaveGroupPtr group;
569 int j;
570
571 if (partitions != NULL) {
572 if (partitions->groups != NULL) {
573 for (j = 0;j < partitions->nbgroups;j++) {
574 group = partitions->groups[j];
575 if (group != NULL) {
576 if (group->defs != NULL)
577 xmlFree(group->defs);
578 xmlFree(group);
579 }
580 }
581 xmlFree(partitions->groups);
582 }
583 xmlFree(partitions);
584 }
585}
586/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000587 * xmlRelaxNGFreeDefine:
588 * @define: a define structure
589 *
590 * Deallocate a RelaxNG define structure.
591 */
592static void
593xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define)
594{
595 if (define == NULL)
596 return;
597
Daniel Veillard419a7682003-02-03 23:22:49 +0000598 if ((define->data != NULL) &&
599 (define->type == XML_RELAXNG_INTERLEAVE))
600 xmlRelaxNGFreePartition((xmlRelaxNGPartitionPtr) define->data);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000601 if (define->name != NULL)
602 xmlFree(define->name);
603 if (define->ns != NULL)
604 xmlFree(define->ns);
Daniel Veillardedc91922003-01-26 00:52:04 +0000605 if (define->value != NULL)
606 xmlFree(define->value);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000607 xmlFree(define);
608}
609
610/**
611 * xmlRelaxNGNewValidState:
612 * @ctxt: a Relax-NG validation context
613 * @node: the current node or NULL for the document
614 *
615 * Allocate a new RelaxNG validation state
616 *
617 * Returns the newly allocated structure or NULL in case or error
618 */
619static xmlRelaxNGValidStatePtr
620xmlRelaxNGNewValidState(xmlRelaxNGValidCtxtPtr ctxt, xmlNodePtr node)
621{
622 xmlRelaxNGValidStatePtr ret;
623 xmlAttrPtr attr;
624 xmlAttrPtr attrs[MAX_ATTR];
625 int nbAttrs = 0;
626 xmlNodePtr root = NULL;
627
628 if (node == NULL) {
629 root = xmlDocGetRootElement(ctxt->doc);
630 if (root == NULL)
631 return(NULL);
632 } else {
633 attr = node->properties;
634 while (attr != NULL) {
635 if (nbAttrs < MAX_ATTR)
636 attrs[nbAttrs++] = attr;
637 else
638 nbAttrs++;
639 attr = attr->next;
640 }
641 }
642
643 if (nbAttrs < MAX_ATTR)
644 attrs[nbAttrs] = NULL;
645 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(sizeof(xmlRelaxNGValidState) +
646 nbAttrs * sizeof(xmlAttrPtr));
647 if (ret == NULL) {
648 if ((ctxt != NULL) && (ctxt->error != NULL))
649 ctxt->error(ctxt->userData, "Out of memory\n");
650 return (NULL);
651 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +0000652 ret->value = NULL;
653 ret->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000654 if (node == NULL) {
655 ret->node = (xmlNodePtr) ctxt->doc;
656 ret->seq = root;
657 ret->nbAttrs = 0;
658 } else {
659 ret->node = node;
660 ret->seq = node->children;
661 ret->nbAttrs = nbAttrs;
662 if (nbAttrs > 0) {
663 if (nbAttrs < MAX_ATTR) {
664 memcpy(&(ret->attrs[0]), attrs,
665 sizeof(xmlAttrPtr) * (nbAttrs + 1));
666 } else {
667 attr = node->properties;
668 nbAttrs = 0;
669 while (attr != NULL) {
670 ret->attrs[nbAttrs++] = attr;
671 attr = attr->next;
672 }
673 ret->attrs[nbAttrs] = NULL;
674 }
675 }
676 }
Daniel Veillard1ed7f362003-02-03 10:57:45 +0000677 ret->nbAttrLeft = ret->nbAttrs;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000678 return (ret);
679}
680
681/**
682 * xmlRelaxNGCopyValidState:
683 * @ctxt: a Relax-NG validation context
684 * @state: a validation state
685 *
686 * Copy the validation state
687 *
688 * Returns the newly allocated structure or NULL in case or error
689 */
690static xmlRelaxNGValidStatePtr
691xmlRelaxNGCopyValidState(xmlRelaxNGValidCtxtPtr ctxt,
692 xmlRelaxNGValidStatePtr state)
693{
694 xmlRelaxNGValidStatePtr ret;
695 unsigned int size;
696
697 if (state == NULL)
698 return(NULL);
699
700 size = sizeof(xmlRelaxNGValidState) +
701 state->nbAttrs * sizeof(xmlAttrPtr);
702 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(size);
703 if (ret == NULL) {
704 if ((ctxt != NULL) && (ctxt->error != NULL))
705 ctxt->error(ctxt->userData, "Out of memory\n");
706 return (NULL);
707 }
708 memcpy(ret, state, size);
709 return(ret);
710}
711
712/**
Daniel Veillardce14fa52003-02-19 17:32:48 +0000713 * xmlRelaxNGEqualValidState:
714 * @ctxt: a Relax-NG validation context
715 * @state1: a validation state
716 * @state2: a validation state
717 *
718 * Compare the validation states for equality
719 *
720 * Returns 1 if equald, 0 otherwise
721 */
722static int
723xmlRelaxNGEqualValidState(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
724 xmlRelaxNGValidStatePtr state1,
725 xmlRelaxNGValidStatePtr state2)
726{
727 int i;
728
729 if ((state1 == NULL) || (state2 == NULL))
730 return(0);
731 if (state1 == state2)
732 return(1);
733 if (state1->node != state2->node)
734 return(0);
735 if (state1->seq != state2->seq)
736 return(0);
737 if (state1->nbAttrLeft != state2->nbAttrLeft)
738 return(0);
739 if (state1->nbAttrs != state2->nbAttrs)
740 return(0);
741 if (state1->endvalue != state2->endvalue)
742 return(0);
743 if ((state1->value != state2->value) &&
744 (!xmlStrEqual(state1->value, state2->value)))
745 return(0);
746 for (i = 0;i < state1->nbAttrs;i++) {
747 if (state1->attrs[i] != state2->attrs[i])
748 return(0);
749 }
750 return(1);
751}
752
753/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000754 * xmlRelaxNGFreeValidState:
755 * @state: a validation state structure
756 *
757 * Deallocate a RelaxNG validation state structure.
758 */
759static void
760xmlRelaxNGFreeValidState(xmlRelaxNGValidStatePtr state)
761{
762 if (state == NULL)
763 return;
764
765 xmlFree(state);
766}
767
768/************************************************************************
769 * *
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000770 * Document functions *
771 * *
772 ************************************************************************/
773static xmlDocPtr xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt,
774 xmlDocPtr doc);
775
776/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000777 * xmlRelaxNGIncludePush:
778 * @ctxt: the parser context
779 * @value: the element doc
780 *
781 * Pushes a new include on top of the include stack
782 *
783 * Returns 0 in case of error, the index in the stack otherwise
784 */
785static int
786xmlRelaxNGIncludePush(xmlRelaxNGParserCtxtPtr ctxt,
787 xmlRelaxNGIncludePtr value)
788{
789 if (ctxt->incTab == NULL) {
790 ctxt->incMax = 4;
791 ctxt->incNr = 0;
792 ctxt->incTab = (xmlRelaxNGIncludePtr *) xmlMalloc(
793 ctxt->incMax * sizeof(ctxt->incTab[0]));
794 if (ctxt->incTab == NULL) {
795 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
796 return (0);
797 }
798 }
799 if (ctxt->incNr >= ctxt->incMax) {
800 ctxt->incMax *= 2;
801 ctxt->incTab =
802 (xmlRelaxNGIncludePtr *) xmlRealloc(ctxt->incTab,
803 ctxt->incMax *
804 sizeof(ctxt->incTab[0]));
805 if (ctxt->incTab == NULL) {
806 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
807 return (0);
808 }
809 }
810 ctxt->incTab[ctxt->incNr] = value;
811 ctxt->inc = value;
812 return (ctxt->incNr++);
813}
814
815/**
816 * xmlRelaxNGIncludePop:
817 * @ctxt: the parser context
818 *
819 * Pops the top include from the include stack
820 *
821 * Returns the include just removed
822 */
823static xmlRelaxNGIncludePtr
824xmlRelaxNGIncludePop(xmlRelaxNGParserCtxtPtr ctxt)
825{
826 xmlRelaxNGIncludePtr ret;
827
828 if (ctxt->incNr <= 0)
829 return (0);
830 ctxt->incNr--;
831 if (ctxt->incNr > 0)
832 ctxt->inc = ctxt->incTab[ctxt->incNr - 1];
833 else
834 ctxt->inc = NULL;
835 ret = ctxt->incTab[ctxt->incNr];
836 ctxt->incTab[ctxt->incNr] = 0;
837 return (ret);
838}
839
840/**
841 * xmlRelaxNGLoadInclude:
842 * @ctxt: the parser context
843 * @URL: the normalized URL
844 * @node: the include node.
Daniel Veillard416589a2003-02-17 17:25:42 +0000845 * @ns: the namespace passed from the context.
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000846 *
847 * First lookup if the document is already loaded into the parser context,
848 * check against recursion. If not found the resource is loaded and
849 * the content is preprocessed before being returned back to the caller.
850 *
851 * Returns the xmlRelaxNGIncludePtr or NULL in case of error
852 */
853static xmlRelaxNGIncludePtr
854xmlRelaxNGLoadInclude(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillard416589a2003-02-17 17:25:42 +0000855 xmlNodePtr node, const xmlChar *ns) {
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000856 xmlRelaxNGIncludePtr ret = NULL;
857 xmlDocPtr doc;
858 int i;
859 xmlNodePtr root, tmp, tmp2, cur;
860
861 /*
862 * check against recursion in the stack
863 */
864 for (i = 0;i < ctxt->incNr;i++) {
865 if (xmlStrEqual(ctxt->incTab[i]->href, URL)) {
866 if (ctxt->error != NULL)
867 ctxt->error(ctxt->userData,
868 "Detected an externalRef recursion for %s\n",
869 URL);
870 ctxt->nbErrors++;
871 return(NULL);
872 }
873 }
874
875 /*
876 * Lookup in the hash table
877 */
878 if (ctxt->includes == NULL) {
879 ctxt->includes = xmlHashCreate(10);
880 if (ctxt->includes == NULL) {
881 if (ctxt->error != NULL)
882 ctxt->error(ctxt->userData,
883 "Failed to allocate hash table for document\n");
884 ctxt->nbErrors++;
885 return(NULL);
886 }
887 } else {
Daniel Veillard416589a2003-02-17 17:25:42 +0000888 if (ns == NULL)
889 ret = xmlHashLookup2(ctxt->includes, BAD_CAST "", URL);
890 else
891 ret = xmlHashLookup2(ctxt->includes, ns, URL);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000892 if (ret != NULL)
893 return(ret);
894 }
895
896
897 /*
898 * load the document
899 */
900 doc = xmlParseFile((const char *) URL);
901 if (doc == NULL) {
902 if (ctxt->error != NULL)
903 ctxt->error(ctxt->userData,
904 "xmlRelaxNG: could not load %s\n", URL);
905 ctxt->nbErrors++;
906 return (NULL);
907 }
908
909 /*
910 * Allocate the document structures and register it first.
911 */
912 ret = (xmlRelaxNGIncludePtr) xmlMalloc(sizeof(xmlRelaxNGInclude));
913 if (ret == NULL) {
914 if (ctxt->error != NULL)
915 ctxt->error(ctxt->userData,
916 "xmlRelaxNG: allocate memory for doc %s\n", URL);
917 ctxt->nbErrors++;
918 xmlFreeDoc(doc);
919 return (NULL);
920 }
921 memset(ret, 0, sizeof(xmlRelaxNGInclude));
922 ret->doc = doc;
923 ret->href = xmlStrdup(URL);
924
925 /*
Daniel Veillard416589a2003-02-17 17:25:42 +0000926 * transmit the ns if needed
927 */
928 if (ns != NULL) {
929 root = xmlDocGetRootElement(doc);
930 if (root != NULL) {
931 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
932 xmlSetProp(root, BAD_CAST"ns", ns);
933 }
934 }
935 }
936
937 /*
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000938 * push it on the stack and register it in the hash table
939 */
Daniel Veillard416589a2003-02-17 17:25:42 +0000940 if (ns == NULL)
941 xmlHashAddEntry2(ctxt->includes, BAD_CAST "", URL, ret);
942 else
943 xmlHashAddEntry2(ctxt->includes, ns, URL, ret);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000944 xmlRelaxNGIncludePush(ctxt, ret);
945
946 /*
947 * Some preprocessing of the document content, this include recursing
948 * in the include stack.
949 */
950 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
951 if (doc == NULL) {
952 /* xmlFreeDoc(ctxt->include); */
953 ctxt->inc = NULL;
954 return(NULL);
955 }
956
957 /*
958 * Pop up the include from the stack
959 */
960 xmlRelaxNGIncludePop(ctxt);
961
962 /*
963 * Check that the top element is a grammar
964 */
965 root = xmlDocGetRootElement(doc);
966 if (root == NULL) {
967 if (ctxt->error != NULL)
968 ctxt->error(ctxt->userData,
969 "xmlRelaxNG: included document is empty %s\n", URL);
970 ctxt->nbErrors++;
971 xmlFreeDoc(doc);
972 return (NULL);
973 }
974 if (!IS_RELAXNG(root, "grammar")) {
975 if (ctxt->error != NULL)
976 ctxt->error(ctxt->userData,
977 "xmlRelaxNG: included document %s root is not a grammar\n",
978 URL);
979 ctxt->nbErrors++;
980 xmlFreeDoc(doc);
981 return (NULL);
982 }
983
984 /*
985 * Elimination of redefined rules in the include.
986 */
987 cur = node->children;
988 while (cur != NULL) {
989 if (IS_RELAXNG(cur, "start")) {
990 int found = 0;
991
992 tmp = root->children;
993 while (tmp != NULL) {
994 tmp2 = tmp->next;
995 if (IS_RELAXNG(tmp, "start")) {
996 found = 1;
997 xmlUnlinkNode(tmp);
998 xmlFreeNode(tmp);
999 }
1000 tmp = tmp2;
1001 }
1002 if (!found) {
1003 if (ctxt->error != NULL)
1004 ctxt->error(ctxt->userData,
1005 "xmlRelaxNG: include %s has a start but not the included grammar\n",
1006 URL);
1007 ctxt->nbErrors++;
1008 }
1009 } else if (IS_RELAXNG(cur, "define")) {
1010 xmlChar *name, *name2;
1011
1012 name = xmlGetProp(cur, BAD_CAST "name");
1013 if (name == NULL) {
1014 if (ctxt->error != NULL)
1015 ctxt->error(ctxt->userData,
1016 "xmlRelaxNG: include %s has define without name\n",
1017 URL);
1018 ctxt->nbErrors++;
1019 } else {
1020 int found = 0;
1021
Daniel Veillardd2298792003-02-14 16:54:11 +00001022 xmlRelaxNGNormExtSpace(name);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001023 tmp = root->children;
1024 while (tmp != NULL) {
1025 tmp2 = tmp->next;
1026 if (IS_RELAXNG(tmp, "define")) {
1027 name2 = xmlGetProp(tmp, BAD_CAST "name");
Daniel Veillardd2298792003-02-14 16:54:11 +00001028 xmlRelaxNGNormExtSpace(name2);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001029 if (name2 != NULL) {
1030 if (xmlStrEqual(name, name2)) {
1031 found = 1;
1032 xmlUnlinkNode(tmp);
1033 xmlFreeNode(tmp);
1034 }
1035 xmlFree(name2);
1036 }
1037 }
1038 tmp = tmp2;
1039 }
1040 if (!found) {
1041 if (ctxt->error != NULL)
1042 ctxt->error(ctxt->userData,
1043 "xmlRelaxNG: include %s has a define %s but not the included grammar\n",
1044 URL, name);
1045 ctxt->nbErrors++;
1046 }
1047 xmlFree(name);
1048 }
1049 }
1050 cur = cur->next;
1051 }
1052
1053
1054 return(ret);
1055}
1056
1057/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001058 * xmlRelaxNGDocumentPush:
1059 * @ctxt: the parser context
1060 * @value: the element doc
1061 *
1062 * Pushes a new doc on top of the doc stack
1063 *
1064 * Returns 0 in case of error, the index in the stack otherwise
1065 */
1066static int
1067xmlRelaxNGDocumentPush(xmlRelaxNGParserCtxtPtr ctxt,
1068 xmlRelaxNGDocumentPtr value)
1069{
1070 if (ctxt->docTab == NULL) {
1071 ctxt->docMax = 4;
1072 ctxt->docNr = 0;
1073 ctxt->docTab = (xmlRelaxNGDocumentPtr *) xmlMalloc(
1074 ctxt->docMax * sizeof(ctxt->docTab[0]));
1075 if (ctxt->docTab == NULL) {
1076 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
1077 return (0);
1078 }
1079 }
1080 if (ctxt->docNr >= ctxt->docMax) {
1081 ctxt->docMax *= 2;
1082 ctxt->docTab =
1083 (xmlRelaxNGDocumentPtr *) xmlRealloc(ctxt->docTab,
1084 ctxt->docMax *
1085 sizeof(ctxt->docTab[0]));
1086 if (ctxt->docTab == NULL) {
1087 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
1088 return (0);
1089 }
1090 }
1091 ctxt->docTab[ctxt->docNr] = value;
1092 ctxt->doc = value;
1093 return (ctxt->docNr++);
1094}
1095
1096/**
1097 * xmlRelaxNGDocumentPop:
1098 * @ctxt: the parser context
1099 *
1100 * Pops the top doc from the doc stack
1101 *
1102 * Returns the doc just removed
1103 */
1104static xmlRelaxNGDocumentPtr
1105xmlRelaxNGDocumentPop(xmlRelaxNGParserCtxtPtr ctxt)
1106{
1107 xmlRelaxNGDocumentPtr ret;
1108
1109 if (ctxt->docNr <= 0)
1110 return (0);
1111 ctxt->docNr--;
1112 if (ctxt->docNr > 0)
1113 ctxt->doc = ctxt->docTab[ctxt->docNr - 1];
1114 else
1115 ctxt->doc = NULL;
1116 ret = ctxt->docTab[ctxt->docNr];
1117 ctxt->docTab[ctxt->docNr] = 0;
1118 return (ret);
1119}
1120
1121/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001122 * xmlRelaxNGLoadExternalRef:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001123 * @ctxt: the parser context
1124 * @URL: the normalized URL
1125 * @ns: the inherited ns if any
1126 *
1127 * First lookup if the document is already loaded into the parser context,
1128 * check against recursion. If not found the resource is loaded and
1129 * the content is preprocessed before being returned back to the caller.
1130 *
1131 * Returns the xmlRelaxNGDocumentPtr or NULL in case of error
1132 */
1133static xmlRelaxNGDocumentPtr
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001134xmlRelaxNGLoadExternalRef(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001135 const xmlChar *ns) {
1136 xmlRelaxNGDocumentPtr ret = NULL;
1137 xmlDocPtr doc;
1138 xmlNodePtr root;
1139 int i;
1140
1141 /*
1142 * check against recursion in the stack
1143 */
1144 for (i = 0;i < ctxt->docNr;i++) {
1145 if (xmlStrEqual(ctxt->docTab[i]->href, URL)) {
1146 if (ctxt->error != NULL)
1147 ctxt->error(ctxt->userData,
1148 "Detected an externalRef recursion for %s\n",
1149 URL);
1150 ctxt->nbErrors++;
1151 return(NULL);
1152 }
1153 }
1154
1155 /*
1156 * Lookup in the hash table
1157 */
1158 if (ctxt->documents == NULL) {
1159 ctxt->documents = xmlHashCreate(10);
1160 if (ctxt->documents == NULL) {
1161 if (ctxt->error != NULL)
1162 ctxt->error(ctxt->userData,
1163 "Failed to allocate hash table for document\n");
1164 ctxt->nbErrors++;
1165 return(NULL);
1166 }
1167 } else {
1168 if (ns == NULL)
1169 ret = xmlHashLookup2(ctxt->documents, BAD_CAST "", URL);
1170 else
1171 ret = xmlHashLookup2(ctxt->documents, ns, URL);
1172 if (ret != NULL)
1173 return(ret);
1174 }
1175
1176
1177 /*
1178 * load the document
1179 */
1180 doc = xmlParseFile((const char *) URL);
1181 if (doc == NULL) {
1182 if (ctxt->error != NULL)
1183 ctxt->error(ctxt->userData,
1184 "xmlRelaxNG: could not load %s\n", URL);
1185 ctxt->nbErrors++;
1186 return (NULL);
1187 }
1188
1189 /*
1190 * Allocate the document structures and register it first.
1191 */
1192 ret = (xmlRelaxNGDocumentPtr) xmlMalloc(sizeof(xmlRelaxNGDocument));
1193 if (ret == NULL) {
1194 if (ctxt->error != NULL)
1195 ctxt->error(ctxt->userData,
1196 "xmlRelaxNG: allocate memory for doc %s\n", URL);
1197 ctxt->nbErrors++;
1198 xmlFreeDoc(doc);
1199 return (NULL);
1200 }
1201 memset(ret, 0, sizeof(xmlRelaxNGDocument));
1202 ret->doc = doc;
1203 ret->href = xmlStrdup(URL);
1204
1205 /*
1206 * transmit the ns if needed
1207 */
1208 if (ns != NULL) {
1209 root = xmlDocGetRootElement(doc);
1210 if (root != NULL) {
1211 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
1212 xmlSetProp(root, BAD_CAST"ns", ns);
1213 }
1214 }
1215 }
1216
1217 /*
1218 * push it on the stack and register it in the hash table
1219 */
1220 if (ns == NULL)
1221 xmlHashAddEntry2(ctxt->documents, BAD_CAST "", URL, ret);
1222 else
1223 xmlHashAddEntry2(ctxt->documents, ns, URL, ret);
1224 xmlRelaxNGDocumentPush(ctxt, ret);
1225
1226 /*
1227 * Some preprocessing of the document content
1228 */
1229 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
1230 if (doc == NULL) {
1231 xmlFreeDoc(ctxt->document);
1232 ctxt->doc = NULL;
1233 return(NULL);
1234 }
1235
1236 xmlRelaxNGDocumentPop(ctxt);
1237
1238 return(ret);
1239}
1240
1241/************************************************************************
1242 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +00001243 * Error functions *
1244 * *
1245 ************************************************************************/
1246
1247#define VALID_CTXT() \
Daniel Veillard231d7912003-02-09 14:22:17 +00001248 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1249 xmlGenericError(xmlGenericErrorContext, \
Daniel Veillard6eadf632003-01-23 18:29:16 +00001250 "error detected at %s:%d\n", \
1251 __FILE__, __LINE__);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001252
1253#define VALID_ERROR(a) \
Daniel Veillard231d7912003-02-09 14:22:17 +00001254 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001255 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a)
1256#define VALID_ERROR2(a, b) \
1257 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1258 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a, b)
1259#define VALID_ERROR3(a, b, c) \
1260 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1261 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a, b, c)
Daniel Veillard6eadf632003-01-23 18:29:16 +00001262
Daniel Veillardd2298792003-02-14 16:54:11 +00001263#ifdef DEBUG
Daniel Veillard231d7912003-02-09 14:22:17 +00001264static const char *
1265xmlRelaxNGDefName(xmlRelaxNGDefinePtr def) {
1266 if (def == NULL)
1267 return("none");
1268 switch(def->type) {
1269 case XML_RELAXNG_EMPTY: return("empty");
1270 case XML_RELAXNG_NOT_ALLOWED: return("notAllowed");
1271 case XML_RELAXNG_EXCEPT: return("except");
1272 case XML_RELAXNG_TEXT: return("text");
1273 case XML_RELAXNG_ELEMENT: return("element");
1274 case XML_RELAXNG_DATATYPE: return("datatype");
1275 case XML_RELAXNG_VALUE: return("value");
1276 case XML_RELAXNG_LIST: return("list");
1277 case XML_RELAXNG_ATTRIBUTE: return("attribute");
1278 case XML_RELAXNG_DEF: return("def");
1279 case XML_RELAXNG_REF: return("ref");
1280 case XML_RELAXNG_EXTERNALREF: return("externalRef");
1281 case XML_RELAXNG_PARENTREF: return("parentRef");
1282 case XML_RELAXNG_OPTIONAL: return("optional");
1283 case XML_RELAXNG_ZEROORMORE: return("zeroOrMore");
1284 case XML_RELAXNG_ONEORMORE: return("oneOrMore");
1285 case XML_RELAXNG_CHOICE: return("choice");
1286 case XML_RELAXNG_GROUP: return("group");
1287 case XML_RELAXNG_INTERLEAVE: return("interleave");
1288 case XML_RELAXNG_START: return("start");
1289 }
1290 return("unknown");
1291}
Daniel Veillardd2298792003-02-14 16:54:11 +00001292#endif
1293
Daniel Veillard6eadf632003-01-23 18:29:16 +00001294#if 0
1295/**
1296 * xmlRelaxNGErrorContext:
1297 * @ctxt: the parsing context
1298 * @schema: the schema being built
1299 * @node: the node being processed
1300 * @child: the child being processed
1301 *
1302 * Dump a RelaxNGType structure
1303 */
1304static void
1305xmlRelaxNGErrorContext(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGPtr schema,
1306 xmlNodePtr node, xmlNodePtr child)
1307{
1308 int line = 0;
1309 const xmlChar *file = NULL;
1310 const xmlChar *name = NULL;
1311 const char *type = "error";
1312
1313 if ((ctxt == NULL) || (ctxt->error == NULL))
1314 return;
1315
1316 if (child != NULL)
1317 node = child;
1318
1319 if (node != NULL) {
1320 if ((node->type == XML_DOCUMENT_NODE) ||
1321 (node->type == XML_HTML_DOCUMENT_NODE)) {
1322 xmlDocPtr doc = (xmlDocPtr) node;
1323
1324 file = doc->URL;
1325 } else {
1326 /*
1327 * Try to find contextual informations to report
1328 */
1329 if (node->type == XML_ELEMENT_NODE) {
1330 line = (int) node->content;
1331 } else if ((node->prev != NULL) &&
1332 (node->prev->type == XML_ELEMENT_NODE)) {
1333 line = (int) node->prev->content;
1334 } else if ((node->parent != NULL) &&
1335 (node->parent->type == XML_ELEMENT_NODE)) {
1336 line = (int) node->parent->content;
1337 }
1338 if ((node->doc != NULL) && (node->doc->URL != NULL))
1339 file = node->doc->URL;
1340 if (node->name != NULL)
1341 name = node->name;
1342 }
1343 }
1344
1345 if (ctxt != NULL)
1346 type = "compilation error";
1347 else if (schema != NULL)
1348 type = "runtime error";
1349
1350 if ((file != NULL) && (line != 0) && (name != NULL))
1351 ctxt->error(ctxt->userData, "%s: file %s line %d element %s\n",
1352 type, file, line, name);
1353 else if ((file != NULL) && (name != NULL))
1354 ctxt->error(ctxt->userData, "%s: file %s element %s\n",
1355 type, file, name);
1356 else if ((file != NULL) && (line != 0))
1357 ctxt->error(ctxt->userData, "%s: file %s line %d\n", type, file, line);
1358 else if (file != NULL)
1359 ctxt->error(ctxt->userData, "%s: file %s\n", type, file);
1360 else if (name != NULL)
1361 ctxt->error(ctxt->userData, "%s: element %s\n", type, name);
1362 else
1363 ctxt->error(ctxt->userData, "%s\n", type);
1364}
1365#endif
1366
1367/************************************************************************
1368 * *
1369 * Type library hooks *
1370 * *
1371 ************************************************************************/
Daniel Veillardea3f3982003-01-26 19:45:18 +00001372static xmlChar *xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt,
1373 const xmlChar *str);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001374
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001375/**
1376 * xmlRelaxNGSchemaTypeHave:
1377 * @data: data needed for the library
1378 * @type: the type name
1379 *
1380 * Check if the given type is provided by
1381 * the W3C XMLSchema Datatype library.
1382 *
1383 * Returns 1 if yes, 0 if no and -1 in case of error.
1384 */
1385static int
1386xmlRelaxNGSchemaTypeHave(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001387 const xmlChar *type) {
1388 xmlSchemaTypePtr typ;
1389
1390 if (type == NULL)
1391 return(-1);
1392 typ = xmlSchemaGetPredefinedType(type,
1393 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1394 if (typ == NULL)
1395 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001396 return(1);
1397}
1398
1399/**
1400 * xmlRelaxNGSchemaTypeCheck:
1401 * @data: data needed for the library
1402 * @type: the type name
1403 * @value: the value to check
1404 *
1405 * Check if the given type and value are validated by
1406 * the W3C XMLSchema Datatype library.
1407 *
1408 * Returns 1 if yes, 0 if no and -1 in case of error.
1409 */
1410static int
1411xmlRelaxNGSchemaTypeCheck(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001412 const xmlChar *type,
1413 const xmlChar *value) {
1414 xmlSchemaTypePtr typ;
1415 int ret;
1416
1417 /*
1418 * TODO: the type should be cached ab provided back, interface subject
1419 * to changes.
1420 * TODO: handle facets, may require an additional interface and keep
1421 * the value returned from the validation.
1422 */
1423 if ((type == NULL) || (value == NULL))
1424 return(-1);
1425 typ = xmlSchemaGetPredefinedType(type,
1426 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1427 if (typ == NULL)
1428 return(-1);
1429 ret = xmlSchemaValidatePredefinedType(typ, value, NULL);
1430 if (ret == 0)
1431 return(1);
1432 if (ret > 0)
1433 return(0);
1434 return(-1);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001435}
1436
1437/**
1438 * xmlRelaxNGSchemaTypeCompare:
1439 * @data: data needed for the library
1440 * @type: the type name
1441 * @value1: the first value
1442 * @value2: the second value
1443 *
1444 * Compare two values accordingly a type from the W3C XMLSchema
1445 * Datatype library.
1446 *
1447 * Returns 1 if yes, 0 if no and -1 in case of error.
1448 */
1449static int
1450xmlRelaxNGSchemaTypeCompare(void *data ATTRIBUTE_UNUSED,
1451 const xmlChar *type ATTRIBUTE_UNUSED,
1452 const xmlChar *value1 ATTRIBUTE_UNUSED,
1453 const xmlChar *value2 ATTRIBUTE_UNUSED) {
1454 TODO
1455 return(1);
1456}
1457
1458/**
1459 * xmlRelaxNGDefaultTypeHave:
1460 * @data: data needed for the library
1461 * @type: the type name
1462 *
1463 * Check if the given type is provided by
1464 * the default datatype library.
1465 *
1466 * Returns 1 if yes, 0 if no and -1 in case of error.
1467 */
1468static int
1469xmlRelaxNGDefaultTypeHave(void *data ATTRIBUTE_UNUSED, const xmlChar *type) {
1470 if (type == NULL)
1471 return(-1);
1472 if (xmlStrEqual(type, BAD_CAST "string"))
1473 return(1);
1474 if (xmlStrEqual(type, BAD_CAST "token"))
1475 return(1);
1476 return(0);
1477}
1478
1479/**
1480 * xmlRelaxNGDefaultTypeCheck:
1481 * @data: data needed for the library
1482 * @type: the type name
1483 * @value: the value to check
1484 *
1485 * Check if the given type and value are validated by
1486 * the default datatype library.
1487 *
1488 * Returns 1 if yes, 0 if no and -1 in case of error.
1489 */
1490static int
1491xmlRelaxNGDefaultTypeCheck(void *data ATTRIBUTE_UNUSED,
1492 const xmlChar *type ATTRIBUTE_UNUSED,
1493 const xmlChar *value ATTRIBUTE_UNUSED) {
Daniel Veillardd4310742003-02-18 21:12:46 +00001494 if (value == NULL)
1495 return(-1);
1496 if (xmlStrEqual(type, BAD_CAST "string"))
1497 return(1);
1498 if (xmlStrEqual(type, BAD_CAST "token")) {
1499#if 0
1500 const xmlChar *cur = value;
1501
1502 while (*cur != 0) {
1503 if (!IS_BLANK(*cur))
1504 return(1);
1505 cur++;
1506 }
1507#endif
1508 return(1);
1509 }
1510
1511 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001512}
1513
1514/**
1515 * xmlRelaxNGDefaultTypeCompare:
1516 * @data: data needed for the library
1517 * @type: the type name
1518 * @value1: the first value
1519 * @value2: the second value
1520 *
1521 * Compare two values accordingly a type from the default
1522 * datatype library.
1523 *
1524 * Returns 1 if yes, 0 if no and -1 in case of error.
1525 */
1526static int
1527xmlRelaxNGDefaultTypeCompare(void *data ATTRIBUTE_UNUSED,
1528 const xmlChar *type ATTRIBUTE_UNUSED,
1529 const xmlChar *value1 ATTRIBUTE_UNUSED,
1530 const xmlChar *value2 ATTRIBUTE_UNUSED) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00001531 int ret = -1;
1532
1533 if (xmlStrEqual(type, BAD_CAST "string")) {
1534 ret = xmlStrEqual(value1, value2);
1535 } else if (xmlStrEqual(type, BAD_CAST "token")) {
1536 if (!xmlStrEqual(value1, value2)) {
1537 xmlChar *nval, *nvalue;
1538
1539 /*
1540 * TODO: trivial optimizations are possible by
1541 * computing at compile-time
1542 */
1543 nval = xmlRelaxNGNormalize(NULL, value1);
1544 nvalue = xmlRelaxNGNormalize(NULL, value2);
1545
Daniel Veillardd4310742003-02-18 21:12:46 +00001546 if ((nval == NULL) || (nvalue == NULL))
Daniel Veillardea3f3982003-01-26 19:45:18 +00001547 ret = -1;
Daniel Veillardd4310742003-02-18 21:12:46 +00001548 else if (xmlStrEqual(nval, nvalue))
1549 ret = 1;
1550 else
1551 ret = 0;
Daniel Veillardea3f3982003-01-26 19:45:18 +00001552 if (nval != NULL)
1553 xmlFree(nval);
1554 if (nvalue != NULL)
1555 xmlFree(nvalue);
Daniel Veillardd4310742003-02-18 21:12:46 +00001556 } else
1557 ret = 1;
Daniel Veillardea3f3982003-01-26 19:45:18 +00001558 }
1559 return(ret);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001560}
1561
1562static int xmlRelaxNGTypeInitialized = 0;
1563static xmlHashTablePtr xmlRelaxNGRegisteredTypes = NULL;
1564
1565/**
1566 * xmlRelaxNGFreeTypeLibrary:
1567 * @lib: the type library structure
1568 * @namespace: the URI bound to the library
1569 *
1570 * Free the structure associated to the type library
1571 */
Daniel Veillard6eadf632003-01-23 18:29:16 +00001572static void
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001573xmlRelaxNGFreeTypeLibrary(xmlRelaxNGTypeLibraryPtr lib,
1574 const xmlChar *namespace ATTRIBUTE_UNUSED) {
1575 if (lib == NULL)
1576 return;
1577 if (lib->namespace != NULL)
1578 xmlFree((xmlChar *)lib->namespace);
1579 xmlFree(lib);
1580}
1581
1582/**
1583 * xmlRelaxNGRegisterTypeLibrary:
1584 * @namespace: the URI bound to the library
1585 * @data: data associated to the library
1586 * @have: the provide function
1587 * @check: the checking function
1588 * @comp: the comparison function
1589 *
1590 * Register a new type library
1591 *
1592 * Returns 0 in case of success and -1 in case of error.
1593 */
1594static int
1595xmlRelaxNGRegisterTypeLibrary(const xmlChar *namespace, void *data,
1596 xmlRelaxNGTypeHave have, xmlRelaxNGTypeCheck check,
1597 xmlRelaxNGTypeCompare comp) {
1598 xmlRelaxNGTypeLibraryPtr lib;
1599 int ret;
1600
1601 if ((xmlRelaxNGRegisteredTypes == NULL) || (namespace == NULL) ||
1602 (check == NULL) || (comp == NULL))
1603 return(-1);
1604 if (xmlHashLookup(xmlRelaxNGRegisteredTypes, namespace) != NULL) {
1605 xmlGenericError(xmlGenericErrorContext,
1606 "Relax-NG types library '%s' already registered\n",
1607 namespace);
1608 return(-1);
1609 }
1610 lib = (xmlRelaxNGTypeLibraryPtr) xmlMalloc(sizeof(xmlRelaxNGTypeLibrary));
1611 if (lib == NULL) {
1612 xmlGenericError(xmlGenericErrorContext,
1613 "Relax-NG types library '%s' malloc() failed\n",
1614 namespace);
1615 return (-1);
1616 }
1617 memset(lib, 0, sizeof(xmlRelaxNGTypeLibrary));
1618 lib->namespace = xmlStrdup(namespace);
1619 lib->data = data;
1620 lib->have = have;
1621 lib->comp = comp;
1622 lib->check = check;
1623 ret = xmlHashAddEntry(xmlRelaxNGRegisteredTypes, namespace, lib);
1624 if (ret < 0) {
1625 xmlGenericError(xmlGenericErrorContext,
1626 "Relax-NG types library failed to register '%s'\n",
1627 namespace);
1628 xmlRelaxNGFreeTypeLibrary(lib, namespace);
1629 return(-1);
1630 }
1631 return(0);
1632}
1633
1634/**
1635 * xmlRelaxNGInitTypes:
1636 *
1637 * Initilize the default type libraries.
1638 *
1639 * Returns 0 in case of success and -1 in case of error.
1640 */
1641static int
Daniel Veillard6eadf632003-01-23 18:29:16 +00001642xmlRelaxNGInitTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001643 if (xmlRelaxNGTypeInitialized != 0)
1644 return(0);
1645 xmlRelaxNGRegisteredTypes = xmlHashCreate(10);
1646 if (xmlRelaxNGRegisteredTypes == NULL) {
1647 xmlGenericError(xmlGenericErrorContext,
1648 "Failed to allocate sh table for Relax-NG types\n");
1649 return(-1);
1650 }
1651 xmlRelaxNGRegisterTypeLibrary(
1652 BAD_CAST "http://www.w3.org/2001/XMLSchema-datatypes",
1653 NULL,
1654 xmlRelaxNGSchemaTypeHave,
1655 xmlRelaxNGSchemaTypeCheck,
1656 xmlRelaxNGSchemaTypeCompare);
1657 xmlRelaxNGRegisterTypeLibrary(
1658 xmlRelaxNGNs,
1659 NULL,
1660 xmlRelaxNGDefaultTypeHave,
1661 xmlRelaxNGDefaultTypeCheck,
1662 xmlRelaxNGDefaultTypeCompare);
1663 xmlRelaxNGTypeInitialized = 1;
1664 return(0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001665}
1666
1667/**
1668 * xmlRelaxNGCleanupTypes:
1669 *
1670 * Cleanup the default Schemas type library associated to RelaxNG
1671 */
1672void
1673xmlRelaxNGCleanupTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001674 if (xmlRelaxNGTypeInitialized == 0)
1675 return;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001676 xmlSchemaCleanupTypes();
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001677 xmlHashFree(xmlRelaxNGRegisteredTypes, (xmlHashDeallocator)
1678 xmlRelaxNGFreeTypeLibrary);
1679 xmlRelaxNGTypeInitialized = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001680}
1681
1682/************************************************************************
1683 * *
1684 * Parsing functions *
1685 * *
1686 ************************************************************************/
1687
1688static xmlRelaxNGDefinePtr xmlRelaxNGParseAttribute(
1689 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1690static xmlRelaxNGDefinePtr xmlRelaxNGParseElement(
1691 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1692static xmlRelaxNGDefinePtr xmlRelaxNGParsePatterns(
Daniel Veillard154877e2003-01-30 12:17:05 +00001693 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes, int group);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001694static xmlRelaxNGDefinePtr xmlRelaxNGParsePattern(
1695 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001696static xmlRelaxNGPtr xmlRelaxNGParseDocument(
1697 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00001698static int xmlRelaxNGParseGrammarContent(
1699 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00001700static xmlRelaxNGDefinePtr xmlRelaxNGParseNameClass(
1701 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
1702 xmlRelaxNGDefinePtr def);
Daniel Veillard419a7682003-02-03 23:22:49 +00001703static xmlRelaxNGGrammarPtr xmlRelaxNGParseGrammar(
1704 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001705
1706
1707#define IS_BLANK_NODE(n) \
1708 (((n)->type == XML_TEXT_NODE) && (xmlRelaxNGIsBlank((n)->content)))
1709
1710/**
1711 * xmlRelaxNGIsBlank:
1712 * @str: a string
1713 *
1714 * Check if a string is ignorable c.f. 4.2. Whitespace
1715 *
1716 * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
1717 */
1718static int
1719xmlRelaxNGIsBlank(xmlChar *str) {
1720 if (str == NULL)
1721 return(1);
1722 while (*str != 0) {
1723 if (!(IS_BLANK(*str))) return(0);
1724 str++;
1725 }
1726 return(1);
1727}
1728
Daniel Veillard6eadf632003-01-23 18:29:16 +00001729/**
1730 * xmlRelaxNGGetDataTypeLibrary:
1731 * @ctxt: a Relax-NG parser context
1732 * @node: the current data or value element
1733 *
1734 * Applies algorithm from 4.3. datatypeLibrary attribute
1735 *
1736 * Returns the datatypeLibary value or NULL if not found
1737 */
1738static xmlChar *
1739xmlRelaxNGGetDataTypeLibrary(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1740 xmlNodePtr node) {
1741 xmlChar *ret, *escape;
1742
Daniel Veillard6eadf632003-01-23 18:29:16 +00001743 if ((IS_RELAXNG(node, "data")) || (IS_RELAXNG(node, "value"))) {
1744 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1745 if (ret != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001746 if (ret[0] == 0) {
1747 xmlFree(ret);
1748 return(NULL);
1749 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001750 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +00001751 if (escape == NULL) {
1752 return(ret);
1753 }
1754 xmlFree(ret);
1755 return(escape);
1756 }
1757 }
1758 node = node->parent;
1759 while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) {
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001760 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1761 if (ret != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001762 if (ret[0] == 0) {
1763 xmlFree(ret);
1764 return(NULL);
1765 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001766 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
1767 if (escape == NULL) {
1768 return(ret);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001769 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001770 xmlFree(ret);
1771 return(escape);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001772 }
1773 node = node->parent;
1774 }
1775 return(NULL);
1776}
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001777
1778/**
Daniel Veillardedc91922003-01-26 00:52:04 +00001779 * xmlRelaxNGParseValue:
1780 * @ctxt: a Relax-NG parser context
1781 * @node: the data node.
1782 *
1783 * parse the content of a RelaxNG value node.
1784 *
1785 * Returns the definition pointer or NULL in case of error
1786 */
1787static xmlRelaxNGDefinePtr
1788xmlRelaxNGParseValue(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1789 xmlRelaxNGDefinePtr def = NULL;
1790 xmlRelaxNGTypeLibraryPtr lib;
1791 xmlChar *type;
1792 xmlChar *library;
1793 int tmp;
1794
1795 def = xmlRelaxNGNewDefine(ctxt, node);
1796 if (def == NULL)
1797 return(NULL);
1798 def->type = XML_RELAXNG_VALUE;
1799
1800 type = xmlGetProp(node, BAD_CAST "type");
1801 if (type != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001802 xmlRelaxNGNormExtSpace(type);
1803 if (xmlValidateNCName(type, 0)) {
1804 if (ctxt->error != NULL)
1805 ctxt->error(ctxt->userData,
1806 "value type '%s' is not an NCName\n",
1807 type);
1808 ctxt->nbErrors++;
1809 }
Daniel Veillardedc91922003-01-26 00:52:04 +00001810 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1811 if (library == NULL)
1812 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1813
1814 def->name = type;
1815 def->ns = library;
1816
1817 lib = (xmlRelaxNGTypeLibraryPtr)
1818 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1819 if (lib == NULL) {
1820 if (ctxt->error != NULL)
1821 ctxt->error(ctxt->userData,
1822 "Use of unregistered type library '%s'\n",
1823 library);
1824 ctxt->nbErrors++;
1825 def->data = NULL;
1826 } else {
1827 def->data = lib;
1828 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001829 if (ctxt->error != NULL)
1830 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00001831 "Internal error with type library '%s': no 'have'\n",
1832 library);
1833 ctxt->nbErrors++;
1834 } else {
1835 tmp = lib->have(lib->data, def->name);
1836 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001837 if (ctxt->error != NULL)
1838 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00001839 "Error type '%s' is not exported by type library '%s'\n",
1840 def->name, library);
1841 ctxt->nbErrors++;
1842 }
1843 }
1844 }
1845 }
1846 if (node->children == NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00001847 def->value = xmlStrdup(BAD_CAST "");
Daniel Veillardedc91922003-01-26 00:52:04 +00001848 } else if ((node->children->type != XML_TEXT_NODE) ||
1849 (node->children->next != NULL)) {
1850 if (ctxt->error != NULL)
1851 ctxt->error(ctxt->userData,
1852 "Expecting a single text value for <value>content\n");
1853 ctxt->nbErrors++;
1854 } else {
1855 def->value = xmlNodeGetContent(node);
1856 if (def->value == NULL) {
1857 if (ctxt->error != NULL)
1858 ctxt->error(ctxt->userData,
1859 "Element <value> has no content\n");
1860 ctxt->nbErrors++;
1861 }
1862 }
1863 /* TODO check ahead of time that the value is okay per the type */
1864 return(def);
1865}
1866
1867/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001868 * xmlRelaxNGParseData:
1869 * @ctxt: a Relax-NG parser context
1870 * @node: the data node.
1871 *
1872 * parse the content of a RelaxNG data node.
1873 *
1874 * Returns the definition pointer or NULL in case of error
1875 */
1876static xmlRelaxNGDefinePtr
1877xmlRelaxNGParseData(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
Daniel Veillard416589a2003-02-17 17:25:42 +00001878 xmlRelaxNGDefinePtr def = NULL, except, last = NULL;
Daniel Veillard8fe98712003-02-19 00:19:14 +00001879 xmlRelaxNGDefinePtr param, lastparam = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001880 xmlRelaxNGTypeLibraryPtr lib;
1881 xmlChar *type;
1882 xmlChar *library;
1883 xmlNodePtr content;
1884 int tmp;
1885
1886 type = xmlGetProp(node, BAD_CAST "type");
1887 if (type == NULL) {
1888 if (ctxt->error != NULL)
1889 ctxt->error(ctxt->userData,
1890 "data has no type\n");
1891 ctxt->nbErrors++;
1892 return(NULL);
1893 }
Daniel Veillardd2298792003-02-14 16:54:11 +00001894 xmlRelaxNGNormExtSpace(type);
1895 if (xmlValidateNCName(type, 0)) {
1896 if (ctxt->error != NULL)
1897 ctxt->error(ctxt->userData,
1898 "data type '%s' is not an NCName\n",
1899 type);
1900 ctxt->nbErrors++;
1901 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001902 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1903 if (library == NULL)
1904 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1905
1906 def = xmlRelaxNGNewDefine(ctxt, node);
1907 if (def == NULL) {
1908 xmlFree(type);
1909 return(NULL);
1910 }
1911 def->type = XML_RELAXNG_DATATYPE;
1912 def->name = type;
1913 def->ns = library;
1914
1915 lib = (xmlRelaxNGTypeLibraryPtr)
1916 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1917 if (lib == NULL) {
1918 if (ctxt->error != NULL)
1919 ctxt->error(ctxt->userData,
1920 "Use of unregistered type library '%s'\n",
1921 library);
1922 ctxt->nbErrors++;
1923 def->data = NULL;
1924 } else {
1925 def->data = lib;
1926 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001927 if (ctxt->error != NULL)
1928 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001929 "Internal error with type library '%s': no 'have'\n",
1930 library);
1931 ctxt->nbErrors++;
1932 } else {
1933 tmp = lib->have(lib->data, def->name);
1934 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001935 if (ctxt->error != NULL)
1936 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001937 "Error type '%s' is not exported by type library '%s'\n",
1938 def->name, library);
1939 ctxt->nbErrors++;
1940 }
1941 }
1942 }
1943 content = node->children;
Daniel Veillard416589a2003-02-17 17:25:42 +00001944
1945 /*
1946 * Handle optional params
1947 */
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001948 while (content != NULL) {
Daniel Veillard416589a2003-02-17 17:25:42 +00001949 if (!xmlStrEqual(content->name, BAD_CAST "param"))
1950 break;
Daniel Veillard8fe98712003-02-19 00:19:14 +00001951 param = xmlRelaxNGNewDefine(ctxt, node);
1952 if (param != NULL) {
1953 param->type = XML_RELAXNG_PARAM;
1954 param->name = xmlGetProp(content, BAD_CAST "name");
1955 if (param->name == NULL) {
1956 if (ctxt->error != NULL)
1957 ctxt->error(ctxt->userData,
1958 "param has no name\n");
1959 ctxt->nbErrors++;
1960 }
1961 param->value = xmlNodeGetContent(content);
1962 if (lastparam == NULL) {
1963 def->attrs = lastparam = param;
1964 } else {
1965 lastparam->next = param;
1966 lastparam = param;
1967 }
1968 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001969 content = content->next;
1970 }
Daniel Veillard416589a2003-02-17 17:25:42 +00001971 /*
1972 * Handle optional except
1973 */
1974 if ((content != NULL) && (xmlStrEqual(content->name, BAD_CAST "except"))) {
1975 xmlNodePtr child;
1976 xmlRelaxNGDefinePtr tmp2, last2 = NULL;
1977
1978 except = xmlRelaxNGNewDefine(ctxt, node);
1979 if (except == NULL) {
1980 return(def);
1981 }
1982 except->type = XML_RELAXNG_EXCEPT;
1983 child = content->children;
1984 if (last == NULL) {
1985 def->content = except;
1986 } else {
1987 last->next = except;
1988 }
1989 if (child == NULL) {
1990 if (ctxt->error != NULL)
1991 ctxt->error(ctxt->userData,
1992 "except has no content\n");
1993 ctxt->nbErrors++;
1994 }
1995 while (child != NULL) {
1996 tmp2 = xmlRelaxNGParsePattern(ctxt, child);
1997 if (tmp2 != NULL) {
1998 if (last2 == NULL) {
1999 except->content = last2 = tmp2;
2000 } else {
2001 last2->next = tmp2;
2002 last2 = tmp2;
2003 }
2004 }
2005 child = child->next;
2006 }
2007 content = content->next;
2008 }
2009 /*
2010 * Check there is no unhandled data
2011 */
2012 if (content != NULL) {
2013 if (ctxt->error != NULL)
2014 ctxt->error(ctxt->userData,
2015 "Element data has unexpected content %s\n", content->name);
2016 ctxt->nbErrors++;
2017 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002018
2019 return(def);
2020}
2021
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002022/**
2023 * xmlRelaxNGCompareElemDefLists:
2024 * @ctxt: a Relax-NG parser context
2025 * @defs1: the first list of element defs
2026 * @defs2: the second list of element defs
2027 *
2028 * Compare the 2 lists of element definitions. The comparison is
2029 * that if both lists do not accept the same QNames, it returns 1
2030 * If the 2 lists can accept the same QName the comparison returns 0
2031 *
2032 * Returns 1 disttinct, 0 if equal
2033 */
2034static int
2035xmlRelaxNGCompareElemDefLists(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
2036 xmlRelaxNGDefinePtr *def1,
2037 xmlRelaxNGDefinePtr *def2) {
2038 xmlRelaxNGDefinePtr *basedef2 = def2;
2039
Daniel Veillard154877e2003-01-30 12:17:05 +00002040 if ((def1 == NULL) || (def2 == NULL))
2041 return(1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002042 if ((*def1 == NULL) || (*def2 == NULL))
2043 return(1);
2044 while (*def1 != NULL) {
2045 while ((*def2) != NULL) {
2046 if ((*def1)->name == NULL) {
2047 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
2048 return(0);
2049 } else if ((*def2)->name == NULL) {
2050 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
2051 return(0);
2052 } else if (xmlStrEqual((*def1)->name, (*def2)->name)) {
2053 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
2054 return(0);
2055 }
2056 def2++;
2057 }
2058 def2 = basedef2;
2059 def1++;
2060 }
2061 return(1);
2062}
2063
2064/**
2065 * xmlRelaxNGGetElements:
2066 * @ctxt: a Relax-NG parser context
2067 * @def: the interleave definition
2068 *
2069 * Compute the list of top elements a definition can generate
2070 *
2071 * Returns a list of elements or NULL if none was found.
2072 */
2073static xmlRelaxNGDefinePtr *
2074xmlRelaxNGGetElements(xmlRelaxNGParserCtxtPtr ctxt,
2075 xmlRelaxNGDefinePtr def) {
2076 xmlRelaxNGDefinePtr *ret = NULL, parent, cur, tmp;
2077 int len = 0;
2078 int max = 0;
2079
2080 parent = NULL;
2081 cur = def;
2082 while (cur != NULL) {
Daniel Veillardb08c9812003-01-28 23:09:49 +00002083 if ((cur->type == XML_RELAXNG_ELEMENT) ||
2084 (cur->type == XML_RELAXNG_TEXT)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002085 if (ret == NULL) {
2086 max = 10;
2087 ret = (xmlRelaxNGDefinePtr *)
2088 xmlMalloc((max + 1) * sizeof(xmlRelaxNGDefinePtr));
2089 if (ret == NULL) {
2090 if (ctxt->error != NULL)
2091 ctxt->error(ctxt->userData,
2092 "Out of memory in element search\n");
2093 ctxt->nbErrors++;
2094 return(NULL);
2095 }
2096 } else if (max <= len) {
2097 max *= 2;
2098 ret = xmlRealloc(ret, (max + 1) * sizeof(xmlRelaxNGDefinePtr));
2099 if (ret == NULL) {
2100 if (ctxt->error != NULL)
2101 ctxt->error(ctxt->userData,
2102 "Out of memory in element search\n");
2103 ctxt->nbErrors++;
2104 return(NULL);
2105 }
2106 }
Daniel Veillardb08c9812003-01-28 23:09:49 +00002107 ret[len++] = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002108 ret[len] = NULL;
2109 } else if ((cur->type == XML_RELAXNG_CHOICE) ||
2110 (cur->type == XML_RELAXNG_INTERLEAVE) ||
2111 (cur->type == XML_RELAXNG_GROUP) ||
2112 (cur->type == XML_RELAXNG_ONEORMORE) ||
Daniel Veillardb08c9812003-01-28 23:09:49 +00002113 (cur->type == XML_RELAXNG_ZEROORMORE) ||
2114 (cur->type == XML_RELAXNG_OPTIONAL) ||
2115 (cur->type == XML_RELAXNG_REF) ||
2116 (cur->type == XML_RELAXNG_DEF)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002117 /*
2118 * Don't go within elements or attributes or string values.
2119 * Just gather the element top list
2120 */
2121 if (cur->content != NULL) {
2122 parent = cur;
2123 cur = cur->content;
2124 tmp = cur;
2125 while (tmp != NULL) {
2126 tmp->parent = parent;
2127 tmp = tmp->next;
2128 }
2129 continue;
2130 }
2131 }
Daniel Veillard154877e2003-01-30 12:17:05 +00002132 if (cur == def)
2133 return(ret);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002134 if (cur->next != NULL) {
2135 cur = cur->next;
2136 continue;
2137 }
2138 do {
2139 cur = cur->parent;
2140 if (cur == NULL) break;
2141 if (cur == def) return(ret);
2142 if (cur->next != NULL) {
2143 cur = cur->next;
2144 break;
2145 }
2146 } while (cur != NULL);
2147 }
2148 return(ret);
2149}
2150
2151/**
2152 * xmlRelaxNGComputeInterleaves:
2153 * @def: the interleave definition
2154 * @ctxt: a Relax-NG parser context
2155 * @node: the data node.
2156 *
2157 * A lot of work for preprocessing interleave definitions
2158 * is potentially needed to get a decent execution speed at runtime
2159 * - trying to get a total order on the element nodes generated
2160 * by the interleaves, order the list of interleave definitions
2161 * following that order.
2162 * - if <text/> is used to handle mixed content, it is better to
2163 * flag this in the define and simplify the runtime checking
2164 * algorithm
2165 */
2166static void
2167xmlRelaxNGComputeInterleaves(xmlRelaxNGDefinePtr def,
2168 xmlRelaxNGParserCtxtPtr ctxt,
2169 xmlChar *name ATTRIBUTE_UNUSED) {
2170 xmlRelaxNGDefinePtr cur;
2171
2172 xmlRelaxNGDefinePtr *list = NULL;
2173 xmlRelaxNGPartitionPtr partitions = NULL;
2174 xmlRelaxNGInterleaveGroupPtr *groups = NULL;
2175 xmlRelaxNGInterleaveGroupPtr group;
2176 int i,j,ret;
2177 int nbgroups = 0;
2178 int nbchild = 0;
2179
2180#ifdef DEBUG_INTERLEAVE
2181 xmlGenericError(xmlGenericErrorContext,
2182 "xmlRelaxNGComputeInterleaves(%s)\n",
2183 name);
2184#endif
2185 cur = def->content;
2186 while (cur != NULL) {
2187 nbchild++;
2188 cur = cur->next;
2189 }
2190
2191#ifdef DEBUG_INTERLEAVE
2192 xmlGenericError(xmlGenericErrorContext, " %d child\n", nbchild);
2193#endif
2194 groups = (xmlRelaxNGInterleaveGroupPtr *)
2195 xmlMalloc(nbchild * sizeof(xmlRelaxNGInterleaveGroupPtr));
2196 if (groups == NULL)
2197 goto error;
2198 cur = def->content;
2199 while (cur != NULL) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002200 groups[nbgroups] = (xmlRelaxNGInterleaveGroupPtr)
2201 xmlMalloc(sizeof(xmlRelaxNGInterleaveGroup));
2202 if (groups[nbgroups] == NULL)
2203 goto error;
2204 groups[nbgroups]->rule = cur;
2205 groups[nbgroups]->defs = xmlRelaxNGGetElements(ctxt, cur);
2206 nbgroups++;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002207 cur = cur->next;
2208 }
2209 list = NULL;
2210#ifdef DEBUG_INTERLEAVE
2211 xmlGenericError(xmlGenericErrorContext, " %d groups\n", nbgroups);
2212#endif
2213
2214 /*
2215 * Let's check that all rules makes a partitions according to 7.4
2216 */
2217 partitions = (xmlRelaxNGPartitionPtr)
2218 xmlMalloc(sizeof(xmlRelaxNGPartition));
2219 if (partitions == NULL)
2220 goto error;
2221 partitions->nbgroups = nbgroups;
2222 for (i = 0;i < nbgroups;i++) {
2223 group = groups[i];
2224 for (j = i+1;j < nbgroups;j++) {
2225 if (groups[j] == NULL)
2226 continue;
2227 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->defs,
2228 groups[j]->defs);
2229 if (ret == 0) {
2230 if (ctxt->error != NULL)
2231 ctxt->error(ctxt->userData,
2232 "Element or text conflicts in interleave\n");
2233 ctxt->nbErrors++;
2234 }
2235 }
2236 }
2237 partitions->groups = groups;
2238
2239 /*
2240 * Free Up the child list, and save the partition list back in the def
2241 */
2242 def->data = partitions;
2243 return;
2244
2245error:
2246 if (ctxt->error != NULL)
2247 ctxt->error(ctxt->userData,
2248 "Out of memory in interleave computation\n");
2249 ctxt->nbErrors++;
2250 if (list == NULL)
2251 xmlFree(list);
2252 if (groups != NULL) {
2253 for (i = 0;i < nbgroups;i++)
2254 if (groups[i] != NULL) {
2255 if (groups[i]->defs != NULL)
2256 xmlFree(groups[i]->defs);
2257 xmlFree(groups[i]);
2258 }
2259 xmlFree(groups);
2260 }
2261 xmlRelaxNGFreePartition(partitions);
2262}
2263
2264/**
2265 * xmlRelaxNGParseInterleave:
2266 * @ctxt: a Relax-NG parser context
2267 * @node: the data node.
2268 *
2269 * parse the content of a RelaxNG interleave node.
2270 *
2271 * Returns the definition pointer or NULL in case of error
2272 */
2273static xmlRelaxNGDefinePtr
2274xmlRelaxNGParseInterleave(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2275 xmlRelaxNGDefinePtr def = NULL;
2276 xmlRelaxNGDefinePtr last = NULL, cur;
2277 xmlNodePtr child;
2278
2279 def = xmlRelaxNGNewDefine(ctxt, node);
2280 if (def == NULL) {
2281 return(NULL);
2282 }
2283 def->type = XML_RELAXNG_INTERLEAVE;
2284
2285 if (ctxt->interleaves == NULL)
2286 ctxt->interleaves = xmlHashCreate(10);
2287 if (ctxt->interleaves == NULL) {
2288 if (ctxt->error != NULL)
2289 ctxt->error(ctxt->userData,
2290 "Failed to create interleaves hash table\n");
2291 ctxt->nbErrors++;
2292 } else {
2293 char name[32];
2294
2295 snprintf(name, 32, "interleave%d", ctxt->nbInterleaves++);
2296 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST name, def) < 0) {
2297 if (ctxt->error != NULL)
2298 ctxt->error(ctxt->userData,
2299 "Failed to add %s to hash table\n", name);
2300 ctxt->nbErrors++;
2301 }
2302 }
2303 child = node->children;
Daniel Veillardd2298792003-02-14 16:54:11 +00002304 if (child == NULL) {
2305 if (ctxt->error != NULL)
2306 ctxt->error(ctxt->userData, "Element interleave is empty\n");
2307 ctxt->nbErrors++;
2308 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002309 while (child != NULL) {
2310 if (IS_RELAXNG(child, "element")) {
2311 cur = xmlRelaxNGParseElement(ctxt, child);
2312 } else {
2313 cur = xmlRelaxNGParsePattern(ctxt, child);
2314 }
2315 if (cur != NULL) {
2316 cur->parent = def;
2317 if (last == NULL) {
2318 def->content = last = cur;
2319 } else {
2320 last->next = cur;
2321 last = cur;
2322 }
2323 }
2324 child = child->next;
2325 }
2326
2327 return(def);
2328}
Daniel Veillard6eadf632003-01-23 18:29:16 +00002329
2330/**
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002331 * xmlRelaxNGParseInclude:
2332 * @ctxt: a Relax-NG parser context
2333 * @node: the include node
2334 *
2335 * Integrate the content of an include node in the current grammar
2336 *
2337 * Returns 0 in case of success or -1 in case of error
2338 */
2339static int
2340xmlRelaxNGParseInclude(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2341 xmlRelaxNGIncludePtr incl;
2342 xmlNodePtr root;
2343 int ret = 0, tmp;
2344
2345 incl = node->_private;
2346 if (incl == NULL) {
2347 if (ctxt->error != NULL)
2348 ctxt->error(ctxt->userData,
2349 "Include node has no data\n");
2350 ctxt->nbErrors++;
2351 return(-1);
2352 }
2353 root = xmlDocGetRootElement(incl->doc);
2354 if (root == NULL) {
2355 if (ctxt->error != NULL)
2356 ctxt->error(ctxt->userData,
2357 "Include document is empty\n");
2358 ctxt->nbErrors++;
2359 return(-1);
2360 }
2361 if (!xmlStrEqual(root->name, BAD_CAST "grammar")) {
2362 if (ctxt->error != NULL)
2363 ctxt->error(ctxt->userData,
2364 "Include document root is not a grammar\n");
2365 ctxt->nbErrors++;
2366 return(-1);
2367 }
2368
2369 /*
2370 * Merge the definition from both the include and the internal list
2371 */
2372 if (root->children != NULL) {
2373 tmp = xmlRelaxNGParseGrammarContent(ctxt, root->children);
2374 if (tmp != 0)
2375 ret = -1;
2376 }
2377 if (node->children != NULL) {
2378 tmp = xmlRelaxNGParseGrammarContent(ctxt, node->children);
2379 if (tmp != 0)
2380 ret = -1;
2381 }
2382 return(ret);
2383}
2384
2385/**
Daniel Veillard276be4a2003-01-24 01:03:34 +00002386 * xmlRelaxNGParseDefine:
2387 * @ctxt: a Relax-NG parser context
2388 * @node: the define node
2389 *
2390 * parse the content of a RelaxNG define element node.
2391 *
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002392 * Returns 0 in case of success or -1 in case of error
Daniel Veillard276be4a2003-01-24 01:03:34 +00002393 */
2394static int
2395xmlRelaxNGParseDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2396 xmlChar *name;
2397 int ret = 0, tmp;
2398 xmlRelaxNGDefinePtr def;
2399 const xmlChar *olddefine;
2400
2401 name = xmlGetProp(node, BAD_CAST "name");
2402 if (name == NULL) {
2403 if (ctxt->error != NULL)
2404 ctxt->error(ctxt->userData,
2405 "define has no name\n");
2406 ctxt->nbErrors++;
2407 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00002408 xmlRelaxNGNormExtSpace(name);
2409 if (xmlValidateNCName(name, 0)) {
2410 if (ctxt->error != NULL)
2411 ctxt->error(ctxt->userData,
2412 "define name '%s' is not an NCName\n",
2413 name);
2414 ctxt->nbErrors++;
2415 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002416 def = xmlRelaxNGNewDefine(ctxt, node);
2417 if (def == NULL) {
2418 xmlFree(name);
2419 return(-1);
2420 }
2421 def->type = XML_RELAXNG_DEF;
2422 def->name = name;
2423 if (node->children == NULL) {
2424 if (ctxt->error != NULL)
2425 ctxt->error(ctxt->userData,
2426 "define has no children\n");
2427 ctxt->nbErrors++;
2428 } else {
2429 olddefine = ctxt->define;
2430 ctxt->define = name;
Daniel Veillard154877e2003-01-30 12:17:05 +00002431 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard276be4a2003-01-24 01:03:34 +00002432 ctxt->define = olddefine;
2433 }
2434 if (ctxt->grammar->defs == NULL)
2435 ctxt->grammar->defs = xmlHashCreate(10);
2436 if (ctxt->grammar->defs == NULL) {
2437 if (ctxt->error != NULL)
2438 ctxt->error(ctxt->userData,
2439 "Could not create definition hash\n");
2440 ctxt->nbErrors++;
2441 ret = -1;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002442 } else {
2443 tmp = xmlHashAddEntry(ctxt->grammar->defs, name, def);
2444 if (tmp < 0) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002445 xmlRelaxNGDefinePtr prev;
2446
2447 prev = xmlHashLookup(ctxt->grammar->defs, name);
2448 if (prev == NULL) {
2449 if (ctxt->error != NULL)
2450 ctxt->error(ctxt->userData,
2451 "Internal error on define aggregation of %s\n",
2452 name);
2453 ctxt->nbErrors++;
2454 ret = -1;
Daniel Veillard154877e2003-01-30 12:17:05 +00002455 } else {
2456 while (prev->nextHash != NULL)
2457 prev = prev->nextHash;
2458 prev->nextHash = def;
2459 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002460 }
2461 }
2462 }
2463 return(ret);
2464}
2465
2466/**
Daniel Veillardfebcca42003-02-16 15:44:18 +00002467 * xmlRelaxNGProcessExternalRef:
2468 * @ctxt: the parser context
2469 * @node: the externlRef node
2470 *
2471 * Process and compile an externlRef node
2472 *
2473 * Returns the xmlRelaxNGDefinePtr or NULL in case of error
2474 */
2475static xmlRelaxNGDefinePtr
2476xmlRelaxNGProcessExternalRef(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2477 xmlRelaxNGDocumentPtr docu;
2478 xmlNodePtr root, tmp;
2479 xmlChar *ns;
2480 int newNs = 0;
2481 xmlRelaxNGDefinePtr def;
2482
2483 docu = node->_private;
2484 if (docu != NULL) {
2485 def = xmlRelaxNGNewDefine(ctxt, node);
2486 if (def == NULL)
2487 return(NULL);
2488 def->type = XML_RELAXNG_EXTERNALREF;
2489
2490 if (docu->content == NULL) {
2491 /*
2492 * Then do the parsing for good
2493 */
2494 root = xmlDocGetRootElement(docu->doc);
2495 if (root == NULL) {
2496 if (ctxt->error != NULL)
2497 ctxt->error(ctxt->userData,
2498 "xmlRelaxNGParse: %s is empty\n",
2499 ctxt->URL);
2500 ctxt->nbErrors++;
2501 return (NULL);
2502 }
2503 /*
2504 * ns transmission rules
2505 */
2506 ns = xmlGetProp(root, BAD_CAST "ns");
2507 if (ns == NULL) {
2508 tmp = node;
2509 while ((tmp != NULL) &&
2510 (tmp->type == XML_ELEMENT_NODE)) {
2511 ns = xmlGetProp(tmp, BAD_CAST "ns");
2512 if (ns != NULL) {
2513 break;
2514 }
2515 tmp = tmp->parent;
2516 }
2517 if (ns != NULL) {
2518 xmlSetProp(root, BAD_CAST "ns", ns);
2519 newNs = 1;
2520 xmlFree(ns);
2521 }
2522 } else {
2523 xmlFree(ns);
2524 }
2525
2526 /*
2527 * Parsing to get a precompiled schemas.
2528 */
2529 docu->schema = xmlRelaxNGParseDocument(ctxt, root);
2530 if ((docu->schema != NULL) &&
2531 (docu->schema->topgrammar != NULL)) {
2532 docu->content = docu->schema->topgrammar->start;
2533 }
2534
2535 /*
2536 * the externalRef may be reused in a different ns context
2537 */
2538 if (newNs == 1) {
2539 xmlUnsetProp(root, BAD_CAST "ns");
2540 }
2541 }
2542 def->content = docu->content;
2543 } else {
2544 def = NULL;
2545 }
2546 return(def);
2547}
2548
2549/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00002550 * xmlRelaxNGParsePattern:
2551 * @ctxt: a Relax-NG parser context
2552 * @node: the pattern node.
2553 *
2554 * parse the content of a RelaxNG pattern node.
2555 *
Daniel Veillard276be4a2003-01-24 01:03:34 +00002556 * Returns the definition pointer or NULL in case of error or if no
2557 * pattern is generated.
Daniel Veillard6eadf632003-01-23 18:29:16 +00002558 */
2559static xmlRelaxNGDefinePtr
2560xmlRelaxNGParsePattern(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2561 xmlRelaxNGDefinePtr def = NULL;
2562
Daniel Veillardd2298792003-02-14 16:54:11 +00002563 if (node == NULL) {
2564 return(NULL);
2565 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002566 if (IS_RELAXNG(node, "element")) {
2567 def = xmlRelaxNGParseElement(ctxt, node);
2568 } else if (IS_RELAXNG(node, "attribute")) {
2569 def = xmlRelaxNGParseAttribute(ctxt, node);
2570 } else if (IS_RELAXNG(node, "empty")) {
2571 def = xmlRelaxNGNewDefine(ctxt, node);
2572 if (def == NULL)
2573 return(NULL);
2574 def->type = XML_RELAXNG_EMPTY;
Daniel Veillardd2298792003-02-14 16:54:11 +00002575 if (node->children != NULL) {
2576 if (ctxt->error != NULL)
2577 ctxt->error(ctxt->userData, "empty: had a child node\n");
2578 ctxt->nbErrors++;
2579 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002580 } else if (IS_RELAXNG(node, "text")) {
2581 def = xmlRelaxNGNewDefine(ctxt, node);
2582 if (def == NULL)
2583 return(NULL);
2584 def->type = XML_RELAXNG_TEXT;
2585 if (node->children != NULL) {
2586 if (ctxt->error != NULL)
2587 ctxt->error(ctxt->userData, "text: had a child node\n");
2588 ctxt->nbErrors++;
2589 }
2590 } else if (IS_RELAXNG(node, "zeroOrMore")) {
2591 def = xmlRelaxNGNewDefine(ctxt, node);
2592 if (def == NULL)
2593 return(NULL);
2594 def->type = XML_RELAXNG_ZEROORMORE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002595 if (node->children == NULL) {
2596 if (ctxt->error != NULL)
2597 ctxt->error(ctxt->userData,
2598 "Element %s is empty\n", node->name);
2599 ctxt->nbErrors++;
2600 } else {
2601 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2602 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002603 } else if (IS_RELAXNG(node, "oneOrMore")) {
2604 def = xmlRelaxNGNewDefine(ctxt, node);
2605 if (def == NULL)
2606 return(NULL);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002607 def->type = XML_RELAXNG_ONEORMORE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002608 if (node->children == NULL) {
2609 if (ctxt->error != NULL)
2610 ctxt->error(ctxt->userData,
2611 "Element %s is empty\n", node->name);
2612 ctxt->nbErrors++;
2613 } else {
2614 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2615 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002616 } else if (IS_RELAXNG(node, "optional")) {
2617 def = xmlRelaxNGNewDefine(ctxt, node);
2618 if (def == NULL)
2619 return(NULL);
2620 def->type = XML_RELAXNG_OPTIONAL;
Daniel Veillardd2298792003-02-14 16:54:11 +00002621 if (node->children == NULL) {
2622 if (ctxt->error != NULL)
2623 ctxt->error(ctxt->userData,
2624 "Element %s is empty\n", node->name);
2625 ctxt->nbErrors++;
2626 } else {
2627 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2628 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002629 } else if (IS_RELAXNG(node, "choice")) {
2630 def = xmlRelaxNGNewDefine(ctxt, node);
2631 if (def == NULL)
2632 return(NULL);
2633 def->type = XML_RELAXNG_CHOICE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002634 if (node->children == NULL) {
2635 if (ctxt->error != NULL)
2636 ctxt->error(ctxt->userData,
2637 "Element %s is empty\n", node->name);
2638 ctxt->nbErrors++;
2639 } else {
2640 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2641 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002642 } else if (IS_RELAXNG(node, "group")) {
2643 def = xmlRelaxNGNewDefine(ctxt, node);
2644 if (def == NULL)
2645 return(NULL);
2646 def->type = XML_RELAXNG_GROUP;
Daniel Veillardd2298792003-02-14 16:54:11 +00002647 if (node->children == NULL) {
2648 if (ctxt->error != NULL)
2649 ctxt->error(ctxt->userData,
2650 "Element %s is empty\n", node->name);
2651 ctxt->nbErrors++;
2652 } else {
2653 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2654 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002655 } else if (IS_RELAXNG(node, "ref")) {
2656 def = xmlRelaxNGNewDefine(ctxt, node);
2657 if (def == NULL)
2658 return(NULL);
2659 def->type = XML_RELAXNG_REF;
2660 def->name = xmlGetProp(node, BAD_CAST "name");
2661 if (def->name == NULL) {
2662 if (ctxt->error != NULL)
2663 ctxt->error(ctxt->userData,
2664 "ref has no name\n");
2665 ctxt->nbErrors++;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002666 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00002667 xmlRelaxNGNormExtSpace(def->name);
2668 if (xmlValidateNCName(def->name, 0)) {
2669 if (ctxt->error != NULL)
2670 ctxt->error(ctxt->userData,
2671 "ref name '%s' is not an NCName\n",
2672 def->name);
2673 ctxt->nbErrors++;
2674 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002675 }
2676 if (node->children != NULL) {
2677 if (ctxt->error != NULL)
2678 ctxt->error(ctxt->userData,
2679 "ref is not empty\n");
2680 ctxt->nbErrors++;
2681 }
2682 if (ctxt->grammar->refs == NULL)
2683 ctxt->grammar->refs = xmlHashCreate(10);
2684 if (ctxt->grammar->refs == NULL) {
2685 if (ctxt->error != NULL)
2686 ctxt->error(ctxt->userData,
2687 "Could not create references hash\n");
2688 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002689 def = NULL;
2690 } else {
2691 int tmp;
2692
2693 tmp = xmlHashAddEntry(ctxt->grammar->refs, def->name, def);
2694 if (tmp < 0) {
2695 xmlRelaxNGDefinePtr prev;
2696
2697 prev = (xmlRelaxNGDefinePtr)
2698 xmlHashLookup(ctxt->grammar->refs, def->name);
2699 if (prev == NULL) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00002700 if (def->name != NULL) {
2701 if (ctxt->error != NULL)
2702 ctxt->error(ctxt->userData,
2703 "Error refs definitions '%s'\n",
2704 def->name);
2705 } else {
2706 if (ctxt->error != NULL)
2707 ctxt->error(ctxt->userData,
2708 "Error refs definitions\n");
2709 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002710 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002711 def = NULL;
2712 } else {
2713 def->nextHash = prev->nextHash;
2714 prev->nextHash = def;
2715 }
2716 }
2717 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002718 } else if (IS_RELAXNG(node, "data")) {
2719 def = xmlRelaxNGParseData(ctxt, node);
Daniel Veillardd2298792003-02-14 16:54:11 +00002720#if 0
Daniel Veillard276be4a2003-01-24 01:03:34 +00002721 } else if (IS_RELAXNG(node, "define")) {
2722 xmlRelaxNGParseDefine(ctxt, node);
2723 def = NULL;
Daniel Veillardd2298792003-02-14 16:54:11 +00002724#endif
Daniel Veillardedc91922003-01-26 00:52:04 +00002725 } else if (IS_RELAXNG(node, "value")) {
2726 def = xmlRelaxNGParseValue(ctxt, node);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002727 } else if (IS_RELAXNG(node, "list")) {
2728 def = xmlRelaxNGNewDefine(ctxt, node);
2729 if (def == NULL)
2730 return(NULL);
2731 def->type = XML_RELAXNG_LIST;
Daniel Veillardd2298792003-02-14 16:54:11 +00002732 if (node->children == NULL) {
2733 if (ctxt->error != NULL)
2734 ctxt->error(ctxt->userData,
2735 "Element %s is empty\n", node->name);
2736 ctxt->nbErrors++;
2737 } else {
2738 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2739 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002740 } else if (IS_RELAXNG(node, "interleave")) {
2741 def = xmlRelaxNGParseInterleave(ctxt, node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002742 } else if (IS_RELAXNG(node, "externalRef")) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00002743 def = xmlRelaxNGProcessExternalRef(ctxt, node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002744 } else if (IS_RELAXNG(node, "notAllowed")) {
2745 def = xmlRelaxNGNewDefine(ctxt, node);
2746 if (def == NULL)
2747 return(NULL);
2748 def->type = XML_RELAXNG_NOT_ALLOWED;
2749 if (node->children != NULL) {
2750 if (ctxt->error != NULL)
2751 ctxt->error(ctxt->userData,
2752 "xmlRelaxNGParse: notAllowed element is not empty\n");
2753 ctxt->nbErrors++;
2754 }
Daniel Veillard419a7682003-02-03 23:22:49 +00002755 } else if (IS_RELAXNG(node, "grammar")) {
2756 xmlRelaxNGGrammarPtr grammar, old;
2757 xmlRelaxNGGrammarPtr oldparent;
2758
2759 oldparent = ctxt->parentgrammar;
2760 old = ctxt->grammar;
2761 ctxt->parentgrammar = old;
2762 grammar = xmlRelaxNGParseGrammar(ctxt, node->children);
2763 if (old != NULL) {
2764 ctxt->grammar = old;
2765 ctxt->parentgrammar = oldparent;
2766 if (grammar != NULL) {
2767 grammar->next = old->next;
2768 old->next = grammar;
2769 }
2770 }
2771 if (grammar != NULL)
2772 def = grammar->start;
2773 else
2774 def = NULL;
2775 } else if (IS_RELAXNG(node, "parentRef")) {
2776 if (ctxt->parentgrammar == NULL) {
2777 if (ctxt->error != NULL)
2778 ctxt->error(ctxt->userData,
2779 "Use of parentRef without a parent grammar\n");
2780 ctxt->nbErrors++;
2781 return(NULL);
2782 }
2783 def = xmlRelaxNGNewDefine(ctxt, node);
2784 if (def == NULL)
2785 return(NULL);
2786 def->type = XML_RELAXNG_PARENTREF;
2787 def->name = xmlGetProp(node, BAD_CAST "name");
2788 if (def->name == NULL) {
2789 if (ctxt->error != NULL)
2790 ctxt->error(ctxt->userData,
2791 "parentRef has no name\n");
2792 ctxt->nbErrors++;
Daniel Veillardd2298792003-02-14 16:54:11 +00002793 } else {
2794 xmlRelaxNGNormExtSpace(def->name);
2795 if (xmlValidateNCName(def->name, 0)) {
2796 if (ctxt->error != NULL)
2797 ctxt->error(ctxt->userData,
2798 "parentRef name '%s' is not an NCName\n",
2799 def->name);
2800 ctxt->nbErrors++;
2801 }
Daniel Veillard419a7682003-02-03 23:22:49 +00002802 }
2803 if (node->children != NULL) {
2804 if (ctxt->error != NULL)
2805 ctxt->error(ctxt->userData,
2806 "parentRef is not empty\n");
2807 ctxt->nbErrors++;
2808 }
2809 if (ctxt->parentgrammar->refs == NULL)
2810 ctxt->parentgrammar->refs = xmlHashCreate(10);
2811 if (ctxt->parentgrammar->refs == NULL) {
2812 if (ctxt->error != NULL)
2813 ctxt->error(ctxt->userData,
2814 "Could not create references hash\n");
2815 ctxt->nbErrors++;
2816 def = NULL;
Daniel Veillardd2298792003-02-14 16:54:11 +00002817 } else if (def->name != NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +00002818 int tmp;
2819
2820 tmp = xmlHashAddEntry(ctxt->parentgrammar->refs, def->name, def);
2821 if (tmp < 0) {
2822 xmlRelaxNGDefinePtr prev;
2823
2824 prev = (xmlRelaxNGDefinePtr)
2825 xmlHashLookup(ctxt->parentgrammar->refs, def->name);
2826 if (prev == NULL) {
2827 if (ctxt->error != NULL)
2828 ctxt->error(ctxt->userData,
2829 "Internal error parentRef definitions '%s'\n",
2830 def->name);
2831 ctxt->nbErrors++;
2832 def = NULL;
2833 } else {
2834 def->nextHash = prev->nextHash;
2835 prev->nextHash = def;
2836 }
2837 }
2838 }
Daniel Veillardd2298792003-02-14 16:54:11 +00002839 } else if (IS_RELAXNG(node, "mixed")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00002840 if (node->children == NULL) {
2841 if (ctxt->error != NULL)
2842 ctxt->error(ctxt->userData,
2843 "Mixed is empty\n");
2844 ctxt->nbErrors++;
2845 def = NULL;
Daniel Veillard416589a2003-02-17 17:25:42 +00002846 } else {
2847 def = xmlRelaxNGParseInterleave(ctxt, node);
2848 if (def != NULL) {
2849 xmlRelaxNGDefinePtr tmp;
Daniel Veillard2df2de22003-02-17 23:34:33 +00002850
2851 if ((def->content != NULL) && (def->content->next != NULL)) {
2852 tmp = xmlRelaxNGNewDefine(ctxt, node);
2853 if (tmp != NULL) {
2854 tmp->type = XML_RELAXNG_GROUP;
2855 tmp->content = def->content;
2856 def->content = tmp;
2857 }
2858 }
2859
Daniel Veillard416589a2003-02-17 17:25:42 +00002860 tmp = xmlRelaxNGNewDefine(ctxt, node);
2861 if (tmp == NULL)
2862 return(def);
2863 tmp->type = XML_RELAXNG_TEXT;
2864 tmp->next = def->content;
2865 def->content = tmp;
2866 }
2867 }
Daniel Veillardd2298792003-02-14 16:54:11 +00002868 } else {
2869 if (ctxt->error != NULL)
2870 ctxt->error(ctxt->userData,
2871 "Unexpected node %s is not a pattern\n",
2872 node->name);
2873 ctxt->nbErrors++;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002874 def = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002875 }
2876 return(def);
2877}
2878
2879/**
2880 * xmlRelaxNGParseAttribute:
2881 * @ctxt: a Relax-NG parser context
2882 * @node: the element node
2883 *
2884 * parse the content of a RelaxNG attribute node.
2885 *
2886 * Returns the definition pointer or NULL in case of error.
2887 */
2888static xmlRelaxNGDefinePtr
2889xmlRelaxNGParseAttribute(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
Daniel Veillardd2298792003-02-14 16:54:11 +00002890 xmlRelaxNGDefinePtr ret, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002891 xmlNodePtr child;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002892 int old_flags;
2893
2894 ret = xmlRelaxNGNewDefine(ctxt, node);
2895 if (ret == NULL)
2896 return(NULL);
2897 ret->type = XML_RELAXNG_ATTRIBUTE;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002898 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002899 child = node->children;
2900 if (child == NULL) {
2901 if (ctxt->error != NULL)
2902 ctxt->error(ctxt->userData,
2903 "xmlRelaxNGParseattribute: attribute has no children\n");
2904 ctxt->nbErrors++;
2905 return(ret);
2906 }
2907 old_flags = ctxt->flags;
2908 ctxt->flags |= XML_RELAXNG_IN_ATTRIBUTE;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002909 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
2910 if (cur != NULL)
2911 child = child->next;
2912
Daniel Veillardd2298792003-02-14 16:54:11 +00002913 if (child != NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00002914 cur = xmlRelaxNGParsePattern(ctxt, child);
2915 if (cur != NULL) {
2916 switch (cur->type) {
2917 case XML_RELAXNG_EMPTY:
2918 case XML_RELAXNG_NOT_ALLOWED:
2919 case XML_RELAXNG_TEXT:
2920 case XML_RELAXNG_ELEMENT:
2921 case XML_RELAXNG_DATATYPE:
2922 case XML_RELAXNG_VALUE:
2923 case XML_RELAXNG_LIST:
2924 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00002925 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002926 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00002927 case XML_RELAXNG_DEF:
2928 case XML_RELAXNG_ONEORMORE:
2929 case XML_RELAXNG_ZEROORMORE:
2930 case XML_RELAXNG_OPTIONAL:
2931 case XML_RELAXNG_CHOICE:
2932 case XML_RELAXNG_GROUP:
2933 case XML_RELAXNG_INTERLEAVE:
Daniel Veillardd2298792003-02-14 16:54:11 +00002934 ret->content = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002935 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002936 break;
2937 case XML_RELAXNG_ATTRIBUTE:
Daniel Veillardd2298792003-02-14 16:54:11 +00002938 if (ctxt->error != NULL)
2939 ctxt->error(ctxt->userData,
2940 "attribute has an attribute child\n");
2941 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002942 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002943 case XML_RELAXNG_START:
Daniel Veillard8fe98712003-02-19 00:19:14 +00002944 case XML_RELAXNG_PARAM:
Daniel Veillard144fae12003-02-03 13:17:57 +00002945 case XML_RELAXNG_EXCEPT:
Daniel Veillardd2298792003-02-14 16:54:11 +00002946 if (ctxt->error != NULL)
2947 ctxt->error(ctxt->userData,
2948 "attribute has invalid content\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002949 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002950 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002951 }
2952 }
2953 child = child->next;
2954 }
Daniel Veillardd2298792003-02-14 16:54:11 +00002955 if (child != NULL) {
2956 if (ctxt->error != NULL)
2957 ctxt->error(ctxt->userData, "attribute has multiple children\n");
2958 ctxt->nbErrors++;
2959 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002960 ctxt->flags = old_flags;
2961 return(ret);
2962}
2963
2964/**
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002965 * xmlRelaxNGParseExceptNameClass:
2966 * @ctxt: a Relax-NG parser context
2967 * @node: the except node
Daniel Veillard144fae12003-02-03 13:17:57 +00002968 * @attr: 1 if within an attribute, 0 if within an element
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002969 *
2970 * parse the content of a RelaxNG nameClass node.
2971 *
2972 * Returns the definition pointer or NULL in case of error.
2973 */
2974static xmlRelaxNGDefinePtr
Daniel Veillard144fae12003-02-03 13:17:57 +00002975xmlRelaxNGParseExceptNameClass(xmlRelaxNGParserCtxtPtr ctxt,
2976 xmlNodePtr node, int attr) {
2977 xmlRelaxNGDefinePtr ret, cur, last = NULL;
2978 xmlNodePtr child;
2979
Daniel Veillardd2298792003-02-14 16:54:11 +00002980 if (!IS_RELAXNG(node, "except")) {
2981 if (ctxt->error != NULL)
2982 ctxt->error(ctxt->userData,
2983 "Expecting an except node\n");
2984 ctxt->nbErrors++;
Daniel Veillard144fae12003-02-03 13:17:57 +00002985 return(NULL);
Daniel Veillardd2298792003-02-14 16:54:11 +00002986 }
2987 if (node->next != NULL) {
2988 if (ctxt->error != NULL)
2989 ctxt->error(ctxt->userData,
2990 "exceptNameClass allows only a single except node\n");
2991 ctxt->nbErrors++;
2992 }
Daniel Veillard144fae12003-02-03 13:17:57 +00002993 if (node->children == NULL) {
2994 if (ctxt->error != NULL)
2995 ctxt->error(ctxt->userData,
2996 "except has no content\n");
2997 ctxt->nbErrors++;
2998 return(NULL);
2999 }
3000
3001 ret = xmlRelaxNGNewDefine(ctxt, node);
3002 if (ret == NULL)
3003 return(NULL);
3004 ret->type = XML_RELAXNG_EXCEPT;
3005 child = node->children;
3006 while (child != NULL) {
3007 cur = xmlRelaxNGNewDefine(ctxt, child);
3008 if (cur == NULL)
3009 break;
3010 if (attr)
3011 cur->type = XML_RELAXNG_ATTRIBUTE;
3012 else
3013 cur->type = XML_RELAXNG_ELEMENT;
3014
Daniel Veillard419a7682003-02-03 23:22:49 +00003015 if (xmlRelaxNGParseNameClass(ctxt, child, cur) != NULL) {
Daniel Veillard144fae12003-02-03 13:17:57 +00003016 if (last == NULL) {
3017 ret->content = cur;
3018 } else {
3019 last->next = cur;
3020 }
3021 last = cur;
3022 }
3023 child = child->next;
3024 }
3025
3026 return(ret);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003027}
3028
3029/**
3030 * xmlRelaxNGParseNameClass:
3031 * @ctxt: a Relax-NG parser context
3032 * @node: the nameClass node
3033 * @def: the current definition
3034 *
3035 * parse the content of a RelaxNG nameClass node.
3036 *
3037 * Returns the definition pointer or NULL in case of error.
3038 */
3039static xmlRelaxNGDefinePtr
3040xmlRelaxNGParseNameClass(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
3041 xmlRelaxNGDefinePtr def) {
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003042 xmlRelaxNGDefinePtr ret, tmp;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003043 xmlChar *val;
3044
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003045 ret = def;
3046 if ((IS_RELAXNG(node, "name")) || (IS_RELAXNG(node, "anyName")) ||
3047 (IS_RELAXNG(node, "nsName"))) {
3048 if ((def->type != XML_RELAXNG_ELEMENT) &&
3049 (def->type != XML_RELAXNG_ATTRIBUTE)) {
3050 ret = xmlRelaxNGNewDefine(ctxt, node);
3051 if (ret == NULL)
3052 return(NULL);
3053 ret->parent = def;
3054 if (ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE)
3055 ret->type = XML_RELAXNG_ATTRIBUTE;
3056 else
3057 ret->type = XML_RELAXNG_ELEMENT;
3058 }
3059 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003060 if (IS_RELAXNG(node, "name")) {
3061 val = xmlNodeGetContent(node);
Daniel Veillardd2298792003-02-14 16:54:11 +00003062 xmlRelaxNGNormExtSpace(val);
3063 if (xmlValidateNCName(val, 0)) {
3064 if (ctxt->error != NULL) {
3065 if (node->parent != NULL)
3066 ctxt->error(ctxt->userData,
3067 "Element %s name '%s' is not an NCName\n",
3068 node->parent->name, val);
3069 else
3070 ctxt->error(ctxt->userData,
3071 "name '%s' is not an NCName\n",
3072 val);
3073 }
3074 ctxt->nbErrors++;
3075 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003076 ret->name = val;
3077 val = xmlGetProp(node, BAD_CAST "ns");
3078 ret->ns = val;
Daniel Veillard416589a2003-02-17 17:25:42 +00003079 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3080 (val != NULL) &&
3081 (xmlStrEqual(val, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
3082 ctxt->error(ctxt->userData,
3083 "Attribute with namespace '%s' is not allowed\n",
3084 val);
3085 ctxt->nbErrors++;
3086 }
3087 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3088 (val != NULL) &&
3089 (val[0] == 0) &&
3090 (xmlStrEqual(ret->name, BAD_CAST "xmlns"))) {
3091 ctxt->error(ctxt->userData,
3092 "Attribute with QName 'xmlns' is not allowed\n",
3093 val);
3094 ctxt->nbErrors++;
3095 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003096 } else if (IS_RELAXNG(node, "anyName")) {
3097 ret->name = NULL;
3098 ret->ns = NULL;
3099 if (node->children != NULL) {
3100 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00003101 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
3102 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003103 }
3104 } else if (IS_RELAXNG(node, "nsName")) {
3105 ret->name = NULL;
3106 ret->ns = xmlGetProp(node, BAD_CAST "ns");
3107 if (ret->ns == NULL) {
3108 if (ctxt->error != NULL)
3109 ctxt->error(ctxt->userData,
3110 "nsName has no ns attribute\n");
3111 ctxt->nbErrors++;
3112 }
Daniel Veillard416589a2003-02-17 17:25:42 +00003113 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3114 (ret->ns != NULL) &&
3115 (xmlStrEqual(ret->ns, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
3116 ctxt->error(ctxt->userData,
3117 "Attribute with namespace '%s' is not allowed\n",
3118 ret->ns);
3119 ctxt->nbErrors++;
3120 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003121 if (node->children != NULL) {
3122 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00003123 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
3124 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003125 }
3126 } else if (IS_RELAXNG(node, "choice")) {
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003127 xmlNodePtr child;
3128 xmlRelaxNGDefinePtr last = NULL;
3129
Daniel Veillardd4310742003-02-18 21:12:46 +00003130 ret = xmlRelaxNGNewDefine(ctxt, node);
3131 if (ret == NULL)
3132 return(NULL);
3133 ret->parent = def;
3134 ret->type = XML_RELAXNG_CHOICE;
3135
Daniel Veillardd2298792003-02-14 16:54:11 +00003136 if (node->children == NULL) {
3137 if (ctxt->error != NULL)
3138 ctxt->error(ctxt->userData,
3139 "Element choice is empty\n");
3140 ctxt->nbErrors++;
3141 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00003142
3143 child = node->children;
3144 while (child != NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00003145 tmp = xmlRelaxNGParseNameClass(ctxt, child, ret);
Daniel Veillardd2298792003-02-14 16:54:11 +00003146 if (tmp != NULL) {
3147 if (last == NULL) {
3148 last = ret->nameClass = tmp;
3149 } else {
3150 last->next = tmp;
3151 last = tmp;
3152 }
3153 }
3154 child = child->next;
3155 }
3156 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003157 } else {
3158 if (ctxt->error != NULL)
3159 ctxt->error(ctxt->userData,
3160 "expecting name, anyName, nsName or choice : got %s\n",
3161 node->name);
3162 ctxt->nbErrors++;
3163 return(NULL);
3164 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003165 if (ret != def) {
3166 if (def->nameClass == NULL) {
3167 def->nameClass = ret;
3168 } else {
3169 tmp = def->nameClass;
3170 while (tmp->next != NULL) {
3171 tmp = tmp->next;
3172 }
3173 tmp->next = ret;
3174 }
3175 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003176 return(ret);
3177}
3178
3179/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00003180 * xmlRelaxNGParseElement:
3181 * @ctxt: a Relax-NG parser context
3182 * @node: the element node
3183 *
3184 * parse the content of a RelaxNG element node.
3185 *
3186 * Returns the definition pointer or NULL in case of error.
3187 */
3188static xmlRelaxNGDefinePtr
3189xmlRelaxNGParseElement(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3190 xmlRelaxNGDefinePtr ret, cur, last;
3191 xmlNodePtr child;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003192 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003193
3194 ret = xmlRelaxNGNewDefine(ctxt, node);
3195 if (ret == NULL)
3196 return(NULL);
3197 ret->type = XML_RELAXNG_ELEMENT;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003198 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003199 child = node->children;
3200 if (child == NULL) {
3201 if (ctxt->error != NULL)
3202 ctxt->error(ctxt->userData,
3203 "xmlRelaxNGParseElement: element has no children\n");
3204 ctxt->nbErrors++;
3205 return(ret);
3206 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003207 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
3208 if (cur != NULL)
3209 child = child->next;
3210
Daniel Veillard6eadf632003-01-23 18:29:16 +00003211 if (child == NULL) {
3212 if (ctxt->error != NULL)
3213 ctxt->error(ctxt->userData,
3214 "xmlRelaxNGParseElement: element has no content\n");
3215 ctxt->nbErrors++;
3216 return(ret);
3217 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003218 olddefine = ctxt->define;
3219 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003220 last = NULL;
3221 while (child != NULL) {
3222 cur = xmlRelaxNGParsePattern(ctxt, child);
3223 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003224 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003225 switch (cur->type) {
3226 case XML_RELAXNG_EMPTY:
3227 case XML_RELAXNG_NOT_ALLOWED:
3228 case XML_RELAXNG_TEXT:
3229 case XML_RELAXNG_ELEMENT:
3230 case XML_RELAXNG_DATATYPE:
3231 case XML_RELAXNG_VALUE:
3232 case XML_RELAXNG_LIST:
3233 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00003234 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003235 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00003236 case XML_RELAXNG_DEF:
3237 case XML_RELAXNG_ZEROORMORE:
3238 case XML_RELAXNG_ONEORMORE:
3239 case XML_RELAXNG_OPTIONAL:
3240 case XML_RELAXNG_CHOICE:
3241 case XML_RELAXNG_GROUP:
3242 case XML_RELAXNG_INTERLEAVE:
3243 if (last == NULL) {
3244 ret->content = last = cur;
3245 } else {
3246 if ((last->type == XML_RELAXNG_ELEMENT) &&
3247 (ret->content == last)) {
3248 ret->content = xmlRelaxNGNewDefine(ctxt, node);
3249 if (ret->content != NULL) {
3250 ret->content->type = XML_RELAXNG_GROUP;
3251 ret->content->content = last;
3252 } else {
3253 ret->content = last;
3254 }
3255 }
3256 last->next = cur;
3257 last = cur;
3258 }
3259 break;
3260 case XML_RELAXNG_ATTRIBUTE:
3261 cur->next = ret->attrs;
3262 ret->attrs = cur;
3263 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003264 case XML_RELAXNG_START:
Daniel Veillard8fe98712003-02-19 00:19:14 +00003265 case XML_RELAXNG_PARAM:
Daniel Veillard144fae12003-02-03 13:17:57 +00003266 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003267 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003268 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003269 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003270 }
3271 }
3272 child = child->next;
3273 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003274 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003275 return(ret);
3276}
3277
3278/**
3279 * xmlRelaxNGParsePatterns:
3280 * @ctxt: a Relax-NG parser context
3281 * @nodes: list of nodes
Daniel Veillard154877e2003-01-30 12:17:05 +00003282 * @group: use an implicit <group> for elements
Daniel Veillard6eadf632003-01-23 18:29:16 +00003283 *
3284 * parse the content of a RelaxNG start node.
3285 *
3286 * Returns the definition pointer or NULL in case of error.
3287 */
3288static xmlRelaxNGDefinePtr
Daniel Veillard154877e2003-01-30 12:17:05 +00003289xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes,
3290 int group) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003291 xmlRelaxNGDefinePtr def = NULL, last = NULL, cur, parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003292
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003293 parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003294 while (nodes != NULL) {
3295 if (IS_RELAXNG(nodes, "element")) {
3296 cur = xmlRelaxNGParseElement(ctxt, nodes);
3297 if (def == NULL) {
3298 def = last = cur;
3299 } else {
Daniel Veillard154877e2003-01-30 12:17:05 +00003300 if ((group == 1) && (def->type == XML_RELAXNG_ELEMENT) &&
3301 (def == last)) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003302 def = xmlRelaxNGNewDefine(ctxt, nodes);
3303 def->type = XML_RELAXNG_GROUP;
3304 def->content = last;
3305 }
3306 last->next = cur;
3307 last = cur;
3308 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003309 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003310 } else {
3311 cur = xmlRelaxNGParsePattern(ctxt, nodes);
Daniel Veillard419a7682003-02-03 23:22:49 +00003312 if (cur != NULL) {
3313 if (def == NULL) {
3314 def = last = cur;
3315 } else {
3316 last->next = cur;
3317 last = cur;
3318 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003319 }
3320 }
3321 nodes = nodes->next;
3322 }
3323 return(def);
3324}
3325
3326/**
3327 * xmlRelaxNGParseStart:
3328 * @ctxt: a Relax-NG parser context
3329 * @nodes: start children nodes
3330 *
3331 * parse the content of a RelaxNG start node.
3332 *
3333 * Returns 0 in case of success, -1 in case of error
3334 */
3335static int
3336xmlRelaxNGParseStart(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
3337 int ret = 0;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003338 xmlRelaxNGDefinePtr def = NULL, last;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003339
Daniel Veillardd2298792003-02-14 16:54:11 +00003340 if (nodes == NULL) {
3341 if (ctxt->error != NULL)
3342 ctxt->error(ctxt->userData,
3343 "start has no children\n");
3344 ctxt->nbErrors++;
3345 return(-1);
3346 }
3347 if (IS_RELAXNG(nodes, "empty")) {
3348 def = xmlRelaxNGNewDefine(ctxt, nodes);
3349 if (def == NULL)
3350 return(-1);
3351 def->type = XML_RELAXNG_EMPTY;
3352 if (nodes->children != NULL) {
3353 if (ctxt->error != NULL)
3354 ctxt->error(ctxt->userData, "element empty is not empty\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003355 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003356 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003357 } else if (IS_RELAXNG(nodes, "notAllowed")) {
3358 def = xmlRelaxNGNewDefine(ctxt, nodes);
3359 if (def == NULL)
3360 return(-1);
3361 def->type = XML_RELAXNG_NOT_ALLOWED;
3362 if (nodes->children != NULL) {
3363 if (ctxt->error != NULL)
3364 ctxt->error(ctxt->userData,
3365 "element notAllowed is not empty\n");
3366 ctxt->nbErrors++;
3367 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003368 } else {
3369 def = xmlRelaxNGParsePatterns(ctxt, nodes, 1);
Daniel Veillard2df2de22003-02-17 23:34:33 +00003370 }
3371 if (ctxt->grammar->start != NULL) {
3372 last = ctxt->grammar->start;
3373 while (last->next != NULL)
3374 last = last->next;
3375 last->next = def;
3376 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00003377 ctxt->grammar->start = def;
3378 }
3379 nodes = nodes->next;
3380 if (nodes != NULL) {
3381 if (ctxt->error != NULL)
3382 ctxt->error(ctxt->userData,
3383 "start more than one children\n");
3384 ctxt->nbErrors++;
3385 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003386 }
3387 return(ret);
3388}
3389
3390/**
3391 * xmlRelaxNGParseGrammarContent:
3392 * @ctxt: a Relax-NG parser context
3393 * @nodes: grammar children nodes
3394 *
3395 * parse the content of a RelaxNG grammar node.
3396 *
3397 * Returns 0 in case of success, -1 in case of error
3398 */
3399static int
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003400xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003401{
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003402 int ret = 0, tmp;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003403
3404 if (nodes == NULL) {
3405 if (ctxt->error != NULL)
3406 ctxt->error(ctxt->userData,
3407 "grammar has no children\n");
3408 ctxt->nbErrors++;
3409 return(-1);
3410 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003411 while (nodes != NULL) {
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003412 if (IS_RELAXNG(nodes, "start")) {
3413 if (nodes->children == NULL) {
3414 if (ctxt->error != NULL)
3415 ctxt->error(ctxt->userData,
Daniel Veillardd2298792003-02-14 16:54:11 +00003416 "start has no children\n");
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003417 ctxt->nbErrors++;
3418 } else {
3419 tmp = xmlRelaxNGParseStart(ctxt, nodes->children);
3420 if (tmp != 0)
3421 ret = -1;
3422 }
3423 } else if (IS_RELAXNG(nodes, "define")) {
3424 tmp = xmlRelaxNGParseDefine(ctxt, nodes);
3425 if (tmp != 0)
3426 ret = -1;
3427 } else if (IS_RELAXNG(nodes, "include")) {
3428 tmp = xmlRelaxNGParseInclude(ctxt, nodes);
3429 if (tmp != 0)
3430 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003431 } else {
3432 if (ctxt->error != NULL)
3433 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003434 "grammar has unexpected child %s\n", nodes->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003435 ctxt->nbErrors++;
3436 ret = -1;
3437 }
3438 nodes = nodes->next;
3439 }
3440 return (ret);
3441}
3442
3443/**
3444 * xmlRelaxNGCheckReference:
3445 * @ref: the ref
3446 * @ctxt: a Relax-NG parser context
3447 * @name: the name associated to the defines
3448 *
3449 * Applies the 4.17. combine attribute rule for all the define
3450 * element of a given grammar using the same name.
3451 */
3452static void
3453xmlRelaxNGCheckReference(xmlRelaxNGDefinePtr ref,
3454 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
3455 xmlRelaxNGGrammarPtr grammar;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003456 xmlRelaxNGDefinePtr def, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003457
3458 grammar = ctxt->grammar;
3459 if (grammar == NULL) {
3460 if (ctxt->error != NULL)
3461 ctxt->error(ctxt->userData,
3462 "Internal error: no grammar in CheckReference %s\n",
3463 name);
3464 ctxt->nbErrors++;
3465 return;
3466 }
3467 if (ref->content != NULL) {
3468 if (ctxt->error != NULL)
3469 ctxt->error(ctxt->userData,
3470 "Internal error: reference has content in CheckReference %s\n",
3471 name);
3472 ctxt->nbErrors++;
3473 return;
3474 }
3475 if (grammar->defs != NULL) {
3476 def = xmlHashLookup(grammar->defs, name);
3477 if (def != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00003478 cur = ref;
3479 while (cur != NULL) {
3480 cur->content = def;
3481 cur = cur->nextHash;
3482 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003483 } else {
Daniel Veillardd4310742003-02-18 21:12:46 +00003484 if (ctxt->error != NULL)
3485 ctxt->error(ctxt->userData,
3486 "Reference %s has no matching definition\n",
3487 name);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003488 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003489 }
Daniel Veillardd4310742003-02-18 21:12:46 +00003490 } else {
3491 if (ctxt->error != NULL)
3492 ctxt->error(ctxt->userData,
3493 "Reference %s has no matching definition\n",
3494 name);
3495 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003496 }
3497 /*
3498 * TODO: make a closure and verify there is no loop !
3499 */
3500}
3501
3502/**
3503 * xmlRelaxNGCheckCombine:
3504 * @define: the define(s) list
3505 * @ctxt: a Relax-NG parser context
3506 * @name: the name associated to the defines
3507 *
3508 * Applies the 4.17. combine attribute rule for all the define
3509 * element of a given grammar using the same name.
3510 */
3511static void
3512xmlRelaxNGCheckCombine(xmlRelaxNGDefinePtr define,
3513 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
3514 xmlChar *combine;
3515 int choiceOrInterleave = -1;
3516 int missing = 0;
3517 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
3518
3519 if (define->nextHash == NULL)
3520 return;
3521 cur = define;
3522 while (cur != NULL) {
3523 combine = xmlGetProp(cur->node, BAD_CAST "combine");
3524 if (combine != NULL) {
3525 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3526 if (choiceOrInterleave == -1)
3527 choiceOrInterleave = 1;
3528 else if (choiceOrInterleave == 0) {
3529 if (ctxt->error != NULL)
3530 ctxt->error(ctxt->userData,
3531 "Defines for %s use both 'choice' and 'interleave'\n",
3532 name);
3533 ctxt->nbErrors++;
3534 }
Daniel Veillard154877e2003-01-30 12:17:05 +00003535 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003536 if (choiceOrInterleave == -1)
3537 choiceOrInterleave = 0;
3538 else if (choiceOrInterleave == 1) {
3539 if (ctxt->error != NULL)
3540 ctxt->error(ctxt->userData,
3541 "Defines for %s use both 'choice' and 'interleave'\n",
3542 name);
3543 ctxt->nbErrors++;
3544 }
3545 } else {
3546 if (ctxt->error != NULL)
3547 ctxt->error(ctxt->userData,
3548 "Defines for %s use unknown combine value '%s''\n",
3549 name, combine);
3550 ctxt->nbErrors++;
3551 }
3552 xmlFree(combine);
3553 } else {
3554 if (missing == 0)
3555 missing = 1;
3556 else {
3557 if (ctxt->error != NULL)
3558 ctxt->error(ctxt->userData,
3559 "Some defines for %s lacks the combine attribute\n",
3560 name);
3561 ctxt->nbErrors++;
3562 }
3563 }
3564
3565 cur = cur->nextHash;
3566 }
3567#ifdef DEBUG
3568 xmlGenericError(xmlGenericErrorContext,
3569 "xmlRelaxNGCheckCombine(): merging %s defines: %d\n",
3570 name, choiceOrInterleave);
3571#endif
3572 if (choiceOrInterleave == -1)
3573 choiceOrInterleave = 0;
3574 cur = xmlRelaxNGNewDefine(ctxt, define->node);
3575 if (cur == NULL)
3576 return;
3577 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003578 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard154877e2003-01-30 12:17:05 +00003579 else
3580 cur->type = XML_RELAXNG_CHOICE;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003581 tmp = define;
3582 last = NULL;
3583 while (tmp != NULL) {
3584 if (tmp->content != NULL) {
3585 if (tmp->content->next != NULL) {
3586 /*
3587 * we need first to create a wrapper.
3588 */
3589 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
3590 if (tmp2 == NULL)
3591 break;
3592 tmp2->type = XML_RELAXNG_GROUP;
3593 tmp2->content = tmp->content;
3594 } else {
3595 tmp2 = tmp->content;
3596 }
3597 if (last == NULL) {
3598 cur->content = tmp2;
3599 } else {
3600 last->next = tmp2;
3601 }
3602 last = tmp2;
3603 tmp->content = NULL;
3604 }
3605 tmp = tmp->nextHash;
3606 }
3607 define->content = cur;
Daniel Veillard154877e2003-01-30 12:17:05 +00003608 if (choiceOrInterleave == 0) {
3609 if (ctxt->interleaves == NULL)
3610 ctxt->interleaves = xmlHashCreate(10);
3611 if (ctxt->interleaves == NULL) {
3612 if (ctxt->error != NULL)
3613 ctxt->error(ctxt->userData,
3614 "Failed to create interleaves hash table\n");
3615 ctxt->nbErrors++;
3616 } else {
3617 char tmpname[32];
3618
3619 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
3620 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
3621 if (ctxt->error != NULL)
3622 ctxt->error(ctxt->userData,
3623 "Failed to add %s to hash table\n", tmpname);
3624 ctxt->nbErrors++;
3625 }
3626 }
3627 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003628}
3629
3630/**
3631 * xmlRelaxNGCombineStart:
3632 * @ctxt: a Relax-NG parser context
3633 * @grammar: the grammar
3634 *
3635 * Applies the 4.17. combine rule for all the start
3636 * element of a given grammar.
3637 */
3638static void
3639xmlRelaxNGCombineStart(xmlRelaxNGParserCtxtPtr ctxt,
3640 xmlRelaxNGGrammarPtr grammar) {
3641 xmlRelaxNGDefinePtr starts;
3642 xmlChar *combine;
3643 int choiceOrInterleave = -1;
3644 int missing = 0;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003645 xmlRelaxNGDefinePtr cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003646
Daniel Veillard2df2de22003-02-17 23:34:33 +00003647 starts = grammar->start;
3648 if ((starts == NULL) || (starts->next == NULL))
Daniel Veillard6eadf632003-01-23 18:29:16 +00003649 return;
3650 cur = starts;
3651 while (cur != NULL) {
Daniel Veillard2df2de22003-02-17 23:34:33 +00003652 if ((cur->node == NULL) || (cur->node->parent == NULL) ||
3653 (!xmlStrEqual(cur->node->parent->name, BAD_CAST "start"))) {
3654 combine = NULL;
3655 if (ctxt->error != NULL)
3656 ctxt->error(ctxt->userData,
3657 "Internal error: start element not found\n");
3658 ctxt->nbErrors++;
3659 } else {
3660 combine = xmlGetProp(cur->node->parent, BAD_CAST "combine");
3661 }
3662
Daniel Veillard6eadf632003-01-23 18:29:16 +00003663 if (combine != NULL) {
3664 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3665 if (choiceOrInterleave == -1)
3666 choiceOrInterleave = 1;
3667 else if (choiceOrInterleave == 0) {
3668 if (ctxt->error != NULL)
3669 ctxt->error(ctxt->userData,
3670 "<start> use both 'choice' and 'interleave'\n");
3671 ctxt->nbErrors++;
3672 }
Daniel Veillard2df2de22003-02-17 23:34:33 +00003673 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003674 if (choiceOrInterleave == -1)
3675 choiceOrInterleave = 0;
3676 else if (choiceOrInterleave == 1) {
3677 if (ctxt->error != NULL)
3678 ctxt->error(ctxt->userData,
3679 "<start> use both 'choice' and 'interleave'\n");
3680 ctxt->nbErrors++;
3681 }
3682 } else {
3683 if (ctxt->error != NULL)
3684 ctxt->error(ctxt->userData,
3685 "<start> uses unknown combine value '%s''\n", combine);
3686 ctxt->nbErrors++;
3687 }
3688 xmlFree(combine);
3689 } else {
3690 if (missing == 0)
3691 missing = 1;
3692 else {
3693 if (ctxt->error != NULL)
3694 ctxt->error(ctxt->userData,
3695 "Some <start> elements lacks the combine attribute\n");
3696 ctxt->nbErrors++;
3697 }
3698 }
3699
Daniel Veillard2df2de22003-02-17 23:34:33 +00003700 cur = cur->next;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003701 }
3702#ifdef DEBUG
3703 xmlGenericError(xmlGenericErrorContext,
3704 "xmlRelaxNGCombineStart(): merging <start>: %d\n",
3705 choiceOrInterleave);
3706#endif
3707 if (choiceOrInterleave == -1)
3708 choiceOrInterleave = 0;
3709 cur = xmlRelaxNGNewDefine(ctxt, starts->node);
3710 if (cur == NULL)
3711 return;
3712 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003713 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003714 else
3715 cur->type = XML_RELAXNG_CHOICE;
3716 cur->content = grammar->start;
3717 grammar->start = cur;
3718 if (choiceOrInterleave == 0) {
3719 if (ctxt->interleaves == NULL)
3720 ctxt->interleaves = xmlHashCreate(10);
3721 if (ctxt->interleaves == NULL) {
3722 if (ctxt->error != NULL)
3723 ctxt->error(ctxt->userData,
3724 "Failed to create interleaves hash table\n");
3725 ctxt->nbErrors++;
3726 } else {
3727 char tmpname[32];
3728
3729 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
3730 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
3731 if (ctxt->error != NULL)
3732 ctxt->error(ctxt->userData,
3733 "Failed to add %s to hash table\n", tmpname);
3734 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003735 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003736 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003737 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003738}
3739
3740/**
Daniel Veillardd4310742003-02-18 21:12:46 +00003741 * xmlRelaxNGCheckCycles:
3742 * @ctxt: a Relax-NG parser context
3743 * @nodes: grammar children nodes
3744 * @depth: the counter
3745 *
3746 * Check for cycles.
3747 *
3748 * Returns 0 if check passed, and -1 in case of error
3749 */
3750static int
3751xmlRelaxNGCheckCycles(xmlRelaxNGParserCtxtPtr ctxt,
3752 xmlRelaxNGDefinePtr cur, int depth) {
3753 int ret = 0;
3754
3755 while ((ret == 0) && (cur != NULL)) {
3756 if ((cur->type == XML_RELAXNG_REF) ||
3757 (cur->type == XML_RELAXNG_PARENTREF)) {
3758 if (cur->depth == -1) {
3759 cur->depth = depth;
3760 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth);
3761 cur->depth = -2;
3762 } else if (depth == cur->depth) {
3763 if (ctxt->error != NULL)
3764 ctxt->error(ctxt->userData,
3765 "Detected a cycle in %s references\n", cur->name);
3766 ctxt->nbErrors++;
3767 return(-1);
3768 }
3769 } else if (cur->type == XML_RELAXNG_ELEMENT) {
3770 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth + 1);
3771 } else {
3772 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth);
3773 }
3774 cur = cur->next;
3775 }
3776 return(ret);
3777}
3778
3779/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00003780 * xmlRelaxNGParseGrammar:
3781 * @ctxt: a Relax-NG parser context
3782 * @nodes: grammar children nodes
3783 *
3784 * parse a Relax-NG <grammar> node
3785 *
3786 * Returns the internal xmlRelaxNGGrammarPtr built or
3787 * NULL in case of error
3788 */
3789static xmlRelaxNGGrammarPtr
3790xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
3791 xmlRelaxNGGrammarPtr ret, tmp, old;
3792
Daniel Veillard6eadf632003-01-23 18:29:16 +00003793 ret = xmlRelaxNGNewGrammar(ctxt);
3794 if (ret == NULL)
3795 return(NULL);
3796
3797 /*
3798 * Link the new grammar in the tree
3799 */
3800 ret->parent = ctxt->grammar;
3801 if (ctxt->grammar != NULL) {
3802 tmp = ctxt->grammar->children;
3803 if (tmp == NULL) {
3804 ctxt->grammar->children = ret;
3805 } else {
3806 while (tmp->next != NULL)
3807 tmp = tmp->next;
3808 tmp->next = ret;
3809 }
3810 }
3811
3812 old = ctxt->grammar;
3813 ctxt->grammar = ret;
3814 xmlRelaxNGParseGrammarContent(ctxt, nodes);
3815 ctxt->grammar = ret;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003816 if (ctxt->grammar == NULL) {
3817 if (ctxt->error != NULL)
3818 ctxt->error(ctxt->userData,
3819 "Failed to parse <grammar> content\n");
3820 ctxt->nbErrors++;
3821 } else if (ctxt->grammar->start == NULL) {
3822 if (ctxt->error != NULL)
3823 ctxt->error(ctxt->userData,
3824 "Element <grammar> has no <start>\n");
3825 ctxt->nbErrors++;
3826 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003827
3828 /*
3829 * Apply 4.17 mergingd rules to defines and starts
3830 */
3831 xmlRelaxNGCombineStart(ctxt, ret);
3832 if (ret->defs != NULL) {
3833 xmlHashScan(ret->defs, (xmlHashScanner) xmlRelaxNGCheckCombine,
3834 ctxt);
3835 }
3836
3837 /*
3838 * link together defines and refs in this grammar
3839 */
3840 if (ret->refs != NULL) {
3841 xmlHashScan(ret->refs, (xmlHashScanner) xmlRelaxNGCheckReference,
3842 ctxt);
3843 }
3844 ctxt->grammar = old;
3845 return(ret);
3846}
3847
3848/**
3849 * xmlRelaxNGParseDocument:
3850 * @ctxt: a Relax-NG parser context
3851 * @node: the root node of the RelaxNG schema
3852 *
3853 * parse a Relax-NG definition resource and build an internal
3854 * xmlRelaxNG struture which can be used to validate instances.
3855 *
3856 * Returns the internal XML RelaxNG structure built or
3857 * NULL in case of error
3858 */
3859static xmlRelaxNGPtr
3860xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3861 xmlRelaxNGPtr schema = NULL;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003862 const xmlChar *olddefine;
Daniel Veillarde431a272003-01-29 23:02:33 +00003863 xmlRelaxNGGrammarPtr old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003864
3865 if ((ctxt == NULL) || (node == NULL))
3866 return (NULL);
3867
3868 schema = xmlRelaxNGNewRelaxNG(ctxt);
3869 if (schema == NULL)
3870 return(NULL);
3871
Daniel Veillard276be4a2003-01-24 01:03:34 +00003872 olddefine = ctxt->define;
3873 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003874 if (IS_RELAXNG(node, "grammar")) {
3875 schema->topgrammar = xmlRelaxNGParseGrammar(ctxt, node->children);
3876 } else {
3877 schema->topgrammar = xmlRelaxNGNewGrammar(ctxt);
3878 if (schema->topgrammar == NULL) {
3879 return(schema);
3880 }
3881 schema->topgrammar->parent = NULL;
Daniel Veillarde431a272003-01-29 23:02:33 +00003882 old = ctxt->grammar;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003883 ctxt->grammar = schema->topgrammar;
3884 xmlRelaxNGParseStart(ctxt, node);
Daniel Veillarde431a272003-01-29 23:02:33 +00003885 if (old != NULL)
3886 ctxt->grammar = old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003887 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003888 ctxt->define = olddefine;
Daniel Veillardd4310742003-02-18 21:12:46 +00003889 if (schema->topgrammar->start != NULL) {
3890 xmlRelaxNGCheckCycles(ctxt, schema->topgrammar->start, 0);
3891 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003892
3893#ifdef DEBUG
3894 if (schema == NULL)
3895 xmlGenericError(xmlGenericErrorContext,
3896 "xmlRelaxNGParseDocument() failed\n");
3897#endif
3898
3899 return (schema);
3900}
3901
3902/************************************************************************
3903 * *
3904 * Reading RelaxNGs *
3905 * *
3906 ************************************************************************/
3907
3908/**
3909 * xmlRelaxNGNewParserCtxt:
3910 * @URL: the location of the schema
3911 *
3912 * Create an XML RelaxNGs parse context for that file/resource expected
3913 * to contain an XML RelaxNGs file.
3914 *
3915 * Returns the parser context or NULL in case of error
3916 */
3917xmlRelaxNGParserCtxtPtr
3918xmlRelaxNGNewParserCtxt(const char *URL) {
3919 xmlRelaxNGParserCtxtPtr ret;
3920
3921 if (URL == NULL)
3922 return(NULL);
3923
3924 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
3925 if (ret == NULL) {
3926 xmlGenericError(xmlGenericErrorContext,
3927 "Failed to allocate new schama parser context for %s\n", URL);
3928 return (NULL);
3929 }
3930 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
3931 ret->URL = xmlStrdup((const xmlChar *)URL);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003932 ret->error = xmlGenericError;
3933 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003934 return (ret);
3935}
3936
3937/**
3938 * xmlRelaxNGNewMemParserCtxt:
3939 * @buffer: a pointer to a char array containing the schemas
3940 * @size: the size of the array
3941 *
3942 * Create an XML RelaxNGs parse context for that memory buffer expected
3943 * to contain an XML RelaxNGs file.
3944 *
3945 * Returns the parser context or NULL in case of error
3946 */
3947xmlRelaxNGParserCtxtPtr
3948xmlRelaxNGNewMemParserCtxt(const char *buffer, int size) {
3949 xmlRelaxNGParserCtxtPtr ret;
3950
3951 if ((buffer == NULL) || (size <= 0))
3952 return(NULL);
3953
3954 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
3955 if (ret == NULL) {
3956 xmlGenericError(xmlGenericErrorContext,
3957 "Failed to allocate new schama parser context\n");
3958 return (NULL);
3959 }
3960 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
3961 ret->buffer = buffer;
3962 ret->size = size;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003963 ret->error = xmlGenericError;
3964 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003965 return (ret);
3966}
3967
3968/**
3969 * xmlRelaxNGFreeParserCtxt:
3970 * @ctxt: the schema parser context
3971 *
3972 * Free the resources associated to the schema parser context
3973 */
3974void
3975xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxtPtr ctxt) {
3976 if (ctxt == NULL)
3977 return;
3978 if (ctxt->URL != NULL)
3979 xmlFree(ctxt->URL);
3980 if (ctxt->doc != NULL)
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003981 xmlFreeDoc(ctxt->document);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003982 if (ctxt->interleaves != NULL)
3983 xmlHashFree(ctxt->interleaves, NULL);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003984 if (ctxt->documents != NULL)
3985 xmlHashFree(ctxt->documents, (xmlHashDeallocator)
3986 xmlRelaxNGFreeDocument);
3987 if (ctxt->docTab != NULL)
3988 xmlFree(ctxt->docTab);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003989 if (ctxt->incTab != NULL)
3990 xmlFree(ctxt->incTab);
Daniel Veillard419a7682003-02-03 23:22:49 +00003991 if (ctxt->defTab != NULL) {
3992 int i;
3993
3994 for (i = 0;i < ctxt->defNr;i++)
3995 xmlRelaxNGFreeDefine(ctxt->defTab[i]);
3996 xmlFree(ctxt->defTab);
3997 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003998 xmlFree(ctxt);
3999}
4000
Daniel Veillard6eadf632003-01-23 18:29:16 +00004001/**
Daniel Veillardd2298792003-02-14 16:54:11 +00004002 * xmlRelaxNGNormExtSpace:
4003 * @value: a value
4004 *
4005 * Removes the leading and ending spaces of the value
4006 * The string is modified "in situ"
4007 */
4008static void
4009xmlRelaxNGNormExtSpace(xmlChar *value) {
4010 xmlChar *start = value;
4011 xmlChar *cur = value;
4012 if (value == NULL)
4013 return;
4014
4015 while (IS_BLANK(*cur)) cur++;
4016 if (cur == start) {
4017 do {
4018 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
4019 if (*cur == 0)
4020 return;
4021 start = cur;
4022 while (IS_BLANK(*cur)) cur++;
4023 if (*cur == 0) {
4024 *start = 0;
4025 return;
4026 }
4027 } while (1);
4028 } else {
4029 do {
4030 while ((*cur != 0) && (!IS_BLANK(*cur)))
4031 *start++ = *cur++;
4032 if (*cur == 0) {
4033 *start = 0;
4034 return;
4035 }
4036 /* don't try to normalize the inner spaces */
4037 while (IS_BLANK(*cur)) cur++;
4038 *start++ = *cur++;
4039 if (*cur == 0) {
4040 *start = 0;
4041 return;
4042 }
4043 } while (1);
4044 }
4045}
4046
4047/**
4048 * xmlRelaxNGCheckAttributes:
4049 * @ctxt: a Relax-NG parser context
4050 * @node: a Relax-NG node
4051 *
4052 * Check all the attributes on the given node
4053 */
4054static void
4055xmlRelaxNGCleanupAttributes(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
4056 xmlAttrPtr cur, next;
4057
4058 cur = node->properties;
4059 while (cur != NULL) {
4060 next = cur->next;
4061 if ((cur->ns == NULL) ||
4062 (xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
4063 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
4064 if ((!xmlStrEqual(node->name, BAD_CAST "element")) &&
4065 (!xmlStrEqual(node->name, BAD_CAST "attribute")) &&
4066 (!xmlStrEqual(node->name, BAD_CAST "ref")) &&
4067 (!xmlStrEqual(node->name, BAD_CAST "parentRef")) &&
Daniel Veillard2df2de22003-02-17 23:34:33 +00004068 (!xmlStrEqual(node->name, BAD_CAST "param")) &&
Daniel Veillardd2298792003-02-14 16:54:11 +00004069 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
4070 if (ctxt->error != NULL)
4071 ctxt->error(ctxt->userData,
4072 "Attribute %s is not allowed on %s\n",
4073 cur->name, node->name);
4074 ctxt->nbErrors++;
4075 }
4076 } else if (xmlStrEqual(cur->name, BAD_CAST "type")) {
4077 if ((!xmlStrEqual(node->name, BAD_CAST "value")) &&
4078 (!xmlStrEqual(node->name, BAD_CAST "data"))) {
4079 if (ctxt->error != NULL)
4080 ctxt->error(ctxt->userData,
4081 "Attribute %s is not allowed on %s\n",
4082 cur->name, node->name);
4083 ctxt->nbErrors++;
4084 }
4085 } else if (xmlStrEqual(cur->name, BAD_CAST "href")) {
4086 if ((!xmlStrEqual(node->name, BAD_CAST "externalRef")) &&
4087 (!xmlStrEqual(node->name, BAD_CAST "include"))) {
4088 if (ctxt->error != NULL)
4089 ctxt->error(ctxt->userData,
4090 "Attribute %s is not allowed on %s\n",
4091 cur->name, node->name);
4092 ctxt->nbErrors++;
4093 }
4094 } else if (xmlStrEqual(cur->name, BAD_CAST "combine")) {
4095 if ((!xmlStrEqual(node->name, BAD_CAST "start")) &&
4096 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
4097 if (ctxt->error != NULL)
4098 ctxt->error(ctxt->userData,
4099 "Attribute %s is not allowed on %s\n",
4100 cur->name, node->name);
4101 ctxt->nbErrors++;
4102 }
4103 } else if (xmlStrEqual(cur->name, BAD_CAST "datatypeLibrary")) {
4104 xmlChar *val;
4105 xmlURIPtr uri;
4106
4107 val = xmlNodeListGetString(node->doc, cur->children, 1);
4108 if (val != NULL) {
4109 if (val[0] != 0) {
4110 uri = xmlParseURI((const char *) val);
4111 if (uri == NULL) {
4112 if (ctxt->error != NULL)
4113 ctxt->error(ctxt->userData,
4114 "Attribute %s contains invalid URI %s\n",
4115 cur->name, val);
4116 ctxt->nbErrors++;
4117 } else {
4118 if (uri->scheme == NULL) {
4119 if (ctxt->error != NULL)
4120 ctxt->error(ctxt->userData,
4121 "Attribute %s URI %s is not absolute\n",
4122 cur->name, val);
4123 ctxt->nbErrors++;
4124 }
4125 if (uri->fragment != NULL) {
4126 if (ctxt->error != NULL)
4127 ctxt->error(ctxt->userData,
4128 "Attribute %s URI %s has a fragment ID\n",
4129 cur->name, val);
4130 ctxt->nbErrors++;
4131 }
4132 xmlFreeURI(uri);
4133 }
4134 }
4135 xmlFree(val);
4136 }
4137 } else if (!xmlStrEqual(cur->name, BAD_CAST "ns")) {
4138 if (ctxt->error != NULL)
4139 ctxt->error(ctxt->userData,
4140 "Unknown attribute %s on %s\n",
4141 cur->name, node->name);
4142 ctxt->nbErrors++;
4143 }
4144 }
4145 cur = next;
4146 }
4147}
4148
4149/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004150 * xmlRelaxNGCleanupDoc:
4151 * @ctxt: a Relax-NG parser context
4152 * @doc: an xmldocPtr document pointer
Daniel Veillard6eadf632003-01-23 18:29:16 +00004153 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004154 * Cleanup the document from unwanted nodes for parsing, resolve
4155 * Include and externalRef lookups.
Daniel Veillard6eadf632003-01-23 18:29:16 +00004156 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004157 * Returns the cleaned up document or NULL in case of error
Daniel Veillard6eadf632003-01-23 18:29:16 +00004158 */
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004159static xmlDocPtr
4160xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt, xmlDocPtr doc) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00004161 xmlNodePtr root, cur, delete;
4162
Daniel Veillard6eadf632003-01-23 18:29:16 +00004163 /*
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004164 * Extract the root
Daniel Veillard6eadf632003-01-23 18:29:16 +00004165 */
4166 root = xmlDocGetRootElement(doc);
4167 if (root == NULL) {
4168 if (ctxt->error != NULL)
4169 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
4170 ctxt->URL);
4171 ctxt->nbErrors++;
4172 return (NULL);
4173 }
4174
4175 /*
4176 * Remove all the blank text nodes
4177 */
4178 delete = NULL;
4179 cur = root;
4180 while (cur != NULL) {
4181 if (delete != NULL) {
4182 xmlUnlinkNode(delete);
4183 xmlFreeNode(delete);
4184 delete = NULL;
4185 }
4186 if (cur->type == XML_ELEMENT_NODE) {
4187 /*
4188 * Simplification 4.1. Annotations
4189 */
4190 if ((cur->ns == NULL) ||
4191 (!xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
Daniel Veillardd2298792003-02-14 16:54:11 +00004192 if ((cur->parent != NULL) &&
4193 (cur->parent->type == XML_ELEMENT_NODE) &&
4194 ((xmlStrEqual(cur->parent->name, BAD_CAST "name")) ||
4195 (xmlStrEqual(cur->parent->name, BAD_CAST "value")) ||
4196 (xmlStrEqual(cur->parent->name, BAD_CAST "param")))) {
4197 if (ctxt->error != NULL)
4198 ctxt->error(ctxt->userData,
4199 "element %s doesn't allow foreign elements\n",
4200 cur->parent->name);
4201 ctxt->nbErrors++;
4202 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004203 delete = cur;
4204 goto skip_children;
4205 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00004206 xmlRelaxNGCleanupAttributes(ctxt, cur);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004207 if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004208 xmlChar *href, *ns, *base, *URL;
4209 xmlRelaxNGDocumentPtr docu;
Daniel Veillard416589a2003-02-17 17:25:42 +00004210 xmlNodePtr tmp;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004211
4212 ns = xmlGetProp(cur, BAD_CAST "ns");
Daniel Veillard416589a2003-02-17 17:25:42 +00004213 if (ns == NULL) {
4214 tmp = cur->parent;
4215 while ((tmp != NULL) &&
4216 (tmp->type == XML_ELEMENT_NODE)) {
4217 ns = xmlGetProp(tmp, BAD_CAST "ns");
4218 if (ns != NULL)
4219 break;
4220 tmp = tmp->parent;
4221 }
4222 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004223 href = xmlGetProp(cur, BAD_CAST "href");
4224 if (href == NULL) {
4225 if (ctxt->error != NULL)
4226 ctxt->error(ctxt->userData,
4227 "xmlRelaxNGParse: externalRef has no href attribute\n");
4228 ctxt->nbErrors++;
4229 delete = cur;
4230 goto skip_children;
4231 }
4232 base = xmlNodeGetBase(cur->doc, cur);
4233 URL = xmlBuildURI(href, base);
4234 if (URL == NULL) {
4235 if (ctxt->error != NULL)
4236 ctxt->error(ctxt->userData,
4237 "Failed to compute URL for externalRef %s\n", href);
4238 ctxt->nbErrors++;
4239 if (href != NULL)
4240 xmlFree(href);
4241 if (base != NULL)
4242 xmlFree(base);
4243 delete = cur;
4244 goto skip_children;
4245 }
4246 if (href != NULL)
4247 xmlFree(href);
4248 if (base != NULL)
4249 xmlFree(base);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004250 docu = xmlRelaxNGLoadExternalRef(ctxt, URL, ns);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004251 if (docu == NULL) {
4252 if (ctxt->error != NULL)
4253 ctxt->error(ctxt->userData,
4254 "Failed to load externalRef %s\n", URL);
4255 ctxt->nbErrors++;
4256 xmlFree(URL);
4257 delete = cur;
4258 goto skip_children;
4259 }
4260 xmlFree(URL);
4261 cur->_private = docu;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004262 } else if (xmlStrEqual(cur->name, BAD_CAST "include")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00004263 xmlChar *href, *ns, *base, *URL;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004264 xmlRelaxNGIncludePtr incl;
Daniel Veillard416589a2003-02-17 17:25:42 +00004265 xmlNodePtr tmp;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004266
4267 href = xmlGetProp(cur, BAD_CAST "href");
4268 if (href == NULL) {
4269 if (ctxt->error != NULL)
4270 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004271 "xmlRelaxNGParse: include has no href attribute\n");
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004272 ctxt->nbErrors++;
4273 delete = cur;
4274 goto skip_children;
4275 }
4276 base = xmlNodeGetBase(cur->doc, cur);
4277 URL = xmlBuildURI(href, base);
4278 if (URL == NULL) {
4279 if (ctxt->error != NULL)
4280 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004281 "Failed to compute URL for include %s\n", href);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004282 ctxt->nbErrors++;
4283 if (href != NULL)
4284 xmlFree(href);
4285 if (base != NULL)
4286 xmlFree(base);
4287 delete = cur;
4288 goto skip_children;
4289 }
4290 if (href != NULL)
4291 xmlFree(href);
4292 if (base != NULL)
4293 xmlFree(base);
Daniel Veillard416589a2003-02-17 17:25:42 +00004294 ns = xmlGetProp(cur, BAD_CAST "ns");
4295 if (ns == NULL) {
4296 tmp = cur->parent;
4297 while ((tmp != NULL) &&
4298 (tmp->type == XML_ELEMENT_NODE)) {
4299 ns = xmlGetProp(tmp, BAD_CAST "ns");
4300 if (ns != NULL)
4301 break;
4302 tmp = tmp->parent;
4303 }
4304 }
4305 incl = xmlRelaxNGLoadInclude(ctxt, URL, cur, ns);
4306 if (ns != NULL)
4307 xmlFree(ns);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004308 if (incl == NULL) {
4309 if (ctxt->error != NULL)
4310 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004311 "Failed to load include %s\n", URL);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004312 ctxt->nbErrors++;
4313 xmlFree(URL);
4314 delete = cur;
4315 goto skip_children;
4316 }
4317 xmlFree(URL);
4318 cur->_private = incl;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004319 } else if ((xmlStrEqual(cur->name, BAD_CAST "element")) ||
4320 (xmlStrEqual(cur->name, BAD_CAST "attribute"))) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00004321 xmlChar *name, *ns;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004322 xmlNodePtr text = NULL;
4323
4324 /*
4325 * Simplification 4.8. name attribute of element
4326 * and attribute elements
4327 */
4328 name = xmlGetProp(cur, BAD_CAST "name");
4329 if (name != NULL) {
4330 if (cur->children == NULL) {
4331 text = xmlNewChild(cur, cur->ns, BAD_CAST "name",
4332 name);
4333 } else {
4334 xmlNodePtr node;
4335 node = xmlNewNode(cur->ns, BAD_CAST "name");
4336 if (node != NULL) {
4337 xmlAddPrevSibling(cur->children, node);
4338 text = xmlNewText(name);
4339 xmlAddChild(node, text);
4340 text = node;
4341 }
4342 }
Daniel Veillardfebcca42003-02-16 15:44:18 +00004343 if (text == NULL) {
4344 if (ctxt->error != NULL)
4345 ctxt->error(ctxt->userData,
4346 "Failed to create a name %s element\n", name);
4347 ctxt->nbErrors++;
4348 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004349 xmlUnsetProp(cur, BAD_CAST "name");
4350 xmlFree(name);
Daniel Veillardfebcca42003-02-16 15:44:18 +00004351 ns = xmlGetProp(cur, BAD_CAST "ns");
4352 if (ns != NULL) {
4353 if (text != NULL) {
4354 xmlSetProp(text, BAD_CAST "ns", ns);
4355 /* xmlUnsetProp(cur, BAD_CAST "ns"); */
Daniel Veillard6eadf632003-01-23 18:29:16 +00004356 }
Daniel Veillardfebcca42003-02-16 15:44:18 +00004357 xmlFree(ns);
4358 } else if (xmlStrEqual(cur->name,
4359 BAD_CAST "attribute")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00004360 xmlSetProp(text, BAD_CAST "ns", BAD_CAST "");
4361 }
4362 }
4363 } else if ((xmlStrEqual(cur->name, BAD_CAST "name")) ||
4364 (xmlStrEqual(cur->name, BAD_CAST "nsName")) ||
4365 (xmlStrEqual(cur->name, BAD_CAST "value"))) {
4366 /*
4367 * Simplification 4.8. name attribute of element
4368 * and attribute elements
4369 */
4370 if (xmlHasProp(cur, BAD_CAST "ns") == NULL) {
4371 xmlNodePtr node;
4372 xmlChar *ns = NULL;
4373
4374 node = cur->parent;
4375 while ((node != NULL) &&
4376 (node->type == XML_ELEMENT_NODE)) {
4377 ns = xmlGetProp(node, BAD_CAST "ns");
4378 if (ns != NULL) {
4379 break;
4380 }
4381 node = node->parent;
4382 }
4383 if (ns == NULL) {
4384 xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
4385 } else {
4386 xmlSetProp(cur, BAD_CAST "ns", ns);
4387 xmlFree(ns);
4388 }
4389 }
4390 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
4391 xmlChar *name, *local, *prefix;
4392
4393 /*
4394 * Simplification: 4.10. QNames
4395 */
4396 name = xmlNodeGetContent(cur);
4397 if (name != NULL) {
4398 local = xmlSplitQName2(name, &prefix);
4399 if (local != NULL) {
4400 xmlNsPtr ns;
4401
4402 ns = xmlSearchNs(cur->doc, cur, prefix);
4403 if (ns == NULL) {
4404 if (ctxt->error != NULL)
4405 ctxt->error(ctxt->userData,
4406 "xmlRelaxNGParse: no namespace for prefix %s\n", prefix);
4407 ctxt->nbErrors++;
4408 } else {
4409 xmlSetProp(cur, BAD_CAST "ns", ns->href);
4410 xmlNodeSetContent(cur, local);
4411 }
4412 xmlFree(local);
4413 xmlFree(prefix);
4414 }
4415 xmlFree(name);
4416 }
4417 }
4418 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004419 /*
4420 * Thisd is not an else since "include" is transformed
4421 * into a div
4422 */
4423 if (xmlStrEqual(cur->name, BAD_CAST "div")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00004424 xmlChar *ns;
4425 xmlNodePtr child, ins, tmp;
4426
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004427 /*
4428 * implements rule 4.11
4429 */
Daniel Veillard416589a2003-02-17 17:25:42 +00004430
4431 ns = xmlGetProp(cur, BAD_CAST "ns");
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004432
4433 child = cur->children;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004434 ins = cur;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004435 while (child != NULL) {
Daniel Veillard416589a2003-02-17 17:25:42 +00004436 if (ns != NULL) {
4437 if (!xmlHasProp(child, BAD_CAST "ns")) {
4438 xmlSetProp(child, BAD_CAST "ns", ns);
4439 }
4440 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004441 tmp = child->next;
4442 xmlUnlinkNode(child);
4443 ins = xmlAddNextSibling(ins, child);
4444 child = tmp;
4445 }
Daniel Veillard416589a2003-02-17 17:25:42 +00004446 if (ns != NULL)
4447 xmlFree(ns);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004448 delete = cur;
4449 goto skip_children;
4450 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004451 }
4452 }
4453 /*
4454 * Simplification 4.2 whitespaces
4455 */
4456 else if (cur->type == XML_TEXT_NODE) {
4457 if (IS_BLANK_NODE(cur)) {
4458 if (cur->parent->type == XML_ELEMENT_NODE) {
4459 if ((!xmlStrEqual(cur->parent->name, BAD_CAST "value")) &&
4460 (!xmlStrEqual(cur->parent->name, BAD_CAST "param")))
4461 delete = cur;
4462 } else {
4463 delete = cur;
4464 goto skip_children;
4465 }
4466 }
4467 } else if (cur->type != XML_CDATA_SECTION_NODE) {
4468 delete = cur;
4469 goto skip_children;
4470 }
4471
4472 /*
4473 * Skip to next node
4474 */
4475 if (cur->children != NULL) {
4476 if ((cur->children->type != XML_ENTITY_DECL) &&
4477 (cur->children->type != XML_ENTITY_REF_NODE) &&
4478 (cur->children->type != XML_ENTITY_NODE)) {
4479 cur = cur->children;
4480 continue;
4481 }
4482 }
4483skip_children:
4484 if (cur->next != NULL) {
4485 cur = cur->next;
4486 continue;
4487 }
4488
4489 do {
4490 cur = cur->parent;
4491 if (cur == NULL)
4492 break;
4493 if (cur == root) {
4494 cur = NULL;
4495 break;
4496 }
4497 if (cur->next != NULL) {
4498 cur = cur->next;
4499 break;
4500 }
4501 } while (cur != NULL);
4502 }
4503 if (delete != NULL) {
4504 xmlUnlinkNode(delete);
4505 xmlFreeNode(delete);
4506 delete = NULL;
4507 }
4508
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004509 return(doc);
4510}
4511
4512/**
4513 * xmlRelaxNGParse:
4514 * @ctxt: a Relax-NG parser context
4515 *
4516 * parse a schema definition resource and build an internal
4517 * XML Shema struture which can be used to validate instances.
4518 * *WARNING* this interface is highly subject to change
4519 *
4520 * Returns the internal XML RelaxNG structure built from the resource or
4521 * NULL in case of error
4522 */
4523xmlRelaxNGPtr
4524xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt)
4525{
4526 xmlRelaxNGPtr ret = NULL;
4527 xmlDocPtr doc;
4528 xmlNodePtr root;
4529
4530 xmlRelaxNGInitTypes();
4531
4532 if (ctxt == NULL)
4533 return (NULL);
4534
4535 /*
4536 * First step is to parse the input document into an DOM/Infoset
4537 */
4538 if (ctxt->URL != NULL) {
4539 doc = xmlParseFile((const char *) ctxt->URL);
4540 if (doc == NULL) {
4541 if (ctxt->error != NULL)
4542 ctxt->error(ctxt->userData,
4543 "xmlRelaxNGParse: could not load %s\n", ctxt->URL);
4544 ctxt->nbErrors++;
4545 return (NULL);
4546 }
4547 } else if (ctxt->buffer != NULL) {
4548 doc = xmlParseMemory(ctxt->buffer, ctxt->size);
4549 if (doc == NULL) {
4550 if (ctxt->error != NULL)
4551 ctxt->error(ctxt->userData,
4552 "xmlRelaxNGParse: could not parse schemas\n");
4553 ctxt->nbErrors++;
4554 return (NULL);
4555 }
4556 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
4557 ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
4558 } else {
4559 if (ctxt->error != NULL)
4560 ctxt->error(ctxt->userData,
4561 "xmlRelaxNGParse: nothing to parse\n");
4562 ctxt->nbErrors++;
4563 return (NULL);
4564 }
4565 ctxt->document = doc;
4566
4567 /*
4568 * Some preprocessing of the document content
4569 */
4570 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
4571 if (doc == NULL) {
4572 xmlFreeDoc(ctxt->document);
4573 ctxt->document = NULL;
4574 return(NULL);
4575 }
4576
Daniel Veillard6eadf632003-01-23 18:29:16 +00004577 /*
4578 * Then do the parsing for good
4579 */
4580 root = xmlDocGetRootElement(doc);
4581 if (root == NULL) {
4582 if (ctxt->error != NULL)
4583 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
4584 ctxt->URL);
4585 ctxt->nbErrors++;
4586 return (NULL);
4587 }
4588 ret = xmlRelaxNGParseDocument(ctxt, root);
4589 if (ret == NULL)
4590 return(NULL);
4591
4592 /*
4593 * Check the ref/defines links
4594 */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004595 /*
4596 * try to preprocess interleaves
4597 */
4598 if (ctxt->interleaves != NULL) {
4599 xmlHashScan(ctxt->interleaves,
4600 (xmlHashScanner)xmlRelaxNGComputeInterleaves, ctxt);
4601 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004602
4603 /*
4604 * if there was a parsing error return NULL
4605 */
4606 if (ctxt->nbErrors > 0) {
4607 xmlRelaxNGFree(ret);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004608 ctxt->document = NULL;
4609 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004610 return(NULL);
4611 }
4612
4613 /*
4614 * Transfer the pointer for cleanup at the schema level.
4615 */
4616 ret->doc = doc;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004617 ctxt->document = NULL;
4618 ret->documents = ctxt->documents;
4619 ctxt->documents = NULL;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004620 ret->includes = ctxt->includes;
4621 ctxt->includes = NULL;
Daniel Veillard419a7682003-02-03 23:22:49 +00004622 ret->defNr = ctxt->defNr;
4623 ret->defTab = ctxt->defTab;
4624 ctxt->defTab = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004625
4626 return (ret);
4627}
4628
4629/**
4630 * xmlRelaxNGSetParserErrors:
4631 * @ctxt: a Relax-NG validation context
4632 * @err: the error callback
4633 * @warn: the warning callback
4634 * @ctx: contextual data for the callbacks
4635 *
4636 * Set the callback functions used to handle errors for a validation context
4637 */
4638void
4639xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
4640 xmlRelaxNGValidityErrorFunc err,
4641 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
4642 if (ctxt == NULL)
4643 return;
4644 ctxt->error = err;
4645 ctxt->warning = warn;
4646 ctxt->userData = ctx;
4647}
4648/************************************************************************
4649 * *
4650 * Dump back a compiled form *
4651 * *
4652 ************************************************************************/
4653static void xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define);
4654
4655/**
4656 * xmlRelaxNGDumpDefines:
4657 * @output: the file output
4658 * @defines: a list of define structures
4659 *
4660 * Dump a RelaxNG structure back
4661 */
4662static void
4663xmlRelaxNGDumpDefines(FILE * output, xmlRelaxNGDefinePtr defines) {
4664 while (defines != NULL) {
4665 xmlRelaxNGDumpDefine(output, defines);
4666 defines = defines->next;
4667 }
4668}
4669
4670/**
4671 * xmlRelaxNGDumpDefine:
4672 * @output: the file output
4673 * @define: a define structure
4674 *
4675 * Dump a RelaxNG structure back
4676 */
4677static void
4678xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define) {
4679 if (define == NULL)
4680 return;
4681 switch(define->type) {
4682 case XML_RELAXNG_EMPTY:
4683 fprintf(output, "<empty/>\n");
4684 break;
4685 case XML_RELAXNG_NOT_ALLOWED:
4686 fprintf(output, "<notAllowed/>\n");
4687 break;
4688 case XML_RELAXNG_TEXT:
4689 fprintf(output, "<text/>\n");
4690 break;
4691 case XML_RELAXNG_ELEMENT:
4692 fprintf(output, "<element>\n");
4693 if (define->name != NULL) {
4694 fprintf(output, "<name");
4695 if (define->ns != NULL)
4696 fprintf(output, " ns=\"%s\"", define->ns);
4697 fprintf(output, ">%s</name>\n", define->name);
4698 }
4699 xmlRelaxNGDumpDefines(output, define->attrs);
4700 xmlRelaxNGDumpDefines(output, define->content);
4701 fprintf(output, "</element>\n");
4702 break;
4703 case XML_RELAXNG_LIST:
4704 fprintf(output, "<list>\n");
4705 xmlRelaxNGDumpDefines(output, define->content);
4706 fprintf(output, "</list>\n");
4707 break;
4708 case XML_RELAXNG_ONEORMORE:
4709 fprintf(output, "<oneOrMore>\n");
4710 xmlRelaxNGDumpDefines(output, define->content);
4711 fprintf(output, "</oneOrMore>\n");
4712 break;
4713 case XML_RELAXNG_ZEROORMORE:
4714 fprintf(output, "<zeroOrMore>\n");
4715 xmlRelaxNGDumpDefines(output, define->content);
4716 fprintf(output, "</zeroOrMore>\n");
4717 break;
4718 case XML_RELAXNG_CHOICE:
4719 fprintf(output, "<choice>\n");
4720 xmlRelaxNGDumpDefines(output, define->content);
4721 fprintf(output, "</choice>\n");
4722 break;
4723 case XML_RELAXNG_GROUP:
4724 fprintf(output, "<group>\n");
4725 xmlRelaxNGDumpDefines(output, define->content);
4726 fprintf(output, "</group>\n");
4727 break;
4728 case XML_RELAXNG_INTERLEAVE:
4729 fprintf(output, "<interleave>\n");
4730 xmlRelaxNGDumpDefines(output, define->content);
4731 fprintf(output, "</interleave>\n");
4732 break;
4733 case XML_RELAXNG_OPTIONAL:
4734 fprintf(output, "<optional>\n");
4735 xmlRelaxNGDumpDefines(output, define->content);
4736 fprintf(output, "</optional>\n");
4737 break;
4738 case XML_RELAXNG_ATTRIBUTE:
4739 fprintf(output, "<attribute>\n");
4740 xmlRelaxNGDumpDefines(output, define->content);
4741 fprintf(output, "</attribute>\n");
4742 break;
4743 case XML_RELAXNG_DEF:
4744 fprintf(output, "<define");
4745 if (define->name != NULL)
4746 fprintf(output, " name=\"%s\"", define->name);
4747 fprintf(output, ">\n");
4748 xmlRelaxNGDumpDefines(output, define->content);
4749 fprintf(output, "</define>\n");
4750 break;
4751 case XML_RELAXNG_REF:
4752 fprintf(output, "<ref");
4753 if (define->name != NULL)
4754 fprintf(output, " name=\"%s\"", define->name);
4755 fprintf(output, ">\n");
4756 xmlRelaxNGDumpDefines(output, define->content);
4757 fprintf(output, "</ref>\n");
4758 break;
Daniel Veillard419a7682003-02-03 23:22:49 +00004759 case XML_RELAXNG_PARENTREF:
4760 fprintf(output, "<parentRef");
4761 if (define->name != NULL)
4762 fprintf(output, " name=\"%s\"", define->name);
4763 fprintf(output, ">\n");
4764 xmlRelaxNGDumpDefines(output, define->content);
4765 fprintf(output, "</parentRef>\n");
4766 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004767 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard416589a2003-02-17 17:25:42 +00004768 fprintf(output, "<externalRef>");
Daniel Veillarde431a272003-01-29 23:02:33 +00004769 xmlRelaxNGDumpDefines(output, define->content);
4770 fprintf(output, "</externalRef>\n");
4771 break;
4772 case XML_RELAXNG_DATATYPE:
Daniel Veillard6eadf632003-01-23 18:29:16 +00004773 case XML_RELAXNG_VALUE:
4774 TODO
4775 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004776 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00004777 case XML_RELAXNG_EXCEPT:
Daniel Veillard8fe98712003-02-19 00:19:14 +00004778 case XML_RELAXNG_PARAM:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004779 TODO
4780 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004781 }
4782}
4783
4784/**
4785 * xmlRelaxNGDumpGrammar:
4786 * @output: the file output
4787 * @grammar: a grammar structure
4788 * @top: is this a top grammar
4789 *
4790 * Dump a RelaxNG structure back
4791 */
4792static void
4793xmlRelaxNGDumpGrammar(FILE * output, xmlRelaxNGGrammarPtr grammar, int top)
4794{
4795 if (grammar == NULL)
4796 return;
4797
4798 fprintf(output, "<grammar");
4799 if (top)
4800 fprintf(output,
4801 " xmlns=\"http://relaxng.org/ns/structure/1.0\"");
4802 switch(grammar->combine) {
4803 case XML_RELAXNG_COMBINE_UNDEFINED:
4804 break;
4805 case XML_RELAXNG_COMBINE_CHOICE:
4806 fprintf(output, " combine=\"choice\"");
4807 break;
4808 case XML_RELAXNG_COMBINE_INTERLEAVE:
4809 fprintf(output, " combine=\"interleave\"");
4810 break;
4811 default:
4812 fprintf(output, " <!-- invalid combine value -->");
4813 }
4814 fprintf(output, ">\n");
4815 if (grammar->start == NULL) {
4816 fprintf(output, " <!-- grammar had no start -->");
4817 } else {
4818 fprintf(output, "<start>\n");
4819 xmlRelaxNGDumpDefine(output, grammar->start);
4820 fprintf(output, "</start>\n");
4821 }
4822 /* TODO ? Dump the defines ? */
4823 fprintf(output, "</grammar>\n");
4824}
4825
4826/**
4827 * xmlRelaxNGDump:
4828 * @output: the file output
4829 * @schema: a schema structure
4830 *
4831 * Dump a RelaxNG structure back
4832 */
4833void
4834xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema)
4835{
4836 if (schema == NULL) {
4837 fprintf(output, "RelaxNG empty or failed to compile\n");
4838 return;
4839 }
4840 fprintf(output, "RelaxNG: ");
4841 if (schema->doc == NULL) {
4842 fprintf(output, "no document\n");
4843 } else if (schema->doc->URL != NULL) {
4844 fprintf(output, "%s\n", schema->doc->URL);
4845 } else {
4846 fprintf(output, "\n");
4847 }
4848 if (schema->topgrammar == NULL) {
4849 fprintf(output, "RelaxNG has no top grammar\n");
4850 return;
4851 }
4852 xmlRelaxNGDumpGrammar(output, schema->topgrammar, 1);
4853}
4854
Daniel Veillardfebcca42003-02-16 15:44:18 +00004855/**
4856 * xmlRelaxNGDumpTree:
4857 * @output: the file output
4858 * @schema: a schema structure
4859 *
4860 * Dump the transformed RelaxNG tree.
4861 */
4862void
4863xmlRelaxNGDumpTree(FILE * output, xmlRelaxNGPtr schema)
4864{
4865 if (schema == NULL) {
4866 fprintf(output, "RelaxNG empty or failed to compile\n");
4867 return;
4868 }
4869 if (schema->doc == NULL) {
4870 fprintf(output, "no document\n");
4871 } else {
4872 xmlDocDump(output, schema->doc);
4873 }
4874}
4875
Daniel Veillard6eadf632003-01-23 18:29:16 +00004876/************************************************************************
4877 * *
4878 * Validation implementation *
4879 * *
4880 ************************************************************************/
4881static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
4882 xmlRelaxNGDefinePtr define);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004883static int xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
4884 xmlRelaxNGDefinePtr define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004885
4886/**
4887 * xmlRelaxNGSkipIgnored:
4888 * @ctxt: a schema validation context
4889 * @node: the top node.
4890 *
4891 * Skip ignorable nodes in that context
4892 *
4893 * Returns the new sibling or NULL in case of error.
4894 */
4895static xmlNodePtr
4896xmlRelaxNGSkipIgnored(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
4897 xmlNodePtr node) {
4898 /*
4899 * TODO complete and handle entities
4900 */
4901 while ((node != NULL) &&
4902 ((node->type == XML_COMMENT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004903 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00004904 ((node->type == XML_TEXT_NODE) &&
4905 (IS_BLANK_NODE(node))))) {
4906 node = node->next;
4907 }
4908 return(node);
4909}
4910
4911/**
Daniel Veillardedc91922003-01-26 00:52:04 +00004912 * xmlRelaxNGNormalize:
4913 * @ctxt: a schema validation context
4914 * @str: the string to normalize
4915 *
4916 * Implements the normalizeWhiteSpace( s ) function from
4917 * section 6.2.9 of the spec
4918 *
4919 * Returns the new string or NULL in case of error.
4920 */
4921static xmlChar *
4922xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *str) {
4923 xmlChar *ret, *p;
4924 const xmlChar *tmp;
4925 int len;
4926
4927 if (str == NULL)
4928 return(NULL);
4929 tmp = str;
4930 while (*tmp != 0) tmp++;
4931 len = tmp - str;
4932
4933 ret = (xmlChar *) xmlMalloc((len + 1) * sizeof(xmlChar));
4934 if (ret == NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00004935 if (ctxt != NULL) {
4936 VALID_CTXT();
4937 VALID_ERROR("xmlRelaxNGNormalize: out of memory\n");
4938 } else {
4939 xmlGenericError(xmlGenericErrorContext,
4940 "xmlRelaxNGNormalize: out of memory\n");
4941 }
Daniel Veillardedc91922003-01-26 00:52:04 +00004942 return(NULL);
4943 }
4944 p = ret;
4945 while (IS_BLANK(*str)) str++;
4946 while (*str != 0) {
4947 if (IS_BLANK(*str)) {
4948 while (IS_BLANK(*str)) str++;
4949 if (*str == 0)
4950 break;
4951 *p++ = ' ';
4952 } else
4953 *p++ = *str++;
4954 }
4955 *p = 0;
4956 return(ret);
4957}
4958
4959/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004960 * xmlRelaxNGValidateDatatype:
4961 * @ctxt: a Relax-NG validation context
4962 * @value: the string value
4963 * @type: the datatype definition
4964 *
4965 * Validate the given value against the dataype
4966 *
4967 * Returns 0 if the validation succeeded or an error code.
4968 */
4969static int
4970xmlRelaxNGValidateDatatype(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *value,
4971 xmlRelaxNGDefinePtr define) {
4972 int ret;
4973 xmlRelaxNGTypeLibraryPtr lib;
4974
4975 if ((define == NULL) || (define->data == NULL)) {
4976 return(-1);
4977 }
4978 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
4979 if (lib->check != NULL)
4980 ret = lib->check(lib->data, define->name, value);
4981 else
4982 ret = -1;
4983 if (ret < 0) {
4984 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004985 VALID_ERROR2("Internal: failed to validate type %s\n", define->name);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004986 return(-1);
4987 } else if (ret == 1) {
4988 ret = 0;
4989 } else {
4990 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004991 VALID_ERROR3("Type %s doesn't allow value %s\n", define->name, value);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004992 return(-1);
4993 ret = -1;
4994 }
Daniel Veillard416589a2003-02-17 17:25:42 +00004995 if ((ret == 0) && (define->content != NULL)) {
4996 const xmlChar *oldvalue, *oldendvalue;
4997
4998 oldvalue = ctxt->state->value;
4999 oldendvalue = ctxt->state->endvalue;
5000 ctxt->state->value = (xmlChar *) value;
5001 ctxt->state->endvalue = NULL;
5002 ret = xmlRelaxNGValidateValue(ctxt, define->content);
5003 ctxt->state->value = (xmlChar *) oldvalue;
5004 ctxt->state->endvalue = (xmlChar *) oldendvalue;
5005 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005006 return(ret);
5007}
5008
5009/**
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005010 * xmlRelaxNGNextValue:
5011 * @ctxt: a Relax-NG validation context
5012 *
5013 * Skip to the next value when validating within a list
5014 *
5015 * Returns 0 if the operation succeeded or an error code.
5016 */
5017static int
5018xmlRelaxNGNextValue(xmlRelaxNGValidCtxtPtr ctxt) {
5019 xmlChar *cur;
5020
5021 cur = ctxt->state->value;
5022 if ((cur == NULL) || (ctxt->state->endvalue == NULL)) {
5023 ctxt->state->value = NULL;
Daniel Veillarde5b110b2003-02-04 14:43:39 +00005024 ctxt->state->endvalue = NULL;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005025 return(0);
5026 }
5027 while (*cur != 0) cur++;
5028 while ((cur != ctxt->state->endvalue) && (*cur == 0)) cur++;
5029 if (cur == ctxt->state->endvalue)
5030 ctxt->state->value = NULL;
5031 else
5032 ctxt->state->value = cur;
5033 return(0);
5034}
5035
5036/**
5037 * xmlRelaxNGValidateValueList:
5038 * @ctxt: a Relax-NG validation context
5039 * @defines: the list of definitions to verify
5040 *
5041 * Validate the given set of definitions for the current value
5042 *
5043 * Returns 0 if the validation succeeded or an error code.
5044 */
5045static int
5046xmlRelaxNGValidateValueList(xmlRelaxNGValidCtxtPtr ctxt,
5047 xmlRelaxNGDefinePtr defines) {
5048 int ret = 0;
5049
5050 while (defines != NULL) {
5051 ret = xmlRelaxNGValidateValue(ctxt, defines);
5052 if (ret != 0)
5053 break;
5054 defines = defines->next;
5055 }
5056 return(ret);
5057}
5058
5059/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00005060 * xmlRelaxNGValidateValue:
5061 * @ctxt: a Relax-NG validation context
5062 * @define: the definition to verify
5063 *
5064 * Validate the given definition for the current value
5065 *
5066 * Returns 0 if the validation succeeded or an error code.
5067 */
5068static int
5069xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
5070 xmlRelaxNGDefinePtr define) {
Daniel Veillardedc91922003-01-26 00:52:04 +00005071 int ret = 0, oldflags;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005072 xmlChar *value;
5073
5074 value = ctxt->state->value;
5075 switch (define->type) {
Daniel Veillardd4310742003-02-18 21:12:46 +00005076 case XML_RELAXNG_EMPTY: {
5077 if ((value != NULL) && (value[0] != 0)) {
5078 int idx = 0;
5079
5080 while (IS_BLANK(value[idx]))
5081 idx++;
5082 if (value[idx] != 0)
5083 ret = -1;
5084 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005085 break;
Daniel Veillardd4310742003-02-18 21:12:46 +00005086 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005087 case XML_RELAXNG_TEXT:
5088 break;
Daniel Veillardedc91922003-01-26 00:52:04 +00005089 case XML_RELAXNG_VALUE: {
5090 if (!xmlStrEqual(value, define->value)) {
5091 if (define->name != NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00005092 xmlRelaxNGTypeLibraryPtr lib;
5093
5094 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
5095 if ((lib != NULL) && (lib->comp != NULL))
5096 ret = lib->comp(lib->data, define->name, value,
5097 define->value);
5098 else
5099 ret = -1;
5100 if (ret < 0) {
5101 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005102 VALID_ERROR2("Internal: failed to compare type %s\n",
Daniel Veillardea3f3982003-01-26 19:45:18 +00005103 define->name);
5104 return(-1);
5105 } else if (ret == 1) {
5106 ret = 0;
5107 } else {
5108 ret = -1;
5109 }
Daniel Veillardedc91922003-01-26 00:52:04 +00005110 } else {
5111 xmlChar *nval, *nvalue;
5112
5113 /*
5114 * TODO: trivial optimizations are possible by
5115 * computing at compile-time
5116 */
5117 nval = xmlRelaxNGNormalize(ctxt, define->value);
5118 nvalue = xmlRelaxNGNormalize(ctxt, value);
5119
Daniel Veillardea3f3982003-01-26 19:45:18 +00005120 if ((nval == NULL) || (nvalue == NULL) ||
5121 (!xmlStrEqual(nval, nvalue)))
Daniel Veillardedc91922003-01-26 00:52:04 +00005122 ret = -1;
5123 if (nval != NULL)
5124 xmlFree(nval);
5125 if (nvalue != NULL)
5126 xmlFree(nvalue);
5127 }
5128 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005129 if (ret == 0)
5130 xmlRelaxNGNextValue(ctxt);
Daniel Veillardedc91922003-01-26 00:52:04 +00005131 break;
5132 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005133 case XML_RELAXNG_DATATYPE: {
5134 ret = xmlRelaxNGValidateDatatype(ctxt, value, define);
5135 if (ret == 0)
5136 xmlRelaxNGNextValue(ctxt);
5137
5138 break;
5139 }
5140 case XML_RELAXNG_CHOICE: {
5141 xmlRelaxNGDefinePtr list = define->content;
5142 xmlChar *oldvalue;
5143
5144 oldflags = ctxt->flags;
5145 ctxt->flags |= FLAGS_IGNORABLE;
5146
5147 oldvalue = ctxt->state->value;
5148 while (list != NULL) {
5149 ret = xmlRelaxNGValidateValue(ctxt, list);
5150 if (ret == 0) {
5151 break;
5152 }
5153 ctxt->state->value = oldvalue;
5154 list = list->next;
5155 }
5156 ctxt->flags = oldflags;
Daniel Veillard416589a2003-02-17 17:25:42 +00005157 if (ret == 0)
5158 xmlRelaxNGNextValue(ctxt);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005159 break;
5160 }
5161 case XML_RELAXNG_LIST: {
5162 xmlRelaxNGDefinePtr list = define->content;
5163 xmlChar *oldvalue, *oldend, *val, *cur;
Daniel Veillard416589a2003-02-17 17:25:42 +00005164#ifdef DEBUG_LIST
5165 int nb_values = 0;
5166#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005167
5168 oldvalue = ctxt->state->value;
5169 oldend = ctxt->state->endvalue;
5170
5171 val = xmlStrdup(oldvalue);
5172 if (val == NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00005173 val = xmlStrdup(BAD_CAST "");
5174 }
5175 if (val == NULL) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005176 VALID_CTXT();
5177 VALID_ERROR("Internal: no state\n");
5178 return(-1);
5179 }
5180 cur = val;
5181 while (*cur != 0) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005182 if (IS_BLANK(*cur)) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005183 *cur = 0;
Daniel Veillard416589a2003-02-17 17:25:42 +00005184 cur++;
5185#ifdef DEBUG_LIST
5186 nb_values++;
5187#endif
5188 while (IS_BLANK(*cur))
5189 *cur++ = 0;
5190 } else
5191 cur++;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005192 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005193#ifdef DEBUG_LIST
5194 xmlGenericError(xmlGenericErrorContext,
5195 "list value: '%s' found %d items\n", oldvalue, nb_values);
5196 nb_values = 0;
5197#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005198 ctxt->state->endvalue = cur;
5199 cur = val;
5200 while ((*cur == 0) && (cur != ctxt->state->endvalue)) cur++;
5201
5202 ctxt->state->value = cur;
5203
5204 while (list != NULL) {
Daniel Veillard2e9b1652003-02-19 13:29:45 +00005205 if (ctxt->state->value == ctxt->state->endvalue)
5206 ctxt->state->value = NULL;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005207 ret = xmlRelaxNGValidateValue(ctxt, list);
5208 if (ret != 0) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005209#ifdef DEBUG_LIST
5210 xmlGenericError(xmlGenericErrorContext,
5211 "Failed to validate value: '%s' with %d rule\n",
5212 ctxt->state->value, nb_values);
5213#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005214 break;
5215 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005216#ifdef DEBUG_LIST
5217 nb_values++;
5218#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005219 list = list->next;
5220 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00005221
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005222 if ((ret == 0) && (ctxt->state->value != NULL) &&
5223 (ctxt->state->value != ctxt->state->endvalue)) {
5224 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005225 VALID_ERROR2("Extra data in list: %s\n", ctxt->state->value);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005226 ret = -1;
5227 }
5228 xmlFree(val);
5229 ctxt->state->value = oldvalue;
5230 ctxt->state->endvalue = oldend;
5231 break;
5232 }
5233 case XML_RELAXNG_ONEORMORE:
5234 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
5235 if (ret != 0) {
5236 break;
5237 }
5238 /* no break on purpose */
5239 case XML_RELAXNG_ZEROORMORE: {
5240 xmlChar *cur, *temp;
5241
5242 oldflags = ctxt->flags;
5243 ctxt->flags |= FLAGS_IGNORABLE;
5244 cur = ctxt->state->value;
5245 temp = NULL;
5246 while ((cur != NULL) && (cur != ctxt->state->endvalue) &&
5247 (temp != cur)) {
5248 temp = cur;
5249 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
5250 if (ret != 0) {
5251 ctxt->state->value = temp;
5252 ret = 0;
5253 break;
5254 }
5255 cur = ctxt->state->value;
5256 }
5257 ctxt->flags = oldflags;
5258 break;
5259 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005260 case XML_RELAXNG_EXCEPT: {
5261 xmlRelaxNGDefinePtr list;
5262
5263 list = define->content;
5264 while (list != NULL) {
5265 ret = xmlRelaxNGValidateValue(ctxt, list);
5266 if (ret == 0) {
5267 ret = -1;
5268 break;
5269 } else
5270 ret = 0;
5271 list = list->next;
5272 }
5273 break;
5274 }
Daniel Veillardd4310742003-02-18 21:12:46 +00005275 case XML_RELAXNG_GROUP: {
5276 xmlRelaxNGDefinePtr list;
5277
5278 list = define->content;
5279 while (list != NULL) {
5280 ret = xmlRelaxNGValidateValue(ctxt, list);
5281 if (ret != 0) {
5282 ret = -1;
5283 break;
5284 } else
5285 ret = 0;
5286 list = list->next;
5287 }
5288 break;
5289 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005290 default:
5291 TODO
5292 ret = -1;
5293 }
5294 return(ret);
5295}
5296
5297/**
5298 * xmlRelaxNGValidateValueContent:
5299 * @ctxt: a Relax-NG validation context
5300 * @defines: the list of definitions to verify
5301 *
5302 * Validate the given definitions for the current value
5303 *
5304 * Returns 0 if the validation succeeded or an error code.
5305 */
5306static int
5307xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt,
5308 xmlRelaxNGDefinePtr defines) {
5309 int ret = 0;
5310
5311 while (defines != NULL) {
5312 ret = xmlRelaxNGValidateValue(ctxt, defines);
5313 if (ret != 0)
5314 break;
5315 defines = defines->next;
5316 }
5317 return(ret);
5318}
5319
5320/**
Daniel Veillard144fae12003-02-03 13:17:57 +00005321 * xmlRelaxNGAttributeMatch:
5322 * @ctxt: a Relax-NG validation context
5323 * @define: the definition to check
5324 * @prop: the attribute
5325 *
5326 * Check if the attribute matches the definition nameClass
5327 *
5328 * Returns 1 if the attribute matches, 0 if no, or -1 in case of error
5329 */
5330static int
5331xmlRelaxNGAttributeMatch(xmlRelaxNGValidCtxtPtr ctxt,
5332 xmlRelaxNGDefinePtr define,
5333 xmlAttrPtr prop) {
5334 int ret;
5335
5336 if (define->name != NULL) {
5337 if (!xmlStrEqual(define->name, prop->name))
5338 return(0);
5339 }
5340 if (define->ns != NULL) {
5341 if (define->ns[0] == 0) {
5342 if (prop->ns != NULL)
5343 return(0);
5344 } else {
5345 if ((prop->ns == NULL) ||
5346 (!xmlStrEqual(define->ns, prop->ns->href)))
5347 return(0);
5348 }
5349 }
5350 if (define->nameClass == NULL)
5351 return(1);
5352 define = define->nameClass;
5353 if (define->type == XML_RELAXNG_EXCEPT) {
5354 xmlRelaxNGDefinePtr list;
5355
5356 list = define->content;
5357 while (list != NULL) {
5358 ret = xmlRelaxNGAttributeMatch(ctxt, list, prop);
5359 if (ret == 1)
5360 return(0);
5361 if (ret < 0)
5362 return(ret);
5363 list = list->next;
5364 }
5365 } else {
5366 TODO
5367 }
5368 return(1);
5369}
5370
5371/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00005372 * xmlRelaxNGValidateAttribute:
5373 * @ctxt: a Relax-NG validation context
5374 * @define: the definition to verify
5375 *
5376 * Validate the given attribute definition for that node
5377 *
5378 * Returns 0 if the validation succeeded or an error code.
5379 */
5380static int
5381xmlRelaxNGValidateAttribute(xmlRelaxNGValidCtxtPtr ctxt,
5382 xmlRelaxNGDefinePtr define) {
5383 int ret = 0, i;
5384 xmlChar *value, *oldvalue;
5385 xmlAttrPtr prop = NULL, tmp;
5386
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005387 if (ctxt->state->nbAttrLeft <= 0)
5388 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005389 if (define->name != NULL) {
5390 for (i = 0;i < ctxt->state->nbAttrs;i++) {
5391 tmp = ctxt->state->attrs[i];
5392 if ((tmp != NULL) && (xmlStrEqual(define->name, tmp->name))) {
5393 if ((((define->ns == NULL) || (define->ns[0] == 0)) &&
5394 (tmp->ns == NULL)) ||
5395 ((tmp->ns != NULL) &&
5396 (xmlStrEqual(define->ns, tmp->ns->href)))) {
5397 prop = tmp;
5398 break;
5399 }
5400 }
5401 }
5402 if (prop != NULL) {
5403 value = xmlNodeListGetString(prop->doc, prop->children, 1);
5404 oldvalue = ctxt->state->value;
5405 ctxt->state->value = value;
Daniel Veillard231d7912003-02-09 14:22:17 +00005406 ctxt->state->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005407 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00005408 if (ctxt->state->value != NULL)
5409 value = ctxt->state->value;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005410 if (value != NULL)
5411 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00005412 ctxt->state->value = oldvalue;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005413 if (ret == 0) {
5414 /*
5415 * flag the attribute as processed
5416 */
5417 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005418 ctxt->state->nbAttrLeft--;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005419 }
5420 } else {
5421 ret = -1;
5422 }
5423#ifdef DEBUG
5424 xmlGenericError(xmlGenericErrorContext,
5425 "xmlRelaxNGValidateAttribute(%s): %d\n", define->name, ret);
5426#endif
5427 } else {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005428 for (i = 0;i < ctxt->state->nbAttrs;i++) {
5429 tmp = ctxt->state->attrs[i];
Daniel Veillard144fae12003-02-03 13:17:57 +00005430 if ((tmp != NULL) &&
5431 (xmlRelaxNGAttributeMatch(ctxt, define, tmp) == 1)) {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005432 prop = tmp;
5433 break;
5434 }
5435 }
5436 if (prop != NULL) {
5437 value = xmlNodeListGetString(prop->doc, prop->children, 1);
5438 oldvalue = ctxt->state->value;
5439 ctxt->state->value = value;
5440 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00005441 if (ctxt->state->value != NULL)
5442 value = ctxt->state->value;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005443 if (value != NULL)
5444 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00005445 ctxt->state->value = oldvalue;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005446 if (ret == 0) {
5447 /*
5448 * flag the attribute as processed
5449 */
5450 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005451 ctxt->state->nbAttrLeft--;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005452 }
5453 } else {
5454 ret = -1;
5455 }
5456#ifdef DEBUG
Daniel Veillard144fae12003-02-03 13:17:57 +00005457 if (define->ns != NULL) {
5458 xmlGenericError(xmlGenericErrorContext,
5459 "xmlRelaxNGValidateAttribute(nsName ns = %s): %d\n",
5460 define->ns, ret);
5461 } else {
5462 xmlGenericError(xmlGenericErrorContext,
5463 "xmlRelaxNGValidateAttribute(anyName): %d\n",
5464 ret);
5465 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005466#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00005467 }
5468
5469 return(ret);
5470}
5471
5472/**
5473 * xmlRelaxNGValidateAttributeList:
5474 * @ctxt: a Relax-NG validation context
5475 * @define: the list of definition to verify
5476 *
5477 * Validate the given node against the list of attribute definitions
5478 *
5479 * Returns 0 if the validation succeeded or an error code.
5480 */
5481static int
5482xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt,
5483 xmlRelaxNGDefinePtr defines) {
5484 int ret = 0;
5485 while (defines != NULL) {
5486 if (xmlRelaxNGValidateAttribute(ctxt, defines) != 0)
5487 ret = -1;
5488 defines = defines->next;
5489 }
5490 return(ret);
5491}
5492
5493/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005494 * xmlRelaxNGValidateTryPermutation:
5495 * @ctxt: a Relax-NG validation context
5496 * @groups: the array of groups
5497 * @nbgroups: the number of groups in the array
5498 * @array: the permutation to try
5499 * @len: the size of the set
5500 *
5501 * Try to validate a permutation for the group of definitions.
5502 *
5503 * Returns 0 if the validation succeeded or an error code.
5504 */
5505static int
5506xmlRelaxNGValidateTryPermutation(xmlRelaxNGValidCtxtPtr ctxt,
5507 xmlRelaxNGDefinePtr rule,
5508 xmlNodePtr *array, int len) {
5509 int i, ret;
5510
5511 if (len > 0) {
5512 /*
5513 * One only need the next pointer set-up to do the validation
5514 */
5515 for (i = 0;i < (len - 1);i++)
5516 array[i]->next = array[i + 1];
5517 array[i]->next = NULL;
5518
5519 /*
5520 * Now try to validate the sequence
5521 */
5522 ctxt->state->seq = array[0];
5523 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
5524 } else {
5525 ctxt->state->seq = NULL;
5526 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
5527 }
5528
5529 /*
5530 * the sequence must be fully consumed
5531 */
5532 if (ctxt->state->seq != NULL)
5533 return(-1);
5534
5535 return(ret);
5536}
5537
5538/**
5539 * xmlRelaxNGValidateWalkPermutations:
5540 * @ctxt: a Relax-NG validation context
5541 * @groups: the array of groups
5542 * @nbgroups: the number of groups in the array
5543 * @nodes: the set of nodes
5544 * @array: the current state of the parmutation
5545 * @len: the size of the set
5546 * @level: a pointer to the level variable
5547 * @k: the index in the array to fill
5548 *
5549 * Validate a set of nodes for a groups of definitions, will try the
5550 * full set of permutations
5551 *
5552 * Returns 0 if the validation succeeded or an error code.
5553 */
5554static int
5555xmlRelaxNGValidateWalkPermutations(xmlRelaxNGValidCtxtPtr ctxt,
5556 xmlRelaxNGDefinePtr rule, xmlNodePtr *nodes,
5557 xmlNodePtr *array, int len,
5558 int *level, int k) {
5559 int i, ret;
5560
5561 if ((k >= 0) && (k < len))
5562 array[k] = nodes[*level];
5563 *level = *level + 1;
5564 if (*level == len) {
5565 ret = xmlRelaxNGValidateTryPermutation(ctxt, rule, array, len);
5566 if (ret == 0)
5567 return(0);
5568 } else {
5569 for (i = 0;i < len;i++) {
5570 if (array[i] == NULL) {
5571 ret = xmlRelaxNGValidateWalkPermutations(ctxt, rule,
5572 nodes, array, len, level, i);
5573 if (ret == 0)
5574 return(0);
5575 }
5576 }
5577 }
5578 *level = *level - 1;
5579 array[k] = NULL;
5580 return(-1);
5581}
5582
5583/**
5584 * xmlRelaxNGNodeMatchesList:
5585 * @node: the node
5586 * @list: a NULL terminated array of definitions
5587 *
5588 * Check if a node can be matched by one of the definitions
5589 *
5590 * Returns 1 if matches 0 otherwise
5591 */
5592static int
5593xmlRelaxNGNodeMatchesList(xmlNodePtr node, xmlRelaxNGDefinePtr *list) {
5594 xmlRelaxNGDefinePtr cur;
5595 int i = 0;
5596
5597 if ((node == NULL) || (list == NULL))
5598 return(0);
5599
5600 cur = list[i++];
5601 while (cur != NULL) {
5602 if ((node->type == XML_ELEMENT_NODE) &&
5603 (cur->type == XML_RELAXNG_ELEMENT)) {
5604 if (cur->name == NULL) {
5605 if ((node->ns != NULL) &&
5606 (xmlStrEqual(node->ns->href, cur->ns)))
5607 return(1);
5608 } else if (xmlStrEqual(cur->name, node->name)) {
5609 if ((cur->ns == NULL) || (cur->ns[0] == 0)) {
5610 if (node->ns == NULL)
5611 return(1);
5612 } else {
5613 if ((node->ns != NULL) &&
5614 (xmlStrEqual(node->ns->href, cur->ns)))
5615 return(1);
5616 }
5617 }
5618 } else if ((node->type == XML_TEXT_NODE) &&
5619 (cur->type == XML_RELAXNG_TEXT)) {
5620 return(1);
5621 }
5622 cur = list[i++];
5623 }
5624 return(0);
5625}
5626
5627/**
5628 * xmlRelaxNGValidatePartGroup:
5629 * @ctxt: a Relax-NG validation context
5630 * @groups: the array of groups
5631 * @nbgroups: the number of groups in the array
5632 * @nodes: the set of nodes
5633 * @len: the size of the set of nodes
5634 *
5635 * Validate a set of nodes for a groups of definitions
5636 *
5637 * Returns 0 if the validation succeeded or an error code.
5638 */
5639static int
5640xmlRelaxNGValidatePartGroup(xmlRelaxNGValidCtxtPtr ctxt,
5641 xmlRelaxNGInterleaveGroupPtr *groups,
5642 int nbgroups, xmlNodePtr *nodes, int len) {
Daniel Veillard231d7912003-02-09 14:22:17 +00005643 int level, ret = -1, i, j, k, top_j, max_j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005644 xmlNodePtr *array = NULL, *list, oldseq;
5645 xmlRelaxNGInterleaveGroupPtr group;
5646
5647 list = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
5648 if (list == NULL) {
5649 return(-1);
5650 }
5651 array = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
5652 if (array == NULL) {
5653 xmlFree(list);
5654 return(-1);
5655 }
5656 memset(array, 0, len * sizeof(xmlNodePtr));
5657
5658 /*
5659 * Partition the elements and validate the subsets.
5660 */
5661 oldseq = ctxt->state->seq;
Daniel Veillard231d7912003-02-09 14:22:17 +00005662 max_j = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005663 for (i = 0;i < nbgroups;i++) {
5664 group = groups[i];
5665 if (group == NULL)
5666 continue;
5667 k = 0;
Daniel Veillard231d7912003-02-09 14:22:17 +00005668 top_j = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005669 for (j = 0;j < len;j++) {
5670 if (nodes[j] == NULL)
5671 continue;
5672 if (xmlRelaxNGNodeMatchesList(nodes[j], group->defs)) {
5673 list[k++] = nodes[j];
5674 nodes[j] = NULL;
Daniel Veillard231d7912003-02-09 14:22:17 +00005675 top_j = j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005676 }
5677 }
Daniel Veillard231d7912003-02-09 14:22:17 +00005678 if (top_j > max_j)
5679 max_j = top_j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005680 ctxt->state->seq = oldseq;
5681 if (k > 1) {
5682 memset(array, 0, k * sizeof(xmlNodePtr));
Daniel Veillardb08c9812003-01-28 23:09:49 +00005683 level = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005684 ret = xmlRelaxNGValidateWalkPermutations(ctxt, group->rule,
5685 list, array, k, &level, -1);
5686 } else {
5687 ret = xmlRelaxNGValidateTryPermutation(ctxt, group->rule, list, k);
5688 }
5689 if (ret != 0) {
5690 ctxt->state->seq = oldseq;
5691 break;
5692 }
5693 }
5694
Daniel Veillard231d7912003-02-09 14:22:17 +00005695 for (j = 0;j < max_j;j++) {
5696 if (nodes[j] != NULL) {
5697 TODO /* problem, one of the nodes didn't got a match */
5698 }
5699 }
5700 if (ret == 0) {
5701 if (max_j + 1 < len)
5702 ctxt->state->seq = nodes[max_j + 1];
5703 else
5704 ctxt->state->seq = NULL;
5705 }
5706
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005707 xmlFree(list);
5708 xmlFree(array);
5709 return(ret);
5710}
5711
5712/**
5713 * xmlRelaxNGValidateInterleave:
5714 * @ctxt: a Relax-NG validation context
5715 * @define: the definition to verify
5716 *
5717 * Validate an interleave definition for a node.
5718 *
5719 * Returns 0 if the validation succeeded or an error code.
5720 */
5721static int
5722xmlRelaxNGValidateInterleave(xmlRelaxNGValidCtxtPtr ctxt,
5723 xmlRelaxNGDefinePtr define) {
5724 int ret = 0, nbchildren, nbtot, i, j;
5725 xmlRelaxNGPartitionPtr partitions;
5726 xmlNodePtr *children = NULL;
5727 xmlNodePtr *order = NULL;
Daniel Veillard231d7912003-02-09 14:22:17 +00005728 xmlNodePtr cur, oldseq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005729
5730 if (define->data != NULL) {
5731 partitions = (xmlRelaxNGPartitionPtr) define->data;
5732 } else {
5733 VALID_CTXT();
5734 VALID_ERROR("Internal: interleave block has no data\n");
5735 return(-1);
5736 }
5737
5738 /*
5739 * Build the sequence of child and an array preserving the children
5740 * initial order.
5741 */
5742 cur = ctxt->state->seq;
Daniel Veillard231d7912003-02-09 14:22:17 +00005743 oldseq = ctxt->state->seq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005744 nbchildren = 0;
5745 nbtot = 0;
5746 while (cur != NULL) {
5747 if ((cur->type == XML_COMMENT_NODE) ||
5748 (cur->type == XML_PI_NODE) ||
5749 ((cur->type == XML_TEXT_NODE) &&
5750 (IS_BLANK_NODE(cur)))) {
5751 nbtot++;
5752 } else {
5753 nbchildren++;
5754 nbtot++;
5755 }
5756 cur = cur->next;
5757 }
5758 children = (xmlNodePtr *) xmlMalloc(nbchildren * sizeof(xmlNodePtr));
5759 if (children == NULL)
5760 goto error;
5761 order = (xmlNodePtr *) xmlMalloc(nbtot * sizeof(xmlNodePtr));
5762 if (order == NULL)
5763 goto error;
5764 cur = ctxt->state->seq;
5765 i = 0;
5766 j = 0;
5767 while (cur != NULL) {
5768 if ((cur->type == XML_COMMENT_NODE) ||
5769 (cur->type == XML_PI_NODE) ||
5770 ((cur->type == XML_TEXT_NODE) &&
5771 (IS_BLANK_NODE(cur)))) {
5772 order[j++] = cur;
5773 } else {
5774 order[j++] = cur;
5775 children[i++] = cur;
5776 }
5777 cur = cur->next;
5778 }
5779
5780 /* TODO: retry with a maller set of child if there is a next... */
5781 ret = xmlRelaxNGValidatePartGroup(ctxt, partitions->groups,
5782 partitions->nbgroups, children, nbchildren);
Daniel Veillard231d7912003-02-09 14:22:17 +00005783 if (ret != 0)
5784 ctxt->state->seq = oldseq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005785
5786 /*
5787 * Cleanup: rebuid the child sequence and free the structure
5788 */
5789 if (order != NULL) {
5790 for (i = 0;i < nbtot;i++) {
5791 if (i == 0)
5792 order[i]->prev = NULL;
5793 else
5794 order[i]->prev = order[i - 1];
5795 if (i == nbtot - 1)
5796 order[i]->next = NULL;
5797 else
5798 order[i]->next = order[i + 1];
5799 }
5800 xmlFree(order);
5801 }
5802 if (children != NULL)
5803 xmlFree(children);
5804
5805 return(ret);
5806
5807error:
5808 if (order != NULL) {
5809 for (i = 0;i < nbtot;i++) {
5810 if (i == 0)
5811 order[i]->prev = NULL;
5812 else
5813 order[i]->prev = order[i - 1];
5814 if (i == nbtot - 1)
5815 order[i]->next = NULL;
5816 else
5817 order[i]->next = order[i + 1];
5818 }
5819 xmlFree(order);
5820 }
5821 if (children != NULL)
5822 xmlFree(children);
5823 return(-1);
5824}
5825
5826/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00005827 * xmlRelaxNGValidateElementContent:
5828 * @ctxt: a Relax-NG validation context
5829 * @define: the list of definition to verify
5830 *
5831 * Validate the given node content against the (list) of definitions
5832 *
5833 * Returns 0 if the validation succeeded or an error code.
5834 */
5835static int
5836xmlRelaxNGValidateElementContent(xmlRelaxNGValidCtxtPtr ctxt,
5837 xmlRelaxNGDefinePtr defines) {
5838 int ret = 0, res;
5839
5840 if (ctxt->state == NULL) {
5841 VALID_CTXT();
5842 VALID_ERROR("Internal: no state\n");
5843 return(-1);
5844 }
5845 while (defines != NULL) {
5846 res = xmlRelaxNGValidateDefinition(ctxt, defines);
5847 if (res < 0)
5848 ret = -1;
5849 defines = defines->next;
5850 }
5851
5852 return(ret);
5853}
5854
5855/**
Daniel Veillard416589a2003-02-17 17:25:42 +00005856 * xmlRelaxNGElementMatch:
5857 * @ctxt: a Relax-NG validation context
5858 * @define: the definition to check
5859 * @elem: the element
5860 *
5861 * Check if the element matches the definition nameClass
5862 *
5863 * Returns 1 if the element matches, 0 if no, or -1 in case of error
5864 */
5865static int
5866xmlRelaxNGElementMatch(xmlRelaxNGValidCtxtPtr ctxt,
5867 xmlRelaxNGDefinePtr define,
5868 xmlNodePtr elem) {
5869 int ret, oldflags;
5870
5871 if (define->name != NULL) {
5872 if (!xmlStrEqual(elem->name, define->name)) {
5873 VALID_CTXT();
5874 VALID_ERROR3("Expecting element %s, got %s\n",
5875 define->name, elem->name);
5876 return(0);
5877 }
5878 }
5879 if ((define->ns != NULL) && (define->ns[0] != 0)) {
5880 if (elem->ns == NULL) {
5881 VALID_CTXT();
5882 VALID_ERROR2("Expecting a namespace for element %s\n",
5883 elem->name);
5884 return(0);
5885 } else if (!xmlStrEqual(elem->ns->href, define->ns)) {
5886 VALID_CTXT();
5887 VALID_ERROR3("Expecting element %s has wrong namespace: expecting %s\n",
5888 elem->name, define->ns);
5889 return(0);
5890 }
Daniel Veillard8fe98712003-02-19 00:19:14 +00005891 } else if ((elem->ns != NULL) && (define->ns != NULL) &&
5892 (define->name == NULL)) {
5893 VALID_CTXT();
5894 VALID_ERROR2("Expecting no namespace for element %s\n",
5895 define->name);
5896 return(0);
5897 } else if ((elem->ns != NULL) && (define->name != NULL)) {
5898 VALID_CTXT();
5899 VALID_ERROR2("Expecting no namespace for element %s\n",
5900 define->name);
5901 return(0);
Daniel Veillard416589a2003-02-17 17:25:42 +00005902 }
5903
5904 if (define->nameClass == NULL)
5905 return(1);
5906
5907 define = define->nameClass;
5908 if (define->type == XML_RELAXNG_EXCEPT) {
5909 xmlRelaxNGDefinePtr list;
5910 oldflags = ctxt->flags;
5911 ctxt->flags |= FLAGS_IGNORABLE;
5912
5913 list = define->content;
5914 while (list != NULL) {
5915 ret = xmlRelaxNGElementMatch(ctxt, list, elem);
5916 if (ret == 1) {
5917 ctxt->flags = oldflags;
5918 return(0);
5919 }
5920 if (ret < 0) {
5921 ctxt->flags = oldflags;
5922 return(ret);
5923 }
5924 list = list->next;
5925 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00005926 ret = 1;
5927 ctxt->flags = oldflags;
5928 } else if (define->type == XML_RELAXNG_CHOICE) {
5929 xmlRelaxNGDefinePtr list;
5930 oldflags = ctxt->flags;
5931 ctxt->flags |= FLAGS_IGNORABLE;
5932
5933 list = define->nameClass;
5934 while (list != NULL) {
5935 ret = xmlRelaxNGElementMatch(ctxt, list, elem);
5936 if (ret == 1) {
5937 ctxt->flags = oldflags;
5938 return(1);
5939 }
5940 if (ret < 0) {
5941 ctxt->flags = oldflags;
5942 return(ret);
5943 }
5944 list = list->next;
5945 }
5946 ret = 0;
Daniel Veillard416589a2003-02-17 17:25:42 +00005947 ctxt->flags = oldflags;
5948 } else {
5949 TODO
Daniel Veillard2e9b1652003-02-19 13:29:45 +00005950 ret = -1;
Daniel Veillard416589a2003-02-17 17:25:42 +00005951 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00005952 return(ret);
Daniel Veillard416589a2003-02-17 17:25:42 +00005953}
5954
5955/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00005956 * xmlRelaxNGValidateDefinition:
5957 * @ctxt: a Relax-NG validation context
5958 * @define: the definition to verify
5959 *
5960 * Validate the current node against the definition
5961 *
5962 * Returns 0 if the validation succeeded or an error code.
5963 */
5964static int
5965xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
5966 xmlRelaxNGDefinePtr define) {
5967 xmlNodePtr node;
5968 int ret = 0, i, tmp, oldflags;
5969 xmlRelaxNGValidStatePtr oldstate, state;
5970
5971 if (define == NULL) {
5972 VALID_CTXT();
5973 VALID_ERROR("internal error: define == NULL\n");
5974 return(-1);
5975 }
5976 if (ctxt->state != NULL) {
5977 node = ctxt->state->seq;
5978 } else {
5979 node = NULL;
5980 }
Daniel Veillard231d7912003-02-09 14:22:17 +00005981#ifdef DEBUG
5982 for (i = 0;i < ctxt->depth;i++)
5983 xmlGenericError(xmlGenericErrorContext, " ");
5984 xmlGenericError(xmlGenericErrorContext,
5985 "Start validating %s ", xmlRelaxNGDefName(define));
5986 if (define->name != NULL)
5987 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
5988 if ((node != NULL) && (node->name != NULL))
5989 xmlGenericError(xmlGenericErrorContext, "on %s\n", node->name);
5990 else
5991 xmlGenericError(xmlGenericErrorContext, "\n");
5992#endif
5993 ctxt->depth++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005994 switch (define->type) {
5995 case XML_RELAXNG_EMPTY:
Daniel Veillardd4310742003-02-18 21:12:46 +00005996 node = xmlRelaxNGSkipIgnored(ctxt, node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005997 if (node != NULL) {
5998 VALID_CTXT();
5999 VALID_ERROR("Expecting an empty element\n");
Daniel Veillard231d7912003-02-09 14:22:17 +00006000 ret = -1;
6001 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006002 }
Daniel Veillard231d7912003-02-09 14:22:17 +00006003 ret = 0;
6004 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006005 case XML_RELAXNG_NOT_ALLOWED:
Daniel Veillard231d7912003-02-09 14:22:17 +00006006 ret = -1;
6007 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006008 case XML_RELAXNG_TEXT:
Daniel Veillardce14fa52003-02-19 17:32:48 +00006009#if 0
Daniel Veillard231d7912003-02-09 14:22:17 +00006010 if (node == NULL) {
6011 ret = 0;
6012 break;
6013 }
Daniel Veillardce14fa52003-02-19 17:32:48 +00006014#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00006015 while ((node != NULL) &&
6016 ((node->type == XML_TEXT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006017 (node->type == XML_COMMENT_NODE) ||
6018 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00006019 (node->type == XML_CDATA_SECTION_NODE)))
6020 node = node->next;
Daniel Veillardce14fa52003-02-19 17:32:48 +00006021#if 0
Daniel Veillard276be4a2003-01-24 01:03:34 +00006022 if (node == ctxt->state->seq) {
6023 VALID_CTXT();
6024 VALID_ERROR("Expecting text content\n");
6025 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006026 }
Daniel Veillardce14fa52003-02-19 17:32:48 +00006027#endif
Daniel Veillard276be4a2003-01-24 01:03:34 +00006028 ctxt->state->seq = node;
6029 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006030 case XML_RELAXNG_ELEMENT:
6031 node = xmlRelaxNGSkipIgnored(ctxt, node);
Daniel Veillard231d7912003-02-09 14:22:17 +00006032 if (node == NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006033 VALID_CTXT();
Daniel Veillard231d7912003-02-09 14:22:17 +00006034 VALID_ERROR("Expecting an element, got empty\n");
6035 ret = -1;
6036 break;
6037 }
6038 if (node->type != XML_ELEMENT_NODE) {
6039 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006040 VALID_ERROR2("Expecting an element got %d type\n", node->type);
Daniel Veillard231d7912003-02-09 14:22:17 +00006041 ret = -1;
6042 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006043 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00006044 /*
6045 * This node was already validated successfully against
6046 * this definition.
6047 */
6048 if (node->_private == define)
6049 break;
Daniel Veillard416589a2003-02-17 17:25:42 +00006050
6051 ret = xmlRelaxNGElementMatch(ctxt, define, node);
6052 if (ret <= 0) {
6053 ret = -1;
6054 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006055 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006056 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006057
6058 state = xmlRelaxNGNewValidState(ctxt, node);
6059 if (state == NULL) {
Daniel Veillard231d7912003-02-09 14:22:17 +00006060 ret = -1;
6061 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006062 }
6063
6064 oldstate = ctxt->state;
6065 ctxt->state = state;
6066 if (define->attrs != NULL) {
6067 tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
Daniel Veillard231d7912003-02-09 14:22:17 +00006068 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006069 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00006070#ifdef DEBUG
6071 xmlGenericError(xmlGenericErrorContext,
6072 "E: Element %s failed to validate attributes\n",
6073 node->name);
6074#endif
6075 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006076 }
6077 if (define->content != NULL) {
6078 tmp = xmlRelaxNGValidateElementContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00006079 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006080 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00006081#ifdef DEBUG
6082 xmlGenericError(xmlGenericErrorContext,
6083 "E: Element %s failed to validate element content\n",
6084 node->name);
6085#endif
6086 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006087 }
6088 state = ctxt->state;
6089 if (state->seq != NULL) {
6090 state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq);
6091 if (state->seq != NULL) {
6092 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006093 VALID_ERROR3("Extra content for element %s: %s\n",
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006094 node->name, state->seq->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006095 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00006096#ifdef DEBUG
6097 xmlGenericError(xmlGenericErrorContext,
6098 "E: Element %s has extra content: %s\n",
6099 node->name, state->seq->name);
6100#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00006101 }
6102 }
6103 for (i = 0;i < state->nbAttrs;i++) {
6104 if (state->attrs[i] != NULL) {
6105 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006106 VALID_ERROR3("Invalid attribute %s for element %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00006107 state->attrs[i]->name, node->name);
6108 ret = -1;
6109 }
6110 }
6111 ctxt->state = oldstate;
6112 xmlRelaxNGFreeValidState(state);
6113 if (oldstate != NULL)
Daniel Veillarde5b110b2003-02-04 14:43:39 +00006114 oldstate->seq = xmlRelaxNGSkipIgnored(ctxt, node->next);
6115 if (ret == 0)
6116 node->_private = define;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006117
6118
6119#ifdef DEBUG
6120 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard231d7912003-02-09 14:22:17 +00006121 "xmlRelaxNGValidateDefinition(): validated %s : %d",
Daniel Veillard6eadf632003-01-23 18:29:16 +00006122 node->name, ret);
Daniel Veillard231d7912003-02-09 14:22:17 +00006123 if (oldstate == NULL)
6124 xmlGenericError(xmlGenericErrorContext, ": no state\n");
6125 else if (oldstate->seq == NULL)
6126 xmlGenericError(xmlGenericErrorContext, ": done\n");
6127 else if (oldstate->seq->type == XML_ELEMENT_NODE)
6128 xmlGenericError(xmlGenericErrorContext, ": next elem %s\n",
6129 oldstate->seq->name);
6130 else
6131 xmlGenericError(xmlGenericErrorContext, ": next %s %d\n",
6132 oldstate->seq->name, oldstate->seq->type);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006133#endif
6134 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006135 case XML_RELAXNG_OPTIONAL:
6136 oldflags = ctxt->flags;
6137 ctxt->flags |= FLAGS_IGNORABLE;
6138 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6139 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6140 if (ret != 0) {
6141 xmlRelaxNGFreeValidState(ctxt->state);
6142 ctxt->state = oldstate;
6143 ret = 0;
6144 break;
6145 }
6146 xmlRelaxNGFreeValidState(oldstate);
6147 ctxt->flags = oldflags;
6148 ret = 0;
6149 break;
6150 case XML_RELAXNG_ONEORMORE:
6151 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6152 if (ret != 0) {
6153 break;
6154 }
6155 /* no break on purpose */
Daniel Veillard276be4a2003-01-24 01:03:34 +00006156 case XML_RELAXNG_ZEROORMORE: {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006157 oldflags = ctxt->flags;
6158 ctxt->flags |= FLAGS_IGNORABLE;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006159 while (ctxt->state->nbAttrLeft != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006160 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6161 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6162 if (ret != 0) {
6163 xmlRelaxNGFreeValidState(ctxt->state);
6164 ctxt->state = oldstate;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006165 break;
6166 }
6167 xmlRelaxNGFreeValidState(oldstate);
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006168 }
6169 if (ret == 0) {
6170 /*
6171 * There is no attribute left to be consumed,
6172 * we can check the closure by looking at ctxt->state->seq
6173 */
6174 xmlNodePtr cur, temp;
6175
Daniel Veillard276be4a2003-01-24 01:03:34 +00006176 cur = ctxt->state->seq;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006177 temp = NULL;
6178 while ((cur != NULL) && (temp != cur)) {
6179 temp = cur;
6180 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6181 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6182 if (ret != 0) {
6183 xmlRelaxNGFreeValidState(ctxt->state);
6184 ctxt->state = oldstate;
6185 break;
6186 }
6187 xmlRelaxNGFreeValidState(oldstate);
6188 cur = ctxt->state->seq;
6189 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006190 }
6191 ctxt->flags = oldflags;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006192 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006193 break;
Daniel Veillard276be4a2003-01-24 01:03:34 +00006194 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006195 case XML_RELAXNG_CHOICE: {
6196 xmlRelaxNGDefinePtr list = define->content;
Daniel Veillardce14fa52003-02-19 17:32:48 +00006197 int success = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006198
6199 oldflags = ctxt->flags;
6200 ctxt->flags |= FLAGS_IGNORABLE;
6201
6202 while (list != NULL) {
6203 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6204 ret = xmlRelaxNGValidateDefinition(ctxt, list);
6205 if (ret == 0) {
Daniel Veillardce14fa52003-02-19 17:32:48 +00006206 if (xmlRelaxNGEqualValidState(ctxt, ctxt->state, oldstate)){
6207 /*
6208 * if that pattern was nullable flag it but try
6209 * to make more progresses
6210 */
6211 success = 1;
6212 } else {
6213 xmlRelaxNGFreeValidState(oldstate);
6214 break;
6215 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006216 }
6217 xmlRelaxNGFreeValidState(ctxt->state);
6218 ctxt->state = oldstate;
6219 list = list->next;
6220 }
6221 ctxt->flags = oldflags;
Daniel Veillardce14fa52003-02-19 17:32:48 +00006222 if (success == 1)
6223 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006224 break;
6225 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00006226 case XML_RELAXNG_DEF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00006227 case XML_RELAXNG_GROUP: {
6228 xmlRelaxNGDefinePtr list = define->content;
6229
6230 while (list != NULL) {
6231 ret = xmlRelaxNGValidateDefinition(ctxt, list);
6232 if (ret != 0)
6233 break;
6234 list = list->next;
6235 }
6236 break;
6237 }
6238 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006239 ret = xmlRelaxNGValidateInterleave(ctxt, define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006240 break;
6241 case XML_RELAXNG_ATTRIBUTE:
6242 ret = xmlRelaxNGValidateAttribute(ctxt, define);
6243 break;
6244 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00006245 case XML_RELAXNG_PARENTREF:
6246 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00006247 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6248 break;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006249 case XML_RELAXNG_DATATYPE: {
Daniel Veillardd4310742003-02-18 21:12:46 +00006250 xmlNodePtr child;
6251 xmlChar *content = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006252
Daniel Veillardd4310742003-02-18 21:12:46 +00006253 child = node;
6254 while (child != NULL) {
6255 if (child->type == XML_ELEMENT_NODE) {
6256 VALID_CTXT();
6257 VALID_ERROR2("Element %s has child elements\n",
6258 node->parent->name);
6259 ret = -1;
6260 break;
6261 } else if ((child->type == XML_TEXT_NODE) ||
6262 (child->type == XML_CDATA_SECTION_NODE)) {
6263 content = xmlStrcat(content, child->content);
6264 }
6265 /* TODO: handle entities ... */
6266 child = child->next;
6267 }
6268 if (ret == -1) {
6269 if (content != NULL)
6270 xmlFree(content);
6271 break;
6272 }
6273 if (content == NULL) {
6274 content = xmlStrdup(BAD_CAST "");
6275 if (content == NULL) {
6276 VALID_CTXT();
6277 VALID_ERROR("Allocation failure\n");
6278 ret = -1;
6279 break;
6280 }
6281 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006282 ret = xmlRelaxNGValidateDatatype(ctxt, content, define);
6283 if (ret == -1) {
6284 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006285 VALID_ERROR2("internal error validating %s\n", define->name);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006286 } else if (ret == 0) {
Daniel Veillardd4310742003-02-18 21:12:46 +00006287 ctxt->state->seq = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006288 }
6289 if (content != NULL)
6290 xmlFree(content);
6291 break;
6292 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00006293 case XML_RELAXNG_VALUE: {
Daniel Veillardd4310742003-02-18 21:12:46 +00006294 xmlChar *content = NULL;
Daniel Veillardea3f3982003-01-26 19:45:18 +00006295 xmlChar *oldvalue;
Daniel Veillardd4310742003-02-18 21:12:46 +00006296 xmlNodePtr child;
Daniel Veillardea3f3982003-01-26 19:45:18 +00006297
Daniel Veillardd4310742003-02-18 21:12:46 +00006298 child = node;
6299 while (child != NULL) {
6300 if (child->type == XML_ELEMENT_NODE) {
6301 VALID_CTXT();
6302 VALID_ERROR2("Element %s has child elements\n",
6303 node->parent->name);
6304 ret = -1;
6305 break;
6306 } else if ((child->type == XML_TEXT_NODE) ||
6307 (child->type == XML_CDATA_SECTION_NODE)) {
6308 content = xmlStrcat(content, child->content);
6309 }
6310 /* TODO: handle entities ... */
6311 child = child->next;
6312 }
6313 if (ret == -1) {
6314 if (content != NULL)
6315 xmlFree(content);
6316 break;
6317 }
6318 if (content == NULL) {
6319 content = xmlStrdup(BAD_CAST "");
6320 if (content == NULL) {
6321 VALID_CTXT();
6322 VALID_ERROR("Allocation failure\n");
6323 ret = -1;
6324 break;
6325 }
6326 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00006327 oldvalue = ctxt->state->value;
6328 ctxt->state->value = content;
6329 ret = xmlRelaxNGValidateValue(ctxt, define);
6330 ctxt->state->value = oldvalue;
6331 if (ret == -1) {
6332 VALID_CTXT();
Daniel Veillardd2298792003-02-14 16:54:11 +00006333 if (define->name != NULL) {
6334 VALID_ERROR2("error validating value %s\n", define->name);
6335 } else {
6336 VALID_ERROR("error validating value\n");
6337 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00006338 } else if (ret == 0) {
Daniel Veillardd4310742003-02-18 21:12:46 +00006339 ctxt->state->seq = NULL;
Daniel Veillardea3f3982003-01-26 19:45:18 +00006340 }
6341 if (content != NULL)
6342 xmlFree(content);
6343 break;
6344 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006345 case XML_RELAXNG_LIST: {
6346 xmlChar *content;
Daniel Veillardd4310742003-02-18 21:12:46 +00006347 xmlNodePtr child;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006348 xmlChar *oldvalue, *oldendvalue;
6349 int len;
Daniel Veillardea3f3982003-01-26 19:45:18 +00006350
Daniel Veillardd4310742003-02-18 21:12:46 +00006351 /*
6352 * Make sure it's only text nodes
6353 */
6354
6355 content = NULL;
6356 child = node;
6357 while (child != NULL) {
6358 if (child->type == XML_ELEMENT_NODE) {
6359 VALID_CTXT();
6360 VALID_ERROR2("Element %s has child elements\n",
6361 node->parent->name);
6362 ret = -1;
6363 break;
6364 } else if ((child->type == XML_TEXT_NODE) ||
6365 (child->type == XML_CDATA_SECTION_NODE)) {
6366 content = xmlStrcat(content, child->content);
6367 }
6368 /* TODO: handle entities ... */
6369 child = child->next;
6370 }
6371 if (ret == -1) {
6372 if (content != NULL)
6373 xmlFree(content);
6374 break;
6375 }
6376 if (content == NULL) {
6377 content = xmlStrdup(BAD_CAST "");
6378 if (content == NULL) {
6379 VALID_CTXT();
6380 VALID_ERROR("Allocation failure\n");
6381 ret = -1;
6382 break;
6383 }
6384 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006385 len = xmlStrlen(content);
6386 oldvalue = ctxt->state->value;
6387 oldendvalue = ctxt->state->endvalue;
6388 ctxt->state->value = content;
6389 ctxt->state->endvalue = content + len;
6390 ret = xmlRelaxNGValidateValue(ctxt, define);
6391 ctxt->state->value = oldvalue;
6392 ctxt->state->endvalue = oldendvalue;
6393 if (ret == -1) {
6394 VALID_CTXT();
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006395 VALID_ERROR("error validating list\n");
Daniel Veillardd4310742003-02-18 21:12:46 +00006396 } else if ((ret == 0) && (node != NULL)) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006397 ctxt->state->seq = node->next;
6398 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006399 if (content != NULL)
6400 xmlFree(content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006401 break;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006402 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006403 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00006404 case XML_RELAXNG_EXCEPT:
Daniel Veillard8fe98712003-02-19 00:19:14 +00006405 case XML_RELAXNG_PARAM:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006406 TODO
Daniel Veillard416589a2003-02-17 17:25:42 +00006407 ret = -1;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006408 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006409 }
Daniel Veillard231d7912003-02-09 14:22:17 +00006410 ctxt->depth--;
6411#ifdef DEBUG
6412 for (i = 0;i < ctxt->depth;i++)
6413 xmlGenericError(xmlGenericErrorContext, " ");
6414 xmlGenericError(xmlGenericErrorContext,
6415 "Validating %s ", xmlRelaxNGDefName(define));
6416 if (define->name != NULL)
6417 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
6418 if (ret == 0)
6419 xmlGenericError(xmlGenericErrorContext, "suceeded\n");
6420 else
6421 xmlGenericError(xmlGenericErrorContext, "failed\n");
6422#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00006423 return(ret);
6424}
6425
6426/**
6427 * xmlRelaxNGValidateDocument:
6428 * @ctxt: a Relax-NG validation context
6429 * @doc: the document
6430 *
6431 * Validate the given document
6432 *
6433 * Returns 0 if the validation succeeded or an error code.
6434 */
6435static int
6436xmlRelaxNGValidateDocument(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
6437 int ret;
6438 xmlRelaxNGPtr schema;
6439 xmlRelaxNGGrammarPtr grammar;
6440 xmlRelaxNGValidStatePtr state;
6441
6442 if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL))
6443 return(-1);
6444
6445 schema = ctxt->schema;
6446 grammar = schema->topgrammar;
6447 if (grammar == NULL) {
6448 VALID_CTXT();
6449 VALID_ERROR("No top grammar defined\n");
6450 return(-1);
6451 }
6452 state = xmlRelaxNGNewValidState(ctxt, NULL);
6453 ctxt->state = state;
6454 ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start);
6455 state = ctxt->state;
6456 if ((state != NULL) && (state->seq != NULL)) {
6457 xmlNodePtr node;
6458
6459 node = state->seq;
6460 node = xmlRelaxNGSkipIgnored(ctxt, node);
6461 if (node != NULL) {
6462 VALID_CTXT();
6463 VALID_ERROR("extra data on the document\n");
6464 ret = -1;
6465 }
6466 }
6467 xmlRelaxNGFreeValidState(state);
6468
6469 return(ret);
6470}
6471
6472/************************************************************************
6473 * *
6474 * Validation interfaces *
6475 * *
6476 ************************************************************************/
6477/**
6478 * xmlRelaxNGNewValidCtxt:
6479 * @schema: a precompiled XML RelaxNGs
6480 *
6481 * Create an XML RelaxNGs validation context based on the given schema
6482 *
6483 * Returns the validation context or NULL in case of error
6484 */
6485xmlRelaxNGValidCtxtPtr
6486xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema) {
6487 xmlRelaxNGValidCtxtPtr ret;
6488
6489 ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt));
6490 if (ret == NULL) {
6491 xmlGenericError(xmlGenericErrorContext,
6492 "Failed to allocate new schama validation context\n");
6493 return (NULL);
6494 }
6495 memset(ret, 0, sizeof(xmlRelaxNGValidCtxt));
6496 ret->schema = schema;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006497 ret->error = xmlGenericError;
6498 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006499 return (ret);
6500}
6501
6502/**
6503 * xmlRelaxNGFreeValidCtxt:
6504 * @ctxt: the schema validation context
6505 *
6506 * Free the resources associated to the schema validation context
6507 */
6508void
6509xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt) {
6510 if (ctxt == NULL)
6511 return;
6512 xmlFree(ctxt);
6513}
6514
6515/**
6516 * xmlRelaxNGSetValidErrors:
6517 * @ctxt: a Relax-NG validation context
6518 * @err: the error function
6519 * @warn: the warning function
6520 * @ctx: the functions context
6521 *
6522 * Set the error and warning callback informations
6523 */
6524void
6525xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
6526 xmlRelaxNGValidityErrorFunc err,
6527 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
6528 if (ctxt == NULL)
6529 return;
6530 ctxt->error = err;
6531 ctxt->warning = warn;
6532 ctxt->userData = ctx;
6533}
6534
6535/**
6536 * xmlRelaxNGValidateDoc:
6537 * @ctxt: a Relax-NG validation context
6538 * @doc: a parsed document tree
6539 *
6540 * Validate a document tree in memory.
6541 *
6542 * Returns 0 if the document is valid, a positive error code
6543 * number otherwise and -1 in case of internal or API error.
6544 */
6545int
6546xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
6547 int ret;
6548
6549 if ((ctxt == NULL) || (doc == NULL))
6550 return(-1);
6551
6552 ctxt->doc = doc;
6553
6554 ret = xmlRelaxNGValidateDocument(ctxt, doc);
Daniel Veillard71531f32003-02-05 13:19:53 +00006555 /*
6556 * TODO: build error codes
6557 */
6558 if (ret == -1)
6559 return(1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006560 return(ret);
6561}
6562
6563#endif /* LIBXML_SCHEMAS_ENABLED */
6564