blob: 229c816477bfd608a8c35e0a76f7d3bc69d5b7d1 [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 Veillard6eadf632003-01-23 18:29:16 +000057
58#define UNBOUNDED (1 << 30)
59#define TODO \
60 xmlGenericError(xmlGenericErrorContext, \
61 "Unimplemented block at %s:%d\n", \
62 __FILE__, __LINE__);
63
64typedef struct _xmlRelaxNGSchema xmlRelaxNGSchema;
65typedef xmlRelaxNGSchema *xmlRelaxNGSchemaPtr;
66
67typedef struct _xmlRelaxNGDefine xmlRelaxNGDefine;
68typedef xmlRelaxNGDefine *xmlRelaxNGDefinePtr;
69
Daniel Veillardd41f4f42003-01-29 21:07:52 +000070typedef struct _xmlRelaxNGDocument xmlRelaxNGDocument;
71typedef xmlRelaxNGDocument *xmlRelaxNGDocumentPtr;
72
Daniel Veillarda9d912d2003-02-01 17:43:10 +000073typedef struct _xmlRelaxNGInclude xmlRelaxNGInclude;
74typedef xmlRelaxNGInclude *xmlRelaxNGIncludePtr;
75
Daniel Veillard6eadf632003-01-23 18:29:16 +000076typedef enum {
77 XML_RELAXNG_COMBINE_UNDEFINED = 0, /* undefined */
78 XML_RELAXNG_COMBINE_CHOICE, /* choice */
79 XML_RELAXNG_COMBINE_INTERLEAVE /* interleave */
80} xmlRelaxNGCombine;
81
82typedef struct _xmlRelaxNGGrammar xmlRelaxNGGrammar;
83typedef xmlRelaxNGGrammar *xmlRelaxNGGrammarPtr;
84
85struct _xmlRelaxNGGrammar {
86 xmlRelaxNGGrammarPtr parent;/* the parent grammar if any */
87 xmlRelaxNGGrammarPtr children;/* the children grammar if any */
88 xmlRelaxNGGrammarPtr next; /* the next grammar if any */
89 xmlRelaxNGDefinePtr start; /* <start> content */
90 xmlRelaxNGCombine combine; /* the default combine value */
Daniel Veillardd41f4f42003-01-29 21:07:52 +000091 xmlRelaxNGDefinePtr startList;/* list of <start> definitions */
Daniel Veillard6eadf632003-01-23 18:29:16 +000092 xmlHashTablePtr defs; /* define* */
93 xmlHashTablePtr refs; /* references */
94};
95
96
Daniel Veillard6eadf632003-01-23 18:29:16 +000097typedef enum {
98 XML_RELAXNG_EMPTY = 0, /* an empty pattern */
99 XML_RELAXNG_NOT_ALLOWED, /* not allowed top */
Daniel Veillard144fae12003-02-03 13:17:57 +0000100 XML_RELAXNG_EXCEPT, /* except present in nameclass defs */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000101 XML_RELAXNG_TEXT, /* textual content */
102 XML_RELAXNG_ELEMENT, /* an element */
103 XML_RELAXNG_DATATYPE, /* extenal data type definition */
104 XML_RELAXNG_VALUE, /* value from an extenal data type definition */
105 XML_RELAXNG_LIST, /* a list of patterns */
106 XML_RELAXNG_ATTRIBUTE, /* an attrbute following a pattern */
107 XML_RELAXNG_DEF, /* a definition */
108 XML_RELAXNG_REF, /* reference to a definition */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000109 XML_RELAXNG_EXTERNALREF, /* reference to an external def */
Daniel Veillard419a7682003-02-03 23:22:49 +0000110 XML_RELAXNG_PARENTREF, /* reference to a def in the parent grammar */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000111 XML_RELAXNG_OPTIONAL, /* optional patterns */
112 XML_RELAXNG_ZEROORMORE, /* zero or more non empty patterns */
113 XML_RELAXNG_ONEORMORE, /* one or more non empty patterns */
114 XML_RELAXNG_CHOICE, /* a choice between non empty patterns */
115 XML_RELAXNG_GROUP, /* a pair/group of non empty patterns */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000116 XML_RELAXNG_INTERLEAVE, /* interleaving choice of non-empty patterns */
117 XML_RELAXNG_START /* Used to keep track of starts on grammars */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000118} xmlRelaxNGType;
119
120struct _xmlRelaxNGDefine {
121 xmlRelaxNGType type; /* the type of definition */
122 xmlNodePtr node; /* the node in the source */
123 xmlChar *name; /* the element local name if present */
124 xmlChar *ns; /* the namespace local name if present */
Daniel Veillardedc91922003-01-26 00:52:04 +0000125 xmlChar *value; /* value when available */
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000126 void *data; /* data lib or specific pointer */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000127 xmlRelaxNGDefinePtr content;/* the expected content */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000128 xmlRelaxNGDefinePtr parent; /* the parent definition, if any */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000129 xmlRelaxNGDefinePtr next; /* list within grouping sequences */
130 xmlRelaxNGDefinePtr attrs; /* list of attributes for elements */
Daniel Veillard3b2e4e12003-02-03 08:52:58 +0000131 xmlRelaxNGDefinePtr nameClass;/* the nameClass definition if any */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000132 xmlRelaxNGDefinePtr nextHash;/* next define in defs/refs hash tables */
133};
134
135/**
136 * _xmlRelaxNG:
137 *
138 * A RelaxNGs definition
139 */
140struct _xmlRelaxNG {
141 xmlRelaxNGGrammarPtr topgrammar;
142 xmlDocPtr doc;
143
144 xmlHashTablePtr defs; /* define */
145 xmlHashTablePtr refs; /* references */
Daniel Veillard419a7682003-02-03 23:22:49 +0000146 xmlHashTablePtr documents; /* all the documents loaded */
147 xmlHashTablePtr includes; /* all the includes loaded */
148 int defNr; /* number of defines used */
149 xmlRelaxNGDefinePtr *defTab;/* pointer to the allocated definitions */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000150 void *_private; /* unused by the library for users or bindings */
151};
152
153typedef enum {
154 XML_RELAXNG_ERR_OK = 0,
155 XML_RELAXNG_ERR_NOROOT = 1,
156 XML_RELAXNG_ERR_
157} xmlRelaxNGValidError;
158
159#define XML_RELAXNG_IN_ATTRIBUTE 1
160
161struct _xmlRelaxNGParserCtxt {
162 void *userData; /* user specific data block */
163 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
164 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
165 xmlRelaxNGValidError err;
166
167 xmlRelaxNGPtr schema; /* The schema in use */
168 xmlRelaxNGGrammarPtr grammar; /* the current grammar */
Daniel Veillard419a7682003-02-03 23:22:49 +0000169 xmlRelaxNGGrammarPtr parentgrammar;/* the parent grammar */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000170 int flags; /* parser flags */
171 int nbErrors; /* number of errors at parse time */
172 int nbWarnings; /* number of warnings at parse time */
Daniel Veillard276be4a2003-01-24 01:03:34 +0000173 const xmlChar *define; /* the current define scope */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000174 xmlRelaxNGDefinePtr def; /* the current define */
175
176 int nbInterleaves;
177 xmlHashTablePtr interleaves; /* keep track of all the interleaves */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000178
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000179 xmlHashTablePtr documents; /* all the documents loaded */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000180 xmlHashTablePtr includes; /* all the includes loaded */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000181 xmlChar *URL;
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000182 xmlDocPtr document;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000183
Daniel Veillard419a7682003-02-03 23:22:49 +0000184 int defNr; /* number of defines used */
185 int defMax; /* number of defines aloocated */
186 xmlRelaxNGDefinePtr *defTab; /* pointer to the allocated definitions */
187
Daniel Veillard6eadf632003-01-23 18:29:16 +0000188 const char *buffer;
189 int size;
190
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000191 /* the document stack */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000192 xmlRelaxNGDocumentPtr doc; /* Current parsed external ref */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000193 int docNr; /* Depth of the parsing stack */
194 int docMax; /* Max depth of the parsing stack */
195 xmlRelaxNGDocumentPtr *docTab; /* array of docs */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000196
197 /* the include stack */
198 xmlRelaxNGIncludePtr inc; /* Current parsed include */
199 int incNr; /* Depth of the include parsing stack */
200 int incMax; /* Max depth of the parsing stack */
201 xmlRelaxNGIncludePtr *incTab; /* array of incs */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000202};
203
204#define FLAGS_IGNORABLE 1
205#define FLAGS_NEGATIVE 2
206
207/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000208 * xmlRelaxNGInterleaveGroup:
209 *
210 * A RelaxNGs partition set associated to lists of definitions
211 */
212typedef struct _xmlRelaxNGInterleaveGroup xmlRelaxNGInterleaveGroup;
213typedef xmlRelaxNGInterleaveGroup *xmlRelaxNGInterleaveGroupPtr;
214struct _xmlRelaxNGInterleaveGroup {
215 xmlRelaxNGDefinePtr rule; /* the rule to satisfy */
216 xmlRelaxNGDefinePtr *defs; /* the array of element definitions */
217};
218
219/**
220 * xmlRelaxNGPartitions:
221 *
222 * A RelaxNGs partition associated to an interleave group
223 */
224typedef struct _xmlRelaxNGPartition xmlRelaxNGPartition;
225typedef xmlRelaxNGPartition *xmlRelaxNGPartitionPtr;
226struct _xmlRelaxNGPartition {
227 int nbgroups; /* number of groups in the partitions */
228 xmlRelaxNGInterleaveGroupPtr *groups;
229};
230
231/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000232 * xmlRelaxNGValidState:
233 *
234 * A RelaxNGs validation state
235 */
236#define MAX_ATTR 20
237typedef struct _xmlRelaxNGValidState xmlRelaxNGValidState;
238typedef xmlRelaxNGValidState *xmlRelaxNGValidStatePtr;
239struct _xmlRelaxNGValidState {
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000240 xmlNodePtr node; /* the current node */
241 xmlNodePtr seq; /* the sequence of children left to validate */
242 int nbAttrs; /* the number of attributes */
Daniel Veillard1ed7f362003-02-03 10:57:45 +0000243 int nbAttrLeft; /* the number of attributes left to validate */
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000244 xmlChar *value; /* the value when operating on string */
245 xmlChar *endvalue; /* the end value when operating on string */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000246 xmlAttrPtr attrs[1]; /* the array of attributes */
247};
248
249/**
250 * xmlRelaxNGValidCtxt:
251 *
252 * A RelaxNGs validation context
253 */
254
255struct _xmlRelaxNGValidCtxt {
256 void *userData; /* user specific data block */
257 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
258 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
259
260 xmlRelaxNGPtr schema; /* The schema in use */
261 xmlDocPtr doc; /* the document being validated */
262 xmlRelaxNGValidStatePtr state; /* the current validation state */
263 int flags; /* validation flags */
Daniel Veillard231d7912003-02-09 14:22:17 +0000264 int depth; /* validation depth */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000265};
266
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000267/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000268 * xmlRelaxNGInclude:
269 *
270 * Structure associated to a RelaxNGs document element
271 */
272struct _xmlRelaxNGInclude {
273 xmlChar *href; /* the normalized href value */
274 xmlDocPtr doc; /* the associated XML document */
275 xmlRelaxNGDefinePtr content;/* the definitions */
276 xmlRelaxNGPtr schema; /* the schema */
277};
278
279/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000280 * xmlRelaxNGDocument:
281 *
282 * Structure associated to a RelaxNGs document element
283 */
284struct _xmlRelaxNGDocument {
285 xmlChar *href; /* the normalized href value */
286 xmlDocPtr doc; /* the associated XML document */
287 xmlRelaxNGDefinePtr content;/* the definitions */
288 xmlRelaxNGPtr schema; /* the schema */
289};
290
Daniel Veillard6eadf632003-01-23 18:29:16 +0000291/************************************************************************
292 * *
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000293 * Preliminary type checking interfaces *
294 * *
295 ************************************************************************/
296/**
297 * xmlRelaxNGTypeHave:
298 * @data: data needed for the library
299 * @type: the type name
300 * @value: the value to check
301 *
302 * Function provided by a type library to check if a type is exported
303 *
304 * Returns 1 if yes, 0 if no and -1 in case of error.
305 */
306typedef int (*xmlRelaxNGTypeHave) (void *data, const xmlChar *type);
307
308/**
309 * xmlRelaxNGTypeCheck:
310 * @data: data needed for the library
311 * @type: the type name
312 * @value: the value to check
313 *
314 * Function provided by a type library to check if a value match a type
315 *
316 * Returns 1 if yes, 0 if no and -1 in case of error.
317 */
318typedef int (*xmlRelaxNGTypeCheck) (void *data, const xmlChar *type,
319 const xmlChar *value);
320
321/**
322 * xmlRelaxNGTypeCompare:
323 * @data: data needed for the library
324 * @type: the type name
325 * @value1: the first value
326 * @value2: the second value
327 *
328 * Function provided by a type library to compare two values accordingly
329 * to a type.
330 *
331 * Returns 1 if yes, 0 if no and -1 in case of error.
332 */
333typedef int (*xmlRelaxNGTypeCompare) (void *data, const xmlChar *type,
334 const xmlChar *value1,
335 const xmlChar *value2);
336typedef struct _xmlRelaxNGTypeLibrary xmlRelaxNGTypeLibrary;
337typedef xmlRelaxNGTypeLibrary *xmlRelaxNGTypeLibraryPtr;
338struct _xmlRelaxNGTypeLibrary {
339 const xmlChar *namespace; /* the datatypeLibrary value */
340 void *data; /* data needed for the library */
341 xmlRelaxNGTypeHave have; /* the export function */
342 xmlRelaxNGTypeCheck check; /* the checking function */
343 xmlRelaxNGTypeCompare comp; /* the compare function */
344};
345
346/************************************************************************
347 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +0000348 * Allocation functions *
349 * *
350 ************************************************************************/
Daniel Veillard6eadf632003-01-23 18:29:16 +0000351static void xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar);
352static void xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define);
Daniel Veillardd2298792003-02-14 16:54:11 +0000353static void xmlRelaxNGNormExtSpace(xmlChar *value);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000354
355/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000356 * xmlRelaxNGFreeDocument:
357 * @docu: a document structure
358 *
359 * Deallocate a RelaxNG document structure.
360 */
361static void
362xmlRelaxNGFreeDocument(xmlRelaxNGDocumentPtr docu)
363{
364 if (docu == NULL)
365 return;
366
367 if (docu->href != NULL)
368 xmlFree(docu->href);
369 if (docu->doc != NULL)
370 xmlFreeDoc(docu->doc);
371 if (docu->schema != NULL)
372 xmlRelaxNGFree(docu->schema);
373 xmlFree(docu);
374}
375
376/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000377 * xmlRelaxNGFreeInclude:
378 * @incl: a include structure
379 *
380 * Deallocate a RelaxNG include structure.
381 */
382static void
383xmlRelaxNGFreeInclude(xmlRelaxNGIncludePtr incl)
384{
385 if (incl == NULL)
386 return;
387
388 if (incl->href != NULL)
389 xmlFree(incl->href);
390 if (incl->doc != NULL)
391 xmlFreeDoc(incl->doc);
392 if (incl->schema != NULL)
393 xmlRelaxNGFree(incl->schema);
394 xmlFree(incl);
395}
396
397/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000398 * xmlRelaxNGNewRelaxNG:
399 * @ctxt: a Relax-NG validation context (optional)
400 *
401 * Allocate a new RelaxNG structure.
402 *
403 * Returns the newly allocated structure or NULL in case or error
404 */
405static xmlRelaxNGPtr
406xmlRelaxNGNewRelaxNG(xmlRelaxNGParserCtxtPtr ctxt)
407{
408 xmlRelaxNGPtr ret;
409
410 ret = (xmlRelaxNGPtr) xmlMalloc(sizeof(xmlRelaxNG));
411 if (ret == NULL) {
412 if ((ctxt != NULL) && (ctxt->error != NULL))
413 ctxt->error(ctxt->userData, "Out of memory\n");
414 ctxt->nbErrors++;
415 return (NULL);
416 }
417 memset(ret, 0, sizeof(xmlRelaxNG));
418
419 return (ret);
420}
421
422/**
423 * xmlRelaxNGFree:
424 * @schema: a schema structure
425 *
426 * Deallocate a RelaxNG structure.
427 */
428void
429xmlRelaxNGFree(xmlRelaxNGPtr schema)
430{
431 if (schema == NULL)
432 return;
433
Daniel Veillard6eadf632003-01-23 18:29:16 +0000434 if (schema->topgrammar != NULL)
435 xmlRelaxNGFreeGrammar(schema->topgrammar);
436 if (schema->doc != NULL)
437 xmlFreeDoc(schema->doc);
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000438 if (schema->documents != NULL)
439 xmlHashFree(schema->documents, (xmlHashDeallocator)
440 xmlRelaxNGFreeDocument);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000441 if (schema->includes != NULL)
442 xmlHashFree(schema->includes, (xmlHashDeallocator)
443 xmlRelaxNGFreeInclude);
Daniel Veillard419a7682003-02-03 23:22:49 +0000444 if (schema->defTab != NULL) {
445 int i;
446
447 for (i = 0;i < schema->defNr;i++)
448 xmlRelaxNGFreeDefine(schema->defTab[i]);
449 xmlFree(schema->defTab);
450 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000451
452 xmlFree(schema);
453}
454
455/**
456 * xmlRelaxNGNewGrammar:
457 * @ctxt: a Relax-NG validation context (optional)
458 *
459 * Allocate a new RelaxNG grammar.
460 *
461 * Returns the newly allocated structure or NULL in case or error
462 */
463static xmlRelaxNGGrammarPtr
464xmlRelaxNGNewGrammar(xmlRelaxNGParserCtxtPtr ctxt)
465{
466 xmlRelaxNGGrammarPtr ret;
467
468 ret = (xmlRelaxNGGrammarPtr) xmlMalloc(sizeof(xmlRelaxNGGrammar));
469 if (ret == NULL) {
470 if ((ctxt != NULL) && (ctxt->error != NULL))
471 ctxt->error(ctxt->userData, "Out of memory\n");
472 ctxt->nbErrors++;
473 return (NULL);
474 }
475 memset(ret, 0, sizeof(xmlRelaxNGGrammar));
476
477 return (ret);
478}
479
480/**
481 * xmlRelaxNGFreeGrammar:
482 * @grammar: a grammar structure
483 *
484 * Deallocate a RelaxNG grammar structure.
485 */
486static void
487xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar)
488{
489 if (grammar == NULL)
490 return;
491
Daniel Veillard419a7682003-02-03 23:22:49 +0000492 if (grammar->next != NULL) {
493 xmlRelaxNGFreeGrammar(grammar->next);
494 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000495 if (grammar->refs != NULL) {
496 xmlHashFree(grammar->refs, NULL);
497 }
498 if (grammar->defs != NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000499 xmlHashFree(grammar->defs, NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000500 }
501
502 xmlFree(grammar);
503}
504
505/**
506 * xmlRelaxNGNewDefine:
507 * @ctxt: a Relax-NG validation context
508 * @node: the node in the input document.
509 *
510 * Allocate a new RelaxNG define.
511 *
512 * Returns the newly allocated structure or NULL in case or error
513 */
514static xmlRelaxNGDefinePtr
515xmlRelaxNGNewDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
516{
517 xmlRelaxNGDefinePtr ret;
518
Daniel Veillard419a7682003-02-03 23:22:49 +0000519 if (ctxt->defMax == 0) {
520 ctxt->defMax = 16;
521 ctxt->defNr = 0;
522 ctxt->defTab = (xmlRelaxNGDefinePtr *)
523 xmlMalloc(ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
524 if (ctxt->defTab == NULL) {
525 if ((ctxt != NULL) && (ctxt->error != NULL))
526 ctxt->error(ctxt->userData, "Out of memory\n");
527 ctxt->nbErrors++;
528 return (NULL);
529 }
530 } else if (ctxt->defMax <= ctxt->defNr) {
531 xmlRelaxNGDefinePtr *tmp;
532 ctxt->defMax *= 2;
533 tmp = (xmlRelaxNGDefinePtr *) xmlRealloc(ctxt->defTab,
534 ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
535 if (tmp == NULL) {
536 if ((ctxt != NULL) && (ctxt->error != NULL))
537 ctxt->error(ctxt->userData, "Out of memory\n");
538 ctxt->nbErrors++;
539 return (NULL);
540 }
541 ctxt->defTab = tmp;
542 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000543 ret = (xmlRelaxNGDefinePtr) xmlMalloc(sizeof(xmlRelaxNGDefine));
544 if (ret == NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000545 if ((ctxt != NULL) && (ctxt->error != NULL))
546 ctxt->error(ctxt->userData, "Out of memory\n");
Daniel Veillard6eadf632003-01-23 18:29:16 +0000547 ctxt->nbErrors++;
Daniel Veillard419a7682003-02-03 23:22:49 +0000548 return(NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000549 }
550 memset(ret, 0, sizeof(xmlRelaxNGDefine));
Daniel Veillard419a7682003-02-03 23:22:49 +0000551 ctxt->defTab[ctxt->defNr++] = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000552 ret->node = node;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000553 return (ret);
554}
555
556/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000557 * xmlRelaxNGFreePartition:
558 * @partitions: a partition set structure
559 *
560 * Deallocate RelaxNG partition set structures.
561 */
562static void
563xmlRelaxNGFreePartition(xmlRelaxNGPartitionPtr partitions) {
564 xmlRelaxNGInterleaveGroupPtr group;
565 int j;
566
567 if (partitions != NULL) {
568 if (partitions->groups != NULL) {
569 for (j = 0;j < partitions->nbgroups;j++) {
570 group = partitions->groups[j];
571 if (group != NULL) {
572 if (group->defs != NULL)
573 xmlFree(group->defs);
574 xmlFree(group);
575 }
576 }
577 xmlFree(partitions->groups);
578 }
579 xmlFree(partitions);
580 }
581}
582/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000583 * xmlRelaxNGFreeDefine:
584 * @define: a define structure
585 *
586 * Deallocate a RelaxNG define structure.
587 */
588static void
589xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define)
590{
591 if (define == NULL)
592 return;
593
Daniel Veillard419a7682003-02-03 23:22:49 +0000594 if ((define->data != NULL) &&
595 (define->type == XML_RELAXNG_INTERLEAVE))
596 xmlRelaxNGFreePartition((xmlRelaxNGPartitionPtr) define->data);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000597 if (define->name != NULL)
598 xmlFree(define->name);
599 if (define->ns != NULL)
600 xmlFree(define->ns);
Daniel Veillardedc91922003-01-26 00:52:04 +0000601 if (define->value != NULL)
602 xmlFree(define->value);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000603 xmlFree(define);
604}
605
606/**
607 * xmlRelaxNGNewValidState:
608 * @ctxt: a Relax-NG validation context
609 * @node: the current node or NULL for the document
610 *
611 * Allocate a new RelaxNG validation state
612 *
613 * Returns the newly allocated structure or NULL in case or error
614 */
615static xmlRelaxNGValidStatePtr
616xmlRelaxNGNewValidState(xmlRelaxNGValidCtxtPtr ctxt, xmlNodePtr node)
617{
618 xmlRelaxNGValidStatePtr ret;
619 xmlAttrPtr attr;
620 xmlAttrPtr attrs[MAX_ATTR];
621 int nbAttrs = 0;
622 xmlNodePtr root = NULL;
623
624 if (node == NULL) {
625 root = xmlDocGetRootElement(ctxt->doc);
626 if (root == NULL)
627 return(NULL);
628 } else {
629 attr = node->properties;
630 while (attr != NULL) {
631 if (nbAttrs < MAX_ATTR)
632 attrs[nbAttrs++] = attr;
633 else
634 nbAttrs++;
635 attr = attr->next;
636 }
637 }
638
639 if (nbAttrs < MAX_ATTR)
640 attrs[nbAttrs] = NULL;
641 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(sizeof(xmlRelaxNGValidState) +
642 nbAttrs * sizeof(xmlAttrPtr));
643 if (ret == NULL) {
644 if ((ctxt != NULL) && (ctxt->error != NULL))
645 ctxt->error(ctxt->userData, "Out of memory\n");
646 return (NULL);
647 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +0000648 ret->value = NULL;
649 ret->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000650 if (node == NULL) {
651 ret->node = (xmlNodePtr) ctxt->doc;
652 ret->seq = root;
653 ret->nbAttrs = 0;
654 } else {
655 ret->node = node;
656 ret->seq = node->children;
657 ret->nbAttrs = nbAttrs;
658 if (nbAttrs > 0) {
659 if (nbAttrs < MAX_ATTR) {
660 memcpy(&(ret->attrs[0]), attrs,
661 sizeof(xmlAttrPtr) * (nbAttrs + 1));
662 } else {
663 attr = node->properties;
664 nbAttrs = 0;
665 while (attr != NULL) {
666 ret->attrs[nbAttrs++] = attr;
667 attr = attr->next;
668 }
669 ret->attrs[nbAttrs] = NULL;
670 }
671 }
672 }
Daniel Veillard1ed7f362003-02-03 10:57:45 +0000673 ret->nbAttrLeft = ret->nbAttrs;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000674 return (ret);
675}
676
677/**
678 * xmlRelaxNGCopyValidState:
679 * @ctxt: a Relax-NG validation context
680 * @state: a validation state
681 *
682 * Copy the validation state
683 *
684 * Returns the newly allocated structure or NULL in case or error
685 */
686static xmlRelaxNGValidStatePtr
687xmlRelaxNGCopyValidState(xmlRelaxNGValidCtxtPtr ctxt,
688 xmlRelaxNGValidStatePtr state)
689{
690 xmlRelaxNGValidStatePtr ret;
691 unsigned int size;
692
693 if (state == NULL)
694 return(NULL);
695
696 size = sizeof(xmlRelaxNGValidState) +
697 state->nbAttrs * sizeof(xmlAttrPtr);
698 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(size);
699 if (ret == NULL) {
700 if ((ctxt != NULL) && (ctxt->error != NULL))
701 ctxt->error(ctxt->userData, "Out of memory\n");
702 return (NULL);
703 }
704 memcpy(ret, state, size);
705 return(ret);
706}
707
708/**
709 * xmlRelaxNGFreeValidState:
710 * @state: a validation state structure
711 *
712 * Deallocate a RelaxNG validation state structure.
713 */
714static void
715xmlRelaxNGFreeValidState(xmlRelaxNGValidStatePtr state)
716{
717 if (state == NULL)
718 return;
719
720 xmlFree(state);
721}
722
723/************************************************************************
724 * *
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000725 * Document functions *
726 * *
727 ************************************************************************/
728static xmlDocPtr xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt,
729 xmlDocPtr doc);
730
731/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000732 * xmlRelaxNGIncludePush:
733 * @ctxt: the parser context
734 * @value: the element doc
735 *
736 * Pushes a new include on top of the include stack
737 *
738 * Returns 0 in case of error, the index in the stack otherwise
739 */
740static int
741xmlRelaxNGIncludePush(xmlRelaxNGParserCtxtPtr ctxt,
742 xmlRelaxNGIncludePtr value)
743{
744 if (ctxt->incTab == NULL) {
745 ctxt->incMax = 4;
746 ctxt->incNr = 0;
747 ctxt->incTab = (xmlRelaxNGIncludePtr *) xmlMalloc(
748 ctxt->incMax * sizeof(ctxt->incTab[0]));
749 if (ctxt->incTab == NULL) {
750 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
751 return (0);
752 }
753 }
754 if (ctxt->incNr >= ctxt->incMax) {
755 ctxt->incMax *= 2;
756 ctxt->incTab =
757 (xmlRelaxNGIncludePtr *) xmlRealloc(ctxt->incTab,
758 ctxt->incMax *
759 sizeof(ctxt->incTab[0]));
760 if (ctxt->incTab == NULL) {
761 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
762 return (0);
763 }
764 }
765 ctxt->incTab[ctxt->incNr] = value;
766 ctxt->inc = value;
767 return (ctxt->incNr++);
768}
769
770/**
771 * xmlRelaxNGIncludePop:
772 * @ctxt: the parser context
773 *
774 * Pops the top include from the include stack
775 *
776 * Returns the include just removed
777 */
778static xmlRelaxNGIncludePtr
779xmlRelaxNGIncludePop(xmlRelaxNGParserCtxtPtr ctxt)
780{
781 xmlRelaxNGIncludePtr ret;
782
783 if (ctxt->incNr <= 0)
784 return (0);
785 ctxt->incNr--;
786 if (ctxt->incNr > 0)
787 ctxt->inc = ctxt->incTab[ctxt->incNr - 1];
788 else
789 ctxt->inc = NULL;
790 ret = ctxt->incTab[ctxt->incNr];
791 ctxt->incTab[ctxt->incNr] = 0;
792 return (ret);
793}
794
795/**
796 * xmlRelaxNGLoadInclude:
797 * @ctxt: the parser context
798 * @URL: the normalized URL
799 * @node: the include node.
800 *
801 * First lookup if the document is already loaded into the parser context,
802 * check against recursion. If not found the resource is loaded and
803 * the content is preprocessed before being returned back to the caller.
804 *
805 * Returns the xmlRelaxNGIncludePtr or NULL in case of error
806 */
807static xmlRelaxNGIncludePtr
808xmlRelaxNGLoadInclude(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
809 xmlNodePtr node) {
810 xmlRelaxNGIncludePtr ret = NULL;
811 xmlDocPtr doc;
812 int i;
813 xmlNodePtr root, tmp, tmp2, cur;
814
815 /*
816 * check against recursion in the stack
817 */
818 for (i = 0;i < ctxt->incNr;i++) {
819 if (xmlStrEqual(ctxt->incTab[i]->href, URL)) {
820 if (ctxt->error != NULL)
821 ctxt->error(ctxt->userData,
822 "Detected an externalRef recursion for %s\n",
823 URL);
824 ctxt->nbErrors++;
825 return(NULL);
826 }
827 }
828
829 /*
830 * Lookup in the hash table
831 */
832 if (ctxt->includes == NULL) {
833 ctxt->includes = xmlHashCreate(10);
834 if (ctxt->includes == NULL) {
835 if (ctxt->error != NULL)
836 ctxt->error(ctxt->userData,
837 "Failed to allocate hash table for document\n");
838 ctxt->nbErrors++;
839 return(NULL);
840 }
841 } else {
842 ret = xmlHashLookup(ctxt->includes, URL);
843 if (ret != NULL)
844 return(ret);
845 }
846
847
848 /*
849 * load the document
850 */
851 doc = xmlParseFile((const char *) URL);
852 if (doc == NULL) {
853 if (ctxt->error != NULL)
854 ctxt->error(ctxt->userData,
855 "xmlRelaxNG: could not load %s\n", URL);
856 ctxt->nbErrors++;
857 return (NULL);
858 }
859
860 /*
861 * Allocate the document structures and register it first.
862 */
863 ret = (xmlRelaxNGIncludePtr) xmlMalloc(sizeof(xmlRelaxNGInclude));
864 if (ret == NULL) {
865 if (ctxt->error != NULL)
866 ctxt->error(ctxt->userData,
867 "xmlRelaxNG: allocate memory for doc %s\n", URL);
868 ctxt->nbErrors++;
869 xmlFreeDoc(doc);
870 return (NULL);
871 }
872 memset(ret, 0, sizeof(xmlRelaxNGInclude));
873 ret->doc = doc;
874 ret->href = xmlStrdup(URL);
875
876 /*
877 * push it on the stack and register it in the hash table
878 */
879 xmlHashAddEntry(ctxt->includes, URL, ret);
880 xmlRelaxNGIncludePush(ctxt, ret);
881
882 /*
883 * Some preprocessing of the document content, this include recursing
884 * in the include stack.
885 */
886 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
887 if (doc == NULL) {
888 /* xmlFreeDoc(ctxt->include); */
889 ctxt->inc = NULL;
890 return(NULL);
891 }
892
893 /*
894 * Pop up the include from the stack
895 */
896 xmlRelaxNGIncludePop(ctxt);
897
898 /*
899 * Check that the top element is a grammar
900 */
901 root = xmlDocGetRootElement(doc);
902 if (root == NULL) {
903 if (ctxt->error != NULL)
904 ctxt->error(ctxt->userData,
905 "xmlRelaxNG: included document is empty %s\n", URL);
906 ctxt->nbErrors++;
907 xmlFreeDoc(doc);
908 return (NULL);
909 }
910 if (!IS_RELAXNG(root, "grammar")) {
911 if (ctxt->error != NULL)
912 ctxt->error(ctxt->userData,
913 "xmlRelaxNG: included document %s root is not a grammar\n",
914 URL);
915 ctxt->nbErrors++;
916 xmlFreeDoc(doc);
917 return (NULL);
918 }
919
920 /*
921 * Elimination of redefined rules in the include.
922 */
923 cur = node->children;
924 while (cur != NULL) {
925 if (IS_RELAXNG(cur, "start")) {
926 int found = 0;
927
928 tmp = root->children;
929 while (tmp != NULL) {
930 tmp2 = tmp->next;
931 if (IS_RELAXNG(tmp, "start")) {
932 found = 1;
933 xmlUnlinkNode(tmp);
934 xmlFreeNode(tmp);
935 }
936 tmp = tmp2;
937 }
938 if (!found) {
939 if (ctxt->error != NULL)
940 ctxt->error(ctxt->userData,
941 "xmlRelaxNG: include %s has a start but not the included grammar\n",
942 URL);
943 ctxt->nbErrors++;
944 }
945 } else if (IS_RELAXNG(cur, "define")) {
946 xmlChar *name, *name2;
947
948 name = xmlGetProp(cur, BAD_CAST "name");
949 if (name == NULL) {
950 if (ctxt->error != NULL)
951 ctxt->error(ctxt->userData,
952 "xmlRelaxNG: include %s has define without name\n",
953 URL);
954 ctxt->nbErrors++;
955 } else {
956 int found = 0;
957
Daniel Veillardd2298792003-02-14 16:54:11 +0000958 xmlRelaxNGNormExtSpace(name);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000959 tmp = root->children;
960 while (tmp != NULL) {
961 tmp2 = tmp->next;
962 if (IS_RELAXNG(tmp, "define")) {
963 name2 = xmlGetProp(tmp, BAD_CAST "name");
Daniel Veillardd2298792003-02-14 16:54:11 +0000964 xmlRelaxNGNormExtSpace(name2);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000965 if (name2 != NULL) {
966 if (xmlStrEqual(name, name2)) {
967 found = 1;
968 xmlUnlinkNode(tmp);
969 xmlFreeNode(tmp);
970 }
971 xmlFree(name2);
972 }
973 }
974 tmp = tmp2;
975 }
976 if (!found) {
977 if (ctxt->error != NULL)
978 ctxt->error(ctxt->userData,
979 "xmlRelaxNG: include %s has a define %s but not the included grammar\n",
980 URL, name);
981 ctxt->nbErrors++;
982 }
983 xmlFree(name);
984 }
985 }
986 cur = cur->next;
987 }
988
989
990 return(ret);
991}
992
993/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000994 * xmlRelaxNGDocumentPush:
995 * @ctxt: the parser context
996 * @value: the element doc
997 *
998 * Pushes a new doc on top of the doc stack
999 *
1000 * Returns 0 in case of error, the index in the stack otherwise
1001 */
1002static int
1003xmlRelaxNGDocumentPush(xmlRelaxNGParserCtxtPtr ctxt,
1004 xmlRelaxNGDocumentPtr value)
1005{
1006 if (ctxt->docTab == NULL) {
1007 ctxt->docMax = 4;
1008 ctxt->docNr = 0;
1009 ctxt->docTab = (xmlRelaxNGDocumentPtr *) xmlMalloc(
1010 ctxt->docMax * sizeof(ctxt->docTab[0]));
1011 if (ctxt->docTab == NULL) {
1012 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
1013 return (0);
1014 }
1015 }
1016 if (ctxt->docNr >= ctxt->docMax) {
1017 ctxt->docMax *= 2;
1018 ctxt->docTab =
1019 (xmlRelaxNGDocumentPtr *) xmlRealloc(ctxt->docTab,
1020 ctxt->docMax *
1021 sizeof(ctxt->docTab[0]));
1022 if (ctxt->docTab == NULL) {
1023 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
1024 return (0);
1025 }
1026 }
1027 ctxt->docTab[ctxt->docNr] = value;
1028 ctxt->doc = value;
1029 return (ctxt->docNr++);
1030}
1031
1032/**
1033 * xmlRelaxNGDocumentPop:
1034 * @ctxt: the parser context
1035 *
1036 * Pops the top doc from the doc stack
1037 *
1038 * Returns the doc just removed
1039 */
1040static xmlRelaxNGDocumentPtr
1041xmlRelaxNGDocumentPop(xmlRelaxNGParserCtxtPtr ctxt)
1042{
1043 xmlRelaxNGDocumentPtr ret;
1044
1045 if (ctxt->docNr <= 0)
1046 return (0);
1047 ctxt->docNr--;
1048 if (ctxt->docNr > 0)
1049 ctxt->doc = ctxt->docTab[ctxt->docNr - 1];
1050 else
1051 ctxt->doc = NULL;
1052 ret = ctxt->docTab[ctxt->docNr];
1053 ctxt->docTab[ctxt->docNr] = 0;
1054 return (ret);
1055}
1056
1057/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001058 * xmlRelaxNGLoadExternalRef:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001059 * @ctxt: the parser context
1060 * @URL: the normalized URL
1061 * @ns: the inherited ns if any
1062 *
1063 * First lookup if the document is already loaded into the parser context,
1064 * check against recursion. If not found the resource is loaded and
1065 * the content is preprocessed before being returned back to the caller.
1066 *
1067 * Returns the xmlRelaxNGDocumentPtr or NULL in case of error
1068 */
1069static xmlRelaxNGDocumentPtr
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001070xmlRelaxNGLoadExternalRef(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001071 const xmlChar *ns) {
1072 xmlRelaxNGDocumentPtr ret = NULL;
1073 xmlDocPtr doc;
1074 xmlNodePtr root;
1075 int i;
1076
1077 /*
1078 * check against recursion in the stack
1079 */
1080 for (i = 0;i < ctxt->docNr;i++) {
1081 if (xmlStrEqual(ctxt->docTab[i]->href, URL)) {
1082 if (ctxt->error != NULL)
1083 ctxt->error(ctxt->userData,
1084 "Detected an externalRef recursion for %s\n",
1085 URL);
1086 ctxt->nbErrors++;
1087 return(NULL);
1088 }
1089 }
1090
1091 /*
1092 * Lookup in the hash table
1093 */
1094 if (ctxt->documents == NULL) {
1095 ctxt->documents = xmlHashCreate(10);
1096 if (ctxt->documents == NULL) {
1097 if (ctxt->error != NULL)
1098 ctxt->error(ctxt->userData,
1099 "Failed to allocate hash table for document\n");
1100 ctxt->nbErrors++;
1101 return(NULL);
1102 }
1103 } else {
1104 if (ns == NULL)
1105 ret = xmlHashLookup2(ctxt->documents, BAD_CAST "", URL);
1106 else
1107 ret = xmlHashLookup2(ctxt->documents, ns, URL);
1108 if (ret != NULL)
1109 return(ret);
1110 }
1111
1112
1113 /*
1114 * load the document
1115 */
1116 doc = xmlParseFile((const char *) URL);
1117 if (doc == NULL) {
1118 if (ctxt->error != NULL)
1119 ctxt->error(ctxt->userData,
1120 "xmlRelaxNG: could not load %s\n", URL);
1121 ctxt->nbErrors++;
1122 return (NULL);
1123 }
1124
1125 /*
1126 * Allocate the document structures and register it first.
1127 */
1128 ret = (xmlRelaxNGDocumentPtr) xmlMalloc(sizeof(xmlRelaxNGDocument));
1129 if (ret == NULL) {
1130 if (ctxt->error != NULL)
1131 ctxt->error(ctxt->userData,
1132 "xmlRelaxNG: allocate memory for doc %s\n", URL);
1133 ctxt->nbErrors++;
1134 xmlFreeDoc(doc);
1135 return (NULL);
1136 }
1137 memset(ret, 0, sizeof(xmlRelaxNGDocument));
1138 ret->doc = doc;
1139 ret->href = xmlStrdup(URL);
1140
1141 /*
1142 * transmit the ns if needed
1143 */
1144 if (ns != NULL) {
1145 root = xmlDocGetRootElement(doc);
1146 if (root != NULL) {
1147 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
1148 xmlSetProp(root, BAD_CAST"ns", ns);
1149 }
1150 }
1151 }
1152
1153 /*
1154 * push it on the stack and register it in the hash table
1155 */
1156 if (ns == NULL)
1157 xmlHashAddEntry2(ctxt->documents, BAD_CAST "", URL, ret);
1158 else
1159 xmlHashAddEntry2(ctxt->documents, ns, URL, ret);
1160 xmlRelaxNGDocumentPush(ctxt, ret);
1161
1162 /*
1163 * Some preprocessing of the document content
1164 */
1165 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
1166 if (doc == NULL) {
1167 xmlFreeDoc(ctxt->document);
1168 ctxt->doc = NULL;
1169 return(NULL);
1170 }
1171
1172 xmlRelaxNGDocumentPop(ctxt);
1173
1174 return(ret);
1175}
1176
1177/************************************************************************
1178 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +00001179 * Error functions *
1180 * *
1181 ************************************************************************/
1182
1183#define VALID_CTXT() \
Daniel Veillard231d7912003-02-09 14:22:17 +00001184 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1185 xmlGenericError(xmlGenericErrorContext, \
Daniel Veillard6eadf632003-01-23 18:29:16 +00001186 "error detected at %s:%d\n", \
1187 __FILE__, __LINE__);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001188
1189#define VALID_ERROR(a) \
Daniel Veillard231d7912003-02-09 14:22:17 +00001190 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001191 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a)
1192#define VALID_ERROR2(a, b) \
1193 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1194 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a, b)
1195#define VALID_ERROR3(a, b, c) \
1196 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1197 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a, b, c)
Daniel Veillard6eadf632003-01-23 18:29:16 +00001198
Daniel Veillardd2298792003-02-14 16:54:11 +00001199#ifdef DEBUG
Daniel Veillard231d7912003-02-09 14:22:17 +00001200static const char *
1201xmlRelaxNGDefName(xmlRelaxNGDefinePtr def) {
1202 if (def == NULL)
1203 return("none");
1204 switch(def->type) {
1205 case XML_RELAXNG_EMPTY: return("empty");
1206 case XML_RELAXNG_NOT_ALLOWED: return("notAllowed");
1207 case XML_RELAXNG_EXCEPT: return("except");
1208 case XML_RELAXNG_TEXT: return("text");
1209 case XML_RELAXNG_ELEMENT: return("element");
1210 case XML_RELAXNG_DATATYPE: return("datatype");
1211 case XML_RELAXNG_VALUE: return("value");
1212 case XML_RELAXNG_LIST: return("list");
1213 case XML_RELAXNG_ATTRIBUTE: return("attribute");
1214 case XML_RELAXNG_DEF: return("def");
1215 case XML_RELAXNG_REF: return("ref");
1216 case XML_RELAXNG_EXTERNALREF: return("externalRef");
1217 case XML_RELAXNG_PARENTREF: return("parentRef");
1218 case XML_RELAXNG_OPTIONAL: return("optional");
1219 case XML_RELAXNG_ZEROORMORE: return("zeroOrMore");
1220 case XML_RELAXNG_ONEORMORE: return("oneOrMore");
1221 case XML_RELAXNG_CHOICE: return("choice");
1222 case XML_RELAXNG_GROUP: return("group");
1223 case XML_RELAXNG_INTERLEAVE: return("interleave");
1224 case XML_RELAXNG_START: return("start");
1225 }
1226 return("unknown");
1227}
Daniel Veillardd2298792003-02-14 16:54:11 +00001228#endif
1229
Daniel Veillard6eadf632003-01-23 18:29:16 +00001230#if 0
1231/**
1232 * xmlRelaxNGErrorContext:
1233 * @ctxt: the parsing context
1234 * @schema: the schema being built
1235 * @node: the node being processed
1236 * @child: the child being processed
1237 *
1238 * Dump a RelaxNGType structure
1239 */
1240static void
1241xmlRelaxNGErrorContext(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGPtr schema,
1242 xmlNodePtr node, xmlNodePtr child)
1243{
1244 int line = 0;
1245 const xmlChar *file = NULL;
1246 const xmlChar *name = NULL;
1247 const char *type = "error";
1248
1249 if ((ctxt == NULL) || (ctxt->error == NULL))
1250 return;
1251
1252 if (child != NULL)
1253 node = child;
1254
1255 if (node != NULL) {
1256 if ((node->type == XML_DOCUMENT_NODE) ||
1257 (node->type == XML_HTML_DOCUMENT_NODE)) {
1258 xmlDocPtr doc = (xmlDocPtr) node;
1259
1260 file = doc->URL;
1261 } else {
1262 /*
1263 * Try to find contextual informations to report
1264 */
1265 if (node->type == XML_ELEMENT_NODE) {
1266 line = (int) node->content;
1267 } else if ((node->prev != NULL) &&
1268 (node->prev->type == XML_ELEMENT_NODE)) {
1269 line = (int) node->prev->content;
1270 } else if ((node->parent != NULL) &&
1271 (node->parent->type == XML_ELEMENT_NODE)) {
1272 line = (int) node->parent->content;
1273 }
1274 if ((node->doc != NULL) && (node->doc->URL != NULL))
1275 file = node->doc->URL;
1276 if (node->name != NULL)
1277 name = node->name;
1278 }
1279 }
1280
1281 if (ctxt != NULL)
1282 type = "compilation error";
1283 else if (schema != NULL)
1284 type = "runtime error";
1285
1286 if ((file != NULL) && (line != 0) && (name != NULL))
1287 ctxt->error(ctxt->userData, "%s: file %s line %d element %s\n",
1288 type, file, line, name);
1289 else if ((file != NULL) && (name != NULL))
1290 ctxt->error(ctxt->userData, "%s: file %s element %s\n",
1291 type, file, name);
1292 else if ((file != NULL) && (line != 0))
1293 ctxt->error(ctxt->userData, "%s: file %s line %d\n", type, file, line);
1294 else if (file != NULL)
1295 ctxt->error(ctxt->userData, "%s: file %s\n", type, file);
1296 else if (name != NULL)
1297 ctxt->error(ctxt->userData, "%s: element %s\n", type, name);
1298 else
1299 ctxt->error(ctxt->userData, "%s\n", type);
1300}
1301#endif
1302
1303/************************************************************************
1304 * *
1305 * Type library hooks *
1306 * *
1307 ************************************************************************/
Daniel Veillardea3f3982003-01-26 19:45:18 +00001308static xmlChar *xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt,
1309 const xmlChar *str);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001310
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001311/**
1312 * xmlRelaxNGSchemaTypeHave:
1313 * @data: data needed for the library
1314 * @type: the type name
1315 *
1316 * Check if the given type is provided by
1317 * the W3C XMLSchema Datatype library.
1318 *
1319 * Returns 1 if yes, 0 if no and -1 in case of error.
1320 */
1321static int
1322xmlRelaxNGSchemaTypeHave(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001323 const xmlChar *type) {
1324 xmlSchemaTypePtr typ;
1325
1326 if (type == NULL)
1327 return(-1);
1328 typ = xmlSchemaGetPredefinedType(type,
1329 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1330 if (typ == NULL)
1331 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001332 return(1);
1333}
1334
1335/**
1336 * xmlRelaxNGSchemaTypeCheck:
1337 * @data: data needed for the library
1338 * @type: the type name
1339 * @value: the value to check
1340 *
1341 * Check if the given type and value are validated by
1342 * the W3C XMLSchema Datatype library.
1343 *
1344 * Returns 1 if yes, 0 if no and -1 in case of error.
1345 */
1346static int
1347xmlRelaxNGSchemaTypeCheck(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001348 const xmlChar *type,
1349 const xmlChar *value) {
1350 xmlSchemaTypePtr typ;
1351 int ret;
1352
1353 /*
1354 * TODO: the type should be cached ab provided back, interface subject
1355 * to changes.
1356 * TODO: handle facets, may require an additional interface and keep
1357 * the value returned from the validation.
1358 */
1359 if ((type == NULL) || (value == NULL))
1360 return(-1);
1361 typ = xmlSchemaGetPredefinedType(type,
1362 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1363 if (typ == NULL)
1364 return(-1);
1365 ret = xmlSchemaValidatePredefinedType(typ, value, NULL);
1366 if (ret == 0)
1367 return(1);
1368 if (ret > 0)
1369 return(0);
1370 return(-1);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001371}
1372
1373/**
1374 * xmlRelaxNGSchemaTypeCompare:
1375 * @data: data needed for the library
1376 * @type: the type name
1377 * @value1: the first value
1378 * @value2: the second value
1379 *
1380 * Compare two values accordingly a type from the W3C XMLSchema
1381 * Datatype library.
1382 *
1383 * Returns 1 if yes, 0 if no and -1 in case of error.
1384 */
1385static int
1386xmlRelaxNGSchemaTypeCompare(void *data ATTRIBUTE_UNUSED,
1387 const xmlChar *type ATTRIBUTE_UNUSED,
1388 const xmlChar *value1 ATTRIBUTE_UNUSED,
1389 const xmlChar *value2 ATTRIBUTE_UNUSED) {
1390 TODO
1391 return(1);
1392}
1393
1394/**
1395 * xmlRelaxNGDefaultTypeHave:
1396 * @data: data needed for the library
1397 * @type: the type name
1398 *
1399 * Check if the given type is provided by
1400 * the default datatype library.
1401 *
1402 * Returns 1 if yes, 0 if no and -1 in case of error.
1403 */
1404static int
1405xmlRelaxNGDefaultTypeHave(void *data ATTRIBUTE_UNUSED, const xmlChar *type) {
1406 if (type == NULL)
1407 return(-1);
1408 if (xmlStrEqual(type, BAD_CAST "string"))
1409 return(1);
1410 if (xmlStrEqual(type, BAD_CAST "token"))
1411 return(1);
1412 return(0);
1413}
1414
1415/**
1416 * xmlRelaxNGDefaultTypeCheck:
1417 * @data: data needed for the library
1418 * @type: the type name
1419 * @value: the value to check
1420 *
1421 * Check if the given type and value are validated by
1422 * the default datatype library.
1423 *
1424 * Returns 1 if yes, 0 if no and -1 in case of error.
1425 */
1426static int
1427xmlRelaxNGDefaultTypeCheck(void *data ATTRIBUTE_UNUSED,
1428 const xmlChar *type ATTRIBUTE_UNUSED,
1429 const xmlChar *value ATTRIBUTE_UNUSED) {
1430 return(1);
1431}
1432
1433/**
1434 * xmlRelaxNGDefaultTypeCompare:
1435 * @data: data needed for the library
1436 * @type: the type name
1437 * @value1: the first value
1438 * @value2: the second value
1439 *
1440 * Compare two values accordingly a type from the default
1441 * datatype library.
1442 *
1443 * Returns 1 if yes, 0 if no and -1 in case of error.
1444 */
1445static int
1446xmlRelaxNGDefaultTypeCompare(void *data ATTRIBUTE_UNUSED,
1447 const xmlChar *type ATTRIBUTE_UNUSED,
1448 const xmlChar *value1 ATTRIBUTE_UNUSED,
1449 const xmlChar *value2 ATTRIBUTE_UNUSED) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00001450 int ret = -1;
1451
1452 if (xmlStrEqual(type, BAD_CAST "string")) {
1453 ret = xmlStrEqual(value1, value2);
1454 } else if (xmlStrEqual(type, BAD_CAST "token")) {
1455 if (!xmlStrEqual(value1, value2)) {
1456 xmlChar *nval, *nvalue;
1457
1458 /*
1459 * TODO: trivial optimizations are possible by
1460 * computing at compile-time
1461 */
1462 nval = xmlRelaxNGNormalize(NULL, value1);
1463 nvalue = xmlRelaxNGNormalize(NULL, value2);
1464
1465 if ((nval == NULL) || (nvalue == NULL) ||
1466 (!xmlStrEqual(nval, nvalue)))
1467 ret = -1;
1468 if (nval != NULL)
1469 xmlFree(nval);
1470 if (nvalue != NULL)
1471 xmlFree(nvalue);
1472 }
1473 }
1474 return(ret);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001475}
1476
1477static int xmlRelaxNGTypeInitialized = 0;
1478static xmlHashTablePtr xmlRelaxNGRegisteredTypes = NULL;
1479
1480/**
1481 * xmlRelaxNGFreeTypeLibrary:
1482 * @lib: the type library structure
1483 * @namespace: the URI bound to the library
1484 *
1485 * Free the structure associated to the type library
1486 */
Daniel Veillard6eadf632003-01-23 18:29:16 +00001487static void
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001488xmlRelaxNGFreeTypeLibrary(xmlRelaxNGTypeLibraryPtr lib,
1489 const xmlChar *namespace ATTRIBUTE_UNUSED) {
1490 if (lib == NULL)
1491 return;
1492 if (lib->namespace != NULL)
1493 xmlFree((xmlChar *)lib->namespace);
1494 xmlFree(lib);
1495}
1496
1497/**
1498 * xmlRelaxNGRegisterTypeLibrary:
1499 * @namespace: the URI bound to the library
1500 * @data: data associated to the library
1501 * @have: the provide function
1502 * @check: the checking function
1503 * @comp: the comparison function
1504 *
1505 * Register a new type library
1506 *
1507 * Returns 0 in case of success and -1 in case of error.
1508 */
1509static int
1510xmlRelaxNGRegisterTypeLibrary(const xmlChar *namespace, void *data,
1511 xmlRelaxNGTypeHave have, xmlRelaxNGTypeCheck check,
1512 xmlRelaxNGTypeCompare comp) {
1513 xmlRelaxNGTypeLibraryPtr lib;
1514 int ret;
1515
1516 if ((xmlRelaxNGRegisteredTypes == NULL) || (namespace == NULL) ||
1517 (check == NULL) || (comp == NULL))
1518 return(-1);
1519 if (xmlHashLookup(xmlRelaxNGRegisteredTypes, namespace) != NULL) {
1520 xmlGenericError(xmlGenericErrorContext,
1521 "Relax-NG types library '%s' already registered\n",
1522 namespace);
1523 return(-1);
1524 }
1525 lib = (xmlRelaxNGTypeLibraryPtr) xmlMalloc(sizeof(xmlRelaxNGTypeLibrary));
1526 if (lib == NULL) {
1527 xmlGenericError(xmlGenericErrorContext,
1528 "Relax-NG types library '%s' malloc() failed\n",
1529 namespace);
1530 return (-1);
1531 }
1532 memset(lib, 0, sizeof(xmlRelaxNGTypeLibrary));
1533 lib->namespace = xmlStrdup(namespace);
1534 lib->data = data;
1535 lib->have = have;
1536 lib->comp = comp;
1537 lib->check = check;
1538 ret = xmlHashAddEntry(xmlRelaxNGRegisteredTypes, namespace, lib);
1539 if (ret < 0) {
1540 xmlGenericError(xmlGenericErrorContext,
1541 "Relax-NG types library failed to register '%s'\n",
1542 namespace);
1543 xmlRelaxNGFreeTypeLibrary(lib, namespace);
1544 return(-1);
1545 }
1546 return(0);
1547}
1548
1549/**
1550 * xmlRelaxNGInitTypes:
1551 *
1552 * Initilize the default type libraries.
1553 *
1554 * Returns 0 in case of success and -1 in case of error.
1555 */
1556static int
Daniel Veillard6eadf632003-01-23 18:29:16 +00001557xmlRelaxNGInitTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001558 if (xmlRelaxNGTypeInitialized != 0)
1559 return(0);
1560 xmlRelaxNGRegisteredTypes = xmlHashCreate(10);
1561 if (xmlRelaxNGRegisteredTypes == NULL) {
1562 xmlGenericError(xmlGenericErrorContext,
1563 "Failed to allocate sh table for Relax-NG types\n");
1564 return(-1);
1565 }
1566 xmlRelaxNGRegisterTypeLibrary(
1567 BAD_CAST "http://www.w3.org/2001/XMLSchema-datatypes",
1568 NULL,
1569 xmlRelaxNGSchemaTypeHave,
1570 xmlRelaxNGSchemaTypeCheck,
1571 xmlRelaxNGSchemaTypeCompare);
1572 xmlRelaxNGRegisterTypeLibrary(
1573 xmlRelaxNGNs,
1574 NULL,
1575 xmlRelaxNGDefaultTypeHave,
1576 xmlRelaxNGDefaultTypeCheck,
1577 xmlRelaxNGDefaultTypeCompare);
1578 xmlRelaxNGTypeInitialized = 1;
1579 return(0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001580}
1581
1582/**
1583 * xmlRelaxNGCleanupTypes:
1584 *
1585 * Cleanup the default Schemas type library associated to RelaxNG
1586 */
1587void
1588xmlRelaxNGCleanupTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001589 if (xmlRelaxNGTypeInitialized == 0)
1590 return;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001591 xmlSchemaCleanupTypes();
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001592 xmlHashFree(xmlRelaxNGRegisteredTypes, (xmlHashDeallocator)
1593 xmlRelaxNGFreeTypeLibrary);
1594 xmlRelaxNGTypeInitialized = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001595}
1596
1597/************************************************************************
1598 * *
1599 * Parsing functions *
1600 * *
1601 ************************************************************************/
1602
1603static xmlRelaxNGDefinePtr xmlRelaxNGParseAttribute(
1604 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1605static xmlRelaxNGDefinePtr xmlRelaxNGParseElement(
1606 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1607static xmlRelaxNGDefinePtr xmlRelaxNGParsePatterns(
Daniel Veillard154877e2003-01-30 12:17:05 +00001608 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes, int group);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001609static xmlRelaxNGDefinePtr xmlRelaxNGParsePattern(
1610 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001611static xmlRelaxNGPtr xmlRelaxNGParseDocument(
1612 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00001613static int xmlRelaxNGParseGrammarContent(
1614 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00001615static xmlRelaxNGDefinePtr xmlRelaxNGParseNameClass(
1616 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
1617 xmlRelaxNGDefinePtr def);
Daniel Veillard419a7682003-02-03 23:22:49 +00001618static xmlRelaxNGGrammarPtr xmlRelaxNGParseGrammar(
1619 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001620
1621
1622#define IS_BLANK_NODE(n) \
1623 (((n)->type == XML_TEXT_NODE) && (xmlRelaxNGIsBlank((n)->content)))
1624
1625/**
1626 * xmlRelaxNGIsBlank:
1627 * @str: a string
1628 *
1629 * Check if a string is ignorable c.f. 4.2. Whitespace
1630 *
1631 * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
1632 */
1633static int
1634xmlRelaxNGIsBlank(xmlChar *str) {
1635 if (str == NULL)
1636 return(1);
1637 while (*str != 0) {
1638 if (!(IS_BLANK(*str))) return(0);
1639 str++;
1640 }
1641 return(1);
1642}
1643
Daniel Veillard6eadf632003-01-23 18:29:16 +00001644/**
1645 * xmlRelaxNGGetDataTypeLibrary:
1646 * @ctxt: a Relax-NG parser context
1647 * @node: the current data or value element
1648 *
1649 * Applies algorithm from 4.3. datatypeLibrary attribute
1650 *
1651 * Returns the datatypeLibary value or NULL if not found
1652 */
1653static xmlChar *
1654xmlRelaxNGGetDataTypeLibrary(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1655 xmlNodePtr node) {
1656 xmlChar *ret, *escape;
1657
Daniel Veillard6eadf632003-01-23 18:29:16 +00001658 if ((IS_RELAXNG(node, "data")) || (IS_RELAXNG(node, "value"))) {
1659 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1660 if (ret != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001661 if (ret[0] == 0) {
1662 xmlFree(ret);
1663 return(NULL);
1664 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001665 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +00001666 if (escape == NULL) {
1667 return(ret);
1668 }
1669 xmlFree(ret);
1670 return(escape);
1671 }
1672 }
1673 node = node->parent;
1674 while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) {
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001675 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1676 if (ret != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001677 if (ret[0] == 0) {
1678 xmlFree(ret);
1679 return(NULL);
1680 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001681 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
1682 if (escape == NULL) {
1683 return(ret);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001684 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001685 xmlFree(ret);
1686 return(escape);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001687 }
1688 node = node->parent;
1689 }
1690 return(NULL);
1691}
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001692
1693/**
Daniel Veillardedc91922003-01-26 00:52:04 +00001694 * xmlRelaxNGParseValue:
1695 * @ctxt: a Relax-NG parser context
1696 * @node: the data node.
1697 *
1698 * parse the content of a RelaxNG value node.
1699 *
1700 * Returns the definition pointer or NULL in case of error
1701 */
1702static xmlRelaxNGDefinePtr
1703xmlRelaxNGParseValue(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1704 xmlRelaxNGDefinePtr def = NULL;
1705 xmlRelaxNGTypeLibraryPtr lib;
1706 xmlChar *type;
1707 xmlChar *library;
1708 int tmp;
1709
1710 def = xmlRelaxNGNewDefine(ctxt, node);
1711 if (def == NULL)
1712 return(NULL);
1713 def->type = XML_RELAXNG_VALUE;
1714
1715 type = xmlGetProp(node, BAD_CAST "type");
1716 if (type != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001717 xmlRelaxNGNormExtSpace(type);
1718 if (xmlValidateNCName(type, 0)) {
1719 if (ctxt->error != NULL)
1720 ctxt->error(ctxt->userData,
1721 "value type '%s' is not an NCName\n",
1722 type);
1723 ctxt->nbErrors++;
1724 }
Daniel Veillardedc91922003-01-26 00:52:04 +00001725 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1726 if (library == NULL)
1727 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1728
1729 def->name = type;
1730 def->ns = library;
1731
1732 lib = (xmlRelaxNGTypeLibraryPtr)
1733 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1734 if (lib == NULL) {
1735 if (ctxt->error != NULL)
1736 ctxt->error(ctxt->userData,
1737 "Use of unregistered type library '%s'\n",
1738 library);
1739 ctxt->nbErrors++;
1740 def->data = NULL;
1741 } else {
1742 def->data = lib;
1743 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001744 if (ctxt->error != NULL)
1745 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00001746 "Internal error with type library '%s': no 'have'\n",
1747 library);
1748 ctxt->nbErrors++;
1749 } else {
1750 tmp = lib->have(lib->data, def->name);
1751 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001752 if (ctxt->error != NULL)
1753 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00001754 "Error type '%s' is not exported by type library '%s'\n",
1755 def->name, library);
1756 ctxt->nbErrors++;
1757 }
1758 }
1759 }
1760 }
1761 if (node->children == NULL) {
1762 if (ctxt->error != NULL)
1763 ctxt->error(ctxt->userData,
1764 "Element <value> has no content\n");
1765 ctxt->nbErrors++;
1766 } else if ((node->children->type != XML_TEXT_NODE) ||
1767 (node->children->next != NULL)) {
1768 if (ctxt->error != NULL)
1769 ctxt->error(ctxt->userData,
1770 "Expecting a single text value for <value>content\n");
1771 ctxt->nbErrors++;
1772 } else {
1773 def->value = xmlNodeGetContent(node);
1774 if (def->value == NULL) {
1775 if (ctxt->error != NULL)
1776 ctxt->error(ctxt->userData,
1777 "Element <value> has no content\n");
1778 ctxt->nbErrors++;
1779 }
1780 }
1781 /* TODO check ahead of time that the value is okay per the type */
1782 return(def);
1783}
1784
1785/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001786 * xmlRelaxNGParseData:
1787 * @ctxt: a Relax-NG parser context
1788 * @node: the data node.
1789 *
1790 * parse the content of a RelaxNG data node.
1791 *
1792 * Returns the definition pointer or NULL in case of error
1793 */
1794static xmlRelaxNGDefinePtr
1795xmlRelaxNGParseData(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1796 xmlRelaxNGDefinePtr def = NULL;
1797 xmlRelaxNGTypeLibraryPtr lib;
1798 xmlChar *type;
1799 xmlChar *library;
1800 xmlNodePtr content;
1801 int tmp;
1802
1803 type = xmlGetProp(node, BAD_CAST "type");
1804 if (type == NULL) {
1805 if (ctxt->error != NULL)
1806 ctxt->error(ctxt->userData,
1807 "data has no type\n");
1808 ctxt->nbErrors++;
1809 return(NULL);
1810 }
Daniel Veillardd2298792003-02-14 16:54:11 +00001811 xmlRelaxNGNormExtSpace(type);
1812 if (xmlValidateNCName(type, 0)) {
1813 if (ctxt->error != NULL)
1814 ctxt->error(ctxt->userData,
1815 "data type '%s' is not an NCName\n",
1816 type);
1817 ctxt->nbErrors++;
1818 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001819 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1820 if (library == NULL)
1821 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1822
1823 def = xmlRelaxNGNewDefine(ctxt, node);
1824 if (def == NULL) {
1825 xmlFree(type);
1826 return(NULL);
1827 }
1828 def->type = XML_RELAXNG_DATATYPE;
1829 def->name = type;
1830 def->ns = library;
1831
1832 lib = (xmlRelaxNGTypeLibraryPtr)
1833 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1834 if (lib == NULL) {
1835 if (ctxt->error != NULL)
1836 ctxt->error(ctxt->userData,
1837 "Use of unregistered type library '%s'\n",
1838 library);
1839 ctxt->nbErrors++;
1840 def->data = NULL;
1841 } else {
1842 def->data = lib;
1843 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001844 if (ctxt->error != NULL)
1845 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001846 "Internal error with type library '%s': no 'have'\n",
1847 library);
1848 ctxt->nbErrors++;
1849 } else {
1850 tmp = lib->have(lib->data, def->name);
1851 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001852 if (ctxt->error != NULL)
1853 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001854 "Error type '%s' is not exported by type library '%s'\n",
1855 def->name, library);
1856 ctxt->nbErrors++;
1857 }
1858 }
1859 }
1860 content = node->children;
1861 while (content != NULL) {
1862 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001863 ctxt->nbErrors++;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001864 content = content->next;
1865 }
1866
1867 return(def);
1868}
1869
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001870/**
1871 * xmlRelaxNGCompareElemDefLists:
1872 * @ctxt: a Relax-NG parser context
1873 * @defs1: the first list of element defs
1874 * @defs2: the second list of element defs
1875 *
1876 * Compare the 2 lists of element definitions. The comparison is
1877 * that if both lists do not accept the same QNames, it returns 1
1878 * If the 2 lists can accept the same QName the comparison returns 0
1879 *
1880 * Returns 1 disttinct, 0 if equal
1881 */
1882static int
1883xmlRelaxNGCompareElemDefLists(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1884 xmlRelaxNGDefinePtr *def1,
1885 xmlRelaxNGDefinePtr *def2) {
1886 xmlRelaxNGDefinePtr *basedef2 = def2;
1887
Daniel Veillard154877e2003-01-30 12:17:05 +00001888 if ((def1 == NULL) || (def2 == NULL))
1889 return(1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001890 if ((*def1 == NULL) || (*def2 == NULL))
1891 return(1);
1892 while (*def1 != NULL) {
1893 while ((*def2) != NULL) {
1894 if ((*def1)->name == NULL) {
1895 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1896 return(0);
1897 } else if ((*def2)->name == NULL) {
1898 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1899 return(0);
1900 } else if (xmlStrEqual((*def1)->name, (*def2)->name)) {
1901 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1902 return(0);
1903 }
1904 def2++;
1905 }
1906 def2 = basedef2;
1907 def1++;
1908 }
1909 return(1);
1910}
1911
1912/**
1913 * xmlRelaxNGGetElements:
1914 * @ctxt: a Relax-NG parser context
1915 * @def: the interleave definition
1916 *
1917 * Compute the list of top elements a definition can generate
1918 *
1919 * Returns a list of elements or NULL if none was found.
1920 */
1921static xmlRelaxNGDefinePtr *
1922xmlRelaxNGGetElements(xmlRelaxNGParserCtxtPtr ctxt,
1923 xmlRelaxNGDefinePtr def) {
1924 xmlRelaxNGDefinePtr *ret = NULL, parent, cur, tmp;
1925 int len = 0;
1926 int max = 0;
1927
1928 parent = NULL;
1929 cur = def;
1930 while (cur != NULL) {
Daniel Veillardb08c9812003-01-28 23:09:49 +00001931 if ((cur->type == XML_RELAXNG_ELEMENT) ||
1932 (cur->type == XML_RELAXNG_TEXT)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001933 if (ret == NULL) {
1934 max = 10;
1935 ret = (xmlRelaxNGDefinePtr *)
1936 xmlMalloc((max + 1) * sizeof(xmlRelaxNGDefinePtr));
1937 if (ret == NULL) {
1938 if (ctxt->error != NULL)
1939 ctxt->error(ctxt->userData,
1940 "Out of memory in element search\n");
1941 ctxt->nbErrors++;
1942 return(NULL);
1943 }
1944 } else if (max <= len) {
1945 max *= 2;
1946 ret = xmlRealloc(ret, (max + 1) * sizeof(xmlRelaxNGDefinePtr));
1947 if (ret == NULL) {
1948 if (ctxt->error != NULL)
1949 ctxt->error(ctxt->userData,
1950 "Out of memory in element search\n");
1951 ctxt->nbErrors++;
1952 return(NULL);
1953 }
1954 }
Daniel Veillardb08c9812003-01-28 23:09:49 +00001955 ret[len++] = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001956 ret[len] = NULL;
1957 } else if ((cur->type == XML_RELAXNG_CHOICE) ||
1958 (cur->type == XML_RELAXNG_INTERLEAVE) ||
1959 (cur->type == XML_RELAXNG_GROUP) ||
1960 (cur->type == XML_RELAXNG_ONEORMORE) ||
Daniel Veillardb08c9812003-01-28 23:09:49 +00001961 (cur->type == XML_RELAXNG_ZEROORMORE) ||
1962 (cur->type == XML_RELAXNG_OPTIONAL) ||
1963 (cur->type == XML_RELAXNG_REF) ||
1964 (cur->type == XML_RELAXNG_DEF)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001965 /*
1966 * Don't go within elements or attributes or string values.
1967 * Just gather the element top list
1968 */
1969 if (cur->content != NULL) {
1970 parent = cur;
1971 cur = cur->content;
1972 tmp = cur;
1973 while (tmp != NULL) {
1974 tmp->parent = parent;
1975 tmp = tmp->next;
1976 }
1977 continue;
1978 }
1979 }
Daniel Veillard154877e2003-01-30 12:17:05 +00001980 if (cur == def)
1981 return(ret);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001982 if (cur->next != NULL) {
1983 cur = cur->next;
1984 continue;
1985 }
1986 do {
1987 cur = cur->parent;
1988 if (cur == NULL) break;
1989 if (cur == def) return(ret);
1990 if (cur->next != NULL) {
1991 cur = cur->next;
1992 break;
1993 }
1994 } while (cur != NULL);
1995 }
1996 return(ret);
1997}
1998
1999/**
2000 * xmlRelaxNGComputeInterleaves:
2001 * @def: the interleave definition
2002 * @ctxt: a Relax-NG parser context
2003 * @node: the data node.
2004 *
2005 * A lot of work for preprocessing interleave definitions
2006 * is potentially needed to get a decent execution speed at runtime
2007 * - trying to get a total order on the element nodes generated
2008 * by the interleaves, order the list of interleave definitions
2009 * following that order.
2010 * - if <text/> is used to handle mixed content, it is better to
2011 * flag this in the define and simplify the runtime checking
2012 * algorithm
2013 */
2014static void
2015xmlRelaxNGComputeInterleaves(xmlRelaxNGDefinePtr def,
2016 xmlRelaxNGParserCtxtPtr ctxt,
2017 xmlChar *name ATTRIBUTE_UNUSED) {
2018 xmlRelaxNGDefinePtr cur;
2019
2020 xmlRelaxNGDefinePtr *list = NULL;
2021 xmlRelaxNGPartitionPtr partitions = NULL;
2022 xmlRelaxNGInterleaveGroupPtr *groups = NULL;
2023 xmlRelaxNGInterleaveGroupPtr group;
2024 int i,j,ret;
2025 int nbgroups = 0;
2026 int nbchild = 0;
2027
2028#ifdef DEBUG_INTERLEAVE
2029 xmlGenericError(xmlGenericErrorContext,
2030 "xmlRelaxNGComputeInterleaves(%s)\n",
2031 name);
2032#endif
2033 cur = def->content;
2034 while (cur != NULL) {
2035 nbchild++;
2036 cur = cur->next;
2037 }
2038
2039#ifdef DEBUG_INTERLEAVE
2040 xmlGenericError(xmlGenericErrorContext, " %d child\n", nbchild);
2041#endif
2042 groups = (xmlRelaxNGInterleaveGroupPtr *)
2043 xmlMalloc(nbchild * sizeof(xmlRelaxNGInterleaveGroupPtr));
2044 if (groups == NULL)
2045 goto error;
2046 cur = def->content;
2047 while (cur != NULL) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002048 groups[nbgroups] = (xmlRelaxNGInterleaveGroupPtr)
2049 xmlMalloc(sizeof(xmlRelaxNGInterleaveGroup));
2050 if (groups[nbgroups] == NULL)
2051 goto error;
2052 groups[nbgroups]->rule = cur;
2053 groups[nbgroups]->defs = xmlRelaxNGGetElements(ctxt, cur);
2054 nbgroups++;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002055 cur = cur->next;
2056 }
2057 list = NULL;
2058#ifdef DEBUG_INTERLEAVE
2059 xmlGenericError(xmlGenericErrorContext, " %d groups\n", nbgroups);
2060#endif
2061
2062 /*
2063 * Let's check that all rules makes a partitions according to 7.4
2064 */
2065 partitions = (xmlRelaxNGPartitionPtr)
2066 xmlMalloc(sizeof(xmlRelaxNGPartition));
2067 if (partitions == NULL)
2068 goto error;
2069 partitions->nbgroups = nbgroups;
2070 for (i = 0;i < nbgroups;i++) {
2071 group = groups[i];
2072 for (j = i+1;j < nbgroups;j++) {
2073 if (groups[j] == NULL)
2074 continue;
2075 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->defs,
2076 groups[j]->defs);
2077 if (ret == 0) {
2078 if (ctxt->error != NULL)
2079 ctxt->error(ctxt->userData,
2080 "Element or text conflicts in interleave\n");
2081 ctxt->nbErrors++;
2082 }
2083 }
2084 }
2085 partitions->groups = groups;
2086
2087 /*
2088 * Free Up the child list, and save the partition list back in the def
2089 */
2090 def->data = partitions;
2091 return;
2092
2093error:
2094 if (ctxt->error != NULL)
2095 ctxt->error(ctxt->userData,
2096 "Out of memory in interleave computation\n");
2097 ctxt->nbErrors++;
2098 if (list == NULL)
2099 xmlFree(list);
2100 if (groups != NULL) {
2101 for (i = 0;i < nbgroups;i++)
2102 if (groups[i] != NULL) {
2103 if (groups[i]->defs != NULL)
2104 xmlFree(groups[i]->defs);
2105 xmlFree(groups[i]);
2106 }
2107 xmlFree(groups);
2108 }
2109 xmlRelaxNGFreePartition(partitions);
2110}
2111
2112/**
2113 * xmlRelaxNGParseInterleave:
2114 * @ctxt: a Relax-NG parser context
2115 * @node: the data node.
2116 *
2117 * parse the content of a RelaxNG interleave node.
2118 *
2119 * Returns the definition pointer or NULL in case of error
2120 */
2121static xmlRelaxNGDefinePtr
2122xmlRelaxNGParseInterleave(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2123 xmlRelaxNGDefinePtr def = NULL;
2124 xmlRelaxNGDefinePtr last = NULL, cur;
2125 xmlNodePtr child;
2126
2127 def = xmlRelaxNGNewDefine(ctxt, node);
2128 if (def == NULL) {
2129 return(NULL);
2130 }
2131 def->type = XML_RELAXNG_INTERLEAVE;
2132
2133 if (ctxt->interleaves == NULL)
2134 ctxt->interleaves = xmlHashCreate(10);
2135 if (ctxt->interleaves == NULL) {
2136 if (ctxt->error != NULL)
2137 ctxt->error(ctxt->userData,
2138 "Failed to create interleaves hash table\n");
2139 ctxt->nbErrors++;
2140 } else {
2141 char name[32];
2142
2143 snprintf(name, 32, "interleave%d", ctxt->nbInterleaves++);
2144 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST name, def) < 0) {
2145 if (ctxt->error != NULL)
2146 ctxt->error(ctxt->userData,
2147 "Failed to add %s to hash table\n", name);
2148 ctxt->nbErrors++;
2149 }
2150 }
2151 child = node->children;
Daniel Veillardd2298792003-02-14 16:54:11 +00002152 if (child == NULL) {
2153 if (ctxt->error != NULL)
2154 ctxt->error(ctxt->userData, "Element interleave is empty\n");
2155 ctxt->nbErrors++;
2156 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002157 while (child != NULL) {
2158 if (IS_RELAXNG(child, "element")) {
2159 cur = xmlRelaxNGParseElement(ctxt, child);
2160 } else {
2161 cur = xmlRelaxNGParsePattern(ctxt, child);
2162 }
2163 if (cur != NULL) {
2164 cur->parent = def;
2165 if (last == NULL) {
2166 def->content = last = cur;
2167 } else {
2168 last->next = cur;
2169 last = cur;
2170 }
2171 }
2172 child = child->next;
2173 }
2174
2175 return(def);
2176}
Daniel Veillard6eadf632003-01-23 18:29:16 +00002177
2178/**
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002179 * xmlRelaxNGParseInclude:
2180 * @ctxt: a Relax-NG parser context
2181 * @node: the include node
2182 *
2183 * Integrate the content of an include node in the current grammar
2184 *
2185 * Returns 0 in case of success or -1 in case of error
2186 */
2187static int
2188xmlRelaxNGParseInclude(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2189 xmlRelaxNGIncludePtr incl;
2190 xmlNodePtr root;
2191 int ret = 0, tmp;
2192
2193 incl = node->_private;
2194 if (incl == NULL) {
2195 if (ctxt->error != NULL)
2196 ctxt->error(ctxt->userData,
2197 "Include node has no data\n");
2198 ctxt->nbErrors++;
2199 return(-1);
2200 }
2201 root = xmlDocGetRootElement(incl->doc);
2202 if (root == NULL) {
2203 if (ctxt->error != NULL)
2204 ctxt->error(ctxt->userData,
2205 "Include document is empty\n");
2206 ctxt->nbErrors++;
2207 return(-1);
2208 }
2209 if (!xmlStrEqual(root->name, BAD_CAST "grammar")) {
2210 if (ctxt->error != NULL)
2211 ctxt->error(ctxt->userData,
2212 "Include document root is not a grammar\n");
2213 ctxt->nbErrors++;
2214 return(-1);
2215 }
2216
2217 /*
2218 * Merge the definition from both the include and the internal list
2219 */
2220 if (root->children != NULL) {
2221 tmp = xmlRelaxNGParseGrammarContent(ctxt, root->children);
2222 if (tmp != 0)
2223 ret = -1;
2224 }
2225 if (node->children != NULL) {
2226 tmp = xmlRelaxNGParseGrammarContent(ctxt, node->children);
2227 if (tmp != 0)
2228 ret = -1;
2229 }
2230 return(ret);
2231}
2232
2233/**
Daniel Veillard276be4a2003-01-24 01:03:34 +00002234 * xmlRelaxNGParseDefine:
2235 * @ctxt: a Relax-NG parser context
2236 * @node: the define node
2237 *
2238 * parse the content of a RelaxNG define element node.
2239 *
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002240 * Returns 0 in case of success or -1 in case of error
Daniel Veillard276be4a2003-01-24 01:03:34 +00002241 */
2242static int
2243xmlRelaxNGParseDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2244 xmlChar *name;
2245 int ret = 0, tmp;
2246 xmlRelaxNGDefinePtr def;
2247 const xmlChar *olddefine;
2248
2249 name = xmlGetProp(node, BAD_CAST "name");
2250 if (name == NULL) {
2251 if (ctxt->error != NULL)
2252 ctxt->error(ctxt->userData,
2253 "define has no name\n");
2254 ctxt->nbErrors++;
2255 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00002256 xmlRelaxNGNormExtSpace(name);
2257 if (xmlValidateNCName(name, 0)) {
2258 if (ctxt->error != NULL)
2259 ctxt->error(ctxt->userData,
2260 "define name '%s' is not an NCName\n",
2261 name);
2262 ctxt->nbErrors++;
2263 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002264 def = xmlRelaxNGNewDefine(ctxt, node);
2265 if (def == NULL) {
2266 xmlFree(name);
2267 return(-1);
2268 }
2269 def->type = XML_RELAXNG_DEF;
2270 def->name = name;
2271 if (node->children == NULL) {
2272 if (ctxt->error != NULL)
2273 ctxt->error(ctxt->userData,
2274 "define has no children\n");
2275 ctxt->nbErrors++;
2276 } else {
2277 olddefine = ctxt->define;
2278 ctxt->define = name;
Daniel Veillard154877e2003-01-30 12:17:05 +00002279 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard276be4a2003-01-24 01:03:34 +00002280 ctxt->define = olddefine;
2281 }
2282 if (ctxt->grammar->defs == NULL)
2283 ctxt->grammar->defs = xmlHashCreate(10);
2284 if (ctxt->grammar->defs == NULL) {
2285 if (ctxt->error != NULL)
2286 ctxt->error(ctxt->userData,
2287 "Could not create definition hash\n");
2288 ctxt->nbErrors++;
2289 ret = -1;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002290 } else {
2291 tmp = xmlHashAddEntry(ctxt->grammar->defs, name, def);
2292 if (tmp < 0) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002293 xmlRelaxNGDefinePtr prev;
2294
2295 prev = xmlHashLookup(ctxt->grammar->defs, name);
2296 if (prev == NULL) {
2297 if (ctxt->error != NULL)
2298 ctxt->error(ctxt->userData,
2299 "Internal error on define aggregation of %s\n",
2300 name);
2301 ctxt->nbErrors++;
2302 ret = -1;
Daniel Veillard154877e2003-01-30 12:17:05 +00002303 } else {
2304 while (prev->nextHash != NULL)
2305 prev = prev->nextHash;
2306 prev->nextHash = def;
2307 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002308 }
2309 }
2310 }
2311 return(ret);
2312}
2313
2314/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00002315 * xmlRelaxNGParsePattern:
2316 * @ctxt: a Relax-NG parser context
2317 * @node: the pattern node.
2318 *
2319 * parse the content of a RelaxNG pattern node.
2320 *
Daniel Veillard276be4a2003-01-24 01:03:34 +00002321 * Returns the definition pointer or NULL in case of error or if no
2322 * pattern is generated.
Daniel Veillard6eadf632003-01-23 18:29:16 +00002323 */
2324static xmlRelaxNGDefinePtr
2325xmlRelaxNGParsePattern(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2326 xmlRelaxNGDefinePtr def = NULL;
2327
Daniel Veillardd2298792003-02-14 16:54:11 +00002328 if (node == NULL) {
2329 return(NULL);
2330 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002331 if (IS_RELAXNG(node, "element")) {
2332 def = xmlRelaxNGParseElement(ctxt, node);
2333 } else if (IS_RELAXNG(node, "attribute")) {
2334 def = xmlRelaxNGParseAttribute(ctxt, node);
2335 } else if (IS_RELAXNG(node, "empty")) {
2336 def = xmlRelaxNGNewDefine(ctxt, node);
2337 if (def == NULL)
2338 return(NULL);
2339 def->type = XML_RELAXNG_EMPTY;
Daniel Veillardd2298792003-02-14 16:54:11 +00002340 if (node->children != NULL) {
2341 if (ctxt->error != NULL)
2342 ctxt->error(ctxt->userData, "empty: had a child node\n");
2343 ctxt->nbErrors++;
2344 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002345 } else if (IS_RELAXNG(node, "text")) {
2346 def = xmlRelaxNGNewDefine(ctxt, node);
2347 if (def == NULL)
2348 return(NULL);
2349 def->type = XML_RELAXNG_TEXT;
2350 if (node->children != NULL) {
2351 if (ctxt->error != NULL)
2352 ctxt->error(ctxt->userData, "text: had a child node\n");
2353 ctxt->nbErrors++;
2354 }
2355 } else if (IS_RELAXNG(node, "zeroOrMore")) {
2356 def = xmlRelaxNGNewDefine(ctxt, node);
2357 if (def == NULL)
2358 return(NULL);
2359 def->type = XML_RELAXNG_ZEROORMORE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002360 if (node->children == NULL) {
2361 if (ctxt->error != NULL)
2362 ctxt->error(ctxt->userData,
2363 "Element %s is empty\n", node->name);
2364 ctxt->nbErrors++;
2365 } else {
2366 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2367 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002368 } else if (IS_RELAXNG(node, "oneOrMore")) {
2369 def = xmlRelaxNGNewDefine(ctxt, node);
2370 if (def == NULL)
2371 return(NULL);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002372 def->type = XML_RELAXNG_ONEORMORE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002373 if (node->children == NULL) {
2374 if (ctxt->error != NULL)
2375 ctxt->error(ctxt->userData,
2376 "Element %s is empty\n", node->name);
2377 ctxt->nbErrors++;
2378 } else {
2379 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2380 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002381 } else if (IS_RELAXNG(node, "optional")) {
2382 def = xmlRelaxNGNewDefine(ctxt, node);
2383 if (def == NULL)
2384 return(NULL);
2385 def->type = XML_RELAXNG_OPTIONAL;
Daniel Veillardd2298792003-02-14 16:54:11 +00002386 if (node->children == NULL) {
2387 if (ctxt->error != NULL)
2388 ctxt->error(ctxt->userData,
2389 "Element %s is empty\n", node->name);
2390 ctxt->nbErrors++;
2391 } else {
2392 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2393 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002394 } else if (IS_RELAXNG(node, "choice")) {
2395 def = xmlRelaxNGNewDefine(ctxt, node);
2396 if (def == NULL)
2397 return(NULL);
2398 def->type = XML_RELAXNG_CHOICE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002399 if (node->children == NULL) {
2400 if (ctxt->error != NULL)
2401 ctxt->error(ctxt->userData,
2402 "Element %s is empty\n", node->name);
2403 ctxt->nbErrors++;
2404 } else {
2405 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2406 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002407 } else if (IS_RELAXNG(node, "group")) {
2408 def = xmlRelaxNGNewDefine(ctxt, node);
2409 if (def == NULL)
2410 return(NULL);
2411 def->type = XML_RELAXNG_GROUP;
Daniel Veillardd2298792003-02-14 16:54:11 +00002412 if (node->children == NULL) {
2413 if (ctxt->error != NULL)
2414 ctxt->error(ctxt->userData,
2415 "Element %s is empty\n", node->name);
2416 ctxt->nbErrors++;
2417 } else {
2418 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2419 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002420 } else if (IS_RELAXNG(node, "ref")) {
2421 def = xmlRelaxNGNewDefine(ctxt, node);
2422 if (def == NULL)
2423 return(NULL);
2424 def->type = XML_RELAXNG_REF;
2425 def->name = xmlGetProp(node, BAD_CAST "name");
2426 if (def->name == NULL) {
2427 if (ctxt->error != NULL)
2428 ctxt->error(ctxt->userData,
2429 "ref has no name\n");
2430 ctxt->nbErrors++;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002431 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00002432 xmlRelaxNGNormExtSpace(def->name);
2433 if (xmlValidateNCName(def->name, 0)) {
2434 if (ctxt->error != NULL)
2435 ctxt->error(ctxt->userData,
2436 "ref name '%s' is not an NCName\n",
2437 def->name);
2438 ctxt->nbErrors++;
2439 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002440 if ((ctxt->define != NULL) &&
2441 (xmlStrEqual(ctxt->define, def->name))) {
2442 if (ctxt->error != NULL)
2443 ctxt->error(ctxt->userData,
2444 "Recursive reference to %s not in an element\n",
2445 def->name);
2446 ctxt->nbErrors++;
2447 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002448 }
2449 if (node->children != NULL) {
2450 if (ctxt->error != NULL)
2451 ctxt->error(ctxt->userData,
2452 "ref is not empty\n");
2453 ctxt->nbErrors++;
2454 }
2455 if (ctxt->grammar->refs == NULL)
2456 ctxt->grammar->refs = xmlHashCreate(10);
2457 if (ctxt->grammar->refs == NULL) {
2458 if (ctxt->error != NULL)
2459 ctxt->error(ctxt->userData,
2460 "Could not create references hash\n");
2461 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002462 def = NULL;
2463 } else {
2464 int tmp;
2465
2466 tmp = xmlHashAddEntry(ctxt->grammar->refs, def->name, def);
2467 if (tmp < 0) {
2468 xmlRelaxNGDefinePtr prev;
2469
2470 prev = (xmlRelaxNGDefinePtr)
2471 xmlHashLookup(ctxt->grammar->refs, def->name);
2472 if (prev == NULL) {
2473 if (ctxt->error != NULL)
2474 ctxt->error(ctxt->userData,
2475 "Internal error refs definitions '%s'\n",
2476 def->name);
2477 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002478 def = NULL;
2479 } else {
2480 def->nextHash = prev->nextHash;
2481 prev->nextHash = def;
2482 }
2483 }
2484 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002485 } else if (IS_RELAXNG(node, "data")) {
2486 def = xmlRelaxNGParseData(ctxt, node);
Daniel Veillardd2298792003-02-14 16:54:11 +00002487#if 0
Daniel Veillard276be4a2003-01-24 01:03:34 +00002488 } else if (IS_RELAXNG(node, "define")) {
2489 xmlRelaxNGParseDefine(ctxt, node);
2490 def = NULL;
Daniel Veillardd2298792003-02-14 16:54:11 +00002491#endif
Daniel Veillardedc91922003-01-26 00:52:04 +00002492 } else if (IS_RELAXNG(node, "value")) {
2493 def = xmlRelaxNGParseValue(ctxt, node);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002494 } else if (IS_RELAXNG(node, "list")) {
2495 def = xmlRelaxNGNewDefine(ctxt, node);
2496 if (def == NULL)
2497 return(NULL);
2498 def->type = XML_RELAXNG_LIST;
Daniel Veillardd2298792003-02-14 16:54:11 +00002499 if (node->children == NULL) {
2500 if (ctxt->error != NULL)
2501 ctxt->error(ctxt->userData,
2502 "Element %s is empty\n", node->name);
2503 ctxt->nbErrors++;
2504 } else {
2505 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2506 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002507 } else if (IS_RELAXNG(node, "interleave")) {
2508 def = xmlRelaxNGParseInterleave(ctxt, node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002509 } else if (IS_RELAXNG(node, "externalRef")) {
2510 xmlRelaxNGDocumentPtr docu;
2511 xmlNodePtr root;
2512
2513 docu = node->_private;
2514 if (docu != NULL) {
2515 def = xmlRelaxNGNewDefine(ctxt, node);
2516 if (def == NULL)
2517 return(NULL);
2518 def->type = XML_RELAXNG_EXTERNALREF;
2519
2520 if (docu->content == NULL) {
2521 /*
2522 * Then do the parsing for good
2523 */
2524 root = xmlDocGetRootElement(docu->doc);
2525 if (root == NULL) {
2526 if (ctxt->error != NULL)
2527 ctxt->error(ctxt->userData,
2528 "xmlRelaxNGParse: %s is empty\n",
2529 ctxt->URL);
2530 ctxt->nbErrors++;
2531 return (NULL);
2532 }
2533 docu->schema = xmlRelaxNGParseDocument(ctxt, root);
2534 if ((docu->schema != NULL) &&
2535 (docu->schema->topgrammar != NULL)) {
2536 docu->content = docu->schema->topgrammar->start;
2537 }
2538 }
2539 def->content = docu->content;
2540 } else {
2541 def = NULL;
2542 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002543 } else if (IS_RELAXNG(node, "notAllowed")) {
2544 def = xmlRelaxNGNewDefine(ctxt, node);
2545 if (def == NULL)
2546 return(NULL);
2547 def->type = XML_RELAXNG_NOT_ALLOWED;
2548 if (node->children != NULL) {
2549 if (ctxt->error != NULL)
2550 ctxt->error(ctxt->userData,
2551 "xmlRelaxNGParse: notAllowed element is not empty\n");
2552 ctxt->nbErrors++;
2553 }
Daniel Veillard419a7682003-02-03 23:22:49 +00002554 } else if (IS_RELAXNG(node, "grammar")) {
2555 xmlRelaxNGGrammarPtr grammar, old;
2556 xmlRelaxNGGrammarPtr oldparent;
2557
2558 oldparent = ctxt->parentgrammar;
2559 old = ctxt->grammar;
2560 ctxt->parentgrammar = old;
2561 grammar = xmlRelaxNGParseGrammar(ctxt, node->children);
2562 if (old != NULL) {
2563 ctxt->grammar = old;
2564 ctxt->parentgrammar = oldparent;
2565 if (grammar != NULL) {
2566 grammar->next = old->next;
2567 old->next = grammar;
2568 }
2569 }
2570 if (grammar != NULL)
2571 def = grammar->start;
2572 else
2573 def = NULL;
2574 } else if (IS_RELAXNG(node, "parentRef")) {
2575 if (ctxt->parentgrammar == NULL) {
2576 if (ctxt->error != NULL)
2577 ctxt->error(ctxt->userData,
2578 "Use of parentRef without a parent grammar\n");
2579 ctxt->nbErrors++;
2580 return(NULL);
2581 }
2582 def = xmlRelaxNGNewDefine(ctxt, node);
2583 if (def == NULL)
2584 return(NULL);
2585 def->type = XML_RELAXNG_PARENTREF;
2586 def->name = xmlGetProp(node, BAD_CAST "name");
2587 if (def->name == NULL) {
2588 if (ctxt->error != NULL)
2589 ctxt->error(ctxt->userData,
2590 "parentRef has no name\n");
2591 ctxt->nbErrors++;
Daniel Veillardd2298792003-02-14 16:54:11 +00002592 } else {
2593 xmlRelaxNGNormExtSpace(def->name);
2594 if (xmlValidateNCName(def->name, 0)) {
2595 if (ctxt->error != NULL)
2596 ctxt->error(ctxt->userData,
2597 "parentRef name '%s' is not an NCName\n",
2598 def->name);
2599 ctxt->nbErrors++;
2600 }
Daniel Veillard419a7682003-02-03 23:22:49 +00002601 }
2602 if (node->children != NULL) {
2603 if (ctxt->error != NULL)
2604 ctxt->error(ctxt->userData,
2605 "parentRef is not empty\n");
2606 ctxt->nbErrors++;
2607 }
2608 if (ctxt->parentgrammar->refs == NULL)
2609 ctxt->parentgrammar->refs = xmlHashCreate(10);
2610 if (ctxt->parentgrammar->refs == NULL) {
2611 if (ctxt->error != NULL)
2612 ctxt->error(ctxt->userData,
2613 "Could not create references hash\n");
2614 ctxt->nbErrors++;
2615 def = NULL;
Daniel Veillardd2298792003-02-14 16:54:11 +00002616 } else if (def->name != NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +00002617 int tmp;
2618
2619 tmp = xmlHashAddEntry(ctxt->parentgrammar->refs, def->name, def);
2620 if (tmp < 0) {
2621 xmlRelaxNGDefinePtr prev;
2622
2623 prev = (xmlRelaxNGDefinePtr)
2624 xmlHashLookup(ctxt->parentgrammar->refs, def->name);
2625 if (prev == NULL) {
2626 if (ctxt->error != NULL)
2627 ctxt->error(ctxt->userData,
2628 "Internal error parentRef definitions '%s'\n",
2629 def->name);
2630 ctxt->nbErrors++;
2631 def = NULL;
2632 } else {
2633 def->nextHash = prev->nextHash;
2634 prev->nextHash = def;
2635 }
2636 }
2637 }
Daniel Veillardd2298792003-02-14 16:54:11 +00002638 } else if (IS_RELAXNG(node, "mixed")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00002639 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002640 ctxt->nbErrors++;
Daniel Veillardd2298792003-02-14 16:54:11 +00002641 } else {
2642 if (ctxt->error != NULL)
2643 ctxt->error(ctxt->userData,
2644 "Unexpected node %s is not a pattern\n",
2645 node->name);
2646 ctxt->nbErrors++;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002647 def = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002648 }
2649 return(def);
2650}
2651
2652/**
2653 * xmlRelaxNGParseAttribute:
2654 * @ctxt: a Relax-NG parser context
2655 * @node: the element node
2656 *
2657 * parse the content of a RelaxNG attribute node.
2658 *
2659 * Returns the definition pointer or NULL in case of error.
2660 */
2661static xmlRelaxNGDefinePtr
2662xmlRelaxNGParseAttribute(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
Daniel Veillardd2298792003-02-14 16:54:11 +00002663 xmlRelaxNGDefinePtr ret, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002664 xmlNodePtr child;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002665 int old_flags;
2666
2667 ret = xmlRelaxNGNewDefine(ctxt, node);
2668 if (ret == NULL)
2669 return(NULL);
2670 ret->type = XML_RELAXNG_ATTRIBUTE;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002671 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002672 child = node->children;
2673 if (child == NULL) {
2674 if (ctxt->error != NULL)
2675 ctxt->error(ctxt->userData,
2676 "xmlRelaxNGParseattribute: attribute has no children\n");
2677 ctxt->nbErrors++;
2678 return(ret);
2679 }
2680 old_flags = ctxt->flags;
2681 ctxt->flags |= XML_RELAXNG_IN_ATTRIBUTE;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002682 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
2683 if (cur != NULL)
2684 child = child->next;
2685
Daniel Veillardd2298792003-02-14 16:54:11 +00002686 if (child != NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00002687 cur = xmlRelaxNGParsePattern(ctxt, child);
2688 if (cur != NULL) {
2689 switch (cur->type) {
2690 case XML_RELAXNG_EMPTY:
2691 case XML_RELAXNG_NOT_ALLOWED:
2692 case XML_RELAXNG_TEXT:
2693 case XML_RELAXNG_ELEMENT:
2694 case XML_RELAXNG_DATATYPE:
2695 case XML_RELAXNG_VALUE:
2696 case XML_RELAXNG_LIST:
2697 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00002698 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002699 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00002700 case XML_RELAXNG_DEF:
2701 case XML_RELAXNG_ONEORMORE:
2702 case XML_RELAXNG_ZEROORMORE:
2703 case XML_RELAXNG_OPTIONAL:
2704 case XML_RELAXNG_CHOICE:
2705 case XML_RELAXNG_GROUP:
2706 case XML_RELAXNG_INTERLEAVE:
Daniel Veillardd2298792003-02-14 16:54:11 +00002707 ret->content = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002708 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002709 break;
2710 case XML_RELAXNG_ATTRIBUTE:
Daniel Veillardd2298792003-02-14 16:54:11 +00002711 if (ctxt->error != NULL)
2712 ctxt->error(ctxt->userData,
2713 "attribute has an attribute child\n");
2714 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002715 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002716 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00002717 case XML_RELAXNG_EXCEPT:
Daniel Veillardd2298792003-02-14 16:54:11 +00002718 if (ctxt->error != NULL)
2719 ctxt->error(ctxt->userData,
2720 "attribute has invalid content\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002721 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002722 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002723 }
2724 }
2725 child = child->next;
2726 }
Daniel Veillardd2298792003-02-14 16:54:11 +00002727 if (child != NULL) {
2728 if (ctxt->error != NULL)
2729 ctxt->error(ctxt->userData, "attribute has multiple children\n");
2730 ctxt->nbErrors++;
2731 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002732 ctxt->flags = old_flags;
2733 return(ret);
2734}
2735
2736/**
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002737 * xmlRelaxNGParseExceptNameClass:
2738 * @ctxt: a Relax-NG parser context
2739 * @node: the except node
Daniel Veillard144fae12003-02-03 13:17:57 +00002740 * @attr: 1 if within an attribute, 0 if within an element
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002741 *
2742 * parse the content of a RelaxNG nameClass node.
2743 *
2744 * Returns the definition pointer or NULL in case of error.
2745 */
2746static xmlRelaxNGDefinePtr
Daniel Veillard144fae12003-02-03 13:17:57 +00002747xmlRelaxNGParseExceptNameClass(xmlRelaxNGParserCtxtPtr ctxt,
2748 xmlNodePtr node, int attr) {
2749 xmlRelaxNGDefinePtr ret, cur, last = NULL;
2750 xmlNodePtr child;
2751
Daniel Veillardd2298792003-02-14 16:54:11 +00002752 if (!IS_RELAXNG(node, "except")) {
2753 if (ctxt->error != NULL)
2754 ctxt->error(ctxt->userData,
2755 "Expecting an except node\n");
2756 ctxt->nbErrors++;
Daniel Veillard144fae12003-02-03 13:17:57 +00002757 return(NULL);
Daniel Veillardd2298792003-02-14 16:54:11 +00002758 }
2759 if (node->next != NULL) {
2760 if (ctxt->error != NULL)
2761 ctxt->error(ctxt->userData,
2762 "exceptNameClass allows only a single except node\n");
2763 ctxt->nbErrors++;
2764 }
Daniel Veillard144fae12003-02-03 13:17:57 +00002765 if (node->children == NULL) {
2766 if (ctxt->error != NULL)
2767 ctxt->error(ctxt->userData,
2768 "except has no content\n");
2769 ctxt->nbErrors++;
2770 return(NULL);
2771 }
2772
2773 ret = xmlRelaxNGNewDefine(ctxt, node);
2774 if (ret == NULL)
2775 return(NULL);
2776 ret->type = XML_RELAXNG_EXCEPT;
2777 child = node->children;
2778 while (child != NULL) {
2779 cur = xmlRelaxNGNewDefine(ctxt, child);
2780 if (cur == NULL)
2781 break;
2782 if (attr)
2783 cur->type = XML_RELAXNG_ATTRIBUTE;
2784 else
2785 cur->type = XML_RELAXNG_ELEMENT;
2786
Daniel Veillard419a7682003-02-03 23:22:49 +00002787 if (xmlRelaxNGParseNameClass(ctxt, child, cur) != NULL) {
Daniel Veillard144fae12003-02-03 13:17:57 +00002788 if (last == NULL) {
2789 ret->content = cur;
2790 } else {
2791 last->next = cur;
2792 }
2793 last = cur;
2794 }
2795 child = child->next;
2796 }
2797
2798 return(ret);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002799}
2800
2801/**
2802 * xmlRelaxNGParseNameClass:
2803 * @ctxt: a Relax-NG parser context
2804 * @node: the nameClass node
2805 * @def: the current definition
2806 *
2807 * parse the content of a RelaxNG nameClass node.
2808 *
2809 * Returns the definition pointer or NULL in case of error.
2810 */
2811static xmlRelaxNGDefinePtr
2812xmlRelaxNGParseNameClass(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
2813 xmlRelaxNGDefinePtr def) {
2814 xmlRelaxNGDefinePtr ret = def;
2815 xmlChar *val;
2816
2817 if (IS_RELAXNG(node, "name")) {
2818 val = xmlNodeGetContent(node);
Daniel Veillardd2298792003-02-14 16:54:11 +00002819 xmlRelaxNGNormExtSpace(val);
2820 if (xmlValidateNCName(val, 0)) {
2821 if (ctxt->error != NULL) {
2822 if (node->parent != NULL)
2823 ctxt->error(ctxt->userData,
2824 "Element %s name '%s' is not an NCName\n",
2825 node->parent->name, val);
2826 else
2827 ctxt->error(ctxt->userData,
2828 "name '%s' is not an NCName\n",
2829 val);
2830 }
2831 ctxt->nbErrors++;
2832 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002833 ret->name = val;
2834 val = xmlGetProp(node, BAD_CAST "ns");
2835 ret->ns = val;
2836 } else if (IS_RELAXNG(node, "anyName")) {
2837 ret->name = NULL;
2838 ret->ns = NULL;
2839 if (node->children != NULL) {
2840 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00002841 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
2842 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002843 }
2844 } else if (IS_RELAXNG(node, "nsName")) {
2845 ret->name = NULL;
2846 ret->ns = xmlGetProp(node, BAD_CAST "ns");
2847 if (ret->ns == NULL) {
2848 if (ctxt->error != NULL)
2849 ctxt->error(ctxt->userData,
2850 "nsName has no ns attribute\n");
2851 ctxt->nbErrors++;
2852 }
2853 if (node->children != NULL) {
2854 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00002855 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
2856 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002857 }
2858 } else if (IS_RELAXNG(node, "choice")) {
Daniel Veillardd2298792003-02-14 16:54:11 +00002859 if (node->children == NULL) {
2860 if (ctxt->error != NULL)
2861 ctxt->error(ctxt->userData,
2862 "Element choice is empty\n");
2863 ctxt->nbErrors++;
2864 } else {
2865 xmlNodePtr child;
2866 xmlRelaxNGDefinePtr last = NULL, tmp;
2867
2868 child = node->children;
2869 while (child != NULL) {
2870 tmp = xmlRelaxNGParseExceptNameClass(ctxt, child,
2871 (def->type == XML_RELAXNG_ATTRIBUTE));
2872 if (tmp != NULL) {
2873 if (last == NULL) {
2874 last = ret->nameClass = tmp;
2875 } else {
2876 last->next = tmp;
2877 last = tmp;
2878 }
2879 }
2880 child = child->next;
2881 }
2882 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002883 } else {
2884 if (ctxt->error != NULL)
2885 ctxt->error(ctxt->userData,
2886 "expecting name, anyName, nsName or choice : got %s\n",
2887 node->name);
2888 ctxt->nbErrors++;
2889 return(NULL);
2890 }
2891 return(ret);
2892}
2893
2894/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00002895 * xmlRelaxNGParseElement:
2896 * @ctxt: a Relax-NG parser context
2897 * @node: the element node
2898 *
2899 * parse the content of a RelaxNG element node.
2900 *
2901 * Returns the definition pointer or NULL in case of error.
2902 */
2903static xmlRelaxNGDefinePtr
2904xmlRelaxNGParseElement(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2905 xmlRelaxNGDefinePtr ret, cur, last;
2906 xmlNodePtr child;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002907 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002908
2909 ret = xmlRelaxNGNewDefine(ctxt, node);
2910 if (ret == NULL)
2911 return(NULL);
2912 ret->type = XML_RELAXNG_ELEMENT;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002913 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002914 child = node->children;
2915 if (child == NULL) {
2916 if (ctxt->error != NULL)
2917 ctxt->error(ctxt->userData,
2918 "xmlRelaxNGParseElement: element has no children\n");
2919 ctxt->nbErrors++;
2920 return(ret);
2921 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002922 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
2923 if (cur != NULL)
2924 child = child->next;
2925
Daniel Veillard6eadf632003-01-23 18:29:16 +00002926 if (child == NULL) {
2927 if (ctxt->error != NULL)
2928 ctxt->error(ctxt->userData,
2929 "xmlRelaxNGParseElement: element has no content\n");
2930 ctxt->nbErrors++;
2931 return(ret);
2932 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002933 olddefine = ctxt->define;
2934 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002935 last = NULL;
2936 while (child != NULL) {
2937 cur = xmlRelaxNGParsePattern(ctxt, child);
2938 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002939 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002940 switch (cur->type) {
2941 case XML_RELAXNG_EMPTY:
2942 case XML_RELAXNG_NOT_ALLOWED:
2943 case XML_RELAXNG_TEXT:
2944 case XML_RELAXNG_ELEMENT:
2945 case XML_RELAXNG_DATATYPE:
2946 case XML_RELAXNG_VALUE:
2947 case XML_RELAXNG_LIST:
2948 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00002949 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002950 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00002951 case XML_RELAXNG_DEF:
2952 case XML_RELAXNG_ZEROORMORE:
2953 case XML_RELAXNG_ONEORMORE:
2954 case XML_RELAXNG_OPTIONAL:
2955 case XML_RELAXNG_CHOICE:
2956 case XML_RELAXNG_GROUP:
2957 case XML_RELAXNG_INTERLEAVE:
2958 if (last == NULL) {
2959 ret->content = last = cur;
2960 } else {
2961 if ((last->type == XML_RELAXNG_ELEMENT) &&
2962 (ret->content == last)) {
2963 ret->content = xmlRelaxNGNewDefine(ctxt, node);
2964 if (ret->content != NULL) {
2965 ret->content->type = XML_RELAXNG_GROUP;
2966 ret->content->content = last;
2967 } else {
2968 ret->content = last;
2969 }
2970 }
2971 last->next = cur;
2972 last = cur;
2973 }
2974 break;
2975 case XML_RELAXNG_ATTRIBUTE:
2976 cur->next = ret->attrs;
2977 ret->attrs = cur;
2978 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002979 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00002980 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002981 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002982 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002983 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002984 }
2985 }
2986 child = child->next;
2987 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002988 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002989 return(ret);
2990}
2991
2992/**
2993 * xmlRelaxNGParsePatterns:
2994 * @ctxt: a Relax-NG parser context
2995 * @nodes: list of nodes
Daniel Veillard154877e2003-01-30 12:17:05 +00002996 * @group: use an implicit <group> for elements
Daniel Veillard6eadf632003-01-23 18:29:16 +00002997 *
2998 * parse the content of a RelaxNG start node.
2999 *
3000 * Returns the definition pointer or NULL in case of error.
3001 */
3002static xmlRelaxNGDefinePtr
Daniel Veillard154877e2003-01-30 12:17:05 +00003003xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes,
3004 int group) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003005 xmlRelaxNGDefinePtr def = NULL, last = NULL, cur, parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003006
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003007 parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003008 while (nodes != NULL) {
3009 if (IS_RELAXNG(nodes, "element")) {
3010 cur = xmlRelaxNGParseElement(ctxt, nodes);
3011 if (def == NULL) {
3012 def = last = cur;
3013 } else {
Daniel Veillard154877e2003-01-30 12:17:05 +00003014 if ((group == 1) && (def->type == XML_RELAXNG_ELEMENT) &&
3015 (def == last)) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003016 def = xmlRelaxNGNewDefine(ctxt, nodes);
3017 def->type = XML_RELAXNG_GROUP;
3018 def->content = last;
3019 }
3020 last->next = cur;
3021 last = cur;
3022 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003023 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003024 } else {
3025 cur = xmlRelaxNGParsePattern(ctxt, nodes);
Daniel Veillard419a7682003-02-03 23:22:49 +00003026 if (cur != NULL) {
3027 if (def == NULL) {
3028 def = last = cur;
3029 } else {
3030 last->next = cur;
3031 last = cur;
3032 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003033 }
3034 }
3035 nodes = nodes->next;
3036 }
3037 return(def);
3038}
3039
3040/**
3041 * xmlRelaxNGParseStart:
3042 * @ctxt: a Relax-NG parser context
3043 * @nodes: start children nodes
3044 *
3045 * parse the content of a RelaxNG start node.
3046 *
3047 * Returns 0 in case of success, -1 in case of error
3048 */
3049static int
3050xmlRelaxNGParseStart(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
3051 int ret = 0;
3052 xmlRelaxNGDefinePtr def = NULL;
3053
Daniel Veillardd2298792003-02-14 16:54:11 +00003054 if (nodes == NULL) {
3055 if (ctxt->error != NULL)
3056 ctxt->error(ctxt->userData,
3057 "start has no children\n");
3058 ctxt->nbErrors++;
3059 return(-1);
3060 }
3061 if (IS_RELAXNG(nodes, "empty")) {
3062 def = xmlRelaxNGNewDefine(ctxt, nodes);
3063 if (def == NULL)
3064 return(-1);
3065 def->type = XML_RELAXNG_EMPTY;
3066 if (nodes->children != NULL) {
3067 if (ctxt->error != NULL)
3068 ctxt->error(ctxt->userData, "element empty is not empty\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003069 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003070 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003071 ctxt->grammar->start = def;
3072 } else if (IS_RELAXNG(nodes, "notAllowed")) {
3073 def = xmlRelaxNGNewDefine(ctxt, nodes);
3074 if (def == NULL)
3075 return(-1);
3076 def->type = XML_RELAXNG_NOT_ALLOWED;
3077 if (nodes->children != NULL) {
3078 if (ctxt->error != NULL)
3079 ctxt->error(ctxt->userData,
3080 "element notAllowed is not empty\n");
3081 ctxt->nbErrors++;
3082 }
3083 ctxt->grammar->start = def;
3084 } else {
3085 def = xmlRelaxNGParsePatterns(ctxt, nodes, 1);
3086 ctxt->grammar->start = def;
3087 }
3088 nodes = nodes->next;
3089 if (nodes != NULL) {
3090 if (ctxt->error != NULL)
3091 ctxt->error(ctxt->userData,
3092 "start more than one children\n");
3093 ctxt->nbErrors++;
3094 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003095 }
3096 return(ret);
3097}
3098
3099/**
3100 * xmlRelaxNGParseGrammarContent:
3101 * @ctxt: a Relax-NG parser context
3102 * @nodes: grammar children nodes
3103 *
3104 * parse the content of a RelaxNG grammar node.
3105 *
3106 * Returns 0 in case of success, -1 in case of error
3107 */
3108static int
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003109xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003110{
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003111 int ret = 0, tmp;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003112
3113 if (nodes == NULL) {
3114 if (ctxt->error != NULL)
3115 ctxt->error(ctxt->userData,
3116 "grammar has no children\n");
3117 ctxt->nbErrors++;
3118 return(-1);
3119 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003120 while (nodes != NULL) {
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003121 if (IS_RELAXNG(nodes, "start")) {
3122 if (nodes->children == NULL) {
3123 if (ctxt->error != NULL)
3124 ctxt->error(ctxt->userData,
Daniel Veillardd2298792003-02-14 16:54:11 +00003125 "start has no children\n");
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003126 ctxt->nbErrors++;
3127 } else {
3128 tmp = xmlRelaxNGParseStart(ctxt, nodes->children);
3129 if (tmp != 0)
3130 ret = -1;
3131 }
3132 } else if (IS_RELAXNG(nodes, "define")) {
3133 tmp = xmlRelaxNGParseDefine(ctxt, nodes);
3134 if (tmp != 0)
3135 ret = -1;
3136 } else if (IS_RELAXNG(nodes, "include")) {
3137 tmp = xmlRelaxNGParseInclude(ctxt, nodes);
3138 if (tmp != 0)
3139 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003140 } else {
3141 if (ctxt->error != NULL)
3142 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003143 "grammar has unexpected child %s\n", nodes->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003144 ctxt->nbErrors++;
3145 ret = -1;
3146 }
3147 nodes = nodes->next;
3148 }
3149 return (ret);
3150}
3151
3152/**
3153 * xmlRelaxNGCheckReference:
3154 * @ref: the ref
3155 * @ctxt: a Relax-NG parser context
3156 * @name: the name associated to the defines
3157 *
3158 * Applies the 4.17. combine attribute rule for all the define
3159 * element of a given grammar using the same name.
3160 */
3161static void
3162xmlRelaxNGCheckReference(xmlRelaxNGDefinePtr ref,
3163 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
3164 xmlRelaxNGGrammarPtr grammar;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003165 xmlRelaxNGDefinePtr def, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003166
3167 grammar = ctxt->grammar;
3168 if (grammar == NULL) {
3169 if (ctxt->error != NULL)
3170 ctxt->error(ctxt->userData,
3171 "Internal error: no grammar in CheckReference %s\n",
3172 name);
3173 ctxt->nbErrors++;
3174 return;
3175 }
3176 if (ref->content != NULL) {
3177 if (ctxt->error != NULL)
3178 ctxt->error(ctxt->userData,
3179 "Internal error: reference has content in CheckReference %s\n",
3180 name);
3181 ctxt->nbErrors++;
3182 return;
3183 }
3184 if (grammar->defs != NULL) {
3185 def = xmlHashLookup(grammar->defs, name);
3186 if (def != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00003187 cur = ref;
3188 while (cur != NULL) {
3189 cur->content = def;
3190 cur = cur->nextHash;
3191 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003192 } else {
3193 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003194 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003195 }
3196 }
3197 /*
3198 * TODO: make a closure and verify there is no loop !
3199 */
3200}
3201
3202/**
3203 * xmlRelaxNGCheckCombine:
3204 * @define: the define(s) list
3205 * @ctxt: a Relax-NG parser context
3206 * @name: the name associated to the defines
3207 *
3208 * Applies the 4.17. combine attribute rule for all the define
3209 * element of a given grammar using the same name.
3210 */
3211static void
3212xmlRelaxNGCheckCombine(xmlRelaxNGDefinePtr define,
3213 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
3214 xmlChar *combine;
3215 int choiceOrInterleave = -1;
3216 int missing = 0;
3217 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
3218
3219 if (define->nextHash == NULL)
3220 return;
3221 cur = define;
3222 while (cur != NULL) {
3223 combine = xmlGetProp(cur->node, BAD_CAST "combine");
3224 if (combine != NULL) {
3225 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3226 if (choiceOrInterleave == -1)
3227 choiceOrInterleave = 1;
3228 else if (choiceOrInterleave == 0) {
3229 if (ctxt->error != NULL)
3230 ctxt->error(ctxt->userData,
3231 "Defines for %s use both 'choice' and 'interleave'\n",
3232 name);
3233 ctxt->nbErrors++;
3234 }
Daniel Veillard154877e2003-01-30 12:17:05 +00003235 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003236 if (choiceOrInterleave == -1)
3237 choiceOrInterleave = 0;
3238 else if (choiceOrInterleave == 1) {
3239 if (ctxt->error != NULL)
3240 ctxt->error(ctxt->userData,
3241 "Defines for %s use both 'choice' and 'interleave'\n",
3242 name);
3243 ctxt->nbErrors++;
3244 }
3245 } else {
3246 if (ctxt->error != NULL)
3247 ctxt->error(ctxt->userData,
3248 "Defines for %s use unknown combine value '%s''\n",
3249 name, combine);
3250 ctxt->nbErrors++;
3251 }
3252 xmlFree(combine);
3253 } else {
3254 if (missing == 0)
3255 missing = 1;
3256 else {
3257 if (ctxt->error != NULL)
3258 ctxt->error(ctxt->userData,
3259 "Some defines for %s lacks the combine attribute\n",
3260 name);
3261 ctxt->nbErrors++;
3262 }
3263 }
3264
3265 cur = cur->nextHash;
3266 }
3267#ifdef DEBUG
3268 xmlGenericError(xmlGenericErrorContext,
3269 "xmlRelaxNGCheckCombine(): merging %s defines: %d\n",
3270 name, choiceOrInterleave);
3271#endif
3272 if (choiceOrInterleave == -1)
3273 choiceOrInterleave = 0;
3274 cur = xmlRelaxNGNewDefine(ctxt, define->node);
3275 if (cur == NULL)
3276 return;
3277 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003278 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard154877e2003-01-30 12:17:05 +00003279 else
3280 cur->type = XML_RELAXNG_CHOICE;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003281 tmp = define;
3282 last = NULL;
3283 while (tmp != NULL) {
3284 if (tmp->content != NULL) {
3285 if (tmp->content->next != NULL) {
3286 /*
3287 * we need first to create a wrapper.
3288 */
3289 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
3290 if (tmp2 == NULL)
3291 break;
3292 tmp2->type = XML_RELAXNG_GROUP;
3293 tmp2->content = tmp->content;
3294 } else {
3295 tmp2 = tmp->content;
3296 }
3297 if (last == NULL) {
3298 cur->content = tmp2;
3299 } else {
3300 last->next = tmp2;
3301 }
3302 last = tmp2;
3303 tmp->content = NULL;
3304 }
3305 tmp = tmp->nextHash;
3306 }
3307 define->content = cur;
Daniel Veillard154877e2003-01-30 12:17:05 +00003308 if (choiceOrInterleave == 0) {
3309 if (ctxt->interleaves == NULL)
3310 ctxt->interleaves = xmlHashCreate(10);
3311 if (ctxt->interleaves == NULL) {
3312 if (ctxt->error != NULL)
3313 ctxt->error(ctxt->userData,
3314 "Failed to create interleaves hash table\n");
3315 ctxt->nbErrors++;
3316 } else {
3317 char tmpname[32];
3318
3319 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
3320 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
3321 if (ctxt->error != NULL)
3322 ctxt->error(ctxt->userData,
3323 "Failed to add %s to hash table\n", tmpname);
3324 ctxt->nbErrors++;
3325 }
3326 }
3327 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003328}
3329
3330/**
3331 * xmlRelaxNGCombineStart:
3332 * @ctxt: a Relax-NG parser context
3333 * @grammar: the grammar
3334 *
3335 * Applies the 4.17. combine rule for all the start
3336 * element of a given grammar.
3337 */
3338static void
3339xmlRelaxNGCombineStart(xmlRelaxNGParserCtxtPtr ctxt,
3340 xmlRelaxNGGrammarPtr grammar) {
3341 xmlRelaxNGDefinePtr starts;
3342 xmlChar *combine;
3343 int choiceOrInterleave = -1;
3344 int missing = 0;
3345 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
3346
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003347 starts = grammar->startList;
3348 if ((starts == NULL) || (starts->nextHash == NULL))
Daniel Veillard6eadf632003-01-23 18:29:16 +00003349 return;
3350 cur = starts;
3351 while (cur != NULL) {
3352 combine = xmlGetProp(cur->node, BAD_CAST "combine");
3353 if (combine != NULL) {
3354 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3355 if (choiceOrInterleave == -1)
3356 choiceOrInterleave = 1;
3357 else if (choiceOrInterleave == 0) {
3358 if (ctxt->error != NULL)
3359 ctxt->error(ctxt->userData,
3360 "<start> use both 'choice' and 'interleave'\n");
3361 ctxt->nbErrors++;
3362 }
3363 } else if (xmlStrEqual(combine, BAD_CAST "choice")) {
3364 if (choiceOrInterleave == -1)
3365 choiceOrInterleave = 0;
3366 else if (choiceOrInterleave == 1) {
3367 if (ctxt->error != NULL)
3368 ctxt->error(ctxt->userData,
3369 "<start> use both 'choice' and 'interleave'\n");
3370 ctxt->nbErrors++;
3371 }
3372 } else {
3373 if (ctxt->error != NULL)
3374 ctxt->error(ctxt->userData,
3375 "<start> uses unknown combine value '%s''\n", combine);
3376 ctxt->nbErrors++;
3377 }
3378 xmlFree(combine);
3379 } else {
3380 if (missing == 0)
3381 missing = 1;
3382 else {
3383 if (ctxt->error != NULL)
3384 ctxt->error(ctxt->userData,
3385 "Some <start> elements lacks the combine attribute\n");
3386 ctxt->nbErrors++;
3387 }
3388 }
3389
3390 cur = cur->nextHash;
3391 }
3392#ifdef DEBUG
3393 xmlGenericError(xmlGenericErrorContext,
3394 "xmlRelaxNGCombineStart(): merging <start>: %d\n",
3395 choiceOrInterleave);
3396#endif
3397 if (choiceOrInterleave == -1)
3398 choiceOrInterleave = 0;
3399 cur = xmlRelaxNGNewDefine(ctxt, starts->node);
3400 if (cur == NULL)
3401 return;
3402 if (choiceOrInterleave == 0)
3403 cur->type = XML_RELAXNG_CHOICE;
3404 else
3405 cur->type = XML_RELAXNG_INTERLEAVE;
3406 tmp = starts;
3407 last = NULL;
3408 while (tmp != NULL) {
3409 if (tmp->content != NULL) {
3410 if (tmp->content->next != NULL) {
3411 /*
3412 * we need first to create a wrapper.
3413 */
3414 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
3415 if (tmp2 == NULL)
3416 break;
3417 tmp2->type = XML_RELAXNG_GROUP;
3418 tmp2->content = tmp->content;
3419 } else {
3420 tmp2 = tmp->content;
3421 }
3422 if (last == NULL) {
3423 cur->content = tmp2;
3424 } else {
3425 last->next = tmp2;
3426 }
3427 last = tmp2;
3428 tmp->content = NULL;
3429 }
3430 tmp = tmp->nextHash;
3431 }
3432 starts->content = cur;
3433}
3434
3435/**
3436 * xmlRelaxNGParseGrammar:
3437 * @ctxt: a Relax-NG parser context
3438 * @nodes: grammar children nodes
3439 *
3440 * parse a Relax-NG <grammar> node
3441 *
3442 * Returns the internal xmlRelaxNGGrammarPtr built or
3443 * NULL in case of error
3444 */
3445static xmlRelaxNGGrammarPtr
3446xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
3447 xmlRelaxNGGrammarPtr ret, tmp, old;
3448
Daniel Veillard6eadf632003-01-23 18:29:16 +00003449 ret = xmlRelaxNGNewGrammar(ctxt);
3450 if (ret == NULL)
3451 return(NULL);
3452
3453 /*
3454 * Link the new grammar in the tree
3455 */
3456 ret->parent = ctxt->grammar;
3457 if (ctxt->grammar != NULL) {
3458 tmp = ctxt->grammar->children;
3459 if (tmp == NULL) {
3460 ctxt->grammar->children = ret;
3461 } else {
3462 while (tmp->next != NULL)
3463 tmp = tmp->next;
3464 tmp->next = ret;
3465 }
3466 }
3467
3468 old = ctxt->grammar;
3469 ctxt->grammar = ret;
3470 xmlRelaxNGParseGrammarContent(ctxt, nodes);
3471 ctxt->grammar = ret;
3472
3473 /*
3474 * Apply 4.17 mergingd rules to defines and starts
3475 */
3476 xmlRelaxNGCombineStart(ctxt, ret);
3477 if (ret->defs != NULL) {
3478 xmlHashScan(ret->defs, (xmlHashScanner) xmlRelaxNGCheckCombine,
3479 ctxt);
3480 }
3481
3482 /*
3483 * link together defines and refs in this grammar
3484 */
3485 if (ret->refs != NULL) {
3486 xmlHashScan(ret->refs, (xmlHashScanner) xmlRelaxNGCheckReference,
3487 ctxt);
3488 }
3489 ctxt->grammar = old;
3490 return(ret);
3491}
3492
3493/**
3494 * xmlRelaxNGParseDocument:
3495 * @ctxt: a Relax-NG parser context
3496 * @node: the root node of the RelaxNG schema
3497 *
3498 * parse a Relax-NG definition resource and build an internal
3499 * xmlRelaxNG struture which can be used to validate instances.
3500 *
3501 * Returns the internal XML RelaxNG structure built or
3502 * NULL in case of error
3503 */
3504static xmlRelaxNGPtr
3505xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3506 xmlRelaxNGPtr schema = NULL;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003507 const xmlChar *olddefine;
Daniel Veillarde431a272003-01-29 23:02:33 +00003508 xmlRelaxNGGrammarPtr old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003509
3510 if ((ctxt == NULL) || (node == NULL))
3511 return (NULL);
3512
3513 schema = xmlRelaxNGNewRelaxNG(ctxt);
3514 if (schema == NULL)
3515 return(NULL);
3516
Daniel Veillard276be4a2003-01-24 01:03:34 +00003517 olddefine = ctxt->define;
3518 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003519 if (IS_RELAXNG(node, "grammar")) {
3520 schema->topgrammar = xmlRelaxNGParseGrammar(ctxt, node->children);
3521 } else {
3522 schema->topgrammar = xmlRelaxNGNewGrammar(ctxt);
3523 if (schema->topgrammar == NULL) {
3524 return(schema);
3525 }
3526 schema->topgrammar->parent = NULL;
Daniel Veillarde431a272003-01-29 23:02:33 +00003527 old = ctxt->grammar;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003528 ctxt->grammar = schema->topgrammar;
3529 xmlRelaxNGParseStart(ctxt, node);
Daniel Veillarde431a272003-01-29 23:02:33 +00003530 if (old != NULL)
3531 ctxt->grammar = old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003532 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003533 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003534
3535#ifdef DEBUG
3536 if (schema == NULL)
3537 xmlGenericError(xmlGenericErrorContext,
3538 "xmlRelaxNGParseDocument() failed\n");
3539#endif
3540
3541 return (schema);
3542}
3543
3544/************************************************************************
3545 * *
3546 * Reading RelaxNGs *
3547 * *
3548 ************************************************************************/
3549
3550/**
3551 * xmlRelaxNGNewParserCtxt:
3552 * @URL: the location of the schema
3553 *
3554 * Create an XML RelaxNGs parse context for that file/resource expected
3555 * to contain an XML RelaxNGs file.
3556 *
3557 * Returns the parser context or NULL in case of error
3558 */
3559xmlRelaxNGParserCtxtPtr
3560xmlRelaxNGNewParserCtxt(const char *URL) {
3561 xmlRelaxNGParserCtxtPtr ret;
3562
3563 if (URL == NULL)
3564 return(NULL);
3565
3566 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
3567 if (ret == NULL) {
3568 xmlGenericError(xmlGenericErrorContext,
3569 "Failed to allocate new schama parser context for %s\n", URL);
3570 return (NULL);
3571 }
3572 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
3573 ret->URL = xmlStrdup((const xmlChar *)URL);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003574 ret->error = xmlGenericError;
3575 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003576 return (ret);
3577}
3578
3579/**
3580 * xmlRelaxNGNewMemParserCtxt:
3581 * @buffer: a pointer to a char array containing the schemas
3582 * @size: the size of the array
3583 *
3584 * Create an XML RelaxNGs parse context for that memory buffer expected
3585 * to contain an XML RelaxNGs file.
3586 *
3587 * Returns the parser context or NULL in case of error
3588 */
3589xmlRelaxNGParserCtxtPtr
3590xmlRelaxNGNewMemParserCtxt(const char *buffer, int size) {
3591 xmlRelaxNGParserCtxtPtr ret;
3592
3593 if ((buffer == NULL) || (size <= 0))
3594 return(NULL);
3595
3596 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
3597 if (ret == NULL) {
3598 xmlGenericError(xmlGenericErrorContext,
3599 "Failed to allocate new schama parser context\n");
3600 return (NULL);
3601 }
3602 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
3603 ret->buffer = buffer;
3604 ret->size = size;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003605 ret->error = xmlGenericError;
3606 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003607 return (ret);
3608}
3609
3610/**
3611 * xmlRelaxNGFreeParserCtxt:
3612 * @ctxt: the schema parser context
3613 *
3614 * Free the resources associated to the schema parser context
3615 */
3616void
3617xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxtPtr ctxt) {
3618 if (ctxt == NULL)
3619 return;
3620 if (ctxt->URL != NULL)
3621 xmlFree(ctxt->URL);
3622 if (ctxt->doc != NULL)
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003623 xmlFreeDoc(ctxt->document);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003624 if (ctxt->interleaves != NULL)
3625 xmlHashFree(ctxt->interleaves, NULL);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003626 if (ctxt->documents != NULL)
3627 xmlHashFree(ctxt->documents, (xmlHashDeallocator)
3628 xmlRelaxNGFreeDocument);
3629 if (ctxt->docTab != NULL)
3630 xmlFree(ctxt->docTab);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003631 if (ctxt->incTab != NULL)
3632 xmlFree(ctxt->incTab);
Daniel Veillard419a7682003-02-03 23:22:49 +00003633 if (ctxt->defTab != NULL) {
3634 int i;
3635
3636 for (i = 0;i < ctxt->defNr;i++)
3637 xmlRelaxNGFreeDefine(ctxt->defTab[i]);
3638 xmlFree(ctxt->defTab);
3639 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003640 xmlFree(ctxt);
3641}
3642
Daniel Veillard6eadf632003-01-23 18:29:16 +00003643/**
Daniel Veillardd2298792003-02-14 16:54:11 +00003644 * xmlRelaxNGNormExtSpace:
3645 * @value: a value
3646 *
3647 * Removes the leading and ending spaces of the value
3648 * The string is modified "in situ"
3649 */
3650static void
3651xmlRelaxNGNormExtSpace(xmlChar *value) {
3652 xmlChar *start = value;
3653 xmlChar *cur = value;
3654 if (value == NULL)
3655 return;
3656
3657 while (IS_BLANK(*cur)) cur++;
3658 if (cur == start) {
3659 do {
3660 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
3661 if (*cur == 0)
3662 return;
3663 start = cur;
3664 while (IS_BLANK(*cur)) cur++;
3665 if (*cur == 0) {
3666 *start = 0;
3667 return;
3668 }
3669 } while (1);
3670 } else {
3671 do {
3672 while ((*cur != 0) && (!IS_BLANK(*cur)))
3673 *start++ = *cur++;
3674 if (*cur == 0) {
3675 *start = 0;
3676 return;
3677 }
3678 /* don't try to normalize the inner spaces */
3679 while (IS_BLANK(*cur)) cur++;
3680 *start++ = *cur++;
3681 if (*cur == 0) {
3682 *start = 0;
3683 return;
3684 }
3685 } while (1);
3686 }
3687}
3688
3689/**
3690 * xmlRelaxNGCheckAttributes:
3691 * @ctxt: a Relax-NG parser context
3692 * @node: a Relax-NG node
3693 *
3694 * Check all the attributes on the given node
3695 */
3696static void
3697xmlRelaxNGCleanupAttributes(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3698 xmlAttrPtr cur, next;
3699
3700 cur = node->properties;
3701 while (cur != NULL) {
3702 next = cur->next;
3703 if ((cur->ns == NULL) ||
3704 (xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
3705 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
3706 if ((!xmlStrEqual(node->name, BAD_CAST "element")) &&
3707 (!xmlStrEqual(node->name, BAD_CAST "attribute")) &&
3708 (!xmlStrEqual(node->name, BAD_CAST "ref")) &&
3709 (!xmlStrEqual(node->name, BAD_CAST "parentRef")) &&
3710 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
3711 if (ctxt->error != NULL)
3712 ctxt->error(ctxt->userData,
3713 "Attribute %s is not allowed on %s\n",
3714 cur->name, node->name);
3715 ctxt->nbErrors++;
3716 }
3717 } else if (xmlStrEqual(cur->name, BAD_CAST "type")) {
3718 if ((!xmlStrEqual(node->name, BAD_CAST "value")) &&
3719 (!xmlStrEqual(node->name, BAD_CAST "data"))) {
3720 if (ctxt->error != NULL)
3721 ctxt->error(ctxt->userData,
3722 "Attribute %s is not allowed on %s\n",
3723 cur->name, node->name);
3724 ctxt->nbErrors++;
3725 }
3726 } else if (xmlStrEqual(cur->name, BAD_CAST "href")) {
3727 if ((!xmlStrEqual(node->name, BAD_CAST "externalRef")) &&
3728 (!xmlStrEqual(node->name, BAD_CAST "include"))) {
3729 if (ctxt->error != NULL)
3730 ctxt->error(ctxt->userData,
3731 "Attribute %s is not allowed on %s\n",
3732 cur->name, node->name);
3733 ctxt->nbErrors++;
3734 }
3735 } else if (xmlStrEqual(cur->name, BAD_CAST "combine")) {
3736 if ((!xmlStrEqual(node->name, BAD_CAST "start")) &&
3737 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
3738 if (ctxt->error != NULL)
3739 ctxt->error(ctxt->userData,
3740 "Attribute %s is not allowed on %s\n",
3741 cur->name, node->name);
3742 ctxt->nbErrors++;
3743 }
3744 } else if (xmlStrEqual(cur->name, BAD_CAST "datatypeLibrary")) {
3745 xmlChar *val;
3746 xmlURIPtr uri;
3747
3748 val = xmlNodeListGetString(node->doc, cur->children, 1);
3749 if (val != NULL) {
3750 if (val[0] != 0) {
3751 uri = xmlParseURI((const char *) val);
3752 if (uri == NULL) {
3753 if (ctxt->error != NULL)
3754 ctxt->error(ctxt->userData,
3755 "Attribute %s contains invalid URI %s\n",
3756 cur->name, val);
3757 ctxt->nbErrors++;
3758 } else {
3759 if (uri->scheme == NULL) {
3760 if (ctxt->error != NULL)
3761 ctxt->error(ctxt->userData,
3762 "Attribute %s URI %s is not absolute\n",
3763 cur->name, val);
3764 ctxt->nbErrors++;
3765 }
3766 if (uri->fragment != NULL) {
3767 if (ctxt->error != NULL)
3768 ctxt->error(ctxt->userData,
3769 "Attribute %s URI %s has a fragment ID\n",
3770 cur->name, val);
3771 ctxt->nbErrors++;
3772 }
3773 xmlFreeURI(uri);
3774 }
3775 }
3776 xmlFree(val);
3777 }
3778 } else if (!xmlStrEqual(cur->name, BAD_CAST "ns")) {
3779 if (ctxt->error != NULL)
3780 ctxt->error(ctxt->userData,
3781 "Unknown attribute %s on %s\n",
3782 cur->name, node->name);
3783 ctxt->nbErrors++;
3784 }
3785 }
3786 cur = next;
3787 }
3788}
3789
3790/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003791 * xmlRelaxNGCleanupDoc:
3792 * @ctxt: a Relax-NG parser context
3793 * @doc: an xmldocPtr document pointer
Daniel Veillard6eadf632003-01-23 18:29:16 +00003794 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003795 * Cleanup the document from unwanted nodes for parsing, resolve
3796 * Include and externalRef lookups.
Daniel Veillard6eadf632003-01-23 18:29:16 +00003797 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003798 * Returns the cleaned up document or NULL in case of error
Daniel Veillard6eadf632003-01-23 18:29:16 +00003799 */
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003800static xmlDocPtr
3801xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt, xmlDocPtr doc) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003802 xmlNodePtr root, cur, delete;
3803
Daniel Veillard6eadf632003-01-23 18:29:16 +00003804 /*
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003805 * Extract the root
Daniel Veillard6eadf632003-01-23 18:29:16 +00003806 */
3807 root = xmlDocGetRootElement(doc);
3808 if (root == NULL) {
3809 if (ctxt->error != NULL)
3810 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
3811 ctxt->URL);
3812 ctxt->nbErrors++;
3813 return (NULL);
3814 }
3815
3816 /*
3817 * Remove all the blank text nodes
3818 */
3819 delete = NULL;
3820 cur = root;
3821 while (cur != NULL) {
3822 if (delete != NULL) {
3823 xmlUnlinkNode(delete);
3824 xmlFreeNode(delete);
3825 delete = NULL;
3826 }
3827 if (cur->type == XML_ELEMENT_NODE) {
3828 /*
3829 * Simplification 4.1. Annotations
3830 */
3831 if ((cur->ns == NULL) ||
3832 (!xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
Daniel Veillardd2298792003-02-14 16:54:11 +00003833 if ((cur->parent != NULL) &&
3834 (cur->parent->type == XML_ELEMENT_NODE) &&
3835 ((xmlStrEqual(cur->parent->name, BAD_CAST "name")) ||
3836 (xmlStrEqual(cur->parent->name, BAD_CAST "value")) ||
3837 (xmlStrEqual(cur->parent->name, BAD_CAST "param")))) {
3838 if (ctxt->error != NULL)
3839 ctxt->error(ctxt->userData,
3840 "element %s doesn't allow foreign elements\n",
3841 cur->parent->name);
3842 ctxt->nbErrors++;
3843 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003844 delete = cur;
3845 goto skip_children;
3846 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00003847 xmlRelaxNGCleanupAttributes(ctxt, cur);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003848 if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003849 xmlChar *href, *ns, *base, *URL;
3850 xmlRelaxNGDocumentPtr docu;
3851
3852 ns = xmlGetProp(cur, BAD_CAST "ns");
3853 href = xmlGetProp(cur, BAD_CAST "href");
3854 if (href == NULL) {
3855 if (ctxt->error != NULL)
3856 ctxt->error(ctxt->userData,
3857 "xmlRelaxNGParse: externalRef has no href attribute\n");
3858 ctxt->nbErrors++;
3859 delete = cur;
3860 goto skip_children;
3861 }
3862 base = xmlNodeGetBase(cur->doc, cur);
3863 URL = xmlBuildURI(href, base);
3864 if (URL == NULL) {
3865 if (ctxt->error != NULL)
3866 ctxt->error(ctxt->userData,
3867 "Failed to compute URL for externalRef %s\n", href);
3868 ctxt->nbErrors++;
3869 if (href != NULL)
3870 xmlFree(href);
3871 if (base != NULL)
3872 xmlFree(base);
3873 delete = cur;
3874 goto skip_children;
3875 }
3876 if (href != NULL)
3877 xmlFree(href);
3878 if (base != NULL)
3879 xmlFree(base);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003880 docu = xmlRelaxNGLoadExternalRef(ctxt, URL, ns);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003881 if (docu == NULL) {
3882 if (ctxt->error != NULL)
3883 ctxt->error(ctxt->userData,
3884 "Failed to load externalRef %s\n", URL);
3885 ctxt->nbErrors++;
3886 xmlFree(URL);
3887 delete = cur;
3888 goto skip_children;
3889 }
3890 xmlFree(URL);
3891 cur->_private = docu;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003892 } else if (xmlStrEqual(cur->name, BAD_CAST "include")) {
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003893 xmlChar *href, *base, *URL;
3894 xmlRelaxNGIncludePtr incl;
3895
3896 href = xmlGetProp(cur, BAD_CAST "href");
3897 if (href == NULL) {
3898 if (ctxt->error != NULL)
3899 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003900 "xmlRelaxNGParse: include has no href attribute\n");
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003901 ctxt->nbErrors++;
3902 delete = cur;
3903 goto skip_children;
3904 }
3905 base = xmlNodeGetBase(cur->doc, cur);
3906 URL = xmlBuildURI(href, base);
3907 if (URL == NULL) {
3908 if (ctxt->error != NULL)
3909 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003910 "Failed to compute URL for include %s\n", href);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003911 ctxt->nbErrors++;
3912 if (href != NULL)
3913 xmlFree(href);
3914 if (base != NULL)
3915 xmlFree(base);
3916 delete = cur;
3917 goto skip_children;
3918 }
3919 if (href != NULL)
3920 xmlFree(href);
3921 if (base != NULL)
3922 xmlFree(base);
3923 incl = xmlRelaxNGLoadInclude(ctxt, URL, cur);
3924 if (incl == NULL) {
3925 if (ctxt->error != NULL)
3926 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003927 "Failed to load include %s\n", URL);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003928 ctxt->nbErrors++;
3929 xmlFree(URL);
3930 delete = cur;
3931 goto skip_children;
3932 }
3933 xmlFree(URL);
3934 cur->_private = incl;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003935 } else if ((xmlStrEqual(cur->name, BAD_CAST "element")) ||
3936 (xmlStrEqual(cur->name, BAD_CAST "attribute"))) {
3937 xmlChar *name;
3938 xmlNodePtr text = NULL;
3939
3940 /*
3941 * Simplification 4.8. name attribute of element
3942 * and attribute elements
3943 */
3944 name = xmlGetProp(cur, BAD_CAST "name");
3945 if (name != NULL) {
3946 if (cur->children == NULL) {
3947 text = xmlNewChild(cur, cur->ns, BAD_CAST "name",
3948 name);
3949 } else {
3950 xmlNodePtr node;
3951 node = xmlNewNode(cur->ns, BAD_CAST "name");
3952 if (node != NULL) {
3953 xmlAddPrevSibling(cur->children, node);
3954 text = xmlNewText(name);
3955 xmlAddChild(node, text);
3956 text = node;
3957 }
3958 }
3959 xmlUnsetProp(cur, BAD_CAST "name");
3960 xmlFree(name);
3961 }
3962 if (xmlStrEqual(cur->name, BAD_CAST "attribute")) {
3963 if (text == NULL) {
3964 text = cur->children;
3965 while (text != NULL) {
3966 if ((text->type == XML_ELEMENT_NODE) &&
3967 (xmlStrEqual(text->name, BAD_CAST "name")))
3968 break;
3969 text = text->next;
3970 }
3971 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003972 if (text != NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003973 xmlSetProp(text, BAD_CAST "ns", BAD_CAST "");
3974 }
3975 }
3976 } else if ((xmlStrEqual(cur->name, BAD_CAST "name")) ||
3977 (xmlStrEqual(cur->name, BAD_CAST "nsName")) ||
3978 (xmlStrEqual(cur->name, BAD_CAST "value"))) {
3979 /*
3980 * Simplification 4.8. name attribute of element
3981 * and attribute elements
3982 */
3983 if (xmlHasProp(cur, BAD_CAST "ns") == NULL) {
3984 xmlNodePtr node;
3985 xmlChar *ns = NULL;
3986
3987 node = cur->parent;
3988 while ((node != NULL) &&
3989 (node->type == XML_ELEMENT_NODE)) {
3990 ns = xmlGetProp(node, BAD_CAST "ns");
3991 if (ns != NULL) {
3992 break;
3993 }
3994 node = node->parent;
3995 }
3996 if (ns == NULL) {
3997 xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
3998 } else {
3999 xmlSetProp(cur, BAD_CAST "ns", ns);
4000 xmlFree(ns);
4001 }
4002 }
4003 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
4004 xmlChar *name, *local, *prefix;
4005
4006 /*
4007 * Simplification: 4.10. QNames
4008 */
4009 name = xmlNodeGetContent(cur);
4010 if (name != NULL) {
4011 local = xmlSplitQName2(name, &prefix);
4012 if (local != NULL) {
4013 xmlNsPtr ns;
4014
4015 ns = xmlSearchNs(cur->doc, cur, prefix);
4016 if (ns == NULL) {
4017 if (ctxt->error != NULL)
4018 ctxt->error(ctxt->userData,
4019 "xmlRelaxNGParse: no namespace for prefix %s\n", prefix);
4020 ctxt->nbErrors++;
4021 } else {
4022 xmlSetProp(cur, BAD_CAST "ns", ns->href);
4023 xmlNodeSetContent(cur, local);
4024 }
4025 xmlFree(local);
4026 xmlFree(prefix);
4027 }
4028 xmlFree(name);
4029 }
4030 }
4031 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004032 /*
4033 * Thisd is not an else since "include" is transformed
4034 * into a div
4035 */
4036 if (xmlStrEqual(cur->name, BAD_CAST "div")) {
4037 /*
4038 * implements rule 4.11
4039 */
4040 xmlNodePtr child, ins, tmp;
4041
4042 child = cur->children;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004043 ins = cur;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004044 while (child != NULL) {
4045 tmp = child->next;
4046 xmlUnlinkNode(child);
4047 ins = xmlAddNextSibling(ins, child);
4048 child = tmp;
4049 }
4050 delete = cur;
4051 goto skip_children;
4052 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004053 }
4054 }
4055 /*
4056 * Simplification 4.2 whitespaces
4057 */
4058 else if (cur->type == XML_TEXT_NODE) {
4059 if (IS_BLANK_NODE(cur)) {
4060 if (cur->parent->type == XML_ELEMENT_NODE) {
4061 if ((!xmlStrEqual(cur->parent->name, BAD_CAST "value")) &&
4062 (!xmlStrEqual(cur->parent->name, BAD_CAST "param")))
4063 delete = cur;
4064 } else {
4065 delete = cur;
4066 goto skip_children;
4067 }
4068 }
4069 } else if (cur->type != XML_CDATA_SECTION_NODE) {
4070 delete = cur;
4071 goto skip_children;
4072 }
4073
4074 /*
4075 * Skip to next node
4076 */
4077 if (cur->children != NULL) {
4078 if ((cur->children->type != XML_ENTITY_DECL) &&
4079 (cur->children->type != XML_ENTITY_REF_NODE) &&
4080 (cur->children->type != XML_ENTITY_NODE)) {
4081 cur = cur->children;
4082 continue;
4083 }
4084 }
4085skip_children:
4086 if (cur->next != NULL) {
4087 cur = cur->next;
4088 continue;
4089 }
4090
4091 do {
4092 cur = cur->parent;
4093 if (cur == NULL)
4094 break;
4095 if (cur == root) {
4096 cur = NULL;
4097 break;
4098 }
4099 if (cur->next != NULL) {
4100 cur = cur->next;
4101 break;
4102 }
4103 } while (cur != NULL);
4104 }
4105 if (delete != NULL) {
4106 xmlUnlinkNode(delete);
4107 xmlFreeNode(delete);
4108 delete = NULL;
4109 }
4110
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004111 return(doc);
4112}
4113
4114/**
4115 * xmlRelaxNGParse:
4116 * @ctxt: a Relax-NG parser context
4117 *
4118 * parse a schema definition resource and build an internal
4119 * XML Shema struture which can be used to validate instances.
4120 * *WARNING* this interface is highly subject to change
4121 *
4122 * Returns the internal XML RelaxNG structure built from the resource or
4123 * NULL in case of error
4124 */
4125xmlRelaxNGPtr
4126xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt)
4127{
4128 xmlRelaxNGPtr ret = NULL;
4129 xmlDocPtr doc;
4130 xmlNodePtr root;
4131
4132 xmlRelaxNGInitTypes();
4133
4134 if (ctxt == NULL)
4135 return (NULL);
4136
4137 /*
4138 * First step is to parse the input document into an DOM/Infoset
4139 */
4140 if (ctxt->URL != NULL) {
4141 doc = xmlParseFile((const char *) ctxt->URL);
4142 if (doc == NULL) {
4143 if (ctxt->error != NULL)
4144 ctxt->error(ctxt->userData,
4145 "xmlRelaxNGParse: could not load %s\n", ctxt->URL);
4146 ctxt->nbErrors++;
4147 return (NULL);
4148 }
4149 } else if (ctxt->buffer != NULL) {
4150 doc = xmlParseMemory(ctxt->buffer, ctxt->size);
4151 if (doc == NULL) {
4152 if (ctxt->error != NULL)
4153 ctxt->error(ctxt->userData,
4154 "xmlRelaxNGParse: could not parse schemas\n");
4155 ctxt->nbErrors++;
4156 return (NULL);
4157 }
4158 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
4159 ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
4160 } else {
4161 if (ctxt->error != NULL)
4162 ctxt->error(ctxt->userData,
4163 "xmlRelaxNGParse: nothing to parse\n");
4164 ctxt->nbErrors++;
4165 return (NULL);
4166 }
4167 ctxt->document = doc;
4168
4169 /*
4170 * Some preprocessing of the document content
4171 */
4172 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
4173 if (doc == NULL) {
4174 xmlFreeDoc(ctxt->document);
4175 ctxt->document = NULL;
4176 return(NULL);
4177 }
4178
Daniel Veillard6eadf632003-01-23 18:29:16 +00004179 /*
4180 * Then do the parsing for good
4181 */
4182 root = xmlDocGetRootElement(doc);
4183 if (root == NULL) {
4184 if (ctxt->error != NULL)
4185 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
4186 ctxt->URL);
4187 ctxt->nbErrors++;
4188 return (NULL);
4189 }
4190 ret = xmlRelaxNGParseDocument(ctxt, root);
4191 if (ret == NULL)
4192 return(NULL);
4193
4194 /*
4195 * Check the ref/defines links
4196 */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004197 /*
4198 * try to preprocess interleaves
4199 */
4200 if (ctxt->interleaves != NULL) {
4201 xmlHashScan(ctxt->interleaves,
4202 (xmlHashScanner)xmlRelaxNGComputeInterleaves, ctxt);
4203 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004204
4205 /*
4206 * if there was a parsing error return NULL
4207 */
4208 if (ctxt->nbErrors > 0) {
4209 xmlRelaxNGFree(ret);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004210 ctxt->document = NULL;
4211 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004212 return(NULL);
4213 }
4214
4215 /*
4216 * Transfer the pointer for cleanup at the schema level.
4217 */
4218 ret->doc = doc;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004219 ctxt->document = NULL;
4220 ret->documents = ctxt->documents;
4221 ctxt->documents = NULL;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004222 ret->includes = ctxt->includes;
4223 ctxt->includes = NULL;
Daniel Veillard419a7682003-02-03 23:22:49 +00004224 ret->defNr = ctxt->defNr;
4225 ret->defTab = ctxt->defTab;
4226 ctxt->defTab = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004227
4228 return (ret);
4229}
4230
4231/**
4232 * xmlRelaxNGSetParserErrors:
4233 * @ctxt: a Relax-NG validation context
4234 * @err: the error callback
4235 * @warn: the warning callback
4236 * @ctx: contextual data for the callbacks
4237 *
4238 * Set the callback functions used to handle errors for a validation context
4239 */
4240void
4241xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
4242 xmlRelaxNGValidityErrorFunc err,
4243 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
4244 if (ctxt == NULL)
4245 return;
4246 ctxt->error = err;
4247 ctxt->warning = warn;
4248 ctxt->userData = ctx;
4249}
4250/************************************************************************
4251 * *
4252 * Dump back a compiled form *
4253 * *
4254 ************************************************************************/
4255static void xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define);
4256
4257/**
4258 * xmlRelaxNGDumpDefines:
4259 * @output: the file output
4260 * @defines: a list of define structures
4261 *
4262 * Dump a RelaxNG structure back
4263 */
4264static void
4265xmlRelaxNGDumpDefines(FILE * output, xmlRelaxNGDefinePtr defines) {
4266 while (defines != NULL) {
4267 xmlRelaxNGDumpDefine(output, defines);
4268 defines = defines->next;
4269 }
4270}
4271
4272/**
4273 * xmlRelaxNGDumpDefine:
4274 * @output: the file output
4275 * @define: a define structure
4276 *
4277 * Dump a RelaxNG structure back
4278 */
4279static void
4280xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define) {
4281 if (define == NULL)
4282 return;
4283 switch(define->type) {
4284 case XML_RELAXNG_EMPTY:
4285 fprintf(output, "<empty/>\n");
4286 break;
4287 case XML_RELAXNG_NOT_ALLOWED:
4288 fprintf(output, "<notAllowed/>\n");
4289 break;
4290 case XML_RELAXNG_TEXT:
4291 fprintf(output, "<text/>\n");
4292 break;
4293 case XML_RELAXNG_ELEMENT:
4294 fprintf(output, "<element>\n");
4295 if (define->name != NULL) {
4296 fprintf(output, "<name");
4297 if (define->ns != NULL)
4298 fprintf(output, " ns=\"%s\"", define->ns);
4299 fprintf(output, ">%s</name>\n", define->name);
4300 }
4301 xmlRelaxNGDumpDefines(output, define->attrs);
4302 xmlRelaxNGDumpDefines(output, define->content);
4303 fprintf(output, "</element>\n");
4304 break;
4305 case XML_RELAXNG_LIST:
4306 fprintf(output, "<list>\n");
4307 xmlRelaxNGDumpDefines(output, define->content);
4308 fprintf(output, "</list>\n");
4309 break;
4310 case XML_RELAXNG_ONEORMORE:
4311 fprintf(output, "<oneOrMore>\n");
4312 xmlRelaxNGDumpDefines(output, define->content);
4313 fprintf(output, "</oneOrMore>\n");
4314 break;
4315 case XML_RELAXNG_ZEROORMORE:
4316 fprintf(output, "<zeroOrMore>\n");
4317 xmlRelaxNGDumpDefines(output, define->content);
4318 fprintf(output, "</zeroOrMore>\n");
4319 break;
4320 case XML_RELAXNG_CHOICE:
4321 fprintf(output, "<choice>\n");
4322 xmlRelaxNGDumpDefines(output, define->content);
4323 fprintf(output, "</choice>\n");
4324 break;
4325 case XML_RELAXNG_GROUP:
4326 fprintf(output, "<group>\n");
4327 xmlRelaxNGDumpDefines(output, define->content);
4328 fprintf(output, "</group>\n");
4329 break;
4330 case XML_RELAXNG_INTERLEAVE:
4331 fprintf(output, "<interleave>\n");
4332 xmlRelaxNGDumpDefines(output, define->content);
4333 fprintf(output, "</interleave>\n");
4334 break;
4335 case XML_RELAXNG_OPTIONAL:
4336 fprintf(output, "<optional>\n");
4337 xmlRelaxNGDumpDefines(output, define->content);
4338 fprintf(output, "</optional>\n");
4339 break;
4340 case XML_RELAXNG_ATTRIBUTE:
4341 fprintf(output, "<attribute>\n");
4342 xmlRelaxNGDumpDefines(output, define->content);
4343 fprintf(output, "</attribute>\n");
4344 break;
4345 case XML_RELAXNG_DEF:
4346 fprintf(output, "<define");
4347 if (define->name != NULL)
4348 fprintf(output, " name=\"%s\"", define->name);
4349 fprintf(output, ">\n");
4350 xmlRelaxNGDumpDefines(output, define->content);
4351 fprintf(output, "</define>\n");
4352 break;
4353 case XML_RELAXNG_REF:
4354 fprintf(output, "<ref");
4355 if (define->name != NULL)
4356 fprintf(output, " name=\"%s\"", define->name);
4357 fprintf(output, ">\n");
4358 xmlRelaxNGDumpDefines(output, define->content);
4359 fprintf(output, "</ref>\n");
4360 break;
Daniel Veillard419a7682003-02-03 23:22:49 +00004361 case XML_RELAXNG_PARENTREF:
4362 fprintf(output, "<parentRef");
4363 if (define->name != NULL)
4364 fprintf(output, " name=\"%s\"", define->name);
4365 fprintf(output, ">\n");
4366 xmlRelaxNGDumpDefines(output, define->content);
4367 fprintf(output, "</parentRef>\n");
4368 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004369 case XML_RELAXNG_EXTERNALREF:
Daniel Veillarde431a272003-01-29 23:02:33 +00004370 fprintf(output, "<externalRef");
4371 xmlRelaxNGDumpDefines(output, define->content);
4372 fprintf(output, "</externalRef>\n");
4373 break;
4374 case XML_RELAXNG_DATATYPE:
Daniel Veillard6eadf632003-01-23 18:29:16 +00004375 case XML_RELAXNG_VALUE:
4376 TODO
4377 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004378 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00004379 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004380 TODO
4381 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004382 }
4383}
4384
4385/**
4386 * xmlRelaxNGDumpGrammar:
4387 * @output: the file output
4388 * @grammar: a grammar structure
4389 * @top: is this a top grammar
4390 *
4391 * Dump a RelaxNG structure back
4392 */
4393static void
4394xmlRelaxNGDumpGrammar(FILE * output, xmlRelaxNGGrammarPtr grammar, int top)
4395{
4396 if (grammar == NULL)
4397 return;
4398
4399 fprintf(output, "<grammar");
4400 if (top)
4401 fprintf(output,
4402 " xmlns=\"http://relaxng.org/ns/structure/1.0\"");
4403 switch(grammar->combine) {
4404 case XML_RELAXNG_COMBINE_UNDEFINED:
4405 break;
4406 case XML_RELAXNG_COMBINE_CHOICE:
4407 fprintf(output, " combine=\"choice\"");
4408 break;
4409 case XML_RELAXNG_COMBINE_INTERLEAVE:
4410 fprintf(output, " combine=\"interleave\"");
4411 break;
4412 default:
4413 fprintf(output, " <!-- invalid combine value -->");
4414 }
4415 fprintf(output, ">\n");
4416 if (grammar->start == NULL) {
4417 fprintf(output, " <!-- grammar had no start -->");
4418 } else {
4419 fprintf(output, "<start>\n");
4420 xmlRelaxNGDumpDefine(output, grammar->start);
4421 fprintf(output, "</start>\n");
4422 }
4423 /* TODO ? Dump the defines ? */
4424 fprintf(output, "</grammar>\n");
4425}
4426
4427/**
4428 * xmlRelaxNGDump:
4429 * @output: the file output
4430 * @schema: a schema structure
4431 *
4432 * Dump a RelaxNG structure back
4433 */
4434void
4435xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema)
4436{
4437 if (schema == NULL) {
4438 fprintf(output, "RelaxNG empty or failed to compile\n");
4439 return;
4440 }
4441 fprintf(output, "RelaxNG: ");
4442 if (schema->doc == NULL) {
4443 fprintf(output, "no document\n");
4444 } else if (schema->doc->URL != NULL) {
4445 fprintf(output, "%s\n", schema->doc->URL);
4446 } else {
4447 fprintf(output, "\n");
4448 }
4449 if (schema->topgrammar == NULL) {
4450 fprintf(output, "RelaxNG has no top grammar\n");
4451 return;
4452 }
4453 xmlRelaxNGDumpGrammar(output, schema->topgrammar, 1);
4454}
4455
4456/************************************************************************
4457 * *
4458 * Validation implementation *
4459 * *
4460 ************************************************************************/
4461static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
4462 xmlRelaxNGDefinePtr define);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004463static int xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
4464 xmlRelaxNGDefinePtr define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004465
4466/**
4467 * xmlRelaxNGSkipIgnored:
4468 * @ctxt: a schema validation context
4469 * @node: the top node.
4470 *
4471 * Skip ignorable nodes in that context
4472 *
4473 * Returns the new sibling or NULL in case of error.
4474 */
4475static xmlNodePtr
4476xmlRelaxNGSkipIgnored(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
4477 xmlNodePtr node) {
4478 /*
4479 * TODO complete and handle entities
4480 */
4481 while ((node != NULL) &&
4482 ((node->type == XML_COMMENT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004483 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00004484 ((node->type == XML_TEXT_NODE) &&
4485 (IS_BLANK_NODE(node))))) {
4486 node = node->next;
4487 }
4488 return(node);
4489}
4490
4491/**
Daniel Veillardedc91922003-01-26 00:52:04 +00004492 * xmlRelaxNGNormalize:
4493 * @ctxt: a schema validation context
4494 * @str: the string to normalize
4495 *
4496 * Implements the normalizeWhiteSpace( s ) function from
4497 * section 6.2.9 of the spec
4498 *
4499 * Returns the new string or NULL in case of error.
4500 */
4501static xmlChar *
4502xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *str) {
4503 xmlChar *ret, *p;
4504 const xmlChar *tmp;
4505 int len;
4506
4507 if (str == NULL)
4508 return(NULL);
4509 tmp = str;
4510 while (*tmp != 0) tmp++;
4511 len = tmp - str;
4512
4513 ret = (xmlChar *) xmlMalloc((len + 1) * sizeof(xmlChar));
4514 if (ret == NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00004515 if (ctxt != NULL) {
4516 VALID_CTXT();
4517 VALID_ERROR("xmlRelaxNGNormalize: out of memory\n");
4518 } else {
4519 xmlGenericError(xmlGenericErrorContext,
4520 "xmlRelaxNGNormalize: out of memory\n");
4521 }
Daniel Veillardedc91922003-01-26 00:52:04 +00004522 return(NULL);
4523 }
4524 p = ret;
4525 while (IS_BLANK(*str)) str++;
4526 while (*str != 0) {
4527 if (IS_BLANK(*str)) {
4528 while (IS_BLANK(*str)) str++;
4529 if (*str == 0)
4530 break;
4531 *p++ = ' ';
4532 } else
4533 *p++ = *str++;
4534 }
4535 *p = 0;
4536 return(ret);
4537}
4538
4539/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004540 * xmlRelaxNGValidateDatatype:
4541 * @ctxt: a Relax-NG validation context
4542 * @value: the string value
4543 * @type: the datatype definition
4544 *
4545 * Validate the given value against the dataype
4546 *
4547 * Returns 0 if the validation succeeded or an error code.
4548 */
4549static int
4550xmlRelaxNGValidateDatatype(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *value,
4551 xmlRelaxNGDefinePtr define) {
4552 int ret;
4553 xmlRelaxNGTypeLibraryPtr lib;
4554
4555 if ((define == NULL) || (define->data == NULL)) {
4556 return(-1);
4557 }
4558 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
4559 if (lib->check != NULL)
4560 ret = lib->check(lib->data, define->name, value);
4561 else
4562 ret = -1;
4563 if (ret < 0) {
4564 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004565 VALID_ERROR2("Internal: failed to validate type %s\n", define->name);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004566 return(-1);
4567 } else if (ret == 1) {
4568 ret = 0;
4569 } else {
4570 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004571 VALID_ERROR3("Type %s doesn't allow value %s\n", define->name, value);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004572 return(-1);
4573 ret = -1;
4574 }
4575 return(ret);
4576}
4577
4578/**
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004579 * xmlRelaxNGNextValue:
4580 * @ctxt: a Relax-NG validation context
4581 *
4582 * Skip to the next value when validating within a list
4583 *
4584 * Returns 0 if the operation succeeded or an error code.
4585 */
4586static int
4587xmlRelaxNGNextValue(xmlRelaxNGValidCtxtPtr ctxt) {
4588 xmlChar *cur;
4589
4590 cur = ctxt->state->value;
4591 if ((cur == NULL) || (ctxt->state->endvalue == NULL)) {
4592 ctxt->state->value = NULL;
Daniel Veillarde5b110b2003-02-04 14:43:39 +00004593 ctxt->state->endvalue = NULL;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004594 return(0);
4595 }
4596 while (*cur != 0) cur++;
4597 while ((cur != ctxt->state->endvalue) && (*cur == 0)) cur++;
4598 if (cur == ctxt->state->endvalue)
4599 ctxt->state->value = NULL;
4600 else
4601 ctxt->state->value = cur;
4602 return(0);
4603}
4604
4605/**
4606 * xmlRelaxNGValidateValueList:
4607 * @ctxt: a Relax-NG validation context
4608 * @defines: the list of definitions to verify
4609 *
4610 * Validate the given set of definitions for the current value
4611 *
4612 * Returns 0 if the validation succeeded or an error code.
4613 */
4614static int
4615xmlRelaxNGValidateValueList(xmlRelaxNGValidCtxtPtr ctxt,
4616 xmlRelaxNGDefinePtr defines) {
4617 int ret = 0;
4618
4619 while (defines != NULL) {
4620 ret = xmlRelaxNGValidateValue(ctxt, defines);
4621 if (ret != 0)
4622 break;
4623 defines = defines->next;
4624 }
4625 return(ret);
4626}
4627
4628/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004629 * xmlRelaxNGValidateValue:
4630 * @ctxt: a Relax-NG validation context
4631 * @define: the definition to verify
4632 *
4633 * Validate the given definition for the current value
4634 *
4635 * Returns 0 if the validation succeeded or an error code.
4636 */
4637static int
4638xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
4639 xmlRelaxNGDefinePtr define) {
Daniel Veillardedc91922003-01-26 00:52:04 +00004640 int ret = 0, oldflags;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004641 xmlChar *value;
4642
4643 value = ctxt->state->value;
4644 switch (define->type) {
4645 case XML_RELAXNG_EMPTY:
4646 if ((value != NULL) && (value[0] != '0'))
4647 ret = -1;
4648 break;
4649 case XML_RELAXNG_TEXT:
4650 break;
Daniel Veillardedc91922003-01-26 00:52:04 +00004651 case XML_RELAXNG_VALUE: {
4652 if (!xmlStrEqual(value, define->value)) {
4653 if (define->name != NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00004654 xmlRelaxNGTypeLibraryPtr lib;
4655
4656 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
4657 if ((lib != NULL) && (lib->comp != NULL))
4658 ret = lib->comp(lib->data, define->name, value,
4659 define->value);
4660 else
4661 ret = -1;
4662 if (ret < 0) {
4663 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004664 VALID_ERROR2("Internal: failed to compare type %s\n",
Daniel Veillardea3f3982003-01-26 19:45:18 +00004665 define->name);
4666 return(-1);
4667 } else if (ret == 1) {
4668 ret = 0;
4669 } else {
4670 ret = -1;
4671 }
Daniel Veillardedc91922003-01-26 00:52:04 +00004672 } else {
4673 xmlChar *nval, *nvalue;
4674
4675 /*
4676 * TODO: trivial optimizations are possible by
4677 * computing at compile-time
4678 */
4679 nval = xmlRelaxNGNormalize(ctxt, define->value);
4680 nvalue = xmlRelaxNGNormalize(ctxt, value);
4681
Daniel Veillardea3f3982003-01-26 19:45:18 +00004682 if ((nval == NULL) || (nvalue == NULL) ||
4683 (!xmlStrEqual(nval, nvalue)))
Daniel Veillardedc91922003-01-26 00:52:04 +00004684 ret = -1;
4685 if (nval != NULL)
4686 xmlFree(nval);
4687 if (nvalue != NULL)
4688 xmlFree(nvalue);
4689 }
4690 }
4691 break;
4692 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004693 case XML_RELAXNG_DATATYPE: {
4694 ret = xmlRelaxNGValidateDatatype(ctxt, value, define);
4695 if (ret == 0)
4696 xmlRelaxNGNextValue(ctxt);
4697
4698 break;
4699 }
4700 case XML_RELAXNG_CHOICE: {
4701 xmlRelaxNGDefinePtr list = define->content;
4702 xmlChar *oldvalue;
4703
4704 oldflags = ctxt->flags;
4705 ctxt->flags |= FLAGS_IGNORABLE;
4706
4707 oldvalue = ctxt->state->value;
4708 while (list != NULL) {
4709 ret = xmlRelaxNGValidateValue(ctxt, list);
4710 if (ret == 0) {
4711 break;
4712 }
4713 ctxt->state->value = oldvalue;
4714 list = list->next;
4715 }
4716 ctxt->flags = oldflags;
4717 break;
4718 }
4719 case XML_RELAXNG_LIST: {
4720 xmlRelaxNGDefinePtr list = define->content;
4721 xmlChar *oldvalue, *oldend, *val, *cur;
4722
4723 oldvalue = ctxt->state->value;
4724 oldend = ctxt->state->endvalue;
4725
4726 val = xmlStrdup(oldvalue);
4727 if (val == NULL) {
4728 VALID_CTXT();
4729 VALID_ERROR("Internal: no state\n");
4730 return(-1);
4731 }
4732 cur = val;
4733 while (*cur != 0) {
4734 if (IS_BLANK(*cur))
4735 *cur = 0;
4736 cur++;
4737 }
4738 ctxt->state->endvalue = cur;
4739 cur = val;
4740 while ((*cur == 0) && (cur != ctxt->state->endvalue)) cur++;
4741
4742 ctxt->state->value = cur;
4743
4744 while (list != NULL) {
4745 ret = xmlRelaxNGValidateValue(ctxt, list);
4746 if (ret != 0) {
4747 break;
4748 }
4749 list = list->next;
4750 }
4751 if ((ret == 0) && (ctxt->state->value != NULL) &&
4752 (ctxt->state->value != ctxt->state->endvalue)) {
4753 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004754 VALID_ERROR2("Extra data in list: %s\n", ctxt->state->value);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004755 ret = -1;
4756 }
4757 xmlFree(val);
4758 ctxt->state->value = oldvalue;
4759 ctxt->state->endvalue = oldend;
4760 break;
4761 }
4762 case XML_RELAXNG_ONEORMORE:
4763 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
4764 if (ret != 0) {
4765 break;
4766 }
4767 /* no break on purpose */
4768 case XML_RELAXNG_ZEROORMORE: {
4769 xmlChar *cur, *temp;
4770
4771 oldflags = ctxt->flags;
4772 ctxt->flags |= FLAGS_IGNORABLE;
4773 cur = ctxt->state->value;
4774 temp = NULL;
4775 while ((cur != NULL) && (cur != ctxt->state->endvalue) &&
4776 (temp != cur)) {
4777 temp = cur;
4778 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
4779 if (ret != 0) {
4780 ctxt->state->value = temp;
4781 ret = 0;
4782 break;
4783 }
4784 cur = ctxt->state->value;
4785 }
4786 ctxt->flags = oldflags;
4787 break;
4788 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004789 default:
4790 TODO
4791 ret = -1;
4792 }
4793 return(ret);
4794}
4795
4796/**
4797 * xmlRelaxNGValidateValueContent:
4798 * @ctxt: a Relax-NG validation context
4799 * @defines: the list of definitions to verify
4800 *
4801 * Validate the given definitions for the current value
4802 *
4803 * Returns 0 if the validation succeeded or an error code.
4804 */
4805static int
4806xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt,
4807 xmlRelaxNGDefinePtr defines) {
4808 int ret = 0;
4809
4810 while (defines != NULL) {
4811 ret = xmlRelaxNGValidateValue(ctxt, defines);
4812 if (ret != 0)
4813 break;
4814 defines = defines->next;
4815 }
4816 return(ret);
4817}
4818
4819/**
Daniel Veillard144fae12003-02-03 13:17:57 +00004820 * xmlRelaxNGAttributeMatch:
4821 * @ctxt: a Relax-NG validation context
4822 * @define: the definition to check
4823 * @prop: the attribute
4824 *
4825 * Check if the attribute matches the definition nameClass
4826 *
4827 * Returns 1 if the attribute matches, 0 if no, or -1 in case of error
4828 */
4829static int
4830xmlRelaxNGAttributeMatch(xmlRelaxNGValidCtxtPtr ctxt,
4831 xmlRelaxNGDefinePtr define,
4832 xmlAttrPtr prop) {
4833 int ret;
4834
4835 if (define->name != NULL) {
4836 if (!xmlStrEqual(define->name, prop->name))
4837 return(0);
4838 }
4839 if (define->ns != NULL) {
4840 if (define->ns[0] == 0) {
4841 if (prop->ns != NULL)
4842 return(0);
4843 } else {
4844 if ((prop->ns == NULL) ||
4845 (!xmlStrEqual(define->ns, prop->ns->href)))
4846 return(0);
4847 }
4848 }
4849 if (define->nameClass == NULL)
4850 return(1);
4851 define = define->nameClass;
4852 if (define->type == XML_RELAXNG_EXCEPT) {
4853 xmlRelaxNGDefinePtr list;
4854
4855 list = define->content;
4856 while (list != NULL) {
4857 ret = xmlRelaxNGAttributeMatch(ctxt, list, prop);
4858 if (ret == 1)
4859 return(0);
4860 if (ret < 0)
4861 return(ret);
4862 list = list->next;
4863 }
4864 } else {
4865 TODO
4866 }
4867 return(1);
4868}
4869
4870/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004871 * xmlRelaxNGValidateAttribute:
4872 * @ctxt: a Relax-NG validation context
4873 * @define: the definition to verify
4874 *
4875 * Validate the given attribute definition for that node
4876 *
4877 * Returns 0 if the validation succeeded or an error code.
4878 */
4879static int
4880xmlRelaxNGValidateAttribute(xmlRelaxNGValidCtxtPtr ctxt,
4881 xmlRelaxNGDefinePtr define) {
4882 int ret = 0, i;
4883 xmlChar *value, *oldvalue;
4884 xmlAttrPtr prop = NULL, tmp;
4885
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004886 if (ctxt->state->nbAttrLeft <= 0)
4887 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004888 if (define->name != NULL) {
4889 for (i = 0;i < ctxt->state->nbAttrs;i++) {
4890 tmp = ctxt->state->attrs[i];
4891 if ((tmp != NULL) && (xmlStrEqual(define->name, tmp->name))) {
4892 if ((((define->ns == NULL) || (define->ns[0] == 0)) &&
4893 (tmp->ns == NULL)) ||
4894 ((tmp->ns != NULL) &&
4895 (xmlStrEqual(define->ns, tmp->ns->href)))) {
4896 prop = tmp;
4897 break;
4898 }
4899 }
4900 }
4901 if (prop != NULL) {
4902 value = xmlNodeListGetString(prop->doc, prop->children, 1);
4903 oldvalue = ctxt->state->value;
4904 ctxt->state->value = value;
Daniel Veillard231d7912003-02-09 14:22:17 +00004905 ctxt->state->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004906 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00004907 if (ctxt->state->value != NULL)
4908 value = ctxt->state->value;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004909 if (value != NULL)
4910 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00004911 ctxt->state->value = oldvalue;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004912 if (ret == 0) {
4913 /*
4914 * flag the attribute as processed
4915 */
4916 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004917 ctxt->state->nbAttrLeft--;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004918 }
4919 } else {
4920 ret = -1;
4921 }
4922#ifdef DEBUG
4923 xmlGenericError(xmlGenericErrorContext,
4924 "xmlRelaxNGValidateAttribute(%s): %d\n", define->name, ret);
4925#endif
4926 } else {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004927 for (i = 0;i < ctxt->state->nbAttrs;i++) {
4928 tmp = ctxt->state->attrs[i];
Daniel Veillard144fae12003-02-03 13:17:57 +00004929 if ((tmp != NULL) &&
4930 (xmlRelaxNGAttributeMatch(ctxt, define, tmp) == 1)) {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004931 prop = tmp;
4932 break;
4933 }
4934 }
4935 if (prop != NULL) {
4936 value = xmlNodeListGetString(prop->doc, prop->children, 1);
4937 oldvalue = ctxt->state->value;
4938 ctxt->state->value = value;
4939 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00004940 if (ctxt->state->value != NULL)
4941 value = ctxt->state->value;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004942 if (value != NULL)
4943 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00004944 ctxt->state->value = oldvalue;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004945 if (ret == 0) {
4946 /*
4947 * flag the attribute as processed
4948 */
4949 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004950 ctxt->state->nbAttrLeft--;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004951 }
4952 } else {
4953 ret = -1;
4954 }
4955#ifdef DEBUG
Daniel Veillard144fae12003-02-03 13:17:57 +00004956 if (define->ns != NULL) {
4957 xmlGenericError(xmlGenericErrorContext,
4958 "xmlRelaxNGValidateAttribute(nsName ns = %s): %d\n",
4959 define->ns, ret);
4960 } else {
4961 xmlGenericError(xmlGenericErrorContext,
4962 "xmlRelaxNGValidateAttribute(anyName): %d\n",
4963 ret);
4964 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004965#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00004966 }
4967
4968 return(ret);
4969}
4970
4971/**
4972 * xmlRelaxNGValidateAttributeList:
4973 * @ctxt: a Relax-NG validation context
4974 * @define: the list of definition to verify
4975 *
4976 * Validate the given node against the list of attribute definitions
4977 *
4978 * Returns 0 if the validation succeeded or an error code.
4979 */
4980static int
4981xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt,
4982 xmlRelaxNGDefinePtr defines) {
4983 int ret = 0;
4984 while (defines != NULL) {
4985 if (xmlRelaxNGValidateAttribute(ctxt, defines) != 0)
4986 ret = -1;
4987 defines = defines->next;
4988 }
4989 return(ret);
4990}
4991
4992/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004993 * xmlRelaxNGValidateTryPermutation:
4994 * @ctxt: a Relax-NG validation context
4995 * @groups: the array of groups
4996 * @nbgroups: the number of groups in the array
4997 * @array: the permutation to try
4998 * @len: the size of the set
4999 *
5000 * Try to validate a permutation for the group of definitions.
5001 *
5002 * Returns 0 if the validation succeeded or an error code.
5003 */
5004static int
5005xmlRelaxNGValidateTryPermutation(xmlRelaxNGValidCtxtPtr ctxt,
5006 xmlRelaxNGDefinePtr rule,
5007 xmlNodePtr *array, int len) {
5008 int i, ret;
5009
5010 if (len > 0) {
5011 /*
5012 * One only need the next pointer set-up to do the validation
5013 */
5014 for (i = 0;i < (len - 1);i++)
5015 array[i]->next = array[i + 1];
5016 array[i]->next = NULL;
5017
5018 /*
5019 * Now try to validate the sequence
5020 */
5021 ctxt->state->seq = array[0];
5022 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
5023 } else {
5024 ctxt->state->seq = NULL;
5025 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
5026 }
5027
5028 /*
5029 * the sequence must be fully consumed
5030 */
5031 if (ctxt->state->seq != NULL)
5032 return(-1);
5033
5034 return(ret);
5035}
5036
5037/**
5038 * xmlRelaxNGValidateWalkPermutations:
5039 * @ctxt: a Relax-NG validation context
5040 * @groups: the array of groups
5041 * @nbgroups: the number of groups in the array
5042 * @nodes: the set of nodes
5043 * @array: the current state of the parmutation
5044 * @len: the size of the set
5045 * @level: a pointer to the level variable
5046 * @k: the index in the array to fill
5047 *
5048 * Validate a set of nodes for a groups of definitions, will try the
5049 * full set of permutations
5050 *
5051 * Returns 0 if the validation succeeded or an error code.
5052 */
5053static int
5054xmlRelaxNGValidateWalkPermutations(xmlRelaxNGValidCtxtPtr ctxt,
5055 xmlRelaxNGDefinePtr rule, xmlNodePtr *nodes,
5056 xmlNodePtr *array, int len,
5057 int *level, int k) {
5058 int i, ret;
5059
5060 if ((k >= 0) && (k < len))
5061 array[k] = nodes[*level];
5062 *level = *level + 1;
5063 if (*level == len) {
5064 ret = xmlRelaxNGValidateTryPermutation(ctxt, rule, array, len);
5065 if (ret == 0)
5066 return(0);
5067 } else {
5068 for (i = 0;i < len;i++) {
5069 if (array[i] == NULL) {
5070 ret = xmlRelaxNGValidateWalkPermutations(ctxt, rule,
5071 nodes, array, len, level, i);
5072 if (ret == 0)
5073 return(0);
5074 }
5075 }
5076 }
5077 *level = *level - 1;
5078 array[k] = NULL;
5079 return(-1);
5080}
5081
5082/**
5083 * xmlRelaxNGNodeMatchesList:
5084 * @node: the node
5085 * @list: a NULL terminated array of definitions
5086 *
5087 * Check if a node can be matched by one of the definitions
5088 *
5089 * Returns 1 if matches 0 otherwise
5090 */
5091static int
5092xmlRelaxNGNodeMatchesList(xmlNodePtr node, xmlRelaxNGDefinePtr *list) {
5093 xmlRelaxNGDefinePtr cur;
5094 int i = 0;
5095
5096 if ((node == NULL) || (list == NULL))
5097 return(0);
5098
5099 cur = list[i++];
5100 while (cur != NULL) {
5101 if ((node->type == XML_ELEMENT_NODE) &&
5102 (cur->type == XML_RELAXNG_ELEMENT)) {
5103 if (cur->name == NULL) {
5104 if ((node->ns != NULL) &&
5105 (xmlStrEqual(node->ns->href, cur->ns)))
5106 return(1);
5107 } else if (xmlStrEqual(cur->name, node->name)) {
5108 if ((cur->ns == NULL) || (cur->ns[0] == 0)) {
5109 if (node->ns == NULL)
5110 return(1);
5111 } else {
5112 if ((node->ns != NULL) &&
5113 (xmlStrEqual(node->ns->href, cur->ns)))
5114 return(1);
5115 }
5116 }
5117 } else if ((node->type == XML_TEXT_NODE) &&
5118 (cur->type == XML_RELAXNG_TEXT)) {
5119 return(1);
5120 }
5121 cur = list[i++];
5122 }
5123 return(0);
5124}
5125
5126/**
5127 * xmlRelaxNGValidatePartGroup:
5128 * @ctxt: a Relax-NG validation context
5129 * @groups: the array of groups
5130 * @nbgroups: the number of groups in the array
5131 * @nodes: the set of nodes
5132 * @len: the size of the set of nodes
5133 *
5134 * Validate a set of nodes for a groups of definitions
5135 *
5136 * Returns 0 if the validation succeeded or an error code.
5137 */
5138static int
5139xmlRelaxNGValidatePartGroup(xmlRelaxNGValidCtxtPtr ctxt,
5140 xmlRelaxNGInterleaveGroupPtr *groups,
5141 int nbgroups, xmlNodePtr *nodes, int len) {
Daniel Veillard231d7912003-02-09 14:22:17 +00005142 int level, ret = -1, i, j, k, top_j, max_j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005143 xmlNodePtr *array = NULL, *list, oldseq;
5144 xmlRelaxNGInterleaveGroupPtr group;
5145
5146 list = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
5147 if (list == NULL) {
5148 return(-1);
5149 }
5150 array = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
5151 if (array == NULL) {
5152 xmlFree(list);
5153 return(-1);
5154 }
5155 memset(array, 0, len * sizeof(xmlNodePtr));
5156
5157 /*
5158 * Partition the elements and validate the subsets.
5159 */
5160 oldseq = ctxt->state->seq;
Daniel Veillard231d7912003-02-09 14:22:17 +00005161 max_j = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005162 for (i = 0;i < nbgroups;i++) {
5163 group = groups[i];
5164 if (group == NULL)
5165 continue;
5166 k = 0;
Daniel Veillard231d7912003-02-09 14:22:17 +00005167 top_j = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005168 for (j = 0;j < len;j++) {
5169 if (nodes[j] == NULL)
5170 continue;
5171 if (xmlRelaxNGNodeMatchesList(nodes[j], group->defs)) {
5172 list[k++] = nodes[j];
5173 nodes[j] = NULL;
Daniel Veillard231d7912003-02-09 14:22:17 +00005174 top_j = j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005175 }
5176 }
Daniel Veillard231d7912003-02-09 14:22:17 +00005177 if (top_j > max_j)
5178 max_j = top_j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005179 ctxt->state->seq = oldseq;
5180 if (k > 1) {
5181 memset(array, 0, k * sizeof(xmlNodePtr));
Daniel Veillardb08c9812003-01-28 23:09:49 +00005182 level = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005183 ret = xmlRelaxNGValidateWalkPermutations(ctxt, group->rule,
5184 list, array, k, &level, -1);
5185 } else {
5186 ret = xmlRelaxNGValidateTryPermutation(ctxt, group->rule, list, k);
5187 }
5188 if (ret != 0) {
5189 ctxt->state->seq = oldseq;
5190 break;
5191 }
5192 }
5193
Daniel Veillard231d7912003-02-09 14:22:17 +00005194 for (j = 0;j < max_j;j++) {
5195 if (nodes[j] != NULL) {
5196 TODO /* problem, one of the nodes didn't got a match */
5197 }
5198 }
5199 if (ret == 0) {
5200 if (max_j + 1 < len)
5201 ctxt->state->seq = nodes[max_j + 1];
5202 else
5203 ctxt->state->seq = NULL;
5204 }
5205
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005206 xmlFree(list);
5207 xmlFree(array);
5208 return(ret);
5209}
5210
5211/**
5212 * xmlRelaxNGValidateInterleave:
5213 * @ctxt: a Relax-NG validation context
5214 * @define: the definition to verify
5215 *
5216 * Validate an interleave definition for a node.
5217 *
5218 * Returns 0 if the validation succeeded or an error code.
5219 */
5220static int
5221xmlRelaxNGValidateInterleave(xmlRelaxNGValidCtxtPtr ctxt,
5222 xmlRelaxNGDefinePtr define) {
5223 int ret = 0, nbchildren, nbtot, i, j;
5224 xmlRelaxNGPartitionPtr partitions;
5225 xmlNodePtr *children = NULL;
5226 xmlNodePtr *order = NULL;
Daniel Veillard231d7912003-02-09 14:22:17 +00005227 xmlNodePtr cur, oldseq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005228
5229 if (define->data != NULL) {
5230 partitions = (xmlRelaxNGPartitionPtr) define->data;
5231 } else {
5232 VALID_CTXT();
5233 VALID_ERROR("Internal: interleave block has no data\n");
5234 return(-1);
5235 }
5236
5237 /*
5238 * Build the sequence of child and an array preserving the children
5239 * initial order.
5240 */
5241 cur = ctxt->state->seq;
Daniel Veillard231d7912003-02-09 14:22:17 +00005242 oldseq = ctxt->state->seq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005243 nbchildren = 0;
5244 nbtot = 0;
5245 while (cur != NULL) {
5246 if ((cur->type == XML_COMMENT_NODE) ||
5247 (cur->type == XML_PI_NODE) ||
5248 ((cur->type == XML_TEXT_NODE) &&
5249 (IS_BLANK_NODE(cur)))) {
5250 nbtot++;
5251 } else {
5252 nbchildren++;
5253 nbtot++;
5254 }
5255 cur = cur->next;
5256 }
5257 children = (xmlNodePtr *) xmlMalloc(nbchildren * sizeof(xmlNodePtr));
5258 if (children == NULL)
5259 goto error;
5260 order = (xmlNodePtr *) xmlMalloc(nbtot * sizeof(xmlNodePtr));
5261 if (order == NULL)
5262 goto error;
5263 cur = ctxt->state->seq;
5264 i = 0;
5265 j = 0;
5266 while (cur != NULL) {
5267 if ((cur->type == XML_COMMENT_NODE) ||
5268 (cur->type == XML_PI_NODE) ||
5269 ((cur->type == XML_TEXT_NODE) &&
5270 (IS_BLANK_NODE(cur)))) {
5271 order[j++] = cur;
5272 } else {
5273 order[j++] = cur;
5274 children[i++] = cur;
5275 }
5276 cur = cur->next;
5277 }
5278
5279 /* TODO: retry with a maller set of child if there is a next... */
5280 ret = xmlRelaxNGValidatePartGroup(ctxt, partitions->groups,
5281 partitions->nbgroups, children, nbchildren);
Daniel Veillard231d7912003-02-09 14:22:17 +00005282 if (ret != 0)
5283 ctxt->state->seq = oldseq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005284
5285 /*
5286 * Cleanup: rebuid the child sequence and free the structure
5287 */
5288 if (order != NULL) {
5289 for (i = 0;i < nbtot;i++) {
5290 if (i == 0)
5291 order[i]->prev = NULL;
5292 else
5293 order[i]->prev = order[i - 1];
5294 if (i == nbtot - 1)
5295 order[i]->next = NULL;
5296 else
5297 order[i]->next = order[i + 1];
5298 }
5299 xmlFree(order);
5300 }
5301 if (children != NULL)
5302 xmlFree(children);
5303
5304 return(ret);
5305
5306error:
5307 if (order != NULL) {
5308 for (i = 0;i < nbtot;i++) {
5309 if (i == 0)
5310 order[i]->prev = NULL;
5311 else
5312 order[i]->prev = order[i - 1];
5313 if (i == nbtot - 1)
5314 order[i]->next = NULL;
5315 else
5316 order[i]->next = order[i + 1];
5317 }
5318 xmlFree(order);
5319 }
5320 if (children != NULL)
5321 xmlFree(children);
5322 return(-1);
5323}
5324
5325/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00005326 * xmlRelaxNGValidateElementContent:
5327 * @ctxt: a Relax-NG validation context
5328 * @define: the list of definition to verify
5329 *
5330 * Validate the given node content against the (list) of definitions
5331 *
5332 * Returns 0 if the validation succeeded or an error code.
5333 */
5334static int
5335xmlRelaxNGValidateElementContent(xmlRelaxNGValidCtxtPtr ctxt,
5336 xmlRelaxNGDefinePtr defines) {
5337 int ret = 0, res;
5338
5339 if (ctxt->state == NULL) {
5340 VALID_CTXT();
5341 VALID_ERROR("Internal: no state\n");
5342 return(-1);
5343 }
5344 while (defines != NULL) {
5345 res = xmlRelaxNGValidateDefinition(ctxt, defines);
5346 if (res < 0)
5347 ret = -1;
5348 defines = defines->next;
5349 }
5350
5351 return(ret);
5352}
5353
5354/**
5355 * xmlRelaxNGValidateDefinition:
5356 * @ctxt: a Relax-NG validation context
5357 * @define: the definition to verify
5358 *
5359 * Validate the current node against the definition
5360 *
5361 * Returns 0 if the validation succeeded or an error code.
5362 */
5363static int
5364xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
5365 xmlRelaxNGDefinePtr define) {
5366 xmlNodePtr node;
5367 int ret = 0, i, tmp, oldflags;
5368 xmlRelaxNGValidStatePtr oldstate, state;
5369
5370 if (define == NULL) {
5371 VALID_CTXT();
5372 VALID_ERROR("internal error: define == NULL\n");
5373 return(-1);
5374 }
5375 if (ctxt->state != NULL) {
5376 node = ctxt->state->seq;
5377 } else {
5378 node = NULL;
5379 }
Daniel Veillard231d7912003-02-09 14:22:17 +00005380#ifdef DEBUG
5381 for (i = 0;i < ctxt->depth;i++)
5382 xmlGenericError(xmlGenericErrorContext, " ");
5383 xmlGenericError(xmlGenericErrorContext,
5384 "Start validating %s ", xmlRelaxNGDefName(define));
5385 if (define->name != NULL)
5386 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
5387 if ((node != NULL) && (node->name != NULL))
5388 xmlGenericError(xmlGenericErrorContext, "on %s\n", node->name);
5389 else
5390 xmlGenericError(xmlGenericErrorContext, "\n");
5391#endif
5392 ctxt->depth++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005393 switch (define->type) {
5394 case XML_RELAXNG_EMPTY:
5395 if (node != NULL) {
5396 VALID_CTXT();
5397 VALID_ERROR("Expecting an empty element\n");
Daniel Veillard231d7912003-02-09 14:22:17 +00005398 ret = -1;
5399 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005400 }
Daniel Veillard231d7912003-02-09 14:22:17 +00005401 ret = 0;
5402 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005403 case XML_RELAXNG_NOT_ALLOWED:
Daniel Veillard231d7912003-02-09 14:22:17 +00005404 ret = -1;
5405 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005406 case XML_RELAXNG_TEXT:
Daniel Veillard231d7912003-02-09 14:22:17 +00005407 if (node == NULL) {
5408 ret = 0;
5409 break;
5410 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005411 while ((node != NULL) &&
5412 ((node->type == XML_TEXT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005413 (node->type == XML_COMMENT_NODE) ||
5414 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00005415 (node->type == XML_CDATA_SECTION_NODE)))
5416 node = node->next;
Daniel Veillard276be4a2003-01-24 01:03:34 +00005417 if (node == ctxt->state->seq) {
5418 VALID_CTXT();
5419 VALID_ERROR("Expecting text content\n");
5420 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005421 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00005422 ctxt->state->seq = node;
5423 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005424 case XML_RELAXNG_ELEMENT:
5425 node = xmlRelaxNGSkipIgnored(ctxt, node);
Daniel Veillard231d7912003-02-09 14:22:17 +00005426 if (node == NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005427 VALID_CTXT();
Daniel Veillard231d7912003-02-09 14:22:17 +00005428 VALID_ERROR("Expecting an element, got empty\n");
5429 ret = -1;
5430 break;
5431 }
5432 if (node->type != XML_ELEMENT_NODE) {
5433 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005434 VALID_ERROR2("Expecting an element got %d type\n", node->type);
Daniel Veillard231d7912003-02-09 14:22:17 +00005435 ret = -1;
5436 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005437 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00005438 /*
5439 * This node was already validated successfully against
5440 * this definition.
5441 */
5442 if (node->_private == define)
5443 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005444 if (define->name != NULL) {
5445 if (!xmlStrEqual(node->name, define->name)) {
5446 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005447 VALID_ERROR3("Expecting element %s, got %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00005448 define->name, node->name);
Daniel Veillard231d7912003-02-09 14:22:17 +00005449 ret = -1;
5450 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005451 }
5452 }
5453 if ((define->ns != NULL) && (define->ns[0] != 0)) {
5454 if (node->ns == NULL) {
5455 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005456 VALID_ERROR2("Expecting a namespace for element %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00005457 node->name);
Daniel Veillard231d7912003-02-09 14:22:17 +00005458 ret = -1;
5459 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005460 } else if (!xmlStrEqual(node->ns->href, define->ns)) {
5461 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005462 VALID_ERROR3("Expecting element %s has wrong namespace: expecting %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00005463 node->name, define->ns);
Daniel Veillard231d7912003-02-09 14:22:17 +00005464 ret = -1;
5465 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005466 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005467 } else if (define->name != NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005468 if (node->ns != NULL) {
5469 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005470 VALID_ERROR2("Expecting no namespace for element %s\n",
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005471 define->name);
Daniel Veillard231d7912003-02-09 14:22:17 +00005472 ret = -1;
5473 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005474 }
5475 }
5476
5477 state = xmlRelaxNGNewValidState(ctxt, node);
5478 if (state == NULL) {
Daniel Veillard231d7912003-02-09 14:22:17 +00005479 ret = -1;
5480 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005481 }
5482
5483 oldstate = ctxt->state;
5484 ctxt->state = state;
5485 if (define->attrs != NULL) {
5486 tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
Daniel Veillard231d7912003-02-09 14:22:17 +00005487 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005488 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00005489#ifdef DEBUG
5490 xmlGenericError(xmlGenericErrorContext,
5491 "E: Element %s failed to validate attributes\n",
5492 node->name);
5493#endif
5494 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005495 }
5496 if (define->content != NULL) {
5497 tmp = xmlRelaxNGValidateElementContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00005498 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005499 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00005500#ifdef DEBUG
5501 xmlGenericError(xmlGenericErrorContext,
5502 "E: Element %s failed to validate element content\n",
5503 node->name);
5504#endif
5505 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005506 }
5507 state = ctxt->state;
5508 if (state->seq != NULL) {
5509 state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq);
5510 if (state->seq != NULL) {
5511 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005512 VALID_ERROR3("Extra content for element %s: %s\n",
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005513 node->name, state->seq->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005514 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00005515#ifdef DEBUG
5516 xmlGenericError(xmlGenericErrorContext,
5517 "E: Element %s has extra content: %s\n",
5518 node->name, state->seq->name);
5519#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00005520 }
5521 }
5522 for (i = 0;i < state->nbAttrs;i++) {
5523 if (state->attrs[i] != NULL) {
5524 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005525 VALID_ERROR3("Invalid attribute %s for element %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00005526 state->attrs[i]->name, node->name);
5527 ret = -1;
5528 }
5529 }
5530 ctxt->state = oldstate;
5531 xmlRelaxNGFreeValidState(state);
5532 if (oldstate != NULL)
Daniel Veillarde5b110b2003-02-04 14:43:39 +00005533 oldstate->seq = xmlRelaxNGSkipIgnored(ctxt, node->next);
5534 if (ret == 0)
5535 node->_private = define;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005536
5537
5538#ifdef DEBUG
5539 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard231d7912003-02-09 14:22:17 +00005540 "xmlRelaxNGValidateDefinition(): validated %s : %d",
Daniel Veillard6eadf632003-01-23 18:29:16 +00005541 node->name, ret);
Daniel Veillard231d7912003-02-09 14:22:17 +00005542 if (oldstate == NULL)
5543 xmlGenericError(xmlGenericErrorContext, ": no state\n");
5544 else if (oldstate->seq == NULL)
5545 xmlGenericError(xmlGenericErrorContext, ": done\n");
5546 else if (oldstate->seq->type == XML_ELEMENT_NODE)
5547 xmlGenericError(xmlGenericErrorContext, ": next elem %s\n",
5548 oldstate->seq->name);
5549 else
5550 xmlGenericError(xmlGenericErrorContext, ": next %s %d\n",
5551 oldstate->seq->name, oldstate->seq->type);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005552#endif
5553 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005554 case XML_RELAXNG_OPTIONAL:
5555 oldflags = ctxt->flags;
5556 ctxt->flags |= FLAGS_IGNORABLE;
5557 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5558 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5559 if (ret != 0) {
5560 xmlRelaxNGFreeValidState(ctxt->state);
5561 ctxt->state = oldstate;
5562 ret = 0;
5563 break;
5564 }
5565 xmlRelaxNGFreeValidState(oldstate);
5566 ctxt->flags = oldflags;
5567 ret = 0;
5568 break;
5569 case XML_RELAXNG_ONEORMORE:
5570 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5571 if (ret != 0) {
5572 break;
5573 }
5574 /* no break on purpose */
Daniel Veillard276be4a2003-01-24 01:03:34 +00005575 case XML_RELAXNG_ZEROORMORE: {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005576 oldflags = ctxt->flags;
5577 ctxt->flags |= FLAGS_IGNORABLE;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005578 while (ctxt->state->nbAttrLeft != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005579 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5580 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5581 if (ret != 0) {
5582 xmlRelaxNGFreeValidState(ctxt->state);
5583 ctxt->state = oldstate;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005584 break;
5585 }
5586 xmlRelaxNGFreeValidState(oldstate);
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005587 }
5588 if (ret == 0) {
5589 /*
5590 * There is no attribute left to be consumed,
5591 * we can check the closure by looking at ctxt->state->seq
5592 */
5593 xmlNodePtr cur, temp;
5594
Daniel Veillard276be4a2003-01-24 01:03:34 +00005595 cur = ctxt->state->seq;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005596 temp = NULL;
5597 while ((cur != NULL) && (temp != cur)) {
5598 temp = cur;
5599 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5600 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5601 if (ret != 0) {
5602 xmlRelaxNGFreeValidState(ctxt->state);
5603 ctxt->state = oldstate;
5604 break;
5605 }
5606 xmlRelaxNGFreeValidState(oldstate);
5607 cur = ctxt->state->seq;
5608 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005609 }
5610 ctxt->flags = oldflags;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005611 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005612 break;
Daniel Veillard276be4a2003-01-24 01:03:34 +00005613 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005614 case XML_RELAXNG_CHOICE: {
5615 xmlRelaxNGDefinePtr list = define->content;
5616
5617 oldflags = ctxt->flags;
5618 ctxt->flags |= FLAGS_IGNORABLE;
5619
5620 while (list != NULL) {
5621 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5622 ret = xmlRelaxNGValidateDefinition(ctxt, list);
5623 if (ret == 0) {
5624 xmlRelaxNGFreeValidState(oldstate);
5625 break;
5626 }
5627 xmlRelaxNGFreeValidState(ctxt->state);
5628 ctxt->state = oldstate;
5629 list = list->next;
5630 }
5631 ctxt->flags = oldflags;
5632 break;
5633 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005634 case XML_RELAXNG_DEF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00005635 case XML_RELAXNG_GROUP: {
5636 xmlRelaxNGDefinePtr list = define->content;
5637
5638 while (list != NULL) {
5639 ret = xmlRelaxNGValidateDefinition(ctxt, list);
5640 if (ret != 0)
5641 break;
5642 list = list->next;
5643 }
5644 break;
5645 }
5646 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005647 ret = xmlRelaxNGValidateInterleave(ctxt, define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005648 break;
5649 case XML_RELAXNG_ATTRIBUTE:
5650 ret = xmlRelaxNGValidateAttribute(ctxt, define);
5651 break;
5652 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00005653 case XML_RELAXNG_PARENTREF:
5654 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00005655 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5656 break;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005657 case XML_RELAXNG_DATATYPE: {
5658 xmlChar *content;
5659
5660 content = xmlNodeGetContent(node);
5661 ret = xmlRelaxNGValidateDatatype(ctxt, content, define);
5662 if (ret == -1) {
5663 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005664 VALID_ERROR2("internal error validating %s\n", define->name);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005665 } else if (ret == 0) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005666 if (node != NULL)
5667 ctxt->state->seq = node->next;
5668 else
5669 ctxt->state->seq = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005670 }
5671 /*
5672 * TODO cover the problems with
5673 * <p>12<!-- comment -->34</p>
5674 * TODO detect full element coverage at compilation time.
5675 */
5676 if ((node != NULL) && (node->next != NULL)) {
5677 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005678 VALID_ERROR2("The data does not cover the full element %s\n",
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005679 node->parent->name);
5680 ret = -1;
5681 }
5682 if (content != NULL)
5683 xmlFree(content);
5684 break;
5685 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00005686 case XML_RELAXNG_VALUE: {
5687 xmlChar *content;
5688 xmlChar *oldvalue;
5689
5690 content = xmlNodeGetContent(node);
5691 oldvalue = ctxt->state->value;
5692 ctxt->state->value = content;
5693 ret = xmlRelaxNGValidateValue(ctxt, define);
5694 ctxt->state->value = oldvalue;
5695 if (ret == -1) {
5696 VALID_CTXT();
Daniel Veillardd2298792003-02-14 16:54:11 +00005697 if (define->name != NULL) {
5698 VALID_ERROR2("error validating value %s\n", define->name);
5699 } else {
5700 VALID_ERROR("error validating value\n");
5701 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00005702 } else if (ret == 0) {
5703 ctxt->state->seq = node->next;
5704 }
5705 /*
5706 * TODO cover the problems with
5707 * <p>12<!-- comment -->34</p>
5708 * TODO detect full element coverage at compilation time.
5709 */
5710 if ((node != NULL) && (node->next != NULL)) {
5711 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005712 VALID_ERROR2("The value does not cover the full element %s\n",
Daniel Veillardea3f3982003-01-26 19:45:18 +00005713 node->parent->name);
5714 ret = -1;
5715 }
5716 if (content != NULL)
5717 xmlFree(content);
5718 break;
5719 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005720 case XML_RELAXNG_LIST: {
5721 xmlChar *content;
5722 xmlChar *oldvalue, *oldendvalue;
5723 int len;
Daniel Veillardea3f3982003-01-26 19:45:18 +00005724
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005725 content = xmlNodeGetContent(node);
5726 len = xmlStrlen(content);
5727 oldvalue = ctxt->state->value;
5728 oldendvalue = ctxt->state->endvalue;
5729 ctxt->state->value = content;
5730 ctxt->state->endvalue = content + len;
5731 ret = xmlRelaxNGValidateValue(ctxt, define);
5732 ctxt->state->value = oldvalue;
5733 ctxt->state->endvalue = oldendvalue;
5734 if (ret == -1) {
5735 VALID_CTXT();
5736 VALID_ERROR("internal error validating list\n");
5737 } else if (ret == 0) {
5738 ctxt->state->seq = node->next;
5739 }
5740 /*
5741 * TODO cover the problems with
5742 * <p>12<!-- comment -->34</p>
5743 * TODO detect full element coverage at compilation time.
5744 */
5745 if ((node != NULL) && (node->next != NULL)) {
5746 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005747 VALID_ERROR2("The list does not cover the full element %s\n",
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005748 node->parent->name);
5749 ret = -1;
5750 }
5751 if (content != NULL)
5752 xmlFree(content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005753 break;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005754 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005755 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00005756 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005757 TODO
5758 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005759 }
Daniel Veillard231d7912003-02-09 14:22:17 +00005760 ctxt->depth--;
5761#ifdef DEBUG
5762 for (i = 0;i < ctxt->depth;i++)
5763 xmlGenericError(xmlGenericErrorContext, " ");
5764 xmlGenericError(xmlGenericErrorContext,
5765 "Validating %s ", xmlRelaxNGDefName(define));
5766 if (define->name != NULL)
5767 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
5768 if (ret == 0)
5769 xmlGenericError(xmlGenericErrorContext, "suceeded\n");
5770 else
5771 xmlGenericError(xmlGenericErrorContext, "failed\n");
5772#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00005773 return(ret);
5774}
5775
5776/**
5777 * xmlRelaxNGValidateDocument:
5778 * @ctxt: a Relax-NG validation context
5779 * @doc: the document
5780 *
5781 * Validate the given document
5782 *
5783 * Returns 0 if the validation succeeded or an error code.
5784 */
5785static int
5786xmlRelaxNGValidateDocument(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
5787 int ret;
5788 xmlRelaxNGPtr schema;
5789 xmlRelaxNGGrammarPtr grammar;
5790 xmlRelaxNGValidStatePtr state;
5791
5792 if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL))
5793 return(-1);
5794
5795 schema = ctxt->schema;
5796 grammar = schema->topgrammar;
5797 if (grammar == NULL) {
5798 VALID_CTXT();
5799 VALID_ERROR("No top grammar defined\n");
5800 return(-1);
5801 }
5802 state = xmlRelaxNGNewValidState(ctxt, NULL);
5803 ctxt->state = state;
5804 ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start);
5805 state = ctxt->state;
5806 if ((state != NULL) && (state->seq != NULL)) {
5807 xmlNodePtr node;
5808
5809 node = state->seq;
5810 node = xmlRelaxNGSkipIgnored(ctxt, node);
5811 if (node != NULL) {
5812 VALID_CTXT();
5813 VALID_ERROR("extra data on the document\n");
5814 ret = -1;
5815 }
5816 }
5817 xmlRelaxNGFreeValidState(state);
5818
5819 return(ret);
5820}
5821
5822/************************************************************************
5823 * *
5824 * Validation interfaces *
5825 * *
5826 ************************************************************************/
5827/**
5828 * xmlRelaxNGNewValidCtxt:
5829 * @schema: a precompiled XML RelaxNGs
5830 *
5831 * Create an XML RelaxNGs validation context based on the given schema
5832 *
5833 * Returns the validation context or NULL in case of error
5834 */
5835xmlRelaxNGValidCtxtPtr
5836xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema) {
5837 xmlRelaxNGValidCtxtPtr ret;
5838
5839 ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt));
5840 if (ret == NULL) {
5841 xmlGenericError(xmlGenericErrorContext,
5842 "Failed to allocate new schama validation context\n");
5843 return (NULL);
5844 }
5845 memset(ret, 0, sizeof(xmlRelaxNGValidCtxt));
5846 ret->schema = schema;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005847 ret->error = xmlGenericError;
5848 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005849 return (ret);
5850}
5851
5852/**
5853 * xmlRelaxNGFreeValidCtxt:
5854 * @ctxt: the schema validation context
5855 *
5856 * Free the resources associated to the schema validation context
5857 */
5858void
5859xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt) {
5860 if (ctxt == NULL)
5861 return;
5862 xmlFree(ctxt);
5863}
5864
5865/**
5866 * xmlRelaxNGSetValidErrors:
5867 * @ctxt: a Relax-NG validation context
5868 * @err: the error function
5869 * @warn: the warning function
5870 * @ctx: the functions context
5871 *
5872 * Set the error and warning callback informations
5873 */
5874void
5875xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
5876 xmlRelaxNGValidityErrorFunc err,
5877 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
5878 if (ctxt == NULL)
5879 return;
5880 ctxt->error = err;
5881 ctxt->warning = warn;
5882 ctxt->userData = ctx;
5883}
5884
5885/**
5886 * xmlRelaxNGValidateDoc:
5887 * @ctxt: a Relax-NG validation context
5888 * @doc: a parsed document tree
5889 *
5890 * Validate a document tree in memory.
5891 *
5892 * Returns 0 if the document is valid, a positive error code
5893 * number otherwise and -1 in case of internal or API error.
5894 */
5895int
5896xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
5897 int ret;
5898
5899 if ((ctxt == NULL) || (doc == NULL))
5900 return(-1);
5901
5902 ctxt->doc = doc;
5903
5904 ret = xmlRelaxNGValidateDocument(ctxt, doc);
Daniel Veillard71531f32003-02-05 13:19:53 +00005905 /*
5906 * TODO: build error codes
5907 */
5908 if (ret == -1)
5909 return(1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005910 return(ret);
5911}
5912
5913#endif /* LIBXML_SCHEMAS_ENABLED */
5914