blob: 08b82d0f2f08fc4d8dafd3aa124986df70145652 [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);
353
354/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000355 * xmlRelaxNGFreeDocument:
356 * @docu: a document structure
357 *
358 * Deallocate a RelaxNG document structure.
359 */
360static void
361xmlRelaxNGFreeDocument(xmlRelaxNGDocumentPtr docu)
362{
363 if (docu == NULL)
364 return;
365
366 if (docu->href != NULL)
367 xmlFree(docu->href);
368 if (docu->doc != NULL)
369 xmlFreeDoc(docu->doc);
370 if (docu->schema != NULL)
371 xmlRelaxNGFree(docu->schema);
372 xmlFree(docu);
373}
374
375/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000376 * xmlRelaxNGFreeInclude:
377 * @incl: a include structure
378 *
379 * Deallocate a RelaxNG include structure.
380 */
381static void
382xmlRelaxNGFreeInclude(xmlRelaxNGIncludePtr incl)
383{
384 if (incl == NULL)
385 return;
386
387 if (incl->href != NULL)
388 xmlFree(incl->href);
389 if (incl->doc != NULL)
390 xmlFreeDoc(incl->doc);
391 if (incl->schema != NULL)
392 xmlRelaxNGFree(incl->schema);
393 xmlFree(incl);
394}
395
396/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000397 * xmlRelaxNGNewRelaxNG:
398 * @ctxt: a Relax-NG validation context (optional)
399 *
400 * Allocate a new RelaxNG structure.
401 *
402 * Returns the newly allocated structure or NULL in case or error
403 */
404static xmlRelaxNGPtr
405xmlRelaxNGNewRelaxNG(xmlRelaxNGParserCtxtPtr ctxt)
406{
407 xmlRelaxNGPtr ret;
408
409 ret = (xmlRelaxNGPtr) xmlMalloc(sizeof(xmlRelaxNG));
410 if (ret == NULL) {
411 if ((ctxt != NULL) && (ctxt->error != NULL))
412 ctxt->error(ctxt->userData, "Out of memory\n");
413 ctxt->nbErrors++;
414 return (NULL);
415 }
416 memset(ret, 0, sizeof(xmlRelaxNG));
417
418 return (ret);
419}
420
421/**
422 * xmlRelaxNGFree:
423 * @schema: a schema structure
424 *
425 * Deallocate a RelaxNG structure.
426 */
427void
428xmlRelaxNGFree(xmlRelaxNGPtr schema)
429{
430 if (schema == NULL)
431 return;
432
Daniel Veillard6eadf632003-01-23 18:29:16 +0000433 if (schema->topgrammar != NULL)
434 xmlRelaxNGFreeGrammar(schema->topgrammar);
435 if (schema->doc != NULL)
436 xmlFreeDoc(schema->doc);
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000437 if (schema->documents != NULL)
438 xmlHashFree(schema->documents, (xmlHashDeallocator)
439 xmlRelaxNGFreeDocument);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000440 if (schema->includes != NULL)
441 xmlHashFree(schema->includes, (xmlHashDeallocator)
442 xmlRelaxNGFreeInclude);
Daniel Veillard419a7682003-02-03 23:22:49 +0000443 if (schema->defTab != NULL) {
444 int i;
445
446 for (i = 0;i < schema->defNr;i++)
447 xmlRelaxNGFreeDefine(schema->defTab[i]);
448 xmlFree(schema->defTab);
449 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000450
451 xmlFree(schema);
452}
453
454/**
455 * xmlRelaxNGNewGrammar:
456 * @ctxt: a Relax-NG validation context (optional)
457 *
458 * Allocate a new RelaxNG grammar.
459 *
460 * Returns the newly allocated structure or NULL in case or error
461 */
462static xmlRelaxNGGrammarPtr
463xmlRelaxNGNewGrammar(xmlRelaxNGParserCtxtPtr ctxt)
464{
465 xmlRelaxNGGrammarPtr ret;
466
467 ret = (xmlRelaxNGGrammarPtr) xmlMalloc(sizeof(xmlRelaxNGGrammar));
468 if (ret == NULL) {
469 if ((ctxt != NULL) && (ctxt->error != NULL))
470 ctxt->error(ctxt->userData, "Out of memory\n");
471 ctxt->nbErrors++;
472 return (NULL);
473 }
474 memset(ret, 0, sizeof(xmlRelaxNGGrammar));
475
476 return (ret);
477}
478
479/**
480 * xmlRelaxNGFreeGrammar:
481 * @grammar: a grammar structure
482 *
483 * Deallocate a RelaxNG grammar structure.
484 */
485static void
486xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar)
487{
488 if (grammar == NULL)
489 return;
490
Daniel Veillard419a7682003-02-03 23:22:49 +0000491 if (grammar->next != NULL) {
492 xmlRelaxNGFreeGrammar(grammar->next);
493 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000494 if (grammar->refs != NULL) {
495 xmlHashFree(grammar->refs, NULL);
496 }
497 if (grammar->defs != NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000498 xmlHashFree(grammar->defs, NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000499 }
500
501 xmlFree(grammar);
502}
503
504/**
505 * xmlRelaxNGNewDefine:
506 * @ctxt: a Relax-NG validation context
507 * @node: the node in the input document.
508 *
509 * Allocate a new RelaxNG define.
510 *
511 * Returns the newly allocated structure or NULL in case or error
512 */
513static xmlRelaxNGDefinePtr
514xmlRelaxNGNewDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
515{
516 xmlRelaxNGDefinePtr ret;
517
Daniel Veillard419a7682003-02-03 23:22:49 +0000518 if (ctxt->defMax == 0) {
519 ctxt->defMax = 16;
520 ctxt->defNr = 0;
521 ctxt->defTab = (xmlRelaxNGDefinePtr *)
522 xmlMalloc(ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
523 if (ctxt->defTab == NULL) {
524 if ((ctxt != NULL) && (ctxt->error != NULL))
525 ctxt->error(ctxt->userData, "Out of memory\n");
526 ctxt->nbErrors++;
527 return (NULL);
528 }
529 } else if (ctxt->defMax <= ctxt->defNr) {
530 xmlRelaxNGDefinePtr *tmp;
531 ctxt->defMax *= 2;
532 tmp = (xmlRelaxNGDefinePtr *) xmlRealloc(ctxt->defTab,
533 ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
534 if (tmp == NULL) {
535 if ((ctxt != NULL) && (ctxt->error != NULL))
536 ctxt->error(ctxt->userData, "Out of memory\n");
537 ctxt->nbErrors++;
538 return (NULL);
539 }
540 ctxt->defTab = tmp;
541 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000542 ret = (xmlRelaxNGDefinePtr) xmlMalloc(sizeof(xmlRelaxNGDefine));
543 if (ret == NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000544 if ((ctxt != NULL) && (ctxt->error != NULL))
545 ctxt->error(ctxt->userData, "Out of memory\n");
Daniel Veillard6eadf632003-01-23 18:29:16 +0000546 ctxt->nbErrors++;
Daniel Veillard419a7682003-02-03 23:22:49 +0000547 return(NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000548 }
549 memset(ret, 0, sizeof(xmlRelaxNGDefine));
Daniel Veillard419a7682003-02-03 23:22:49 +0000550 ctxt->defTab[ctxt->defNr++] = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000551 ret->node = node;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000552 return (ret);
553}
554
555/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000556 * xmlRelaxNGFreePartition:
557 * @partitions: a partition set structure
558 *
559 * Deallocate RelaxNG partition set structures.
560 */
561static void
562xmlRelaxNGFreePartition(xmlRelaxNGPartitionPtr partitions) {
563 xmlRelaxNGInterleaveGroupPtr group;
564 int j;
565
566 if (partitions != NULL) {
567 if (partitions->groups != NULL) {
568 for (j = 0;j < partitions->nbgroups;j++) {
569 group = partitions->groups[j];
570 if (group != NULL) {
571 if (group->defs != NULL)
572 xmlFree(group->defs);
573 xmlFree(group);
574 }
575 }
576 xmlFree(partitions->groups);
577 }
578 xmlFree(partitions);
579 }
580}
581/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000582 * xmlRelaxNGFreeDefine:
583 * @define: a define structure
584 *
585 * Deallocate a RelaxNG define structure.
586 */
587static void
588xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define)
589{
590 if (define == NULL)
591 return;
592
Daniel Veillard419a7682003-02-03 23:22:49 +0000593 if ((define->data != NULL) &&
594 (define->type == XML_RELAXNG_INTERLEAVE))
595 xmlRelaxNGFreePartition((xmlRelaxNGPartitionPtr) define->data);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000596 if (define->name != NULL)
597 xmlFree(define->name);
598 if (define->ns != NULL)
599 xmlFree(define->ns);
Daniel Veillardedc91922003-01-26 00:52:04 +0000600 if (define->value != NULL)
601 xmlFree(define->value);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000602 xmlFree(define);
603}
604
605/**
606 * xmlRelaxNGNewValidState:
607 * @ctxt: a Relax-NG validation context
608 * @node: the current node or NULL for the document
609 *
610 * Allocate a new RelaxNG validation state
611 *
612 * Returns the newly allocated structure or NULL in case or error
613 */
614static xmlRelaxNGValidStatePtr
615xmlRelaxNGNewValidState(xmlRelaxNGValidCtxtPtr ctxt, xmlNodePtr node)
616{
617 xmlRelaxNGValidStatePtr ret;
618 xmlAttrPtr attr;
619 xmlAttrPtr attrs[MAX_ATTR];
620 int nbAttrs = 0;
621 xmlNodePtr root = NULL;
622
623 if (node == NULL) {
624 root = xmlDocGetRootElement(ctxt->doc);
625 if (root == NULL)
626 return(NULL);
627 } else {
628 attr = node->properties;
629 while (attr != NULL) {
630 if (nbAttrs < MAX_ATTR)
631 attrs[nbAttrs++] = attr;
632 else
633 nbAttrs++;
634 attr = attr->next;
635 }
636 }
637
638 if (nbAttrs < MAX_ATTR)
639 attrs[nbAttrs] = NULL;
640 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(sizeof(xmlRelaxNGValidState) +
641 nbAttrs * sizeof(xmlAttrPtr));
642 if (ret == NULL) {
643 if ((ctxt != NULL) && (ctxt->error != NULL))
644 ctxt->error(ctxt->userData, "Out of memory\n");
645 return (NULL);
646 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +0000647 ret->value = NULL;
648 ret->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000649 if (node == NULL) {
650 ret->node = (xmlNodePtr) ctxt->doc;
651 ret->seq = root;
652 ret->nbAttrs = 0;
653 } else {
654 ret->node = node;
655 ret->seq = node->children;
656 ret->nbAttrs = nbAttrs;
657 if (nbAttrs > 0) {
658 if (nbAttrs < MAX_ATTR) {
659 memcpy(&(ret->attrs[0]), attrs,
660 sizeof(xmlAttrPtr) * (nbAttrs + 1));
661 } else {
662 attr = node->properties;
663 nbAttrs = 0;
664 while (attr != NULL) {
665 ret->attrs[nbAttrs++] = attr;
666 attr = attr->next;
667 }
668 ret->attrs[nbAttrs] = NULL;
669 }
670 }
671 }
Daniel Veillard1ed7f362003-02-03 10:57:45 +0000672 ret->nbAttrLeft = ret->nbAttrs;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000673 return (ret);
674}
675
676/**
677 * xmlRelaxNGCopyValidState:
678 * @ctxt: a Relax-NG validation context
679 * @state: a validation state
680 *
681 * Copy the validation state
682 *
683 * Returns the newly allocated structure or NULL in case or error
684 */
685static xmlRelaxNGValidStatePtr
686xmlRelaxNGCopyValidState(xmlRelaxNGValidCtxtPtr ctxt,
687 xmlRelaxNGValidStatePtr state)
688{
689 xmlRelaxNGValidStatePtr ret;
690 unsigned int size;
691
692 if (state == NULL)
693 return(NULL);
694
695 size = sizeof(xmlRelaxNGValidState) +
696 state->nbAttrs * sizeof(xmlAttrPtr);
697 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(size);
698 if (ret == NULL) {
699 if ((ctxt != NULL) && (ctxt->error != NULL))
700 ctxt->error(ctxt->userData, "Out of memory\n");
701 return (NULL);
702 }
703 memcpy(ret, state, size);
704 return(ret);
705}
706
707/**
708 * xmlRelaxNGFreeValidState:
709 * @state: a validation state structure
710 *
711 * Deallocate a RelaxNG validation state structure.
712 */
713static void
714xmlRelaxNGFreeValidState(xmlRelaxNGValidStatePtr state)
715{
716 if (state == NULL)
717 return;
718
719 xmlFree(state);
720}
721
722/************************************************************************
723 * *
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000724 * Document functions *
725 * *
726 ************************************************************************/
727static xmlDocPtr xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt,
728 xmlDocPtr doc);
729
730/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000731 * xmlRelaxNGIncludePush:
732 * @ctxt: the parser context
733 * @value: the element doc
734 *
735 * Pushes a new include on top of the include stack
736 *
737 * Returns 0 in case of error, the index in the stack otherwise
738 */
739static int
740xmlRelaxNGIncludePush(xmlRelaxNGParserCtxtPtr ctxt,
741 xmlRelaxNGIncludePtr value)
742{
743 if (ctxt->incTab == NULL) {
744 ctxt->incMax = 4;
745 ctxt->incNr = 0;
746 ctxt->incTab = (xmlRelaxNGIncludePtr *) xmlMalloc(
747 ctxt->incMax * sizeof(ctxt->incTab[0]));
748 if (ctxt->incTab == NULL) {
749 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
750 return (0);
751 }
752 }
753 if (ctxt->incNr >= ctxt->incMax) {
754 ctxt->incMax *= 2;
755 ctxt->incTab =
756 (xmlRelaxNGIncludePtr *) xmlRealloc(ctxt->incTab,
757 ctxt->incMax *
758 sizeof(ctxt->incTab[0]));
759 if (ctxt->incTab == NULL) {
760 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
761 return (0);
762 }
763 }
764 ctxt->incTab[ctxt->incNr] = value;
765 ctxt->inc = value;
766 return (ctxt->incNr++);
767}
768
769/**
770 * xmlRelaxNGIncludePop:
771 * @ctxt: the parser context
772 *
773 * Pops the top include from the include stack
774 *
775 * Returns the include just removed
776 */
777static xmlRelaxNGIncludePtr
778xmlRelaxNGIncludePop(xmlRelaxNGParserCtxtPtr ctxt)
779{
780 xmlRelaxNGIncludePtr ret;
781
782 if (ctxt->incNr <= 0)
783 return (0);
784 ctxt->incNr--;
785 if (ctxt->incNr > 0)
786 ctxt->inc = ctxt->incTab[ctxt->incNr - 1];
787 else
788 ctxt->inc = NULL;
789 ret = ctxt->incTab[ctxt->incNr];
790 ctxt->incTab[ctxt->incNr] = 0;
791 return (ret);
792}
793
794/**
795 * xmlRelaxNGLoadInclude:
796 * @ctxt: the parser context
797 * @URL: the normalized URL
798 * @node: the include node.
799 *
800 * First lookup if the document is already loaded into the parser context,
801 * check against recursion. If not found the resource is loaded and
802 * the content is preprocessed before being returned back to the caller.
803 *
804 * Returns the xmlRelaxNGIncludePtr or NULL in case of error
805 */
806static xmlRelaxNGIncludePtr
807xmlRelaxNGLoadInclude(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
808 xmlNodePtr node) {
809 xmlRelaxNGIncludePtr ret = NULL;
810 xmlDocPtr doc;
811 int i;
812 xmlNodePtr root, tmp, tmp2, cur;
813
814 /*
815 * check against recursion in the stack
816 */
817 for (i = 0;i < ctxt->incNr;i++) {
818 if (xmlStrEqual(ctxt->incTab[i]->href, URL)) {
819 if (ctxt->error != NULL)
820 ctxt->error(ctxt->userData,
821 "Detected an externalRef recursion for %s\n",
822 URL);
823 ctxt->nbErrors++;
824 return(NULL);
825 }
826 }
827
828 /*
829 * Lookup in the hash table
830 */
831 if (ctxt->includes == NULL) {
832 ctxt->includes = xmlHashCreate(10);
833 if (ctxt->includes == NULL) {
834 if (ctxt->error != NULL)
835 ctxt->error(ctxt->userData,
836 "Failed to allocate hash table for document\n");
837 ctxt->nbErrors++;
838 return(NULL);
839 }
840 } else {
841 ret = xmlHashLookup(ctxt->includes, URL);
842 if (ret != NULL)
843 return(ret);
844 }
845
846
847 /*
848 * load the document
849 */
850 doc = xmlParseFile((const char *) URL);
851 if (doc == NULL) {
852 if (ctxt->error != NULL)
853 ctxt->error(ctxt->userData,
854 "xmlRelaxNG: could not load %s\n", URL);
855 ctxt->nbErrors++;
856 return (NULL);
857 }
858
859 /*
860 * Allocate the document structures and register it first.
861 */
862 ret = (xmlRelaxNGIncludePtr) xmlMalloc(sizeof(xmlRelaxNGInclude));
863 if (ret == NULL) {
864 if (ctxt->error != NULL)
865 ctxt->error(ctxt->userData,
866 "xmlRelaxNG: allocate memory for doc %s\n", URL);
867 ctxt->nbErrors++;
868 xmlFreeDoc(doc);
869 return (NULL);
870 }
871 memset(ret, 0, sizeof(xmlRelaxNGInclude));
872 ret->doc = doc;
873 ret->href = xmlStrdup(URL);
874
875 /*
876 * push it on the stack and register it in the hash table
877 */
878 xmlHashAddEntry(ctxt->includes, URL, ret);
879 xmlRelaxNGIncludePush(ctxt, ret);
880
881 /*
882 * Some preprocessing of the document content, this include recursing
883 * in the include stack.
884 */
885 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
886 if (doc == NULL) {
887 /* xmlFreeDoc(ctxt->include); */
888 ctxt->inc = NULL;
889 return(NULL);
890 }
891
892 /*
893 * Pop up the include from the stack
894 */
895 xmlRelaxNGIncludePop(ctxt);
896
897 /*
898 * Check that the top element is a grammar
899 */
900 root = xmlDocGetRootElement(doc);
901 if (root == NULL) {
902 if (ctxt->error != NULL)
903 ctxt->error(ctxt->userData,
904 "xmlRelaxNG: included document is empty %s\n", URL);
905 ctxt->nbErrors++;
906 xmlFreeDoc(doc);
907 return (NULL);
908 }
909 if (!IS_RELAXNG(root, "grammar")) {
910 if (ctxt->error != NULL)
911 ctxt->error(ctxt->userData,
912 "xmlRelaxNG: included document %s root is not a grammar\n",
913 URL);
914 ctxt->nbErrors++;
915 xmlFreeDoc(doc);
916 return (NULL);
917 }
918
919 /*
920 * Elimination of redefined rules in the include.
921 */
922 cur = node->children;
923 while (cur != NULL) {
924 if (IS_RELAXNG(cur, "start")) {
925 int found = 0;
926
927 tmp = root->children;
928 while (tmp != NULL) {
929 tmp2 = tmp->next;
930 if (IS_RELAXNG(tmp, "start")) {
931 found = 1;
932 xmlUnlinkNode(tmp);
933 xmlFreeNode(tmp);
934 }
935 tmp = tmp2;
936 }
937 if (!found) {
938 if (ctxt->error != NULL)
939 ctxt->error(ctxt->userData,
940 "xmlRelaxNG: include %s has a start but not the included grammar\n",
941 URL);
942 ctxt->nbErrors++;
943 }
944 } else if (IS_RELAXNG(cur, "define")) {
945 xmlChar *name, *name2;
946
947 name = xmlGetProp(cur, BAD_CAST "name");
948 if (name == NULL) {
949 if (ctxt->error != NULL)
950 ctxt->error(ctxt->userData,
951 "xmlRelaxNG: include %s has define without name\n",
952 URL);
953 ctxt->nbErrors++;
954 } else {
955 int found = 0;
956
957 tmp = root->children;
958 while (tmp != NULL) {
959 tmp2 = tmp->next;
960 if (IS_RELAXNG(tmp, "define")) {
961 name2 = xmlGetProp(tmp, BAD_CAST "name");
962 if (name2 != NULL) {
963 if (xmlStrEqual(name, name2)) {
964 found = 1;
965 xmlUnlinkNode(tmp);
966 xmlFreeNode(tmp);
967 }
968 xmlFree(name2);
969 }
970 }
971 tmp = tmp2;
972 }
973 if (!found) {
974 if (ctxt->error != NULL)
975 ctxt->error(ctxt->userData,
976 "xmlRelaxNG: include %s has a define %s but not the included grammar\n",
977 URL, name);
978 ctxt->nbErrors++;
979 }
980 xmlFree(name);
981 }
982 }
983 cur = cur->next;
984 }
985
986
987 return(ret);
988}
989
990/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000991 * xmlRelaxNGDocumentPush:
992 * @ctxt: the parser context
993 * @value: the element doc
994 *
995 * Pushes a new doc on top of the doc stack
996 *
997 * Returns 0 in case of error, the index in the stack otherwise
998 */
999static int
1000xmlRelaxNGDocumentPush(xmlRelaxNGParserCtxtPtr ctxt,
1001 xmlRelaxNGDocumentPtr value)
1002{
1003 if (ctxt->docTab == NULL) {
1004 ctxt->docMax = 4;
1005 ctxt->docNr = 0;
1006 ctxt->docTab = (xmlRelaxNGDocumentPtr *) xmlMalloc(
1007 ctxt->docMax * sizeof(ctxt->docTab[0]));
1008 if (ctxt->docTab == NULL) {
1009 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
1010 return (0);
1011 }
1012 }
1013 if (ctxt->docNr >= ctxt->docMax) {
1014 ctxt->docMax *= 2;
1015 ctxt->docTab =
1016 (xmlRelaxNGDocumentPtr *) xmlRealloc(ctxt->docTab,
1017 ctxt->docMax *
1018 sizeof(ctxt->docTab[0]));
1019 if (ctxt->docTab == NULL) {
1020 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
1021 return (0);
1022 }
1023 }
1024 ctxt->docTab[ctxt->docNr] = value;
1025 ctxt->doc = value;
1026 return (ctxt->docNr++);
1027}
1028
1029/**
1030 * xmlRelaxNGDocumentPop:
1031 * @ctxt: the parser context
1032 *
1033 * Pops the top doc from the doc stack
1034 *
1035 * Returns the doc just removed
1036 */
1037static xmlRelaxNGDocumentPtr
1038xmlRelaxNGDocumentPop(xmlRelaxNGParserCtxtPtr ctxt)
1039{
1040 xmlRelaxNGDocumentPtr ret;
1041
1042 if (ctxt->docNr <= 0)
1043 return (0);
1044 ctxt->docNr--;
1045 if (ctxt->docNr > 0)
1046 ctxt->doc = ctxt->docTab[ctxt->docNr - 1];
1047 else
1048 ctxt->doc = NULL;
1049 ret = ctxt->docTab[ctxt->docNr];
1050 ctxt->docTab[ctxt->docNr] = 0;
1051 return (ret);
1052}
1053
1054/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001055 * xmlRelaxNGLoadExternalRef:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001056 * @ctxt: the parser context
1057 * @URL: the normalized URL
1058 * @ns: the inherited ns if any
1059 *
1060 * First lookup if the document is already loaded into the parser context,
1061 * check against recursion. If not found the resource is loaded and
1062 * the content is preprocessed before being returned back to the caller.
1063 *
1064 * Returns the xmlRelaxNGDocumentPtr or NULL in case of error
1065 */
1066static xmlRelaxNGDocumentPtr
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001067xmlRelaxNGLoadExternalRef(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001068 const xmlChar *ns) {
1069 xmlRelaxNGDocumentPtr ret = NULL;
1070 xmlDocPtr doc;
1071 xmlNodePtr root;
1072 int i;
1073
1074 /*
1075 * check against recursion in the stack
1076 */
1077 for (i = 0;i < ctxt->docNr;i++) {
1078 if (xmlStrEqual(ctxt->docTab[i]->href, URL)) {
1079 if (ctxt->error != NULL)
1080 ctxt->error(ctxt->userData,
1081 "Detected an externalRef recursion for %s\n",
1082 URL);
1083 ctxt->nbErrors++;
1084 return(NULL);
1085 }
1086 }
1087
1088 /*
1089 * Lookup in the hash table
1090 */
1091 if (ctxt->documents == NULL) {
1092 ctxt->documents = xmlHashCreate(10);
1093 if (ctxt->documents == NULL) {
1094 if (ctxt->error != NULL)
1095 ctxt->error(ctxt->userData,
1096 "Failed to allocate hash table for document\n");
1097 ctxt->nbErrors++;
1098 return(NULL);
1099 }
1100 } else {
1101 if (ns == NULL)
1102 ret = xmlHashLookup2(ctxt->documents, BAD_CAST "", URL);
1103 else
1104 ret = xmlHashLookup2(ctxt->documents, ns, URL);
1105 if (ret != NULL)
1106 return(ret);
1107 }
1108
1109
1110 /*
1111 * load the document
1112 */
1113 doc = xmlParseFile((const char *) URL);
1114 if (doc == NULL) {
1115 if (ctxt->error != NULL)
1116 ctxt->error(ctxt->userData,
1117 "xmlRelaxNG: could not load %s\n", URL);
1118 ctxt->nbErrors++;
1119 return (NULL);
1120 }
1121
1122 /*
1123 * Allocate the document structures and register it first.
1124 */
1125 ret = (xmlRelaxNGDocumentPtr) xmlMalloc(sizeof(xmlRelaxNGDocument));
1126 if (ret == NULL) {
1127 if (ctxt->error != NULL)
1128 ctxt->error(ctxt->userData,
1129 "xmlRelaxNG: allocate memory for doc %s\n", URL);
1130 ctxt->nbErrors++;
1131 xmlFreeDoc(doc);
1132 return (NULL);
1133 }
1134 memset(ret, 0, sizeof(xmlRelaxNGDocument));
1135 ret->doc = doc;
1136 ret->href = xmlStrdup(URL);
1137
1138 /*
1139 * transmit the ns if needed
1140 */
1141 if (ns != NULL) {
1142 root = xmlDocGetRootElement(doc);
1143 if (root != NULL) {
1144 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
1145 xmlSetProp(root, BAD_CAST"ns", ns);
1146 }
1147 }
1148 }
1149
1150 /*
1151 * push it on the stack and register it in the hash table
1152 */
1153 if (ns == NULL)
1154 xmlHashAddEntry2(ctxt->documents, BAD_CAST "", URL, ret);
1155 else
1156 xmlHashAddEntry2(ctxt->documents, ns, URL, ret);
1157 xmlRelaxNGDocumentPush(ctxt, ret);
1158
1159 /*
1160 * Some preprocessing of the document content
1161 */
1162 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
1163 if (doc == NULL) {
1164 xmlFreeDoc(ctxt->document);
1165 ctxt->doc = NULL;
1166 return(NULL);
1167 }
1168
1169 xmlRelaxNGDocumentPop(ctxt);
1170
1171 return(ret);
1172}
1173
1174/************************************************************************
1175 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +00001176 * Error functions *
1177 * *
1178 ************************************************************************/
1179
1180#define VALID_CTXT() \
Daniel Veillard231d7912003-02-09 14:22:17 +00001181 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1182 xmlGenericError(xmlGenericErrorContext, \
Daniel Veillard6eadf632003-01-23 18:29:16 +00001183 "error detected at %s:%d\n", \
1184 __FILE__, __LINE__);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001185
1186#define VALID_ERROR(a) \
Daniel Veillard231d7912003-02-09 14:22:17 +00001187 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001188 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a)
1189#define VALID_ERROR2(a, b) \
1190 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1191 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a, b)
1192#define VALID_ERROR3(a, b, c) \
1193 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1194 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a, b, c)
Daniel Veillard6eadf632003-01-23 18:29:16 +00001195
Daniel Veillard231d7912003-02-09 14:22:17 +00001196static const char *
1197xmlRelaxNGDefName(xmlRelaxNGDefinePtr def) {
1198 if (def == NULL)
1199 return("none");
1200 switch(def->type) {
1201 case XML_RELAXNG_EMPTY: return("empty");
1202 case XML_RELAXNG_NOT_ALLOWED: return("notAllowed");
1203 case XML_RELAXNG_EXCEPT: return("except");
1204 case XML_RELAXNG_TEXT: return("text");
1205 case XML_RELAXNG_ELEMENT: return("element");
1206 case XML_RELAXNG_DATATYPE: return("datatype");
1207 case XML_RELAXNG_VALUE: return("value");
1208 case XML_RELAXNG_LIST: return("list");
1209 case XML_RELAXNG_ATTRIBUTE: return("attribute");
1210 case XML_RELAXNG_DEF: return("def");
1211 case XML_RELAXNG_REF: return("ref");
1212 case XML_RELAXNG_EXTERNALREF: return("externalRef");
1213 case XML_RELAXNG_PARENTREF: return("parentRef");
1214 case XML_RELAXNG_OPTIONAL: return("optional");
1215 case XML_RELAXNG_ZEROORMORE: return("zeroOrMore");
1216 case XML_RELAXNG_ONEORMORE: return("oneOrMore");
1217 case XML_RELAXNG_CHOICE: return("choice");
1218 case XML_RELAXNG_GROUP: return("group");
1219 case XML_RELAXNG_INTERLEAVE: return("interleave");
1220 case XML_RELAXNG_START: return("start");
1221 }
1222 return("unknown");
1223}
Daniel Veillard6eadf632003-01-23 18:29:16 +00001224#if 0
1225/**
1226 * xmlRelaxNGErrorContext:
1227 * @ctxt: the parsing context
1228 * @schema: the schema being built
1229 * @node: the node being processed
1230 * @child: the child being processed
1231 *
1232 * Dump a RelaxNGType structure
1233 */
1234static void
1235xmlRelaxNGErrorContext(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGPtr schema,
1236 xmlNodePtr node, xmlNodePtr child)
1237{
1238 int line = 0;
1239 const xmlChar *file = NULL;
1240 const xmlChar *name = NULL;
1241 const char *type = "error";
1242
1243 if ((ctxt == NULL) || (ctxt->error == NULL))
1244 return;
1245
1246 if (child != NULL)
1247 node = child;
1248
1249 if (node != NULL) {
1250 if ((node->type == XML_DOCUMENT_NODE) ||
1251 (node->type == XML_HTML_DOCUMENT_NODE)) {
1252 xmlDocPtr doc = (xmlDocPtr) node;
1253
1254 file = doc->URL;
1255 } else {
1256 /*
1257 * Try to find contextual informations to report
1258 */
1259 if (node->type == XML_ELEMENT_NODE) {
1260 line = (int) node->content;
1261 } else if ((node->prev != NULL) &&
1262 (node->prev->type == XML_ELEMENT_NODE)) {
1263 line = (int) node->prev->content;
1264 } else if ((node->parent != NULL) &&
1265 (node->parent->type == XML_ELEMENT_NODE)) {
1266 line = (int) node->parent->content;
1267 }
1268 if ((node->doc != NULL) && (node->doc->URL != NULL))
1269 file = node->doc->URL;
1270 if (node->name != NULL)
1271 name = node->name;
1272 }
1273 }
1274
1275 if (ctxt != NULL)
1276 type = "compilation error";
1277 else if (schema != NULL)
1278 type = "runtime error";
1279
1280 if ((file != NULL) && (line != 0) && (name != NULL))
1281 ctxt->error(ctxt->userData, "%s: file %s line %d element %s\n",
1282 type, file, line, name);
1283 else if ((file != NULL) && (name != NULL))
1284 ctxt->error(ctxt->userData, "%s: file %s element %s\n",
1285 type, file, name);
1286 else if ((file != NULL) && (line != 0))
1287 ctxt->error(ctxt->userData, "%s: file %s line %d\n", type, file, line);
1288 else if (file != NULL)
1289 ctxt->error(ctxt->userData, "%s: file %s\n", type, file);
1290 else if (name != NULL)
1291 ctxt->error(ctxt->userData, "%s: element %s\n", type, name);
1292 else
1293 ctxt->error(ctxt->userData, "%s\n", type);
1294}
1295#endif
1296
1297/************************************************************************
1298 * *
1299 * Type library hooks *
1300 * *
1301 ************************************************************************/
Daniel Veillardea3f3982003-01-26 19:45:18 +00001302static xmlChar *xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt,
1303 const xmlChar *str);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001304
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001305/**
1306 * xmlRelaxNGSchemaTypeHave:
1307 * @data: data needed for the library
1308 * @type: the type name
1309 *
1310 * Check if the given type is provided by
1311 * the W3C XMLSchema Datatype library.
1312 *
1313 * Returns 1 if yes, 0 if no and -1 in case of error.
1314 */
1315static int
1316xmlRelaxNGSchemaTypeHave(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001317 const xmlChar *type) {
1318 xmlSchemaTypePtr typ;
1319
1320 if (type == NULL)
1321 return(-1);
1322 typ = xmlSchemaGetPredefinedType(type,
1323 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1324 if (typ == NULL)
1325 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001326 return(1);
1327}
1328
1329/**
1330 * xmlRelaxNGSchemaTypeCheck:
1331 * @data: data needed for the library
1332 * @type: the type name
1333 * @value: the value to check
1334 *
1335 * Check if the given type and value are validated by
1336 * the W3C XMLSchema Datatype library.
1337 *
1338 * Returns 1 if yes, 0 if no and -1 in case of error.
1339 */
1340static int
1341xmlRelaxNGSchemaTypeCheck(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001342 const xmlChar *type,
1343 const xmlChar *value) {
1344 xmlSchemaTypePtr typ;
1345 int ret;
1346
1347 /*
1348 * TODO: the type should be cached ab provided back, interface subject
1349 * to changes.
1350 * TODO: handle facets, may require an additional interface and keep
1351 * the value returned from the validation.
1352 */
1353 if ((type == NULL) || (value == NULL))
1354 return(-1);
1355 typ = xmlSchemaGetPredefinedType(type,
1356 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1357 if (typ == NULL)
1358 return(-1);
1359 ret = xmlSchemaValidatePredefinedType(typ, value, NULL);
1360 if (ret == 0)
1361 return(1);
1362 if (ret > 0)
1363 return(0);
1364 return(-1);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001365}
1366
1367/**
1368 * xmlRelaxNGSchemaTypeCompare:
1369 * @data: data needed for the library
1370 * @type: the type name
1371 * @value1: the first value
1372 * @value2: the second value
1373 *
1374 * Compare two values accordingly a type from the W3C XMLSchema
1375 * Datatype library.
1376 *
1377 * Returns 1 if yes, 0 if no and -1 in case of error.
1378 */
1379static int
1380xmlRelaxNGSchemaTypeCompare(void *data ATTRIBUTE_UNUSED,
1381 const xmlChar *type ATTRIBUTE_UNUSED,
1382 const xmlChar *value1 ATTRIBUTE_UNUSED,
1383 const xmlChar *value2 ATTRIBUTE_UNUSED) {
1384 TODO
1385 return(1);
1386}
1387
1388/**
1389 * xmlRelaxNGDefaultTypeHave:
1390 * @data: data needed for the library
1391 * @type: the type name
1392 *
1393 * Check if the given type is provided by
1394 * the default datatype library.
1395 *
1396 * Returns 1 if yes, 0 if no and -1 in case of error.
1397 */
1398static int
1399xmlRelaxNGDefaultTypeHave(void *data ATTRIBUTE_UNUSED, const xmlChar *type) {
1400 if (type == NULL)
1401 return(-1);
1402 if (xmlStrEqual(type, BAD_CAST "string"))
1403 return(1);
1404 if (xmlStrEqual(type, BAD_CAST "token"))
1405 return(1);
1406 return(0);
1407}
1408
1409/**
1410 * xmlRelaxNGDefaultTypeCheck:
1411 * @data: data needed for the library
1412 * @type: the type name
1413 * @value: the value to check
1414 *
1415 * Check if the given type and value are validated by
1416 * the default datatype library.
1417 *
1418 * Returns 1 if yes, 0 if no and -1 in case of error.
1419 */
1420static int
1421xmlRelaxNGDefaultTypeCheck(void *data ATTRIBUTE_UNUSED,
1422 const xmlChar *type ATTRIBUTE_UNUSED,
1423 const xmlChar *value ATTRIBUTE_UNUSED) {
1424 return(1);
1425}
1426
1427/**
1428 * xmlRelaxNGDefaultTypeCompare:
1429 * @data: data needed for the library
1430 * @type: the type name
1431 * @value1: the first value
1432 * @value2: the second value
1433 *
1434 * Compare two values accordingly a type from the default
1435 * datatype library.
1436 *
1437 * Returns 1 if yes, 0 if no and -1 in case of error.
1438 */
1439static int
1440xmlRelaxNGDefaultTypeCompare(void *data ATTRIBUTE_UNUSED,
1441 const xmlChar *type ATTRIBUTE_UNUSED,
1442 const xmlChar *value1 ATTRIBUTE_UNUSED,
1443 const xmlChar *value2 ATTRIBUTE_UNUSED) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00001444 int ret = -1;
1445
1446 if (xmlStrEqual(type, BAD_CAST "string")) {
1447 ret = xmlStrEqual(value1, value2);
1448 } else if (xmlStrEqual(type, BAD_CAST "token")) {
1449 if (!xmlStrEqual(value1, value2)) {
1450 xmlChar *nval, *nvalue;
1451
1452 /*
1453 * TODO: trivial optimizations are possible by
1454 * computing at compile-time
1455 */
1456 nval = xmlRelaxNGNormalize(NULL, value1);
1457 nvalue = xmlRelaxNGNormalize(NULL, value2);
1458
1459 if ((nval == NULL) || (nvalue == NULL) ||
1460 (!xmlStrEqual(nval, nvalue)))
1461 ret = -1;
1462 if (nval != NULL)
1463 xmlFree(nval);
1464 if (nvalue != NULL)
1465 xmlFree(nvalue);
1466 }
1467 }
1468 return(ret);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001469}
1470
1471static int xmlRelaxNGTypeInitialized = 0;
1472static xmlHashTablePtr xmlRelaxNGRegisteredTypes = NULL;
1473
1474/**
1475 * xmlRelaxNGFreeTypeLibrary:
1476 * @lib: the type library structure
1477 * @namespace: the URI bound to the library
1478 *
1479 * Free the structure associated to the type library
1480 */
Daniel Veillard6eadf632003-01-23 18:29:16 +00001481static void
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001482xmlRelaxNGFreeTypeLibrary(xmlRelaxNGTypeLibraryPtr lib,
1483 const xmlChar *namespace ATTRIBUTE_UNUSED) {
1484 if (lib == NULL)
1485 return;
1486 if (lib->namespace != NULL)
1487 xmlFree((xmlChar *)lib->namespace);
1488 xmlFree(lib);
1489}
1490
1491/**
1492 * xmlRelaxNGRegisterTypeLibrary:
1493 * @namespace: the URI bound to the library
1494 * @data: data associated to the library
1495 * @have: the provide function
1496 * @check: the checking function
1497 * @comp: the comparison function
1498 *
1499 * Register a new type library
1500 *
1501 * Returns 0 in case of success and -1 in case of error.
1502 */
1503static int
1504xmlRelaxNGRegisterTypeLibrary(const xmlChar *namespace, void *data,
1505 xmlRelaxNGTypeHave have, xmlRelaxNGTypeCheck check,
1506 xmlRelaxNGTypeCompare comp) {
1507 xmlRelaxNGTypeLibraryPtr lib;
1508 int ret;
1509
1510 if ((xmlRelaxNGRegisteredTypes == NULL) || (namespace == NULL) ||
1511 (check == NULL) || (comp == NULL))
1512 return(-1);
1513 if (xmlHashLookup(xmlRelaxNGRegisteredTypes, namespace) != NULL) {
1514 xmlGenericError(xmlGenericErrorContext,
1515 "Relax-NG types library '%s' already registered\n",
1516 namespace);
1517 return(-1);
1518 }
1519 lib = (xmlRelaxNGTypeLibraryPtr) xmlMalloc(sizeof(xmlRelaxNGTypeLibrary));
1520 if (lib == NULL) {
1521 xmlGenericError(xmlGenericErrorContext,
1522 "Relax-NG types library '%s' malloc() failed\n",
1523 namespace);
1524 return (-1);
1525 }
1526 memset(lib, 0, sizeof(xmlRelaxNGTypeLibrary));
1527 lib->namespace = xmlStrdup(namespace);
1528 lib->data = data;
1529 lib->have = have;
1530 lib->comp = comp;
1531 lib->check = check;
1532 ret = xmlHashAddEntry(xmlRelaxNGRegisteredTypes, namespace, lib);
1533 if (ret < 0) {
1534 xmlGenericError(xmlGenericErrorContext,
1535 "Relax-NG types library failed to register '%s'\n",
1536 namespace);
1537 xmlRelaxNGFreeTypeLibrary(lib, namespace);
1538 return(-1);
1539 }
1540 return(0);
1541}
1542
1543/**
1544 * xmlRelaxNGInitTypes:
1545 *
1546 * Initilize the default type libraries.
1547 *
1548 * Returns 0 in case of success and -1 in case of error.
1549 */
1550static int
Daniel Veillard6eadf632003-01-23 18:29:16 +00001551xmlRelaxNGInitTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001552 if (xmlRelaxNGTypeInitialized != 0)
1553 return(0);
1554 xmlRelaxNGRegisteredTypes = xmlHashCreate(10);
1555 if (xmlRelaxNGRegisteredTypes == NULL) {
1556 xmlGenericError(xmlGenericErrorContext,
1557 "Failed to allocate sh table for Relax-NG types\n");
1558 return(-1);
1559 }
1560 xmlRelaxNGRegisterTypeLibrary(
1561 BAD_CAST "http://www.w3.org/2001/XMLSchema-datatypes",
1562 NULL,
1563 xmlRelaxNGSchemaTypeHave,
1564 xmlRelaxNGSchemaTypeCheck,
1565 xmlRelaxNGSchemaTypeCompare);
1566 xmlRelaxNGRegisterTypeLibrary(
1567 xmlRelaxNGNs,
1568 NULL,
1569 xmlRelaxNGDefaultTypeHave,
1570 xmlRelaxNGDefaultTypeCheck,
1571 xmlRelaxNGDefaultTypeCompare);
1572 xmlRelaxNGTypeInitialized = 1;
1573 return(0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001574}
1575
1576/**
1577 * xmlRelaxNGCleanupTypes:
1578 *
1579 * Cleanup the default Schemas type library associated to RelaxNG
1580 */
1581void
1582xmlRelaxNGCleanupTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001583 if (xmlRelaxNGTypeInitialized == 0)
1584 return;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001585 xmlSchemaCleanupTypes();
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001586 xmlHashFree(xmlRelaxNGRegisteredTypes, (xmlHashDeallocator)
1587 xmlRelaxNGFreeTypeLibrary);
1588 xmlRelaxNGTypeInitialized = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001589}
1590
1591/************************************************************************
1592 * *
1593 * Parsing functions *
1594 * *
1595 ************************************************************************/
1596
1597static xmlRelaxNGDefinePtr xmlRelaxNGParseAttribute(
1598 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1599static xmlRelaxNGDefinePtr xmlRelaxNGParseElement(
1600 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1601static xmlRelaxNGDefinePtr xmlRelaxNGParsePatterns(
Daniel Veillard154877e2003-01-30 12:17:05 +00001602 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes, int group);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001603static xmlRelaxNGDefinePtr xmlRelaxNGParsePattern(
1604 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001605static xmlRelaxNGPtr xmlRelaxNGParseDocument(
1606 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00001607static int xmlRelaxNGParseGrammarContent(
1608 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00001609static xmlRelaxNGDefinePtr xmlRelaxNGParseNameClass(
1610 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
1611 xmlRelaxNGDefinePtr def);
Daniel Veillard419a7682003-02-03 23:22:49 +00001612static xmlRelaxNGGrammarPtr xmlRelaxNGParseGrammar(
1613 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001614
1615
1616#define IS_BLANK_NODE(n) \
1617 (((n)->type == XML_TEXT_NODE) && (xmlRelaxNGIsBlank((n)->content)))
1618
1619/**
1620 * xmlRelaxNGIsBlank:
1621 * @str: a string
1622 *
1623 * Check if a string is ignorable c.f. 4.2. Whitespace
1624 *
1625 * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
1626 */
1627static int
1628xmlRelaxNGIsBlank(xmlChar *str) {
1629 if (str == NULL)
1630 return(1);
1631 while (*str != 0) {
1632 if (!(IS_BLANK(*str))) return(0);
1633 str++;
1634 }
1635 return(1);
1636}
1637
Daniel Veillard6eadf632003-01-23 18:29:16 +00001638/**
1639 * xmlRelaxNGGetDataTypeLibrary:
1640 * @ctxt: a Relax-NG parser context
1641 * @node: the current data or value element
1642 *
1643 * Applies algorithm from 4.3. datatypeLibrary attribute
1644 *
1645 * Returns the datatypeLibary value or NULL if not found
1646 */
1647static xmlChar *
1648xmlRelaxNGGetDataTypeLibrary(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1649 xmlNodePtr node) {
1650 xmlChar *ret, *escape;
1651
Daniel Veillard6eadf632003-01-23 18:29:16 +00001652 if ((IS_RELAXNG(node, "data")) || (IS_RELAXNG(node, "value"))) {
1653 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1654 if (ret != NULL) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001655 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +00001656 if (escape == NULL) {
1657 return(ret);
1658 }
1659 xmlFree(ret);
1660 return(escape);
1661 }
1662 }
1663 node = node->parent;
1664 while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) {
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001665 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1666 if (ret != NULL) {
1667 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
1668 if (escape == NULL) {
1669 return(ret);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001670 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001671 xmlFree(ret);
1672 return(escape);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001673 }
1674 node = node->parent;
1675 }
1676 return(NULL);
1677}
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001678
1679/**
Daniel Veillardedc91922003-01-26 00:52:04 +00001680 * xmlRelaxNGParseValue:
1681 * @ctxt: a Relax-NG parser context
1682 * @node: the data node.
1683 *
1684 * parse the content of a RelaxNG value node.
1685 *
1686 * Returns the definition pointer or NULL in case of error
1687 */
1688static xmlRelaxNGDefinePtr
1689xmlRelaxNGParseValue(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1690 xmlRelaxNGDefinePtr def = NULL;
1691 xmlRelaxNGTypeLibraryPtr lib;
1692 xmlChar *type;
1693 xmlChar *library;
1694 int tmp;
1695
1696 def = xmlRelaxNGNewDefine(ctxt, node);
1697 if (def == NULL)
1698 return(NULL);
1699 def->type = XML_RELAXNG_VALUE;
1700
1701 type = xmlGetProp(node, BAD_CAST "type");
1702 if (type != NULL) {
1703 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1704 if (library == NULL)
1705 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1706
1707 def->name = type;
1708 def->ns = library;
1709
1710 lib = (xmlRelaxNGTypeLibraryPtr)
1711 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1712 if (lib == NULL) {
1713 if (ctxt->error != NULL)
1714 ctxt->error(ctxt->userData,
1715 "Use of unregistered type library '%s'\n",
1716 library);
1717 ctxt->nbErrors++;
1718 def->data = NULL;
1719 } else {
1720 def->data = lib;
1721 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001722 if (ctxt->error != NULL)
1723 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00001724 "Internal error with type library '%s': no 'have'\n",
1725 library);
1726 ctxt->nbErrors++;
1727 } else {
1728 tmp = lib->have(lib->data, def->name);
1729 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001730 if (ctxt->error != NULL)
1731 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00001732 "Error type '%s' is not exported by type library '%s'\n",
1733 def->name, library);
1734 ctxt->nbErrors++;
1735 }
1736 }
1737 }
1738 }
1739 if (node->children == NULL) {
1740 if (ctxt->error != NULL)
1741 ctxt->error(ctxt->userData,
1742 "Element <value> has no content\n");
1743 ctxt->nbErrors++;
1744 } else if ((node->children->type != XML_TEXT_NODE) ||
1745 (node->children->next != NULL)) {
1746 if (ctxt->error != NULL)
1747 ctxt->error(ctxt->userData,
1748 "Expecting a single text value for <value>content\n");
1749 ctxt->nbErrors++;
1750 } else {
1751 def->value = xmlNodeGetContent(node);
1752 if (def->value == NULL) {
1753 if (ctxt->error != NULL)
1754 ctxt->error(ctxt->userData,
1755 "Element <value> has no content\n");
1756 ctxt->nbErrors++;
1757 }
1758 }
1759 /* TODO check ahead of time that the value is okay per the type */
1760 return(def);
1761}
1762
1763/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001764 * xmlRelaxNGParseData:
1765 * @ctxt: a Relax-NG parser context
1766 * @node: the data node.
1767 *
1768 * parse the content of a RelaxNG data node.
1769 *
1770 * Returns the definition pointer or NULL in case of error
1771 */
1772static xmlRelaxNGDefinePtr
1773xmlRelaxNGParseData(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1774 xmlRelaxNGDefinePtr def = NULL;
1775 xmlRelaxNGTypeLibraryPtr lib;
1776 xmlChar *type;
1777 xmlChar *library;
1778 xmlNodePtr content;
1779 int tmp;
1780
1781 type = xmlGetProp(node, BAD_CAST "type");
1782 if (type == NULL) {
1783 if (ctxt->error != NULL)
1784 ctxt->error(ctxt->userData,
1785 "data has no type\n");
1786 ctxt->nbErrors++;
1787 return(NULL);
1788 }
1789 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1790 if (library == NULL)
1791 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1792
1793 def = xmlRelaxNGNewDefine(ctxt, node);
1794 if (def == NULL) {
1795 xmlFree(type);
1796 return(NULL);
1797 }
1798 def->type = XML_RELAXNG_DATATYPE;
1799 def->name = type;
1800 def->ns = library;
1801
1802 lib = (xmlRelaxNGTypeLibraryPtr)
1803 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1804 if (lib == NULL) {
1805 if (ctxt->error != NULL)
1806 ctxt->error(ctxt->userData,
1807 "Use of unregistered type library '%s'\n",
1808 library);
1809 ctxt->nbErrors++;
1810 def->data = NULL;
1811 } else {
1812 def->data = lib;
1813 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001814 if (ctxt->error != NULL)
1815 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001816 "Internal error with type library '%s': no 'have'\n",
1817 library);
1818 ctxt->nbErrors++;
1819 } else {
1820 tmp = lib->have(lib->data, def->name);
1821 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001822 if (ctxt->error != NULL)
1823 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001824 "Error type '%s' is not exported by type library '%s'\n",
1825 def->name, library);
1826 ctxt->nbErrors++;
1827 }
1828 }
1829 }
1830 content = node->children;
1831 while (content != NULL) {
1832 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001833 ctxt->nbErrors++;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001834 content = content->next;
1835 }
1836
1837 return(def);
1838}
1839
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001840/**
1841 * xmlRelaxNGCompareElemDefLists:
1842 * @ctxt: a Relax-NG parser context
1843 * @defs1: the first list of element defs
1844 * @defs2: the second list of element defs
1845 *
1846 * Compare the 2 lists of element definitions. The comparison is
1847 * that if both lists do not accept the same QNames, it returns 1
1848 * If the 2 lists can accept the same QName the comparison returns 0
1849 *
1850 * Returns 1 disttinct, 0 if equal
1851 */
1852static int
1853xmlRelaxNGCompareElemDefLists(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1854 xmlRelaxNGDefinePtr *def1,
1855 xmlRelaxNGDefinePtr *def2) {
1856 xmlRelaxNGDefinePtr *basedef2 = def2;
1857
Daniel Veillard154877e2003-01-30 12:17:05 +00001858 if ((def1 == NULL) || (def2 == NULL))
1859 return(1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001860 if ((*def1 == NULL) || (*def2 == NULL))
1861 return(1);
1862 while (*def1 != NULL) {
1863 while ((*def2) != NULL) {
1864 if ((*def1)->name == NULL) {
1865 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1866 return(0);
1867 } else if ((*def2)->name == NULL) {
1868 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1869 return(0);
1870 } else if (xmlStrEqual((*def1)->name, (*def2)->name)) {
1871 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1872 return(0);
1873 }
1874 def2++;
1875 }
1876 def2 = basedef2;
1877 def1++;
1878 }
1879 return(1);
1880}
1881
1882/**
1883 * xmlRelaxNGGetElements:
1884 * @ctxt: a Relax-NG parser context
1885 * @def: the interleave definition
1886 *
1887 * Compute the list of top elements a definition can generate
1888 *
1889 * Returns a list of elements or NULL if none was found.
1890 */
1891static xmlRelaxNGDefinePtr *
1892xmlRelaxNGGetElements(xmlRelaxNGParserCtxtPtr ctxt,
1893 xmlRelaxNGDefinePtr def) {
1894 xmlRelaxNGDefinePtr *ret = NULL, parent, cur, tmp;
1895 int len = 0;
1896 int max = 0;
1897
1898 parent = NULL;
1899 cur = def;
1900 while (cur != NULL) {
Daniel Veillardb08c9812003-01-28 23:09:49 +00001901 if ((cur->type == XML_RELAXNG_ELEMENT) ||
1902 (cur->type == XML_RELAXNG_TEXT)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001903 if (ret == NULL) {
1904 max = 10;
1905 ret = (xmlRelaxNGDefinePtr *)
1906 xmlMalloc((max + 1) * sizeof(xmlRelaxNGDefinePtr));
1907 if (ret == NULL) {
1908 if (ctxt->error != NULL)
1909 ctxt->error(ctxt->userData,
1910 "Out of memory in element search\n");
1911 ctxt->nbErrors++;
1912 return(NULL);
1913 }
1914 } else if (max <= len) {
1915 max *= 2;
1916 ret = xmlRealloc(ret, (max + 1) * sizeof(xmlRelaxNGDefinePtr));
1917 if (ret == NULL) {
1918 if (ctxt->error != NULL)
1919 ctxt->error(ctxt->userData,
1920 "Out of memory in element search\n");
1921 ctxt->nbErrors++;
1922 return(NULL);
1923 }
1924 }
Daniel Veillardb08c9812003-01-28 23:09:49 +00001925 ret[len++] = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001926 ret[len] = NULL;
1927 } else if ((cur->type == XML_RELAXNG_CHOICE) ||
1928 (cur->type == XML_RELAXNG_INTERLEAVE) ||
1929 (cur->type == XML_RELAXNG_GROUP) ||
1930 (cur->type == XML_RELAXNG_ONEORMORE) ||
Daniel Veillardb08c9812003-01-28 23:09:49 +00001931 (cur->type == XML_RELAXNG_ZEROORMORE) ||
1932 (cur->type == XML_RELAXNG_OPTIONAL) ||
1933 (cur->type == XML_RELAXNG_REF) ||
1934 (cur->type == XML_RELAXNG_DEF)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001935 /*
1936 * Don't go within elements or attributes or string values.
1937 * Just gather the element top list
1938 */
1939 if (cur->content != NULL) {
1940 parent = cur;
1941 cur = cur->content;
1942 tmp = cur;
1943 while (tmp != NULL) {
1944 tmp->parent = parent;
1945 tmp = tmp->next;
1946 }
1947 continue;
1948 }
1949 }
Daniel Veillard154877e2003-01-30 12:17:05 +00001950 if (cur == def)
1951 return(ret);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001952 if (cur->next != NULL) {
1953 cur = cur->next;
1954 continue;
1955 }
1956 do {
1957 cur = cur->parent;
1958 if (cur == NULL) break;
1959 if (cur == def) return(ret);
1960 if (cur->next != NULL) {
1961 cur = cur->next;
1962 break;
1963 }
1964 } while (cur != NULL);
1965 }
1966 return(ret);
1967}
1968
1969/**
1970 * xmlRelaxNGComputeInterleaves:
1971 * @def: the interleave definition
1972 * @ctxt: a Relax-NG parser context
1973 * @node: the data node.
1974 *
1975 * A lot of work for preprocessing interleave definitions
1976 * is potentially needed to get a decent execution speed at runtime
1977 * - trying to get a total order on the element nodes generated
1978 * by the interleaves, order the list of interleave definitions
1979 * following that order.
1980 * - if <text/> is used to handle mixed content, it is better to
1981 * flag this in the define and simplify the runtime checking
1982 * algorithm
1983 */
1984static void
1985xmlRelaxNGComputeInterleaves(xmlRelaxNGDefinePtr def,
1986 xmlRelaxNGParserCtxtPtr ctxt,
1987 xmlChar *name ATTRIBUTE_UNUSED) {
1988 xmlRelaxNGDefinePtr cur;
1989
1990 xmlRelaxNGDefinePtr *list = NULL;
1991 xmlRelaxNGPartitionPtr partitions = NULL;
1992 xmlRelaxNGInterleaveGroupPtr *groups = NULL;
1993 xmlRelaxNGInterleaveGroupPtr group;
1994 int i,j,ret;
1995 int nbgroups = 0;
1996 int nbchild = 0;
1997
1998#ifdef DEBUG_INTERLEAVE
1999 xmlGenericError(xmlGenericErrorContext,
2000 "xmlRelaxNGComputeInterleaves(%s)\n",
2001 name);
2002#endif
2003 cur = def->content;
2004 while (cur != NULL) {
2005 nbchild++;
2006 cur = cur->next;
2007 }
2008
2009#ifdef DEBUG_INTERLEAVE
2010 xmlGenericError(xmlGenericErrorContext, " %d child\n", nbchild);
2011#endif
2012 groups = (xmlRelaxNGInterleaveGroupPtr *)
2013 xmlMalloc(nbchild * sizeof(xmlRelaxNGInterleaveGroupPtr));
2014 if (groups == NULL)
2015 goto error;
2016 cur = def->content;
2017 while (cur != NULL) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002018 groups[nbgroups] = (xmlRelaxNGInterleaveGroupPtr)
2019 xmlMalloc(sizeof(xmlRelaxNGInterleaveGroup));
2020 if (groups[nbgroups] == NULL)
2021 goto error;
2022 groups[nbgroups]->rule = cur;
2023 groups[nbgroups]->defs = xmlRelaxNGGetElements(ctxt, cur);
2024 nbgroups++;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002025 cur = cur->next;
2026 }
2027 list = NULL;
2028#ifdef DEBUG_INTERLEAVE
2029 xmlGenericError(xmlGenericErrorContext, " %d groups\n", nbgroups);
2030#endif
2031
2032 /*
2033 * Let's check that all rules makes a partitions according to 7.4
2034 */
2035 partitions = (xmlRelaxNGPartitionPtr)
2036 xmlMalloc(sizeof(xmlRelaxNGPartition));
2037 if (partitions == NULL)
2038 goto error;
2039 partitions->nbgroups = nbgroups;
2040 for (i = 0;i < nbgroups;i++) {
2041 group = groups[i];
2042 for (j = i+1;j < nbgroups;j++) {
2043 if (groups[j] == NULL)
2044 continue;
2045 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->defs,
2046 groups[j]->defs);
2047 if (ret == 0) {
2048 if (ctxt->error != NULL)
2049 ctxt->error(ctxt->userData,
2050 "Element or text conflicts in interleave\n");
2051 ctxt->nbErrors++;
2052 }
2053 }
2054 }
2055 partitions->groups = groups;
2056
2057 /*
2058 * Free Up the child list, and save the partition list back in the def
2059 */
2060 def->data = partitions;
2061 return;
2062
2063error:
2064 if (ctxt->error != NULL)
2065 ctxt->error(ctxt->userData,
2066 "Out of memory in interleave computation\n");
2067 ctxt->nbErrors++;
2068 if (list == NULL)
2069 xmlFree(list);
2070 if (groups != NULL) {
2071 for (i = 0;i < nbgroups;i++)
2072 if (groups[i] != NULL) {
2073 if (groups[i]->defs != NULL)
2074 xmlFree(groups[i]->defs);
2075 xmlFree(groups[i]);
2076 }
2077 xmlFree(groups);
2078 }
2079 xmlRelaxNGFreePartition(partitions);
2080}
2081
2082/**
2083 * xmlRelaxNGParseInterleave:
2084 * @ctxt: a Relax-NG parser context
2085 * @node: the data node.
2086 *
2087 * parse the content of a RelaxNG interleave node.
2088 *
2089 * Returns the definition pointer or NULL in case of error
2090 */
2091static xmlRelaxNGDefinePtr
2092xmlRelaxNGParseInterleave(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2093 xmlRelaxNGDefinePtr def = NULL;
2094 xmlRelaxNGDefinePtr last = NULL, cur;
2095 xmlNodePtr child;
2096
2097 def = xmlRelaxNGNewDefine(ctxt, node);
2098 if (def == NULL) {
2099 return(NULL);
2100 }
2101 def->type = XML_RELAXNG_INTERLEAVE;
2102
2103 if (ctxt->interleaves == NULL)
2104 ctxt->interleaves = xmlHashCreate(10);
2105 if (ctxt->interleaves == NULL) {
2106 if (ctxt->error != NULL)
2107 ctxt->error(ctxt->userData,
2108 "Failed to create interleaves hash table\n");
2109 ctxt->nbErrors++;
2110 } else {
2111 char name[32];
2112
2113 snprintf(name, 32, "interleave%d", ctxt->nbInterleaves++);
2114 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST name, def) < 0) {
2115 if (ctxt->error != NULL)
2116 ctxt->error(ctxt->userData,
2117 "Failed to add %s to hash table\n", name);
2118 ctxt->nbErrors++;
2119 }
2120 }
2121 child = node->children;
2122 while (child != NULL) {
2123 if (IS_RELAXNG(child, "element")) {
2124 cur = xmlRelaxNGParseElement(ctxt, child);
2125 } else {
2126 cur = xmlRelaxNGParsePattern(ctxt, child);
2127 }
2128 if (cur != NULL) {
2129 cur->parent = def;
2130 if (last == NULL) {
2131 def->content = last = cur;
2132 } else {
2133 last->next = cur;
2134 last = cur;
2135 }
2136 }
2137 child = child->next;
2138 }
2139
2140 return(def);
2141}
Daniel Veillard6eadf632003-01-23 18:29:16 +00002142
2143/**
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002144 * xmlRelaxNGParseInclude:
2145 * @ctxt: a Relax-NG parser context
2146 * @node: the include node
2147 *
2148 * Integrate the content of an include node in the current grammar
2149 *
2150 * Returns 0 in case of success or -1 in case of error
2151 */
2152static int
2153xmlRelaxNGParseInclude(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2154 xmlRelaxNGIncludePtr incl;
2155 xmlNodePtr root;
2156 int ret = 0, tmp;
2157
2158 incl = node->_private;
2159 if (incl == NULL) {
2160 if (ctxt->error != NULL)
2161 ctxt->error(ctxt->userData,
2162 "Include node has no data\n");
2163 ctxt->nbErrors++;
2164 return(-1);
2165 }
2166 root = xmlDocGetRootElement(incl->doc);
2167 if (root == NULL) {
2168 if (ctxt->error != NULL)
2169 ctxt->error(ctxt->userData,
2170 "Include document is empty\n");
2171 ctxt->nbErrors++;
2172 return(-1);
2173 }
2174 if (!xmlStrEqual(root->name, BAD_CAST "grammar")) {
2175 if (ctxt->error != NULL)
2176 ctxt->error(ctxt->userData,
2177 "Include document root is not a grammar\n");
2178 ctxt->nbErrors++;
2179 return(-1);
2180 }
2181
2182 /*
2183 * Merge the definition from both the include and the internal list
2184 */
2185 if (root->children != NULL) {
2186 tmp = xmlRelaxNGParseGrammarContent(ctxt, root->children);
2187 if (tmp != 0)
2188 ret = -1;
2189 }
2190 if (node->children != NULL) {
2191 tmp = xmlRelaxNGParseGrammarContent(ctxt, node->children);
2192 if (tmp != 0)
2193 ret = -1;
2194 }
2195 return(ret);
2196}
2197
2198/**
Daniel Veillard276be4a2003-01-24 01:03:34 +00002199 * xmlRelaxNGParseDefine:
2200 * @ctxt: a Relax-NG parser context
2201 * @node: the define node
2202 *
2203 * parse the content of a RelaxNG define element node.
2204 *
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002205 * Returns 0 in case of success or -1 in case of error
Daniel Veillard276be4a2003-01-24 01:03:34 +00002206 */
2207static int
2208xmlRelaxNGParseDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2209 xmlChar *name;
2210 int ret = 0, tmp;
2211 xmlRelaxNGDefinePtr def;
2212 const xmlChar *olddefine;
2213
2214 name = xmlGetProp(node, BAD_CAST "name");
2215 if (name == NULL) {
2216 if (ctxt->error != NULL)
2217 ctxt->error(ctxt->userData,
2218 "define has no name\n");
2219 ctxt->nbErrors++;
2220 } else {
2221 def = xmlRelaxNGNewDefine(ctxt, node);
2222 if (def == NULL) {
2223 xmlFree(name);
2224 return(-1);
2225 }
2226 def->type = XML_RELAXNG_DEF;
2227 def->name = name;
2228 if (node->children == NULL) {
2229 if (ctxt->error != NULL)
2230 ctxt->error(ctxt->userData,
2231 "define has no children\n");
2232 ctxt->nbErrors++;
2233 } else {
2234 olddefine = ctxt->define;
2235 ctxt->define = name;
Daniel Veillard154877e2003-01-30 12:17:05 +00002236 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard276be4a2003-01-24 01:03:34 +00002237 ctxt->define = olddefine;
2238 }
2239 if (ctxt->grammar->defs == NULL)
2240 ctxt->grammar->defs = xmlHashCreate(10);
2241 if (ctxt->grammar->defs == NULL) {
2242 if (ctxt->error != NULL)
2243 ctxt->error(ctxt->userData,
2244 "Could not create definition hash\n");
2245 ctxt->nbErrors++;
2246 ret = -1;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002247 } else {
2248 tmp = xmlHashAddEntry(ctxt->grammar->defs, name, def);
2249 if (tmp < 0) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002250 xmlRelaxNGDefinePtr prev;
2251
2252 prev = xmlHashLookup(ctxt->grammar->defs, name);
2253 if (prev == NULL) {
2254 if (ctxt->error != NULL)
2255 ctxt->error(ctxt->userData,
2256 "Internal error on define aggregation of %s\n",
2257 name);
2258 ctxt->nbErrors++;
2259 ret = -1;
Daniel Veillard154877e2003-01-30 12:17:05 +00002260 } else {
2261 while (prev->nextHash != NULL)
2262 prev = prev->nextHash;
2263 prev->nextHash = def;
2264 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002265 }
2266 }
2267 }
2268 return(ret);
2269}
2270
2271/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00002272 * xmlRelaxNGParsePattern:
2273 * @ctxt: a Relax-NG parser context
2274 * @node: the pattern node.
2275 *
2276 * parse the content of a RelaxNG pattern node.
2277 *
Daniel Veillard276be4a2003-01-24 01:03:34 +00002278 * Returns the definition pointer or NULL in case of error or if no
2279 * pattern is generated.
Daniel Veillard6eadf632003-01-23 18:29:16 +00002280 */
2281static xmlRelaxNGDefinePtr
2282xmlRelaxNGParsePattern(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2283 xmlRelaxNGDefinePtr def = NULL;
2284
2285 if (IS_RELAXNG(node, "element")) {
2286 def = xmlRelaxNGParseElement(ctxt, node);
2287 } else if (IS_RELAXNG(node, "attribute")) {
2288 def = xmlRelaxNGParseAttribute(ctxt, node);
2289 } else if (IS_RELAXNG(node, "empty")) {
2290 def = xmlRelaxNGNewDefine(ctxt, node);
2291 if (def == NULL)
2292 return(NULL);
2293 def->type = XML_RELAXNG_EMPTY;
2294 } else if (IS_RELAXNG(node, "text")) {
2295 def = xmlRelaxNGNewDefine(ctxt, node);
2296 if (def == NULL)
2297 return(NULL);
2298 def->type = XML_RELAXNG_TEXT;
2299 if (node->children != NULL) {
2300 if (ctxt->error != NULL)
2301 ctxt->error(ctxt->userData, "text: had a child node\n");
2302 ctxt->nbErrors++;
2303 }
2304 } else if (IS_RELAXNG(node, "zeroOrMore")) {
2305 def = xmlRelaxNGNewDefine(ctxt, node);
2306 if (def == NULL)
2307 return(NULL);
2308 def->type = XML_RELAXNG_ZEROORMORE;
Daniel Veillard154877e2003-01-30 12:17:05 +00002309 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002310 } else if (IS_RELAXNG(node, "oneOrMore")) {
2311 def = xmlRelaxNGNewDefine(ctxt, node);
2312 if (def == NULL)
2313 return(NULL);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002314 def->type = XML_RELAXNG_ONEORMORE;
Daniel Veillard154877e2003-01-30 12:17:05 +00002315 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002316 } else if (IS_RELAXNG(node, "optional")) {
2317 def = xmlRelaxNGNewDefine(ctxt, node);
2318 if (def == NULL)
2319 return(NULL);
2320 def->type = XML_RELAXNG_OPTIONAL;
Daniel Veillard154877e2003-01-30 12:17:05 +00002321 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002322 } else if (IS_RELAXNG(node, "choice")) {
2323 def = xmlRelaxNGNewDefine(ctxt, node);
2324 if (def == NULL)
2325 return(NULL);
2326 def->type = XML_RELAXNG_CHOICE;
Daniel Veillard154877e2003-01-30 12:17:05 +00002327 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002328 } else if (IS_RELAXNG(node, "group")) {
2329 def = xmlRelaxNGNewDefine(ctxt, node);
2330 if (def == NULL)
2331 return(NULL);
2332 def->type = XML_RELAXNG_GROUP;
Daniel Veillard154877e2003-01-30 12:17:05 +00002333 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002334 } else if (IS_RELAXNG(node, "ref")) {
2335 def = xmlRelaxNGNewDefine(ctxt, node);
2336 if (def == NULL)
2337 return(NULL);
2338 def->type = XML_RELAXNG_REF;
2339 def->name = xmlGetProp(node, BAD_CAST "name");
2340 if (def->name == NULL) {
2341 if (ctxt->error != NULL)
2342 ctxt->error(ctxt->userData,
2343 "ref has no name\n");
2344 ctxt->nbErrors++;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002345 } else {
2346 if ((ctxt->define != NULL) &&
2347 (xmlStrEqual(ctxt->define, def->name))) {
2348 if (ctxt->error != NULL)
2349 ctxt->error(ctxt->userData,
2350 "Recursive reference to %s not in an element\n",
2351 def->name);
2352 ctxt->nbErrors++;
2353 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002354 }
2355 if (node->children != NULL) {
2356 if (ctxt->error != NULL)
2357 ctxt->error(ctxt->userData,
2358 "ref is not empty\n");
2359 ctxt->nbErrors++;
2360 }
2361 if (ctxt->grammar->refs == NULL)
2362 ctxt->grammar->refs = xmlHashCreate(10);
2363 if (ctxt->grammar->refs == NULL) {
2364 if (ctxt->error != NULL)
2365 ctxt->error(ctxt->userData,
2366 "Could not create references hash\n");
2367 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002368 def = NULL;
2369 } else {
2370 int tmp;
2371
2372 tmp = xmlHashAddEntry(ctxt->grammar->refs, def->name, def);
2373 if (tmp < 0) {
2374 xmlRelaxNGDefinePtr prev;
2375
2376 prev = (xmlRelaxNGDefinePtr)
2377 xmlHashLookup(ctxt->grammar->refs, def->name);
2378 if (prev == NULL) {
2379 if (ctxt->error != NULL)
2380 ctxt->error(ctxt->userData,
2381 "Internal error refs definitions '%s'\n",
2382 def->name);
2383 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002384 def = NULL;
2385 } else {
2386 def->nextHash = prev->nextHash;
2387 prev->nextHash = def;
2388 }
2389 }
2390 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002391 } else if (IS_RELAXNG(node, "data")) {
2392 def = xmlRelaxNGParseData(ctxt, node);
Daniel Veillard276be4a2003-01-24 01:03:34 +00002393 } else if (IS_RELAXNG(node, "define")) {
2394 xmlRelaxNGParseDefine(ctxt, node);
2395 def = NULL;
Daniel Veillardedc91922003-01-26 00:52:04 +00002396 } else if (IS_RELAXNG(node, "value")) {
2397 def = xmlRelaxNGParseValue(ctxt, node);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002398 } else if (IS_RELAXNG(node, "list")) {
2399 def = xmlRelaxNGNewDefine(ctxt, node);
2400 if (def == NULL)
2401 return(NULL);
2402 def->type = XML_RELAXNG_LIST;
Daniel Veillard154877e2003-01-30 12:17:05 +00002403 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002404 } else if (IS_RELAXNG(node, "interleave")) {
2405 def = xmlRelaxNGParseInterleave(ctxt, node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002406 } else if (IS_RELAXNG(node, "externalRef")) {
2407 xmlRelaxNGDocumentPtr docu;
2408 xmlNodePtr root;
2409
2410 docu = node->_private;
2411 if (docu != NULL) {
2412 def = xmlRelaxNGNewDefine(ctxt, node);
2413 if (def == NULL)
2414 return(NULL);
2415 def->type = XML_RELAXNG_EXTERNALREF;
2416
2417 if (docu->content == NULL) {
2418 /*
2419 * Then do the parsing for good
2420 */
2421 root = xmlDocGetRootElement(docu->doc);
2422 if (root == NULL) {
2423 if (ctxt->error != NULL)
2424 ctxt->error(ctxt->userData,
2425 "xmlRelaxNGParse: %s is empty\n",
2426 ctxt->URL);
2427 ctxt->nbErrors++;
2428 return (NULL);
2429 }
2430 docu->schema = xmlRelaxNGParseDocument(ctxt, root);
2431 if ((docu->schema != NULL) &&
2432 (docu->schema->topgrammar != NULL)) {
2433 docu->content = docu->schema->topgrammar->start;
2434 }
2435 }
2436 def->content = docu->content;
2437 } else {
2438 def = NULL;
2439 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002440 } else if (IS_RELAXNG(node, "notAllowed")) {
2441 def = xmlRelaxNGNewDefine(ctxt, node);
2442 if (def == NULL)
2443 return(NULL);
2444 def->type = XML_RELAXNG_NOT_ALLOWED;
2445 if (node->children != NULL) {
2446 if (ctxt->error != NULL)
2447 ctxt->error(ctxt->userData,
2448 "xmlRelaxNGParse: notAllowed element is not empty\n");
2449 ctxt->nbErrors++;
2450 }
Daniel Veillard419a7682003-02-03 23:22:49 +00002451 } else if (IS_RELAXNG(node, "grammar")) {
2452 xmlRelaxNGGrammarPtr grammar, old;
2453 xmlRelaxNGGrammarPtr oldparent;
2454
2455 oldparent = ctxt->parentgrammar;
2456 old = ctxt->grammar;
2457 ctxt->parentgrammar = old;
2458 grammar = xmlRelaxNGParseGrammar(ctxt, node->children);
2459 if (old != NULL) {
2460 ctxt->grammar = old;
2461 ctxt->parentgrammar = oldparent;
2462 if (grammar != NULL) {
2463 grammar->next = old->next;
2464 old->next = grammar;
2465 }
2466 }
2467 if (grammar != NULL)
2468 def = grammar->start;
2469 else
2470 def = NULL;
2471 } else if (IS_RELAXNG(node, "parentRef")) {
2472 if (ctxt->parentgrammar == NULL) {
2473 if (ctxt->error != NULL)
2474 ctxt->error(ctxt->userData,
2475 "Use of parentRef without a parent grammar\n");
2476 ctxt->nbErrors++;
2477 return(NULL);
2478 }
2479 def = xmlRelaxNGNewDefine(ctxt, node);
2480 if (def == NULL)
2481 return(NULL);
2482 def->type = XML_RELAXNG_PARENTREF;
2483 def->name = xmlGetProp(node, BAD_CAST "name");
2484 if (def->name == NULL) {
2485 if (ctxt->error != NULL)
2486 ctxt->error(ctxt->userData,
2487 "parentRef has no name\n");
2488 ctxt->nbErrors++;
2489 }
2490 if (node->children != NULL) {
2491 if (ctxt->error != NULL)
2492 ctxt->error(ctxt->userData,
2493 "parentRef is not empty\n");
2494 ctxt->nbErrors++;
2495 }
2496 if (ctxt->parentgrammar->refs == NULL)
2497 ctxt->parentgrammar->refs = xmlHashCreate(10);
2498 if (ctxt->parentgrammar->refs == NULL) {
2499 if (ctxt->error != NULL)
2500 ctxt->error(ctxt->userData,
2501 "Could not create references hash\n");
2502 ctxt->nbErrors++;
2503 def = NULL;
2504 } else {
2505 int tmp;
2506
2507 tmp = xmlHashAddEntry(ctxt->parentgrammar->refs, def->name, def);
2508 if (tmp < 0) {
2509 xmlRelaxNGDefinePtr prev;
2510
2511 prev = (xmlRelaxNGDefinePtr)
2512 xmlHashLookup(ctxt->parentgrammar->refs, def->name);
2513 if (prev == NULL) {
2514 if (ctxt->error != NULL)
2515 ctxt->error(ctxt->userData,
2516 "Internal error parentRef definitions '%s'\n",
2517 def->name);
2518 ctxt->nbErrors++;
2519 def = NULL;
2520 } else {
2521 def->nextHash = prev->nextHash;
2522 prev->nextHash = def;
2523 }
2524 }
2525 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002526 } else {
2527 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002528 ctxt->nbErrors++;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002529 def = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002530 }
2531 return(def);
2532}
2533
2534/**
2535 * xmlRelaxNGParseAttribute:
2536 * @ctxt: a Relax-NG parser context
2537 * @node: the element node
2538 *
2539 * parse the content of a RelaxNG attribute node.
2540 *
2541 * Returns the definition pointer or NULL in case of error.
2542 */
2543static xmlRelaxNGDefinePtr
2544xmlRelaxNGParseAttribute(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2545 xmlRelaxNGDefinePtr ret, cur, last;
2546 xmlNodePtr child;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002547 int old_flags;
2548
2549 ret = xmlRelaxNGNewDefine(ctxt, node);
2550 if (ret == NULL)
2551 return(NULL);
2552 ret->type = XML_RELAXNG_ATTRIBUTE;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002553 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002554 child = node->children;
2555 if (child == NULL) {
2556 if (ctxt->error != NULL)
2557 ctxt->error(ctxt->userData,
2558 "xmlRelaxNGParseattribute: attribute has no children\n");
2559 ctxt->nbErrors++;
2560 return(ret);
2561 }
2562 old_flags = ctxt->flags;
2563 ctxt->flags |= XML_RELAXNG_IN_ATTRIBUTE;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002564 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
2565 if (cur != NULL)
2566 child = child->next;
2567
Daniel Veillard6eadf632003-01-23 18:29:16 +00002568 last = NULL;
2569 while (child != NULL) {
2570 cur = xmlRelaxNGParsePattern(ctxt, child);
2571 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002572 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002573 switch (cur->type) {
2574 case XML_RELAXNG_EMPTY:
2575 case XML_RELAXNG_NOT_ALLOWED:
2576 case XML_RELAXNG_TEXT:
2577 case XML_RELAXNG_ELEMENT:
2578 case XML_RELAXNG_DATATYPE:
2579 case XML_RELAXNG_VALUE:
2580 case XML_RELAXNG_LIST:
2581 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00002582 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002583 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00002584 case XML_RELAXNG_DEF:
2585 case XML_RELAXNG_ONEORMORE:
2586 case XML_RELAXNG_ZEROORMORE:
2587 case XML_RELAXNG_OPTIONAL:
2588 case XML_RELAXNG_CHOICE:
2589 case XML_RELAXNG_GROUP:
2590 case XML_RELAXNG_INTERLEAVE:
2591 if (last == NULL) {
2592 ret->content = last = cur;
2593 } else {
2594 if ((last->type == XML_RELAXNG_ELEMENT) &&
2595 (ret->content == last)) {
2596 ret->content = xmlRelaxNGNewDefine(ctxt, node);
2597 if (ret->content != NULL) {
2598 ret->content->type = XML_RELAXNG_GROUP;
2599 ret->content->content = last;
2600 } else {
2601 ret->content = last;
2602 }
2603 }
2604 last->next = cur;
2605 last = cur;
2606 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002607 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002608 break;
2609 case XML_RELAXNG_ATTRIBUTE:
2610 cur->next = ret->attrs;
2611 ret->attrs = cur;
2612 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002613 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00002614 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002615 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002616 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002617 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002618 }
2619 }
2620 child = child->next;
2621 }
2622 ctxt->flags = old_flags;
2623 return(ret);
2624}
2625
2626/**
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002627 * xmlRelaxNGParseExceptNameClass:
2628 * @ctxt: a Relax-NG parser context
2629 * @node: the except node
Daniel Veillard144fae12003-02-03 13:17:57 +00002630 * @attr: 1 if within an attribute, 0 if within an element
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002631 *
2632 * parse the content of a RelaxNG nameClass node.
2633 *
2634 * Returns the definition pointer or NULL in case of error.
2635 */
2636static xmlRelaxNGDefinePtr
Daniel Veillard144fae12003-02-03 13:17:57 +00002637xmlRelaxNGParseExceptNameClass(xmlRelaxNGParserCtxtPtr ctxt,
2638 xmlNodePtr node, int attr) {
2639 xmlRelaxNGDefinePtr ret, cur, last = NULL;
2640 xmlNodePtr child;
2641
2642 if (!IS_RELAXNG(node, "except"))
2643 return(NULL);
2644 if (node->children == NULL) {
2645 if (ctxt->error != NULL)
2646 ctxt->error(ctxt->userData,
2647 "except has no content\n");
2648 ctxt->nbErrors++;
2649 return(NULL);
2650 }
2651
2652 ret = xmlRelaxNGNewDefine(ctxt, node);
2653 if (ret == NULL)
2654 return(NULL);
2655 ret->type = XML_RELAXNG_EXCEPT;
2656 child = node->children;
2657 while (child != NULL) {
2658 cur = xmlRelaxNGNewDefine(ctxt, child);
2659 if (cur == NULL)
2660 break;
2661 if (attr)
2662 cur->type = XML_RELAXNG_ATTRIBUTE;
2663 else
2664 cur->type = XML_RELAXNG_ELEMENT;
2665
Daniel Veillard419a7682003-02-03 23:22:49 +00002666 if (xmlRelaxNGParseNameClass(ctxt, child, cur) != NULL) {
Daniel Veillard144fae12003-02-03 13:17:57 +00002667 if (last == NULL) {
2668 ret->content = cur;
2669 } else {
2670 last->next = cur;
2671 }
2672 last = cur;
2673 }
2674 child = child->next;
2675 }
2676
2677 return(ret);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002678}
2679
2680/**
2681 * xmlRelaxNGParseNameClass:
2682 * @ctxt: a Relax-NG parser context
2683 * @node: the nameClass node
2684 * @def: the current definition
2685 *
2686 * parse the content of a RelaxNG nameClass node.
2687 *
2688 * Returns the definition pointer or NULL in case of error.
2689 */
2690static xmlRelaxNGDefinePtr
2691xmlRelaxNGParseNameClass(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
2692 xmlRelaxNGDefinePtr def) {
2693 xmlRelaxNGDefinePtr ret = def;
2694 xmlChar *val;
2695
2696 if (IS_RELAXNG(node, "name")) {
2697 val = xmlNodeGetContent(node);
2698 ret->name = val;
2699 val = xmlGetProp(node, BAD_CAST "ns");
2700 ret->ns = val;
2701 } else if (IS_RELAXNG(node, "anyName")) {
2702 ret->name = NULL;
2703 ret->ns = NULL;
2704 if (node->children != NULL) {
2705 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00002706 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
2707 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002708 }
2709 } else if (IS_RELAXNG(node, "nsName")) {
2710 ret->name = NULL;
2711 ret->ns = xmlGetProp(node, BAD_CAST "ns");
2712 if (ret->ns == NULL) {
2713 if (ctxt->error != NULL)
2714 ctxt->error(ctxt->userData,
2715 "nsName has no ns attribute\n");
2716 ctxt->nbErrors++;
2717 }
2718 if (node->children != NULL) {
2719 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00002720 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
2721 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002722 }
2723 } else if (IS_RELAXNG(node, "choice")) {
2724 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002725 ctxt->nbErrors++;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002726 } else {
2727 if (ctxt->error != NULL)
2728 ctxt->error(ctxt->userData,
2729 "expecting name, anyName, nsName or choice : got %s\n",
2730 node->name);
2731 ctxt->nbErrors++;
2732 return(NULL);
2733 }
2734 return(ret);
2735}
2736
2737/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00002738 * xmlRelaxNGParseElement:
2739 * @ctxt: a Relax-NG parser context
2740 * @node: the element node
2741 *
2742 * parse the content of a RelaxNG element node.
2743 *
2744 * Returns the definition pointer or NULL in case of error.
2745 */
2746static xmlRelaxNGDefinePtr
2747xmlRelaxNGParseElement(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2748 xmlRelaxNGDefinePtr ret, cur, last;
2749 xmlNodePtr child;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002750 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002751
2752 ret = xmlRelaxNGNewDefine(ctxt, node);
2753 if (ret == NULL)
2754 return(NULL);
2755 ret->type = XML_RELAXNG_ELEMENT;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002756 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002757 child = node->children;
2758 if (child == NULL) {
2759 if (ctxt->error != NULL)
2760 ctxt->error(ctxt->userData,
2761 "xmlRelaxNGParseElement: element has no children\n");
2762 ctxt->nbErrors++;
2763 return(ret);
2764 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002765 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
2766 if (cur != NULL)
2767 child = child->next;
2768
Daniel Veillard6eadf632003-01-23 18:29:16 +00002769 if (child == NULL) {
2770 if (ctxt->error != NULL)
2771 ctxt->error(ctxt->userData,
2772 "xmlRelaxNGParseElement: element has no content\n");
2773 ctxt->nbErrors++;
2774 return(ret);
2775 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002776 olddefine = ctxt->define;
2777 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002778 last = NULL;
2779 while (child != NULL) {
2780 cur = xmlRelaxNGParsePattern(ctxt, child);
2781 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002782 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002783 switch (cur->type) {
2784 case XML_RELAXNG_EMPTY:
2785 case XML_RELAXNG_NOT_ALLOWED:
2786 case XML_RELAXNG_TEXT:
2787 case XML_RELAXNG_ELEMENT:
2788 case XML_RELAXNG_DATATYPE:
2789 case XML_RELAXNG_VALUE:
2790 case XML_RELAXNG_LIST:
2791 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00002792 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002793 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00002794 case XML_RELAXNG_DEF:
2795 case XML_RELAXNG_ZEROORMORE:
2796 case XML_RELAXNG_ONEORMORE:
2797 case XML_RELAXNG_OPTIONAL:
2798 case XML_RELAXNG_CHOICE:
2799 case XML_RELAXNG_GROUP:
2800 case XML_RELAXNG_INTERLEAVE:
2801 if (last == NULL) {
2802 ret->content = last = cur;
2803 } else {
2804 if ((last->type == XML_RELAXNG_ELEMENT) &&
2805 (ret->content == last)) {
2806 ret->content = xmlRelaxNGNewDefine(ctxt, node);
2807 if (ret->content != NULL) {
2808 ret->content->type = XML_RELAXNG_GROUP;
2809 ret->content->content = last;
2810 } else {
2811 ret->content = last;
2812 }
2813 }
2814 last->next = cur;
2815 last = cur;
2816 }
2817 break;
2818 case XML_RELAXNG_ATTRIBUTE:
2819 cur->next = ret->attrs;
2820 ret->attrs = cur;
2821 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002822 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00002823 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002824 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002825 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002826 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002827 }
2828 }
2829 child = child->next;
2830 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002831 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002832 return(ret);
2833}
2834
2835/**
2836 * xmlRelaxNGParsePatterns:
2837 * @ctxt: a Relax-NG parser context
2838 * @nodes: list of nodes
Daniel Veillard154877e2003-01-30 12:17:05 +00002839 * @group: use an implicit <group> for elements
Daniel Veillard6eadf632003-01-23 18:29:16 +00002840 *
2841 * parse the content of a RelaxNG start node.
2842 *
2843 * Returns the definition pointer or NULL in case of error.
2844 */
2845static xmlRelaxNGDefinePtr
Daniel Veillard154877e2003-01-30 12:17:05 +00002846xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes,
2847 int group) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002848 xmlRelaxNGDefinePtr def = NULL, last = NULL, cur, parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002849
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002850 parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002851 while (nodes != NULL) {
2852 if (IS_RELAXNG(nodes, "element")) {
2853 cur = xmlRelaxNGParseElement(ctxt, nodes);
2854 if (def == NULL) {
2855 def = last = cur;
2856 } else {
Daniel Veillard154877e2003-01-30 12:17:05 +00002857 if ((group == 1) && (def->type == XML_RELAXNG_ELEMENT) &&
2858 (def == last)) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00002859 def = xmlRelaxNGNewDefine(ctxt, nodes);
2860 def->type = XML_RELAXNG_GROUP;
2861 def->content = last;
2862 }
2863 last->next = cur;
2864 last = cur;
2865 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002866 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002867 } else {
2868 cur = xmlRelaxNGParsePattern(ctxt, nodes);
Daniel Veillard419a7682003-02-03 23:22:49 +00002869 if (cur != NULL) {
2870 if (def == NULL) {
2871 def = last = cur;
2872 } else {
2873 last->next = cur;
2874 last = cur;
2875 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002876 }
2877 }
2878 nodes = nodes->next;
2879 }
2880 return(def);
2881}
2882
2883/**
2884 * xmlRelaxNGParseStart:
2885 * @ctxt: a Relax-NG parser context
2886 * @nodes: start children nodes
2887 *
2888 * parse the content of a RelaxNG start node.
2889 *
2890 * Returns 0 in case of success, -1 in case of error
2891 */
2892static int
2893xmlRelaxNGParseStart(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
2894 int ret = 0;
2895 xmlRelaxNGDefinePtr def = NULL;
2896
2897 while (nodes != NULL) {
2898 if (IS_RELAXNG(nodes, "empty")) {
2899 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002900 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002901 } else if (IS_RELAXNG(nodes, "notAllowed")) {
2902 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002903 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002904 } else {
Daniel Veillard154877e2003-01-30 12:17:05 +00002905 def = xmlRelaxNGParsePatterns(ctxt, nodes, 1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002906 ctxt->grammar->start = def;
2907 }
2908 nodes = nodes->next;
2909 }
2910 return(ret);
2911}
2912
2913/**
2914 * xmlRelaxNGParseGrammarContent:
2915 * @ctxt: a Relax-NG parser context
2916 * @nodes: grammar children nodes
2917 *
2918 * parse the content of a RelaxNG grammar node.
2919 *
2920 * Returns 0 in case of success, -1 in case of error
2921 */
2922static int
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002923xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes)
Daniel Veillard6eadf632003-01-23 18:29:16 +00002924{
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002925 int ret = 0, tmp;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002926
2927 if (nodes == NULL) {
2928 if (ctxt->error != NULL)
2929 ctxt->error(ctxt->userData,
2930 "grammar has no children\n");
2931 ctxt->nbErrors++;
2932 return(-1);
2933 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002934 while (nodes != NULL) {
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002935 if (IS_RELAXNG(nodes, "start")) {
2936 if (nodes->children == NULL) {
2937 if (ctxt->error != NULL)
2938 ctxt->error(ctxt->userData,
2939 "grammar has no children\n");
2940 ctxt->nbErrors++;
2941 } else {
2942 tmp = xmlRelaxNGParseStart(ctxt, nodes->children);
2943 if (tmp != 0)
2944 ret = -1;
2945 }
2946 } else if (IS_RELAXNG(nodes, "define")) {
2947 tmp = xmlRelaxNGParseDefine(ctxt, nodes);
2948 if (tmp != 0)
2949 ret = -1;
2950 } else if (IS_RELAXNG(nodes, "include")) {
2951 tmp = xmlRelaxNGParseInclude(ctxt, nodes);
2952 if (tmp != 0)
2953 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002954 } else {
2955 if (ctxt->error != NULL)
2956 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002957 "grammar has unexpected child %s\n", nodes->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002958 ctxt->nbErrors++;
2959 ret = -1;
2960 }
2961 nodes = nodes->next;
2962 }
2963 return (ret);
2964}
2965
2966/**
2967 * xmlRelaxNGCheckReference:
2968 * @ref: the ref
2969 * @ctxt: a Relax-NG parser context
2970 * @name: the name associated to the defines
2971 *
2972 * Applies the 4.17. combine attribute rule for all the define
2973 * element of a given grammar using the same name.
2974 */
2975static void
2976xmlRelaxNGCheckReference(xmlRelaxNGDefinePtr ref,
2977 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
2978 xmlRelaxNGGrammarPtr grammar;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002979 xmlRelaxNGDefinePtr def, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002980
2981 grammar = ctxt->grammar;
2982 if (grammar == NULL) {
2983 if (ctxt->error != NULL)
2984 ctxt->error(ctxt->userData,
2985 "Internal error: no grammar in CheckReference %s\n",
2986 name);
2987 ctxt->nbErrors++;
2988 return;
2989 }
2990 if (ref->content != NULL) {
2991 if (ctxt->error != NULL)
2992 ctxt->error(ctxt->userData,
2993 "Internal error: reference has content in CheckReference %s\n",
2994 name);
2995 ctxt->nbErrors++;
2996 return;
2997 }
2998 if (grammar->defs != NULL) {
2999 def = xmlHashLookup(grammar->defs, name);
3000 if (def != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00003001 cur = ref;
3002 while (cur != NULL) {
3003 cur->content = def;
3004 cur = cur->nextHash;
3005 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003006 } else {
3007 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003008 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003009 }
3010 }
3011 /*
3012 * TODO: make a closure and verify there is no loop !
3013 */
3014}
3015
3016/**
3017 * xmlRelaxNGCheckCombine:
3018 * @define: the define(s) list
3019 * @ctxt: a Relax-NG parser context
3020 * @name: the name associated to the defines
3021 *
3022 * Applies the 4.17. combine attribute rule for all the define
3023 * element of a given grammar using the same name.
3024 */
3025static void
3026xmlRelaxNGCheckCombine(xmlRelaxNGDefinePtr define,
3027 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
3028 xmlChar *combine;
3029 int choiceOrInterleave = -1;
3030 int missing = 0;
3031 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
3032
3033 if (define->nextHash == NULL)
3034 return;
3035 cur = define;
3036 while (cur != NULL) {
3037 combine = xmlGetProp(cur->node, BAD_CAST "combine");
3038 if (combine != NULL) {
3039 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3040 if (choiceOrInterleave == -1)
3041 choiceOrInterleave = 1;
3042 else if (choiceOrInterleave == 0) {
3043 if (ctxt->error != NULL)
3044 ctxt->error(ctxt->userData,
3045 "Defines for %s use both 'choice' and 'interleave'\n",
3046 name);
3047 ctxt->nbErrors++;
3048 }
Daniel Veillard154877e2003-01-30 12:17:05 +00003049 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003050 if (choiceOrInterleave == -1)
3051 choiceOrInterleave = 0;
3052 else if (choiceOrInterleave == 1) {
3053 if (ctxt->error != NULL)
3054 ctxt->error(ctxt->userData,
3055 "Defines for %s use both 'choice' and 'interleave'\n",
3056 name);
3057 ctxt->nbErrors++;
3058 }
3059 } else {
3060 if (ctxt->error != NULL)
3061 ctxt->error(ctxt->userData,
3062 "Defines for %s use unknown combine value '%s''\n",
3063 name, combine);
3064 ctxt->nbErrors++;
3065 }
3066 xmlFree(combine);
3067 } else {
3068 if (missing == 0)
3069 missing = 1;
3070 else {
3071 if (ctxt->error != NULL)
3072 ctxt->error(ctxt->userData,
3073 "Some defines for %s lacks the combine attribute\n",
3074 name);
3075 ctxt->nbErrors++;
3076 }
3077 }
3078
3079 cur = cur->nextHash;
3080 }
3081#ifdef DEBUG
3082 xmlGenericError(xmlGenericErrorContext,
3083 "xmlRelaxNGCheckCombine(): merging %s defines: %d\n",
3084 name, choiceOrInterleave);
3085#endif
3086 if (choiceOrInterleave == -1)
3087 choiceOrInterleave = 0;
3088 cur = xmlRelaxNGNewDefine(ctxt, define->node);
3089 if (cur == NULL)
3090 return;
3091 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003092 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard154877e2003-01-30 12:17:05 +00003093 else
3094 cur->type = XML_RELAXNG_CHOICE;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003095 tmp = define;
3096 last = NULL;
3097 while (tmp != NULL) {
3098 if (tmp->content != NULL) {
3099 if (tmp->content->next != NULL) {
3100 /*
3101 * we need first to create a wrapper.
3102 */
3103 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
3104 if (tmp2 == NULL)
3105 break;
3106 tmp2->type = XML_RELAXNG_GROUP;
3107 tmp2->content = tmp->content;
3108 } else {
3109 tmp2 = tmp->content;
3110 }
3111 if (last == NULL) {
3112 cur->content = tmp2;
3113 } else {
3114 last->next = tmp2;
3115 }
3116 last = tmp2;
3117 tmp->content = NULL;
3118 }
3119 tmp = tmp->nextHash;
3120 }
3121 define->content = cur;
Daniel Veillard154877e2003-01-30 12:17:05 +00003122 if (choiceOrInterleave == 0) {
3123 if (ctxt->interleaves == NULL)
3124 ctxt->interleaves = xmlHashCreate(10);
3125 if (ctxt->interleaves == NULL) {
3126 if (ctxt->error != NULL)
3127 ctxt->error(ctxt->userData,
3128 "Failed to create interleaves hash table\n");
3129 ctxt->nbErrors++;
3130 } else {
3131 char tmpname[32];
3132
3133 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
3134 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
3135 if (ctxt->error != NULL)
3136 ctxt->error(ctxt->userData,
3137 "Failed to add %s to hash table\n", tmpname);
3138 ctxt->nbErrors++;
3139 }
3140 }
3141 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003142}
3143
3144/**
3145 * xmlRelaxNGCombineStart:
3146 * @ctxt: a Relax-NG parser context
3147 * @grammar: the grammar
3148 *
3149 * Applies the 4.17. combine rule for all the start
3150 * element of a given grammar.
3151 */
3152static void
3153xmlRelaxNGCombineStart(xmlRelaxNGParserCtxtPtr ctxt,
3154 xmlRelaxNGGrammarPtr grammar) {
3155 xmlRelaxNGDefinePtr starts;
3156 xmlChar *combine;
3157 int choiceOrInterleave = -1;
3158 int missing = 0;
3159 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
3160
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003161 starts = grammar->startList;
3162 if ((starts == NULL) || (starts->nextHash == NULL))
Daniel Veillard6eadf632003-01-23 18:29:16 +00003163 return;
3164 cur = starts;
3165 while (cur != NULL) {
3166 combine = xmlGetProp(cur->node, BAD_CAST "combine");
3167 if (combine != NULL) {
3168 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3169 if (choiceOrInterleave == -1)
3170 choiceOrInterleave = 1;
3171 else if (choiceOrInterleave == 0) {
3172 if (ctxt->error != NULL)
3173 ctxt->error(ctxt->userData,
3174 "<start> use both 'choice' and 'interleave'\n");
3175 ctxt->nbErrors++;
3176 }
3177 } else if (xmlStrEqual(combine, BAD_CAST "choice")) {
3178 if (choiceOrInterleave == -1)
3179 choiceOrInterleave = 0;
3180 else if (choiceOrInterleave == 1) {
3181 if (ctxt->error != NULL)
3182 ctxt->error(ctxt->userData,
3183 "<start> use both 'choice' and 'interleave'\n");
3184 ctxt->nbErrors++;
3185 }
3186 } else {
3187 if (ctxt->error != NULL)
3188 ctxt->error(ctxt->userData,
3189 "<start> uses unknown combine value '%s''\n", combine);
3190 ctxt->nbErrors++;
3191 }
3192 xmlFree(combine);
3193 } else {
3194 if (missing == 0)
3195 missing = 1;
3196 else {
3197 if (ctxt->error != NULL)
3198 ctxt->error(ctxt->userData,
3199 "Some <start> elements lacks the combine attribute\n");
3200 ctxt->nbErrors++;
3201 }
3202 }
3203
3204 cur = cur->nextHash;
3205 }
3206#ifdef DEBUG
3207 xmlGenericError(xmlGenericErrorContext,
3208 "xmlRelaxNGCombineStart(): merging <start>: %d\n",
3209 choiceOrInterleave);
3210#endif
3211 if (choiceOrInterleave == -1)
3212 choiceOrInterleave = 0;
3213 cur = xmlRelaxNGNewDefine(ctxt, starts->node);
3214 if (cur == NULL)
3215 return;
3216 if (choiceOrInterleave == 0)
3217 cur->type = XML_RELAXNG_CHOICE;
3218 else
3219 cur->type = XML_RELAXNG_INTERLEAVE;
3220 tmp = starts;
3221 last = NULL;
3222 while (tmp != NULL) {
3223 if (tmp->content != NULL) {
3224 if (tmp->content->next != NULL) {
3225 /*
3226 * we need first to create a wrapper.
3227 */
3228 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
3229 if (tmp2 == NULL)
3230 break;
3231 tmp2->type = XML_RELAXNG_GROUP;
3232 tmp2->content = tmp->content;
3233 } else {
3234 tmp2 = tmp->content;
3235 }
3236 if (last == NULL) {
3237 cur->content = tmp2;
3238 } else {
3239 last->next = tmp2;
3240 }
3241 last = tmp2;
3242 tmp->content = NULL;
3243 }
3244 tmp = tmp->nextHash;
3245 }
3246 starts->content = cur;
3247}
3248
3249/**
3250 * xmlRelaxNGParseGrammar:
3251 * @ctxt: a Relax-NG parser context
3252 * @nodes: grammar children nodes
3253 *
3254 * parse a Relax-NG <grammar> node
3255 *
3256 * Returns the internal xmlRelaxNGGrammarPtr built or
3257 * NULL in case of error
3258 */
3259static xmlRelaxNGGrammarPtr
3260xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
3261 xmlRelaxNGGrammarPtr ret, tmp, old;
3262
Daniel Veillard6eadf632003-01-23 18:29:16 +00003263 ret = xmlRelaxNGNewGrammar(ctxt);
3264 if (ret == NULL)
3265 return(NULL);
3266
3267 /*
3268 * Link the new grammar in the tree
3269 */
3270 ret->parent = ctxt->grammar;
3271 if (ctxt->grammar != NULL) {
3272 tmp = ctxt->grammar->children;
3273 if (tmp == NULL) {
3274 ctxt->grammar->children = ret;
3275 } else {
3276 while (tmp->next != NULL)
3277 tmp = tmp->next;
3278 tmp->next = ret;
3279 }
3280 }
3281
3282 old = ctxt->grammar;
3283 ctxt->grammar = ret;
3284 xmlRelaxNGParseGrammarContent(ctxt, nodes);
3285 ctxt->grammar = ret;
3286
3287 /*
3288 * Apply 4.17 mergingd rules to defines and starts
3289 */
3290 xmlRelaxNGCombineStart(ctxt, ret);
3291 if (ret->defs != NULL) {
3292 xmlHashScan(ret->defs, (xmlHashScanner) xmlRelaxNGCheckCombine,
3293 ctxt);
3294 }
3295
3296 /*
3297 * link together defines and refs in this grammar
3298 */
3299 if (ret->refs != NULL) {
3300 xmlHashScan(ret->refs, (xmlHashScanner) xmlRelaxNGCheckReference,
3301 ctxt);
3302 }
3303 ctxt->grammar = old;
3304 return(ret);
3305}
3306
3307/**
3308 * xmlRelaxNGParseDocument:
3309 * @ctxt: a Relax-NG parser context
3310 * @node: the root node of the RelaxNG schema
3311 *
3312 * parse a Relax-NG definition resource and build an internal
3313 * xmlRelaxNG struture which can be used to validate instances.
3314 *
3315 * Returns the internal XML RelaxNG structure built or
3316 * NULL in case of error
3317 */
3318static xmlRelaxNGPtr
3319xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3320 xmlRelaxNGPtr schema = NULL;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003321 const xmlChar *olddefine;
Daniel Veillarde431a272003-01-29 23:02:33 +00003322 xmlRelaxNGGrammarPtr old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003323
3324 if ((ctxt == NULL) || (node == NULL))
3325 return (NULL);
3326
3327 schema = xmlRelaxNGNewRelaxNG(ctxt);
3328 if (schema == NULL)
3329 return(NULL);
3330
Daniel Veillard276be4a2003-01-24 01:03:34 +00003331 olddefine = ctxt->define;
3332 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003333 if (IS_RELAXNG(node, "grammar")) {
3334 schema->topgrammar = xmlRelaxNGParseGrammar(ctxt, node->children);
3335 } else {
3336 schema->topgrammar = xmlRelaxNGNewGrammar(ctxt);
3337 if (schema->topgrammar == NULL) {
3338 return(schema);
3339 }
3340 schema->topgrammar->parent = NULL;
Daniel Veillarde431a272003-01-29 23:02:33 +00003341 old = ctxt->grammar;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003342 ctxt->grammar = schema->topgrammar;
3343 xmlRelaxNGParseStart(ctxt, node);
Daniel Veillarde431a272003-01-29 23:02:33 +00003344 if (old != NULL)
3345 ctxt->grammar = old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003346 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003347 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003348
3349#ifdef DEBUG
3350 if (schema == NULL)
3351 xmlGenericError(xmlGenericErrorContext,
3352 "xmlRelaxNGParseDocument() failed\n");
3353#endif
3354
3355 return (schema);
3356}
3357
3358/************************************************************************
3359 * *
3360 * Reading RelaxNGs *
3361 * *
3362 ************************************************************************/
3363
3364/**
3365 * xmlRelaxNGNewParserCtxt:
3366 * @URL: the location of the schema
3367 *
3368 * Create an XML RelaxNGs parse context for that file/resource expected
3369 * to contain an XML RelaxNGs file.
3370 *
3371 * Returns the parser context or NULL in case of error
3372 */
3373xmlRelaxNGParserCtxtPtr
3374xmlRelaxNGNewParserCtxt(const char *URL) {
3375 xmlRelaxNGParserCtxtPtr ret;
3376
3377 if (URL == NULL)
3378 return(NULL);
3379
3380 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
3381 if (ret == NULL) {
3382 xmlGenericError(xmlGenericErrorContext,
3383 "Failed to allocate new schama parser context for %s\n", URL);
3384 return (NULL);
3385 }
3386 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
3387 ret->URL = xmlStrdup((const xmlChar *)URL);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003388 ret->error = xmlGenericError;
3389 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003390 return (ret);
3391}
3392
3393/**
3394 * xmlRelaxNGNewMemParserCtxt:
3395 * @buffer: a pointer to a char array containing the schemas
3396 * @size: the size of the array
3397 *
3398 * Create an XML RelaxNGs parse context for that memory buffer expected
3399 * to contain an XML RelaxNGs file.
3400 *
3401 * Returns the parser context or NULL in case of error
3402 */
3403xmlRelaxNGParserCtxtPtr
3404xmlRelaxNGNewMemParserCtxt(const char *buffer, int size) {
3405 xmlRelaxNGParserCtxtPtr ret;
3406
3407 if ((buffer == NULL) || (size <= 0))
3408 return(NULL);
3409
3410 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
3411 if (ret == NULL) {
3412 xmlGenericError(xmlGenericErrorContext,
3413 "Failed to allocate new schama parser context\n");
3414 return (NULL);
3415 }
3416 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
3417 ret->buffer = buffer;
3418 ret->size = size;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003419 ret->error = xmlGenericError;
3420 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003421 return (ret);
3422}
3423
3424/**
3425 * xmlRelaxNGFreeParserCtxt:
3426 * @ctxt: the schema parser context
3427 *
3428 * Free the resources associated to the schema parser context
3429 */
3430void
3431xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxtPtr ctxt) {
3432 if (ctxt == NULL)
3433 return;
3434 if (ctxt->URL != NULL)
3435 xmlFree(ctxt->URL);
3436 if (ctxt->doc != NULL)
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003437 xmlFreeDoc(ctxt->document);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003438 if (ctxt->interleaves != NULL)
3439 xmlHashFree(ctxt->interleaves, NULL);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003440 if (ctxt->documents != NULL)
3441 xmlHashFree(ctxt->documents, (xmlHashDeallocator)
3442 xmlRelaxNGFreeDocument);
3443 if (ctxt->docTab != NULL)
3444 xmlFree(ctxt->docTab);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003445 if (ctxt->incTab != NULL)
3446 xmlFree(ctxt->incTab);
Daniel Veillard419a7682003-02-03 23:22:49 +00003447 if (ctxt->defTab != NULL) {
3448 int i;
3449
3450 for (i = 0;i < ctxt->defNr;i++)
3451 xmlRelaxNGFreeDefine(ctxt->defTab[i]);
3452 xmlFree(ctxt->defTab);
3453 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003454 xmlFree(ctxt);
3455}
3456
Daniel Veillard6eadf632003-01-23 18:29:16 +00003457/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003458 * xmlRelaxNGCleanupDoc:
3459 * @ctxt: a Relax-NG parser context
3460 * @doc: an xmldocPtr document pointer
Daniel Veillard6eadf632003-01-23 18:29:16 +00003461 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003462 * Cleanup the document from unwanted nodes for parsing, resolve
3463 * Include and externalRef lookups.
Daniel Veillard6eadf632003-01-23 18:29:16 +00003464 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003465 * Returns the cleaned up document or NULL in case of error
Daniel Veillard6eadf632003-01-23 18:29:16 +00003466 */
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003467static xmlDocPtr
3468xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt, xmlDocPtr doc) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003469 xmlNodePtr root, cur, delete;
3470
Daniel Veillard6eadf632003-01-23 18:29:16 +00003471 /*
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003472 * Extract the root
Daniel Veillard6eadf632003-01-23 18:29:16 +00003473 */
3474 root = xmlDocGetRootElement(doc);
3475 if (root == NULL) {
3476 if (ctxt->error != NULL)
3477 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
3478 ctxt->URL);
3479 ctxt->nbErrors++;
3480 return (NULL);
3481 }
3482
3483 /*
3484 * Remove all the blank text nodes
3485 */
3486 delete = NULL;
3487 cur = root;
3488 while (cur != NULL) {
3489 if (delete != NULL) {
3490 xmlUnlinkNode(delete);
3491 xmlFreeNode(delete);
3492 delete = NULL;
3493 }
3494 if (cur->type == XML_ELEMENT_NODE) {
3495 /*
3496 * Simplification 4.1. Annotations
3497 */
3498 if ((cur->ns == NULL) ||
3499 (!xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
3500 delete = cur;
3501 goto skip_children;
3502 } else {
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003503 if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003504 xmlChar *href, *ns, *base, *URL;
3505 xmlRelaxNGDocumentPtr docu;
3506
3507 ns = xmlGetProp(cur, BAD_CAST "ns");
3508 href = xmlGetProp(cur, BAD_CAST "href");
3509 if (href == NULL) {
3510 if (ctxt->error != NULL)
3511 ctxt->error(ctxt->userData,
3512 "xmlRelaxNGParse: externalRef has no href attribute\n");
3513 ctxt->nbErrors++;
3514 delete = cur;
3515 goto skip_children;
3516 }
3517 base = xmlNodeGetBase(cur->doc, cur);
3518 URL = xmlBuildURI(href, base);
3519 if (URL == NULL) {
3520 if (ctxt->error != NULL)
3521 ctxt->error(ctxt->userData,
3522 "Failed to compute URL for externalRef %s\n", href);
3523 ctxt->nbErrors++;
3524 if (href != NULL)
3525 xmlFree(href);
3526 if (base != NULL)
3527 xmlFree(base);
3528 delete = cur;
3529 goto skip_children;
3530 }
3531 if (href != NULL)
3532 xmlFree(href);
3533 if (base != NULL)
3534 xmlFree(base);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003535 docu = xmlRelaxNGLoadExternalRef(ctxt, URL, ns);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003536 if (docu == NULL) {
3537 if (ctxt->error != NULL)
3538 ctxt->error(ctxt->userData,
3539 "Failed to load externalRef %s\n", URL);
3540 ctxt->nbErrors++;
3541 xmlFree(URL);
3542 delete = cur;
3543 goto skip_children;
3544 }
3545 xmlFree(URL);
3546 cur->_private = docu;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003547 } else if (xmlStrEqual(cur->name, BAD_CAST "include")) {
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003548 xmlChar *href, *base, *URL;
3549 xmlRelaxNGIncludePtr incl;
3550
3551 href = xmlGetProp(cur, BAD_CAST "href");
3552 if (href == NULL) {
3553 if (ctxt->error != NULL)
3554 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003555 "xmlRelaxNGParse: include has no href attribute\n");
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003556 ctxt->nbErrors++;
3557 delete = cur;
3558 goto skip_children;
3559 }
3560 base = xmlNodeGetBase(cur->doc, cur);
3561 URL = xmlBuildURI(href, base);
3562 if (URL == NULL) {
3563 if (ctxt->error != NULL)
3564 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003565 "Failed to compute URL for include %s\n", href);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003566 ctxt->nbErrors++;
3567 if (href != NULL)
3568 xmlFree(href);
3569 if (base != NULL)
3570 xmlFree(base);
3571 delete = cur;
3572 goto skip_children;
3573 }
3574 if (href != NULL)
3575 xmlFree(href);
3576 if (base != NULL)
3577 xmlFree(base);
3578 incl = xmlRelaxNGLoadInclude(ctxt, URL, cur);
3579 if (incl == NULL) {
3580 if (ctxt->error != NULL)
3581 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003582 "Failed to load include %s\n", URL);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003583 ctxt->nbErrors++;
3584 xmlFree(URL);
3585 delete = cur;
3586 goto skip_children;
3587 }
3588 xmlFree(URL);
3589 cur->_private = incl;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003590 } else if ((xmlStrEqual(cur->name, BAD_CAST "element")) ||
3591 (xmlStrEqual(cur->name, BAD_CAST "attribute"))) {
3592 xmlChar *name;
3593 xmlNodePtr text = NULL;
3594
3595 /*
3596 * Simplification 4.8. name attribute of element
3597 * and attribute elements
3598 */
3599 name = xmlGetProp(cur, BAD_CAST "name");
3600 if (name != NULL) {
3601 if (cur->children == NULL) {
3602 text = xmlNewChild(cur, cur->ns, BAD_CAST "name",
3603 name);
3604 } else {
3605 xmlNodePtr node;
3606 node = xmlNewNode(cur->ns, BAD_CAST "name");
3607 if (node != NULL) {
3608 xmlAddPrevSibling(cur->children, node);
3609 text = xmlNewText(name);
3610 xmlAddChild(node, text);
3611 text = node;
3612 }
3613 }
3614 xmlUnsetProp(cur, BAD_CAST "name");
3615 xmlFree(name);
3616 }
3617 if (xmlStrEqual(cur->name, BAD_CAST "attribute")) {
3618 if (text == NULL) {
3619 text = cur->children;
3620 while (text != NULL) {
3621 if ((text->type == XML_ELEMENT_NODE) &&
3622 (xmlStrEqual(text->name, BAD_CAST "name")))
3623 break;
3624 text = text->next;
3625 }
3626 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003627 if (text != NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003628 xmlSetProp(text, BAD_CAST "ns", BAD_CAST "");
3629 }
3630 }
3631 } else if ((xmlStrEqual(cur->name, BAD_CAST "name")) ||
3632 (xmlStrEqual(cur->name, BAD_CAST "nsName")) ||
3633 (xmlStrEqual(cur->name, BAD_CAST "value"))) {
3634 /*
3635 * Simplification 4.8. name attribute of element
3636 * and attribute elements
3637 */
3638 if (xmlHasProp(cur, BAD_CAST "ns") == NULL) {
3639 xmlNodePtr node;
3640 xmlChar *ns = NULL;
3641
3642 node = cur->parent;
3643 while ((node != NULL) &&
3644 (node->type == XML_ELEMENT_NODE)) {
3645 ns = xmlGetProp(node, BAD_CAST "ns");
3646 if (ns != NULL) {
3647 break;
3648 }
3649 node = node->parent;
3650 }
3651 if (ns == NULL) {
3652 xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
3653 } else {
3654 xmlSetProp(cur, BAD_CAST "ns", ns);
3655 xmlFree(ns);
3656 }
3657 }
3658 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
3659 xmlChar *name, *local, *prefix;
3660
3661 /*
3662 * Simplification: 4.10. QNames
3663 */
3664 name = xmlNodeGetContent(cur);
3665 if (name != NULL) {
3666 local = xmlSplitQName2(name, &prefix);
3667 if (local != NULL) {
3668 xmlNsPtr ns;
3669
3670 ns = xmlSearchNs(cur->doc, cur, prefix);
3671 if (ns == NULL) {
3672 if (ctxt->error != NULL)
3673 ctxt->error(ctxt->userData,
3674 "xmlRelaxNGParse: no namespace for prefix %s\n", prefix);
3675 ctxt->nbErrors++;
3676 } else {
3677 xmlSetProp(cur, BAD_CAST "ns", ns->href);
3678 xmlNodeSetContent(cur, local);
3679 }
3680 xmlFree(local);
3681 xmlFree(prefix);
3682 }
3683 xmlFree(name);
3684 }
3685 }
3686 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003687 /*
3688 * Thisd is not an else since "include" is transformed
3689 * into a div
3690 */
3691 if (xmlStrEqual(cur->name, BAD_CAST "div")) {
3692 /*
3693 * implements rule 4.11
3694 */
3695 xmlNodePtr child, ins, tmp;
3696
3697 child = cur->children;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003698 ins = cur;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003699 while (child != NULL) {
3700 tmp = child->next;
3701 xmlUnlinkNode(child);
3702 ins = xmlAddNextSibling(ins, child);
3703 child = tmp;
3704 }
3705 delete = cur;
3706 goto skip_children;
3707 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003708 }
3709 }
3710 /*
3711 * Simplification 4.2 whitespaces
3712 */
3713 else if (cur->type == XML_TEXT_NODE) {
3714 if (IS_BLANK_NODE(cur)) {
3715 if (cur->parent->type == XML_ELEMENT_NODE) {
3716 if ((!xmlStrEqual(cur->parent->name, BAD_CAST "value")) &&
3717 (!xmlStrEqual(cur->parent->name, BAD_CAST "param")))
3718 delete = cur;
3719 } else {
3720 delete = cur;
3721 goto skip_children;
3722 }
3723 }
3724 } else if (cur->type != XML_CDATA_SECTION_NODE) {
3725 delete = cur;
3726 goto skip_children;
3727 }
3728
3729 /*
3730 * Skip to next node
3731 */
3732 if (cur->children != NULL) {
3733 if ((cur->children->type != XML_ENTITY_DECL) &&
3734 (cur->children->type != XML_ENTITY_REF_NODE) &&
3735 (cur->children->type != XML_ENTITY_NODE)) {
3736 cur = cur->children;
3737 continue;
3738 }
3739 }
3740skip_children:
3741 if (cur->next != NULL) {
3742 cur = cur->next;
3743 continue;
3744 }
3745
3746 do {
3747 cur = cur->parent;
3748 if (cur == NULL)
3749 break;
3750 if (cur == root) {
3751 cur = NULL;
3752 break;
3753 }
3754 if (cur->next != NULL) {
3755 cur = cur->next;
3756 break;
3757 }
3758 } while (cur != NULL);
3759 }
3760 if (delete != NULL) {
3761 xmlUnlinkNode(delete);
3762 xmlFreeNode(delete);
3763 delete = NULL;
3764 }
3765
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003766 return(doc);
3767}
3768
3769/**
3770 * xmlRelaxNGParse:
3771 * @ctxt: a Relax-NG parser context
3772 *
3773 * parse a schema definition resource and build an internal
3774 * XML Shema struture which can be used to validate instances.
3775 * *WARNING* this interface is highly subject to change
3776 *
3777 * Returns the internal XML RelaxNG structure built from the resource or
3778 * NULL in case of error
3779 */
3780xmlRelaxNGPtr
3781xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt)
3782{
3783 xmlRelaxNGPtr ret = NULL;
3784 xmlDocPtr doc;
3785 xmlNodePtr root;
3786
3787 xmlRelaxNGInitTypes();
3788
3789 if (ctxt == NULL)
3790 return (NULL);
3791
3792 /*
3793 * First step is to parse the input document into an DOM/Infoset
3794 */
3795 if (ctxt->URL != NULL) {
3796 doc = xmlParseFile((const char *) ctxt->URL);
3797 if (doc == NULL) {
3798 if (ctxt->error != NULL)
3799 ctxt->error(ctxt->userData,
3800 "xmlRelaxNGParse: could not load %s\n", ctxt->URL);
3801 ctxt->nbErrors++;
3802 return (NULL);
3803 }
3804 } else if (ctxt->buffer != NULL) {
3805 doc = xmlParseMemory(ctxt->buffer, ctxt->size);
3806 if (doc == NULL) {
3807 if (ctxt->error != NULL)
3808 ctxt->error(ctxt->userData,
3809 "xmlRelaxNGParse: could not parse schemas\n");
3810 ctxt->nbErrors++;
3811 return (NULL);
3812 }
3813 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
3814 ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
3815 } else {
3816 if (ctxt->error != NULL)
3817 ctxt->error(ctxt->userData,
3818 "xmlRelaxNGParse: nothing to parse\n");
3819 ctxt->nbErrors++;
3820 return (NULL);
3821 }
3822 ctxt->document = doc;
3823
3824 /*
3825 * Some preprocessing of the document content
3826 */
3827 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
3828 if (doc == NULL) {
3829 xmlFreeDoc(ctxt->document);
3830 ctxt->document = NULL;
3831 return(NULL);
3832 }
3833
Daniel Veillard6eadf632003-01-23 18:29:16 +00003834 /*
3835 * Then do the parsing for good
3836 */
3837 root = xmlDocGetRootElement(doc);
3838 if (root == NULL) {
3839 if (ctxt->error != NULL)
3840 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
3841 ctxt->URL);
3842 ctxt->nbErrors++;
3843 return (NULL);
3844 }
3845 ret = xmlRelaxNGParseDocument(ctxt, root);
3846 if (ret == NULL)
3847 return(NULL);
3848
3849 /*
3850 * Check the ref/defines links
3851 */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003852 /*
3853 * try to preprocess interleaves
3854 */
3855 if (ctxt->interleaves != NULL) {
3856 xmlHashScan(ctxt->interleaves,
3857 (xmlHashScanner)xmlRelaxNGComputeInterleaves, ctxt);
3858 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003859
3860 /*
3861 * if there was a parsing error return NULL
3862 */
3863 if (ctxt->nbErrors > 0) {
3864 xmlRelaxNGFree(ret);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003865 ctxt->document = NULL;
3866 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003867 return(NULL);
3868 }
3869
3870 /*
3871 * Transfer the pointer for cleanup at the schema level.
3872 */
3873 ret->doc = doc;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003874 ctxt->document = NULL;
3875 ret->documents = ctxt->documents;
3876 ctxt->documents = NULL;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003877 ret->includes = ctxt->includes;
3878 ctxt->includes = NULL;
Daniel Veillard419a7682003-02-03 23:22:49 +00003879 ret->defNr = ctxt->defNr;
3880 ret->defTab = ctxt->defTab;
3881 ctxt->defTab = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003882
3883 return (ret);
3884}
3885
3886/**
3887 * xmlRelaxNGSetParserErrors:
3888 * @ctxt: a Relax-NG validation context
3889 * @err: the error callback
3890 * @warn: the warning callback
3891 * @ctx: contextual data for the callbacks
3892 *
3893 * Set the callback functions used to handle errors for a validation context
3894 */
3895void
3896xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
3897 xmlRelaxNGValidityErrorFunc err,
3898 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
3899 if (ctxt == NULL)
3900 return;
3901 ctxt->error = err;
3902 ctxt->warning = warn;
3903 ctxt->userData = ctx;
3904}
3905/************************************************************************
3906 * *
3907 * Dump back a compiled form *
3908 * *
3909 ************************************************************************/
3910static void xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define);
3911
3912/**
3913 * xmlRelaxNGDumpDefines:
3914 * @output: the file output
3915 * @defines: a list of define structures
3916 *
3917 * Dump a RelaxNG structure back
3918 */
3919static void
3920xmlRelaxNGDumpDefines(FILE * output, xmlRelaxNGDefinePtr defines) {
3921 while (defines != NULL) {
3922 xmlRelaxNGDumpDefine(output, defines);
3923 defines = defines->next;
3924 }
3925}
3926
3927/**
3928 * xmlRelaxNGDumpDefine:
3929 * @output: the file output
3930 * @define: a define structure
3931 *
3932 * Dump a RelaxNG structure back
3933 */
3934static void
3935xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define) {
3936 if (define == NULL)
3937 return;
3938 switch(define->type) {
3939 case XML_RELAXNG_EMPTY:
3940 fprintf(output, "<empty/>\n");
3941 break;
3942 case XML_RELAXNG_NOT_ALLOWED:
3943 fprintf(output, "<notAllowed/>\n");
3944 break;
3945 case XML_RELAXNG_TEXT:
3946 fprintf(output, "<text/>\n");
3947 break;
3948 case XML_RELAXNG_ELEMENT:
3949 fprintf(output, "<element>\n");
3950 if (define->name != NULL) {
3951 fprintf(output, "<name");
3952 if (define->ns != NULL)
3953 fprintf(output, " ns=\"%s\"", define->ns);
3954 fprintf(output, ">%s</name>\n", define->name);
3955 }
3956 xmlRelaxNGDumpDefines(output, define->attrs);
3957 xmlRelaxNGDumpDefines(output, define->content);
3958 fprintf(output, "</element>\n");
3959 break;
3960 case XML_RELAXNG_LIST:
3961 fprintf(output, "<list>\n");
3962 xmlRelaxNGDumpDefines(output, define->content);
3963 fprintf(output, "</list>\n");
3964 break;
3965 case XML_RELAXNG_ONEORMORE:
3966 fprintf(output, "<oneOrMore>\n");
3967 xmlRelaxNGDumpDefines(output, define->content);
3968 fprintf(output, "</oneOrMore>\n");
3969 break;
3970 case XML_RELAXNG_ZEROORMORE:
3971 fprintf(output, "<zeroOrMore>\n");
3972 xmlRelaxNGDumpDefines(output, define->content);
3973 fprintf(output, "</zeroOrMore>\n");
3974 break;
3975 case XML_RELAXNG_CHOICE:
3976 fprintf(output, "<choice>\n");
3977 xmlRelaxNGDumpDefines(output, define->content);
3978 fprintf(output, "</choice>\n");
3979 break;
3980 case XML_RELAXNG_GROUP:
3981 fprintf(output, "<group>\n");
3982 xmlRelaxNGDumpDefines(output, define->content);
3983 fprintf(output, "</group>\n");
3984 break;
3985 case XML_RELAXNG_INTERLEAVE:
3986 fprintf(output, "<interleave>\n");
3987 xmlRelaxNGDumpDefines(output, define->content);
3988 fprintf(output, "</interleave>\n");
3989 break;
3990 case XML_RELAXNG_OPTIONAL:
3991 fprintf(output, "<optional>\n");
3992 xmlRelaxNGDumpDefines(output, define->content);
3993 fprintf(output, "</optional>\n");
3994 break;
3995 case XML_RELAXNG_ATTRIBUTE:
3996 fprintf(output, "<attribute>\n");
3997 xmlRelaxNGDumpDefines(output, define->content);
3998 fprintf(output, "</attribute>\n");
3999 break;
4000 case XML_RELAXNG_DEF:
4001 fprintf(output, "<define");
4002 if (define->name != NULL)
4003 fprintf(output, " name=\"%s\"", define->name);
4004 fprintf(output, ">\n");
4005 xmlRelaxNGDumpDefines(output, define->content);
4006 fprintf(output, "</define>\n");
4007 break;
4008 case XML_RELAXNG_REF:
4009 fprintf(output, "<ref");
4010 if (define->name != NULL)
4011 fprintf(output, " name=\"%s\"", define->name);
4012 fprintf(output, ">\n");
4013 xmlRelaxNGDumpDefines(output, define->content);
4014 fprintf(output, "</ref>\n");
4015 break;
Daniel Veillard419a7682003-02-03 23:22:49 +00004016 case XML_RELAXNG_PARENTREF:
4017 fprintf(output, "<parentRef");
4018 if (define->name != NULL)
4019 fprintf(output, " name=\"%s\"", define->name);
4020 fprintf(output, ">\n");
4021 xmlRelaxNGDumpDefines(output, define->content);
4022 fprintf(output, "</parentRef>\n");
4023 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004024 case XML_RELAXNG_EXTERNALREF:
Daniel Veillarde431a272003-01-29 23:02:33 +00004025 fprintf(output, "<externalRef");
4026 xmlRelaxNGDumpDefines(output, define->content);
4027 fprintf(output, "</externalRef>\n");
4028 break;
4029 case XML_RELAXNG_DATATYPE:
Daniel Veillard6eadf632003-01-23 18:29:16 +00004030 case XML_RELAXNG_VALUE:
4031 TODO
4032 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004033 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00004034 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004035 TODO
4036 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004037 }
4038}
4039
4040/**
4041 * xmlRelaxNGDumpGrammar:
4042 * @output: the file output
4043 * @grammar: a grammar structure
4044 * @top: is this a top grammar
4045 *
4046 * Dump a RelaxNG structure back
4047 */
4048static void
4049xmlRelaxNGDumpGrammar(FILE * output, xmlRelaxNGGrammarPtr grammar, int top)
4050{
4051 if (grammar == NULL)
4052 return;
4053
4054 fprintf(output, "<grammar");
4055 if (top)
4056 fprintf(output,
4057 " xmlns=\"http://relaxng.org/ns/structure/1.0\"");
4058 switch(grammar->combine) {
4059 case XML_RELAXNG_COMBINE_UNDEFINED:
4060 break;
4061 case XML_RELAXNG_COMBINE_CHOICE:
4062 fprintf(output, " combine=\"choice\"");
4063 break;
4064 case XML_RELAXNG_COMBINE_INTERLEAVE:
4065 fprintf(output, " combine=\"interleave\"");
4066 break;
4067 default:
4068 fprintf(output, " <!-- invalid combine value -->");
4069 }
4070 fprintf(output, ">\n");
4071 if (grammar->start == NULL) {
4072 fprintf(output, " <!-- grammar had no start -->");
4073 } else {
4074 fprintf(output, "<start>\n");
4075 xmlRelaxNGDumpDefine(output, grammar->start);
4076 fprintf(output, "</start>\n");
4077 }
4078 /* TODO ? Dump the defines ? */
4079 fprintf(output, "</grammar>\n");
4080}
4081
4082/**
4083 * xmlRelaxNGDump:
4084 * @output: the file output
4085 * @schema: a schema structure
4086 *
4087 * Dump a RelaxNG structure back
4088 */
4089void
4090xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema)
4091{
4092 if (schema == NULL) {
4093 fprintf(output, "RelaxNG empty or failed to compile\n");
4094 return;
4095 }
4096 fprintf(output, "RelaxNG: ");
4097 if (schema->doc == NULL) {
4098 fprintf(output, "no document\n");
4099 } else if (schema->doc->URL != NULL) {
4100 fprintf(output, "%s\n", schema->doc->URL);
4101 } else {
4102 fprintf(output, "\n");
4103 }
4104 if (schema->topgrammar == NULL) {
4105 fprintf(output, "RelaxNG has no top grammar\n");
4106 return;
4107 }
4108 xmlRelaxNGDumpGrammar(output, schema->topgrammar, 1);
4109}
4110
4111/************************************************************************
4112 * *
4113 * Validation implementation *
4114 * *
4115 ************************************************************************/
4116static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
4117 xmlRelaxNGDefinePtr define);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004118static int xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
4119 xmlRelaxNGDefinePtr define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004120
4121/**
4122 * xmlRelaxNGSkipIgnored:
4123 * @ctxt: a schema validation context
4124 * @node: the top node.
4125 *
4126 * Skip ignorable nodes in that context
4127 *
4128 * Returns the new sibling or NULL in case of error.
4129 */
4130static xmlNodePtr
4131xmlRelaxNGSkipIgnored(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
4132 xmlNodePtr node) {
4133 /*
4134 * TODO complete and handle entities
4135 */
4136 while ((node != NULL) &&
4137 ((node->type == XML_COMMENT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004138 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00004139 ((node->type == XML_TEXT_NODE) &&
4140 (IS_BLANK_NODE(node))))) {
4141 node = node->next;
4142 }
4143 return(node);
4144}
4145
4146/**
Daniel Veillardedc91922003-01-26 00:52:04 +00004147 * xmlRelaxNGNormalize:
4148 * @ctxt: a schema validation context
4149 * @str: the string to normalize
4150 *
4151 * Implements the normalizeWhiteSpace( s ) function from
4152 * section 6.2.9 of the spec
4153 *
4154 * Returns the new string or NULL in case of error.
4155 */
4156static xmlChar *
4157xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *str) {
4158 xmlChar *ret, *p;
4159 const xmlChar *tmp;
4160 int len;
4161
4162 if (str == NULL)
4163 return(NULL);
4164 tmp = str;
4165 while (*tmp != 0) tmp++;
4166 len = tmp - str;
4167
4168 ret = (xmlChar *) xmlMalloc((len + 1) * sizeof(xmlChar));
4169 if (ret == NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00004170 if (ctxt != NULL) {
4171 VALID_CTXT();
4172 VALID_ERROR("xmlRelaxNGNormalize: out of memory\n");
4173 } else {
4174 xmlGenericError(xmlGenericErrorContext,
4175 "xmlRelaxNGNormalize: out of memory\n");
4176 }
Daniel Veillardedc91922003-01-26 00:52:04 +00004177 return(NULL);
4178 }
4179 p = ret;
4180 while (IS_BLANK(*str)) str++;
4181 while (*str != 0) {
4182 if (IS_BLANK(*str)) {
4183 while (IS_BLANK(*str)) str++;
4184 if (*str == 0)
4185 break;
4186 *p++ = ' ';
4187 } else
4188 *p++ = *str++;
4189 }
4190 *p = 0;
4191 return(ret);
4192}
4193
4194/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004195 * xmlRelaxNGValidateDatatype:
4196 * @ctxt: a Relax-NG validation context
4197 * @value: the string value
4198 * @type: the datatype definition
4199 *
4200 * Validate the given value against the dataype
4201 *
4202 * Returns 0 if the validation succeeded or an error code.
4203 */
4204static int
4205xmlRelaxNGValidateDatatype(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *value,
4206 xmlRelaxNGDefinePtr define) {
4207 int ret;
4208 xmlRelaxNGTypeLibraryPtr lib;
4209
4210 if ((define == NULL) || (define->data == NULL)) {
4211 return(-1);
4212 }
4213 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
4214 if (lib->check != NULL)
4215 ret = lib->check(lib->data, define->name, value);
4216 else
4217 ret = -1;
4218 if (ret < 0) {
4219 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004220 VALID_ERROR2("Internal: failed to validate type %s\n", define->name);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004221 return(-1);
4222 } else if (ret == 1) {
4223 ret = 0;
4224 } else {
4225 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004226 VALID_ERROR3("Type %s doesn't allow value %s\n", define->name, value);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004227 return(-1);
4228 ret = -1;
4229 }
4230 return(ret);
4231}
4232
4233/**
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004234 * xmlRelaxNGNextValue:
4235 * @ctxt: a Relax-NG validation context
4236 *
4237 * Skip to the next value when validating within a list
4238 *
4239 * Returns 0 if the operation succeeded or an error code.
4240 */
4241static int
4242xmlRelaxNGNextValue(xmlRelaxNGValidCtxtPtr ctxt) {
4243 xmlChar *cur;
4244
4245 cur = ctxt->state->value;
4246 if ((cur == NULL) || (ctxt->state->endvalue == NULL)) {
4247 ctxt->state->value = NULL;
Daniel Veillarde5b110b2003-02-04 14:43:39 +00004248 ctxt->state->endvalue = NULL;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004249 return(0);
4250 }
4251 while (*cur != 0) cur++;
4252 while ((cur != ctxt->state->endvalue) && (*cur == 0)) cur++;
4253 if (cur == ctxt->state->endvalue)
4254 ctxt->state->value = NULL;
4255 else
4256 ctxt->state->value = cur;
4257 return(0);
4258}
4259
4260/**
4261 * xmlRelaxNGValidateValueList:
4262 * @ctxt: a Relax-NG validation context
4263 * @defines: the list of definitions to verify
4264 *
4265 * Validate the given set of definitions for the current value
4266 *
4267 * Returns 0 if the validation succeeded or an error code.
4268 */
4269static int
4270xmlRelaxNGValidateValueList(xmlRelaxNGValidCtxtPtr ctxt,
4271 xmlRelaxNGDefinePtr defines) {
4272 int ret = 0;
4273
4274 while (defines != NULL) {
4275 ret = xmlRelaxNGValidateValue(ctxt, defines);
4276 if (ret != 0)
4277 break;
4278 defines = defines->next;
4279 }
4280 return(ret);
4281}
4282
4283/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004284 * xmlRelaxNGValidateValue:
4285 * @ctxt: a Relax-NG validation context
4286 * @define: the definition to verify
4287 *
4288 * Validate the given definition for the current value
4289 *
4290 * Returns 0 if the validation succeeded or an error code.
4291 */
4292static int
4293xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
4294 xmlRelaxNGDefinePtr define) {
Daniel Veillardedc91922003-01-26 00:52:04 +00004295 int ret = 0, oldflags;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004296 xmlChar *value;
4297
4298 value = ctxt->state->value;
4299 switch (define->type) {
4300 case XML_RELAXNG_EMPTY:
4301 if ((value != NULL) && (value[0] != '0'))
4302 ret = -1;
4303 break;
4304 case XML_RELAXNG_TEXT:
4305 break;
Daniel Veillardedc91922003-01-26 00:52:04 +00004306 case XML_RELAXNG_VALUE: {
4307 if (!xmlStrEqual(value, define->value)) {
4308 if (define->name != NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00004309 xmlRelaxNGTypeLibraryPtr lib;
4310
4311 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
4312 if ((lib != NULL) && (lib->comp != NULL))
4313 ret = lib->comp(lib->data, define->name, value,
4314 define->value);
4315 else
4316 ret = -1;
4317 if (ret < 0) {
4318 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004319 VALID_ERROR2("Internal: failed to compare type %s\n",
Daniel Veillardea3f3982003-01-26 19:45:18 +00004320 define->name);
4321 return(-1);
4322 } else if (ret == 1) {
4323 ret = 0;
4324 } else {
4325 ret = -1;
4326 }
Daniel Veillardedc91922003-01-26 00:52:04 +00004327 } else {
4328 xmlChar *nval, *nvalue;
4329
4330 /*
4331 * TODO: trivial optimizations are possible by
4332 * computing at compile-time
4333 */
4334 nval = xmlRelaxNGNormalize(ctxt, define->value);
4335 nvalue = xmlRelaxNGNormalize(ctxt, value);
4336
Daniel Veillardea3f3982003-01-26 19:45:18 +00004337 if ((nval == NULL) || (nvalue == NULL) ||
4338 (!xmlStrEqual(nval, nvalue)))
Daniel Veillardedc91922003-01-26 00:52:04 +00004339 ret = -1;
4340 if (nval != NULL)
4341 xmlFree(nval);
4342 if (nvalue != NULL)
4343 xmlFree(nvalue);
4344 }
4345 }
4346 break;
4347 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004348 case XML_RELAXNG_DATATYPE: {
4349 ret = xmlRelaxNGValidateDatatype(ctxt, value, define);
4350 if (ret == 0)
4351 xmlRelaxNGNextValue(ctxt);
4352
4353 break;
4354 }
4355 case XML_RELAXNG_CHOICE: {
4356 xmlRelaxNGDefinePtr list = define->content;
4357 xmlChar *oldvalue;
4358
4359 oldflags = ctxt->flags;
4360 ctxt->flags |= FLAGS_IGNORABLE;
4361
4362 oldvalue = ctxt->state->value;
4363 while (list != NULL) {
4364 ret = xmlRelaxNGValidateValue(ctxt, list);
4365 if (ret == 0) {
4366 break;
4367 }
4368 ctxt->state->value = oldvalue;
4369 list = list->next;
4370 }
4371 ctxt->flags = oldflags;
4372 break;
4373 }
4374 case XML_RELAXNG_LIST: {
4375 xmlRelaxNGDefinePtr list = define->content;
4376 xmlChar *oldvalue, *oldend, *val, *cur;
4377
4378 oldvalue = ctxt->state->value;
4379 oldend = ctxt->state->endvalue;
4380
4381 val = xmlStrdup(oldvalue);
4382 if (val == NULL) {
4383 VALID_CTXT();
4384 VALID_ERROR("Internal: no state\n");
4385 return(-1);
4386 }
4387 cur = val;
4388 while (*cur != 0) {
4389 if (IS_BLANK(*cur))
4390 *cur = 0;
4391 cur++;
4392 }
4393 ctxt->state->endvalue = cur;
4394 cur = val;
4395 while ((*cur == 0) && (cur != ctxt->state->endvalue)) cur++;
4396
4397 ctxt->state->value = cur;
4398
4399 while (list != NULL) {
4400 ret = xmlRelaxNGValidateValue(ctxt, list);
4401 if (ret != 0) {
4402 break;
4403 }
4404 list = list->next;
4405 }
4406 if ((ret == 0) && (ctxt->state->value != NULL) &&
4407 (ctxt->state->value != ctxt->state->endvalue)) {
4408 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004409 VALID_ERROR2("Extra data in list: %s\n", ctxt->state->value);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004410 ret = -1;
4411 }
4412 xmlFree(val);
4413 ctxt->state->value = oldvalue;
4414 ctxt->state->endvalue = oldend;
4415 break;
4416 }
4417 case XML_RELAXNG_ONEORMORE:
4418 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
4419 if (ret != 0) {
4420 break;
4421 }
4422 /* no break on purpose */
4423 case XML_RELAXNG_ZEROORMORE: {
4424 xmlChar *cur, *temp;
4425
4426 oldflags = ctxt->flags;
4427 ctxt->flags |= FLAGS_IGNORABLE;
4428 cur = ctxt->state->value;
4429 temp = NULL;
4430 while ((cur != NULL) && (cur != ctxt->state->endvalue) &&
4431 (temp != cur)) {
4432 temp = cur;
4433 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
4434 if (ret != 0) {
4435 ctxt->state->value = temp;
4436 ret = 0;
4437 break;
4438 }
4439 cur = ctxt->state->value;
4440 }
4441 ctxt->flags = oldflags;
4442 break;
4443 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004444 default:
4445 TODO
4446 ret = -1;
4447 }
4448 return(ret);
4449}
4450
4451/**
4452 * xmlRelaxNGValidateValueContent:
4453 * @ctxt: a Relax-NG validation context
4454 * @defines: the list of definitions to verify
4455 *
4456 * Validate the given definitions for the current value
4457 *
4458 * Returns 0 if the validation succeeded or an error code.
4459 */
4460static int
4461xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt,
4462 xmlRelaxNGDefinePtr defines) {
4463 int ret = 0;
4464
4465 while (defines != NULL) {
4466 ret = xmlRelaxNGValidateValue(ctxt, defines);
4467 if (ret != 0)
4468 break;
4469 defines = defines->next;
4470 }
4471 return(ret);
4472}
4473
4474/**
Daniel Veillard144fae12003-02-03 13:17:57 +00004475 * xmlRelaxNGAttributeMatch:
4476 * @ctxt: a Relax-NG validation context
4477 * @define: the definition to check
4478 * @prop: the attribute
4479 *
4480 * Check if the attribute matches the definition nameClass
4481 *
4482 * Returns 1 if the attribute matches, 0 if no, or -1 in case of error
4483 */
4484static int
4485xmlRelaxNGAttributeMatch(xmlRelaxNGValidCtxtPtr ctxt,
4486 xmlRelaxNGDefinePtr define,
4487 xmlAttrPtr prop) {
4488 int ret;
4489
4490 if (define->name != NULL) {
4491 if (!xmlStrEqual(define->name, prop->name))
4492 return(0);
4493 }
4494 if (define->ns != NULL) {
4495 if (define->ns[0] == 0) {
4496 if (prop->ns != NULL)
4497 return(0);
4498 } else {
4499 if ((prop->ns == NULL) ||
4500 (!xmlStrEqual(define->ns, prop->ns->href)))
4501 return(0);
4502 }
4503 }
4504 if (define->nameClass == NULL)
4505 return(1);
4506 define = define->nameClass;
4507 if (define->type == XML_RELAXNG_EXCEPT) {
4508 xmlRelaxNGDefinePtr list;
4509
4510 list = define->content;
4511 while (list != NULL) {
4512 ret = xmlRelaxNGAttributeMatch(ctxt, list, prop);
4513 if (ret == 1)
4514 return(0);
4515 if (ret < 0)
4516 return(ret);
4517 list = list->next;
4518 }
4519 } else {
4520 TODO
4521 }
4522 return(1);
4523}
4524
4525/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004526 * xmlRelaxNGValidateAttribute:
4527 * @ctxt: a Relax-NG validation context
4528 * @define: the definition to verify
4529 *
4530 * Validate the given attribute definition for that node
4531 *
4532 * Returns 0 if the validation succeeded or an error code.
4533 */
4534static int
4535xmlRelaxNGValidateAttribute(xmlRelaxNGValidCtxtPtr ctxt,
4536 xmlRelaxNGDefinePtr define) {
4537 int ret = 0, i;
4538 xmlChar *value, *oldvalue;
4539 xmlAttrPtr prop = NULL, tmp;
4540
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004541 if (ctxt->state->nbAttrLeft <= 0)
4542 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004543 if (define->name != NULL) {
4544 for (i = 0;i < ctxt->state->nbAttrs;i++) {
4545 tmp = ctxt->state->attrs[i];
4546 if ((tmp != NULL) && (xmlStrEqual(define->name, tmp->name))) {
4547 if ((((define->ns == NULL) || (define->ns[0] == 0)) &&
4548 (tmp->ns == NULL)) ||
4549 ((tmp->ns != NULL) &&
4550 (xmlStrEqual(define->ns, tmp->ns->href)))) {
4551 prop = tmp;
4552 break;
4553 }
4554 }
4555 }
4556 if (prop != NULL) {
4557 value = xmlNodeListGetString(prop->doc, prop->children, 1);
4558 oldvalue = ctxt->state->value;
4559 ctxt->state->value = value;
Daniel Veillard231d7912003-02-09 14:22:17 +00004560 ctxt->state->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004561 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00004562 if (ctxt->state->value != NULL)
4563 value = ctxt->state->value;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004564 if (value != NULL)
4565 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00004566 ctxt->state->value = oldvalue;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004567 if (ret == 0) {
4568 /*
4569 * flag the attribute as processed
4570 */
4571 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004572 ctxt->state->nbAttrLeft--;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004573 }
4574 } else {
4575 ret = -1;
4576 }
4577#ifdef DEBUG
4578 xmlGenericError(xmlGenericErrorContext,
4579 "xmlRelaxNGValidateAttribute(%s): %d\n", define->name, ret);
4580#endif
4581 } else {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004582 for (i = 0;i < ctxt->state->nbAttrs;i++) {
4583 tmp = ctxt->state->attrs[i];
Daniel Veillard144fae12003-02-03 13:17:57 +00004584 if ((tmp != NULL) &&
4585 (xmlRelaxNGAttributeMatch(ctxt, define, tmp) == 1)) {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004586 prop = tmp;
4587 break;
4588 }
4589 }
4590 if (prop != NULL) {
4591 value = xmlNodeListGetString(prop->doc, prop->children, 1);
4592 oldvalue = ctxt->state->value;
4593 ctxt->state->value = value;
4594 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00004595 if (ctxt->state->value != NULL)
4596 value = ctxt->state->value;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004597 if (value != NULL)
4598 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00004599 ctxt->state->value = oldvalue;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004600 if (ret == 0) {
4601 /*
4602 * flag the attribute as processed
4603 */
4604 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004605 ctxt->state->nbAttrLeft--;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004606 }
4607 } else {
4608 ret = -1;
4609 }
4610#ifdef DEBUG
Daniel Veillard144fae12003-02-03 13:17:57 +00004611 if (define->ns != NULL) {
4612 xmlGenericError(xmlGenericErrorContext,
4613 "xmlRelaxNGValidateAttribute(nsName ns = %s): %d\n",
4614 define->ns, ret);
4615 } else {
4616 xmlGenericError(xmlGenericErrorContext,
4617 "xmlRelaxNGValidateAttribute(anyName): %d\n",
4618 ret);
4619 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004620#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00004621 }
4622
4623 return(ret);
4624}
4625
4626/**
4627 * xmlRelaxNGValidateAttributeList:
4628 * @ctxt: a Relax-NG validation context
4629 * @define: the list of definition to verify
4630 *
4631 * Validate the given node against the list of attribute definitions
4632 *
4633 * Returns 0 if the validation succeeded or an error code.
4634 */
4635static int
4636xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt,
4637 xmlRelaxNGDefinePtr defines) {
4638 int ret = 0;
4639 while (defines != NULL) {
4640 if (xmlRelaxNGValidateAttribute(ctxt, defines) != 0)
4641 ret = -1;
4642 defines = defines->next;
4643 }
4644 return(ret);
4645}
4646
4647/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004648 * xmlRelaxNGValidateTryPermutation:
4649 * @ctxt: a Relax-NG validation context
4650 * @groups: the array of groups
4651 * @nbgroups: the number of groups in the array
4652 * @array: the permutation to try
4653 * @len: the size of the set
4654 *
4655 * Try to validate a permutation for the group of definitions.
4656 *
4657 * Returns 0 if the validation succeeded or an error code.
4658 */
4659static int
4660xmlRelaxNGValidateTryPermutation(xmlRelaxNGValidCtxtPtr ctxt,
4661 xmlRelaxNGDefinePtr rule,
4662 xmlNodePtr *array, int len) {
4663 int i, ret;
4664
4665 if (len > 0) {
4666 /*
4667 * One only need the next pointer set-up to do the validation
4668 */
4669 for (i = 0;i < (len - 1);i++)
4670 array[i]->next = array[i + 1];
4671 array[i]->next = NULL;
4672
4673 /*
4674 * Now try to validate the sequence
4675 */
4676 ctxt->state->seq = array[0];
4677 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
4678 } else {
4679 ctxt->state->seq = NULL;
4680 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
4681 }
4682
4683 /*
4684 * the sequence must be fully consumed
4685 */
4686 if (ctxt->state->seq != NULL)
4687 return(-1);
4688
4689 return(ret);
4690}
4691
4692/**
4693 * xmlRelaxNGValidateWalkPermutations:
4694 * @ctxt: a Relax-NG validation context
4695 * @groups: the array of groups
4696 * @nbgroups: the number of groups in the array
4697 * @nodes: the set of nodes
4698 * @array: the current state of the parmutation
4699 * @len: the size of the set
4700 * @level: a pointer to the level variable
4701 * @k: the index in the array to fill
4702 *
4703 * Validate a set of nodes for a groups of definitions, will try the
4704 * full set of permutations
4705 *
4706 * Returns 0 if the validation succeeded or an error code.
4707 */
4708static int
4709xmlRelaxNGValidateWalkPermutations(xmlRelaxNGValidCtxtPtr ctxt,
4710 xmlRelaxNGDefinePtr rule, xmlNodePtr *nodes,
4711 xmlNodePtr *array, int len,
4712 int *level, int k) {
4713 int i, ret;
4714
4715 if ((k >= 0) && (k < len))
4716 array[k] = nodes[*level];
4717 *level = *level + 1;
4718 if (*level == len) {
4719 ret = xmlRelaxNGValidateTryPermutation(ctxt, rule, array, len);
4720 if (ret == 0)
4721 return(0);
4722 } else {
4723 for (i = 0;i < len;i++) {
4724 if (array[i] == NULL) {
4725 ret = xmlRelaxNGValidateWalkPermutations(ctxt, rule,
4726 nodes, array, len, level, i);
4727 if (ret == 0)
4728 return(0);
4729 }
4730 }
4731 }
4732 *level = *level - 1;
4733 array[k] = NULL;
4734 return(-1);
4735}
4736
4737/**
4738 * xmlRelaxNGNodeMatchesList:
4739 * @node: the node
4740 * @list: a NULL terminated array of definitions
4741 *
4742 * Check if a node can be matched by one of the definitions
4743 *
4744 * Returns 1 if matches 0 otherwise
4745 */
4746static int
4747xmlRelaxNGNodeMatchesList(xmlNodePtr node, xmlRelaxNGDefinePtr *list) {
4748 xmlRelaxNGDefinePtr cur;
4749 int i = 0;
4750
4751 if ((node == NULL) || (list == NULL))
4752 return(0);
4753
4754 cur = list[i++];
4755 while (cur != NULL) {
4756 if ((node->type == XML_ELEMENT_NODE) &&
4757 (cur->type == XML_RELAXNG_ELEMENT)) {
4758 if (cur->name == NULL) {
4759 if ((node->ns != NULL) &&
4760 (xmlStrEqual(node->ns->href, cur->ns)))
4761 return(1);
4762 } else if (xmlStrEqual(cur->name, node->name)) {
4763 if ((cur->ns == NULL) || (cur->ns[0] == 0)) {
4764 if (node->ns == NULL)
4765 return(1);
4766 } else {
4767 if ((node->ns != NULL) &&
4768 (xmlStrEqual(node->ns->href, cur->ns)))
4769 return(1);
4770 }
4771 }
4772 } else if ((node->type == XML_TEXT_NODE) &&
4773 (cur->type == XML_RELAXNG_TEXT)) {
4774 return(1);
4775 }
4776 cur = list[i++];
4777 }
4778 return(0);
4779}
4780
4781/**
4782 * xmlRelaxNGValidatePartGroup:
4783 * @ctxt: a Relax-NG validation context
4784 * @groups: the array of groups
4785 * @nbgroups: the number of groups in the array
4786 * @nodes: the set of nodes
4787 * @len: the size of the set of nodes
4788 *
4789 * Validate a set of nodes for a groups of definitions
4790 *
4791 * Returns 0 if the validation succeeded or an error code.
4792 */
4793static int
4794xmlRelaxNGValidatePartGroup(xmlRelaxNGValidCtxtPtr ctxt,
4795 xmlRelaxNGInterleaveGroupPtr *groups,
4796 int nbgroups, xmlNodePtr *nodes, int len) {
Daniel Veillard231d7912003-02-09 14:22:17 +00004797 int level, ret = -1, i, j, k, top_j, max_j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004798 xmlNodePtr *array = NULL, *list, oldseq;
4799 xmlRelaxNGInterleaveGroupPtr group;
4800
4801 list = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
4802 if (list == NULL) {
4803 return(-1);
4804 }
4805 array = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
4806 if (array == NULL) {
4807 xmlFree(list);
4808 return(-1);
4809 }
4810 memset(array, 0, len * sizeof(xmlNodePtr));
4811
4812 /*
4813 * Partition the elements and validate the subsets.
4814 */
4815 oldseq = ctxt->state->seq;
Daniel Veillard231d7912003-02-09 14:22:17 +00004816 max_j = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004817 for (i = 0;i < nbgroups;i++) {
4818 group = groups[i];
4819 if (group == NULL)
4820 continue;
4821 k = 0;
Daniel Veillard231d7912003-02-09 14:22:17 +00004822 top_j = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004823 for (j = 0;j < len;j++) {
4824 if (nodes[j] == NULL)
4825 continue;
4826 if (xmlRelaxNGNodeMatchesList(nodes[j], group->defs)) {
4827 list[k++] = nodes[j];
4828 nodes[j] = NULL;
Daniel Veillard231d7912003-02-09 14:22:17 +00004829 top_j = j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004830 }
4831 }
Daniel Veillard231d7912003-02-09 14:22:17 +00004832 if (top_j > max_j)
4833 max_j = top_j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004834 ctxt->state->seq = oldseq;
4835 if (k > 1) {
4836 memset(array, 0, k * sizeof(xmlNodePtr));
Daniel Veillardb08c9812003-01-28 23:09:49 +00004837 level = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004838 ret = xmlRelaxNGValidateWalkPermutations(ctxt, group->rule,
4839 list, array, k, &level, -1);
4840 } else {
4841 ret = xmlRelaxNGValidateTryPermutation(ctxt, group->rule, list, k);
4842 }
4843 if (ret != 0) {
4844 ctxt->state->seq = oldseq;
4845 break;
4846 }
4847 }
4848
Daniel Veillard231d7912003-02-09 14:22:17 +00004849 for (j = 0;j < max_j;j++) {
4850 if (nodes[j] != NULL) {
4851 TODO /* problem, one of the nodes didn't got a match */
4852 }
4853 }
4854 if (ret == 0) {
4855 if (max_j + 1 < len)
4856 ctxt->state->seq = nodes[max_j + 1];
4857 else
4858 ctxt->state->seq = NULL;
4859 }
4860
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004861 xmlFree(list);
4862 xmlFree(array);
4863 return(ret);
4864}
4865
4866/**
4867 * xmlRelaxNGValidateInterleave:
4868 * @ctxt: a Relax-NG validation context
4869 * @define: the definition to verify
4870 *
4871 * Validate an interleave definition for a node.
4872 *
4873 * Returns 0 if the validation succeeded or an error code.
4874 */
4875static int
4876xmlRelaxNGValidateInterleave(xmlRelaxNGValidCtxtPtr ctxt,
4877 xmlRelaxNGDefinePtr define) {
4878 int ret = 0, nbchildren, nbtot, i, j;
4879 xmlRelaxNGPartitionPtr partitions;
4880 xmlNodePtr *children = NULL;
4881 xmlNodePtr *order = NULL;
Daniel Veillard231d7912003-02-09 14:22:17 +00004882 xmlNodePtr cur, oldseq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004883
4884 if (define->data != NULL) {
4885 partitions = (xmlRelaxNGPartitionPtr) define->data;
4886 } else {
4887 VALID_CTXT();
4888 VALID_ERROR("Internal: interleave block has no data\n");
4889 return(-1);
4890 }
4891
4892 /*
4893 * Build the sequence of child and an array preserving the children
4894 * initial order.
4895 */
4896 cur = ctxt->state->seq;
Daniel Veillard231d7912003-02-09 14:22:17 +00004897 oldseq = ctxt->state->seq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004898 nbchildren = 0;
4899 nbtot = 0;
4900 while (cur != NULL) {
4901 if ((cur->type == XML_COMMENT_NODE) ||
4902 (cur->type == XML_PI_NODE) ||
4903 ((cur->type == XML_TEXT_NODE) &&
4904 (IS_BLANK_NODE(cur)))) {
4905 nbtot++;
4906 } else {
4907 nbchildren++;
4908 nbtot++;
4909 }
4910 cur = cur->next;
4911 }
4912 children = (xmlNodePtr *) xmlMalloc(nbchildren * sizeof(xmlNodePtr));
4913 if (children == NULL)
4914 goto error;
4915 order = (xmlNodePtr *) xmlMalloc(nbtot * sizeof(xmlNodePtr));
4916 if (order == NULL)
4917 goto error;
4918 cur = ctxt->state->seq;
4919 i = 0;
4920 j = 0;
4921 while (cur != NULL) {
4922 if ((cur->type == XML_COMMENT_NODE) ||
4923 (cur->type == XML_PI_NODE) ||
4924 ((cur->type == XML_TEXT_NODE) &&
4925 (IS_BLANK_NODE(cur)))) {
4926 order[j++] = cur;
4927 } else {
4928 order[j++] = cur;
4929 children[i++] = cur;
4930 }
4931 cur = cur->next;
4932 }
4933
4934 /* TODO: retry with a maller set of child if there is a next... */
4935 ret = xmlRelaxNGValidatePartGroup(ctxt, partitions->groups,
4936 partitions->nbgroups, children, nbchildren);
Daniel Veillard231d7912003-02-09 14:22:17 +00004937 if (ret != 0)
4938 ctxt->state->seq = oldseq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004939
4940 /*
4941 * Cleanup: rebuid the child sequence and free the structure
4942 */
4943 if (order != NULL) {
4944 for (i = 0;i < nbtot;i++) {
4945 if (i == 0)
4946 order[i]->prev = NULL;
4947 else
4948 order[i]->prev = order[i - 1];
4949 if (i == nbtot - 1)
4950 order[i]->next = NULL;
4951 else
4952 order[i]->next = order[i + 1];
4953 }
4954 xmlFree(order);
4955 }
4956 if (children != NULL)
4957 xmlFree(children);
4958
4959 return(ret);
4960
4961error:
4962 if (order != NULL) {
4963 for (i = 0;i < nbtot;i++) {
4964 if (i == 0)
4965 order[i]->prev = NULL;
4966 else
4967 order[i]->prev = order[i - 1];
4968 if (i == nbtot - 1)
4969 order[i]->next = NULL;
4970 else
4971 order[i]->next = order[i + 1];
4972 }
4973 xmlFree(order);
4974 }
4975 if (children != NULL)
4976 xmlFree(children);
4977 return(-1);
4978}
4979
4980/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004981 * xmlRelaxNGValidateElementContent:
4982 * @ctxt: a Relax-NG validation context
4983 * @define: the list of definition to verify
4984 *
4985 * Validate the given node content against the (list) of definitions
4986 *
4987 * Returns 0 if the validation succeeded or an error code.
4988 */
4989static int
4990xmlRelaxNGValidateElementContent(xmlRelaxNGValidCtxtPtr ctxt,
4991 xmlRelaxNGDefinePtr defines) {
4992 int ret = 0, res;
4993
4994 if (ctxt->state == NULL) {
4995 VALID_CTXT();
4996 VALID_ERROR("Internal: no state\n");
4997 return(-1);
4998 }
4999 while (defines != NULL) {
5000 res = xmlRelaxNGValidateDefinition(ctxt, defines);
5001 if (res < 0)
5002 ret = -1;
5003 defines = defines->next;
5004 }
5005
5006 return(ret);
5007}
5008
5009/**
5010 * xmlRelaxNGValidateDefinition:
5011 * @ctxt: a Relax-NG validation context
5012 * @define: the definition to verify
5013 *
5014 * Validate the current node against the definition
5015 *
5016 * Returns 0 if the validation succeeded or an error code.
5017 */
5018static int
5019xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
5020 xmlRelaxNGDefinePtr define) {
5021 xmlNodePtr node;
5022 int ret = 0, i, tmp, oldflags;
5023 xmlRelaxNGValidStatePtr oldstate, state;
5024
5025 if (define == NULL) {
5026 VALID_CTXT();
5027 VALID_ERROR("internal error: define == NULL\n");
5028 return(-1);
5029 }
5030 if (ctxt->state != NULL) {
5031 node = ctxt->state->seq;
5032 } else {
5033 node = NULL;
5034 }
Daniel Veillard231d7912003-02-09 14:22:17 +00005035#ifdef DEBUG
5036 for (i = 0;i < ctxt->depth;i++)
5037 xmlGenericError(xmlGenericErrorContext, " ");
5038 xmlGenericError(xmlGenericErrorContext,
5039 "Start validating %s ", xmlRelaxNGDefName(define));
5040 if (define->name != NULL)
5041 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
5042 if ((node != NULL) && (node->name != NULL))
5043 xmlGenericError(xmlGenericErrorContext, "on %s\n", node->name);
5044 else
5045 xmlGenericError(xmlGenericErrorContext, "\n");
5046#endif
5047 ctxt->depth++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005048 switch (define->type) {
5049 case XML_RELAXNG_EMPTY:
5050 if (node != NULL) {
5051 VALID_CTXT();
5052 VALID_ERROR("Expecting an empty element\n");
Daniel Veillard231d7912003-02-09 14:22:17 +00005053 ret = -1;
5054 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005055 }
Daniel Veillard231d7912003-02-09 14:22:17 +00005056 ret = 0;
5057 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005058 case XML_RELAXNG_NOT_ALLOWED:
Daniel Veillard231d7912003-02-09 14:22:17 +00005059 ret = -1;
5060 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005061 case XML_RELAXNG_TEXT:
Daniel Veillard231d7912003-02-09 14:22:17 +00005062 if (node == NULL) {
5063 ret = 0;
5064 break;
5065 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005066 while ((node != NULL) &&
5067 ((node->type == XML_TEXT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005068 (node->type == XML_COMMENT_NODE) ||
5069 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00005070 (node->type == XML_CDATA_SECTION_NODE)))
5071 node = node->next;
Daniel Veillard276be4a2003-01-24 01:03:34 +00005072 if (node == ctxt->state->seq) {
5073 VALID_CTXT();
5074 VALID_ERROR("Expecting text content\n");
5075 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005076 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00005077 ctxt->state->seq = node;
5078 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005079 case XML_RELAXNG_ELEMENT:
5080 node = xmlRelaxNGSkipIgnored(ctxt, node);
Daniel Veillard231d7912003-02-09 14:22:17 +00005081 if (node == NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005082 VALID_CTXT();
Daniel Veillard231d7912003-02-09 14:22:17 +00005083 VALID_ERROR("Expecting an element, got empty\n");
5084 ret = -1;
5085 break;
5086 }
5087 if (node->type != XML_ELEMENT_NODE) {
5088 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005089 VALID_ERROR2("Expecting an element got %d type\n", node->type);
Daniel Veillard231d7912003-02-09 14:22:17 +00005090 ret = -1;
5091 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005092 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00005093 /*
5094 * This node was already validated successfully against
5095 * this definition.
5096 */
5097 if (node->_private == define)
5098 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005099 if (define->name != NULL) {
5100 if (!xmlStrEqual(node->name, define->name)) {
5101 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005102 VALID_ERROR3("Expecting element %s, got %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00005103 define->name, node->name);
Daniel Veillard231d7912003-02-09 14:22:17 +00005104 ret = -1;
5105 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005106 }
5107 }
5108 if ((define->ns != NULL) && (define->ns[0] != 0)) {
5109 if (node->ns == NULL) {
5110 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005111 VALID_ERROR2("Expecting a namespace for element %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00005112 node->name);
Daniel Veillard231d7912003-02-09 14:22:17 +00005113 ret = -1;
5114 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005115 } else if (!xmlStrEqual(node->ns->href, define->ns)) {
5116 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005117 VALID_ERROR3("Expecting element %s has wrong namespace: expecting %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00005118 node->name, define->ns);
Daniel Veillard231d7912003-02-09 14:22:17 +00005119 ret = -1;
5120 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005121 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005122 } else if (define->name != NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005123 if (node->ns != NULL) {
5124 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005125 VALID_ERROR2("Expecting no namespace for element %s\n",
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005126 define->name);
Daniel Veillard231d7912003-02-09 14:22:17 +00005127 ret = -1;
5128 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005129 }
5130 }
5131
5132 state = xmlRelaxNGNewValidState(ctxt, node);
5133 if (state == NULL) {
Daniel Veillard231d7912003-02-09 14:22:17 +00005134 ret = -1;
5135 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005136 }
5137
5138 oldstate = ctxt->state;
5139 ctxt->state = state;
5140 if (define->attrs != NULL) {
5141 tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
Daniel Veillard231d7912003-02-09 14:22:17 +00005142 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005143 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00005144#ifdef DEBUG
5145 xmlGenericError(xmlGenericErrorContext,
5146 "E: Element %s failed to validate attributes\n",
5147 node->name);
5148#endif
5149 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005150 }
5151 if (define->content != NULL) {
5152 tmp = xmlRelaxNGValidateElementContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00005153 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005154 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00005155#ifdef DEBUG
5156 xmlGenericError(xmlGenericErrorContext,
5157 "E: Element %s failed to validate element content\n",
5158 node->name);
5159#endif
5160 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005161 }
5162 state = ctxt->state;
5163 if (state->seq != NULL) {
5164 state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq);
5165 if (state->seq != NULL) {
5166 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005167 VALID_ERROR3("Extra content for element %s: %s\n",
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005168 node->name, state->seq->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005169 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00005170#ifdef DEBUG
5171 xmlGenericError(xmlGenericErrorContext,
5172 "E: Element %s has extra content: %s\n",
5173 node->name, state->seq->name);
5174#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00005175 }
5176 }
5177 for (i = 0;i < state->nbAttrs;i++) {
5178 if (state->attrs[i] != NULL) {
5179 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005180 VALID_ERROR3("Invalid attribute %s for element %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00005181 state->attrs[i]->name, node->name);
5182 ret = -1;
5183 }
5184 }
5185 ctxt->state = oldstate;
5186 xmlRelaxNGFreeValidState(state);
5187 if (oldstate != NULL)
Daniel Veillarde5b110b2003-02-04 14:43:39 +00005188 oldstate->seq = xmlRelaxNGSkipIgnored(ctxt, node->next);
5189 if (ret == 0)
5190 node->_private = define;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005191
5192
5193#ifdef DEBUG
5194 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard231d7912003-02-09 14:22:17 +00005195 "xmlRelaxNGValidateDefinition(): validated %s : %d",
Daniel Veillard6eadf632003-01-23 18:29:16 +00005196 node->name, ret);
Daniel Veillard231d7912003-02-09 14:22:17 +00005197 if (oldstate == NULL)
5198 xmlGenericError(xmlGenericErrorContext, ": no state\n");
5199 else if (oldstate->seq == NULL)
5200 xmlGenericError(xmlGenericErrorContext, ": done\n");
5201 else if (oldstate->seq->type == XML_ELEMENT_NODE)
5202 xmlGenericError(xmlGenericErrorContext, ": next elem %s\n",
5203 oldstate->seq->name);
5204 else
5205 xmlGenericError(xmlGenericErrorContext, ": next %s %d\n",
5206 oldstate->seq->name, oldstate->seq->type);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005207#endif
5208 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005209 case XML_RELAXNG_OPTIONAL:
5210 oldflags = ctxt->flags;
5211 ctxt->flags |= FLAGS_IGNORABLE;
5212 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5213 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5214 if (ret != 0) {
5215 xmlRelaxNGFreeValidState(ctxt->state);
5216 ctxt->state = oldstate;
5217 ret = 0;
5218 break;
5219 }
5220 xmlRelaxNGFreeValidState(oldstate);
5221 ctxt->flags = oldflags;
5222 ret = 0;
5223 break;
5224 case XML_RELAXNG_ONEORMORE:
5225 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5226 if (ret != 0) {
5227 break;
5228 }
5229 /* no break on purpose */
Daniel Veillard276be4a2003-01-24 01:03:34 +00005230 case XML_RELAXNG_ZEROORMORE: {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005231 oldflags = ctxt->flags;
5232 ctxt->flags |= FLAGS_IGNORABLE;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005233 while (ctxt->state->nbAttrLeft != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005234 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5235 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5236 if (ret != 0) {
5237 xmlRelaxNGFreeValidState(ctxt->state);
5238 ctxt->state = oldstate;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005239 break;
5240 }
5241 xmlRelaxNGFreeValidState(oldstate);
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005242 }
5243 if (ret == 0) {
5244 /*
5245 * There is no attribute left to be consumed,
5246 * we can check the closure by looking at ctxt->state->seq
5247 */
5248 xmlNodePtr cur, temp;
5249
Daniel Veillard276be4a2003-01-24 01:03:34 +00005250 cur = ctxt->state->seq;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005251 temp = NULL;
5252 while ((cur != NULL) && (temp != cur)) {
5253 temp = cur;
5254 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5255 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5256 if (ret != 0) {
5257 xmlRelaxNGFreeValidState(ctxt->state);
5258 ctxt->state = oldstate;
5259 break;
5260 }
5261 xmlRelaxNGFreeValidState(oldstate);
5262 cur = ctxt->state->seq;
5263 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005264 }
5265 ctxt->flags = oldflags;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005266 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005267 break;
Daniel Veillard276be4a2003-01-24 01:03:34 +00005268 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005269 case XML_RELAXNG_CHOICE: {
5270 xmlRelaxNGDefinePtr list = define->content;
5271
5272 oldflags = ctxt->flags;
5273 ctxt->flags |= FLAGS_IGNORABLE;
5274
5275 while (list != NULL) {
5276 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5277 ret = xmlRelaxNGValidateDefinition(ctxt, list);
5278 if (ret == 0) {
5279 xmlRelaxNGFreeValidState(oldstate);
5280 break;
5281 }
5282 xmlRelaxNGFreeValidState(ctxt->state);
5283 ctxt->state = oldstate;
5284 list = list->next;
5285 }
5286 ctxt->flags = oldflags;
5287 break;
5288 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005289 case XML_RELAXNG_DEF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00005290 case XML_RELAXNG_GROUP: {
5291 xmlRelaxNGDefinePtr list = define->content;
5292
5293 while (list != NULL) {
5294 ret = xmlRelaxNGValidateDefinition(ctxt, list);
5295 if (ret != 0)
5296 break;
5297 list = list->next;
5298 }
5299 break;
5300 }
5301 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005302 ret = xmlRelaxNGValidateInterleave(ctxt, define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005303 break;
5304 case XML_RELAXNG_ATTRIBUTE:
5305 ret = xmlRelaxNGValidateAttribute(ctxt, define);
5306 break;
5307 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00005308 case XML_RELAXNG_PARENTREF:
5309 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00005310 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5311 break;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005312 case XML_RELAXNG_DATATYPE: {
5313 xmlChar *content;
5314
5315 content = xmlNodeGetContent(node);
5316 ret = xmlRelaxNGValidateDatatype(ctxt, content, define);
5317 if (ret == -1) {
5318 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005319 VALID_ERROR2("internal error validating %s\n", define->name);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005320 } else if (ret == 0) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005321 if (node != NULL)
5322 ctxt->state->seq = node->next;
5323 else
5324 ctxt->state->seq = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005325 }
5326 /*
5327 * TODO cover the problems with
5328 * <p>12<!-- comment -->34</p>
5329 * TODO detect full element coverage at compilation time.
5330 */
5331 if ((node != NULL) && (node->next != NULL)) {
5332 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005333 VALID_ERROR2("The data does not cover the full element %s\n",
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005334 node->parent->name);
5335 ret = -1;
5336 }
5337 if (content != NULL)
5338 xmlFree(content);
5339 break;
5340 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00005341 case XML_RELAXNG_VALUE: {
5342 xmlChar *content;
5343 xmlChar *oldvalue;
5344
5345 content = xmlNodeGetContent(node);
5346 oldvalue = ctxt->state->value;
5347 ctxt->state->value = content;
5348 ret = xmlRelaxNGValidateValue(ctxt, define);
5349 ctxt->state->value = oldvalue;
5350 if (ret == -1) {
5351 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005352 VALID_ERROR2("internal error validating %s\n", define->name);
Daniel Veillardea3f3982003-01-26 19:45:18 +00005353 } else if (ret == 0) {
5354 ctxt->state->seq = node->next;
5355 }
5356 /*
5357 * TODO cover the problems with
5358 * <p>12<!-- comment -->34</p>
5359 * TODO detect full element coverage at compilation time.
5360 */
5361 if ((node != NULL) && (node->next != NULL)) {
5362 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005363 VALID_ERROR2("The value does not cover the full element %s\n",
Daniel Veillardea3f3982003-01-26 19:45:18 +00005364 node->parent->name);
5365 ret = -1;
5366 }
5367 if (content != NULL)
5368 xmlFree(content);
5369 break;
5370 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005371 case XML_RELAXNG_LIST: {
5372 xmlChar *content;
5373 xmlChar *oldvalue, *oldendvalue;
5374 int len;
Daniel Veillardea3f3982003-01-26 19:45:18 +00005375
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005376 content = xmlNodeGetContent(node);
5377 len = xmlStrlen(content);
5378 oldvalue = ctxt->state->value;
5379 oldendvalue = ctxt->state->endvalue;
5380 ctxt->state->value = content;
5381 ctxt->state->endvalue = content + len;
5382 ret = xmlRelaxNGValidateValue(ctxt, define);
5383 ctxt->state->value = oldvalue;
5384 ctxt->state->endvalue = oldendvalue;
5385 if (ret == -1) {
5386 VALID_CTXT();
5387 VALID_ERROR("internal error validating list\n");
5388 } else if (ret == 0) {
5389 ctxt->state->seq = node->next;
5390 }
5391 /*
5392 * TODO cover the problems with
5393 * <p>12<!-- comment -->34</p>
5394 * TODO detect full element coverage at compilation time.
5395 */
5396 if ((node != NULL) && (node->next != NULL)) {
5397 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005398 VALID_ERROR2("The list does not cover the full element %s\n",
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005399 node->parent->name);
5400 ret = -1;
5401 }
5402 if (content != NULL)
5403 xmlFree(content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005404 break;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005405 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005406 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00005407 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005408 TODO
5409 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005410 }
Daniel Veillard231d7912003-02-09 14:22:17 +00005411 ctxt->depth--;
5412#ifdef DEBUG
5413 for (i = 0;i < ctxt->depth;i++)
5414 xmlGenericError(xmlGenericErrorContext, " ");
5415 xmlGenericError(xmlGenericErrorContext,
5416 "Validating %s ", xmlRelaxNGDefName(define));
5417 if (define->name != NULL)
5418 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
5419 if (ret == 0)
5420 xmlGenericError(xmlGenericErrorContext, "suceeded\n");
5421 else
5422 xmlGenericError(xmlGenericErrorContext, "failed\n");
5423#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00005424 return(ret);
5425}
5426
5427/**
5428 * xmlRelaxNGValidateDocument:
5429 * @ctxt: a Relax-NG validation context
5430 * @doc: the document
5431 *
5432 * Validate the given document
5433 *
5434 * Returns 0 if the validation succeeded or an error code.
5435 */
5436static int
5437xmlRelaxNGValidateDocument(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
5438 int ret;
5439 xmlRelaxNGPtr schema;
5440 xmlRelaxNGGrammarPtr grammar;
5441 xmlRelaxNGValidStatePtr state;
5442
5443 if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL))
5444 return(-1);
5445
5446 schema = ctxt->schema;
5447 grammar = schema->topgrammar;
5448 if (grammar == NULL) {
5449 VALID_CTXT();
5450 VALID_ERROR("No top grammar defined\n");
5451 return(-1);
5452 }
5453 state = xmlRelaxNGNewValidState(ctxt, NULL);
5454 ctxt->state = state;
5455 ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start);
5456 state = ctxt->state;
5457 if ((state != NULL) && (state->seq != NULL)) {
5458 xmlNodePtr node;
5459
5460 node = state->seq;
5461 node = xmlRelaxNGSkipIgnored(ctxt, node);
5462 if (node != NULL) {
5463 VALID_CTXT();
5464 VALID_ERROR("extra data on the document\n");
5465 ret = -1;
5466 }
5467 }
5468 xmlRelaxNGFreeValidState(state);
5469
5470 return(ret);
5471}
5472
5473/************************************************************************
5474 * *
5475 * Validation interfaces *
5476 * *
5477 ************************************************************************/
5478/**
5479 * xmlRelaxNGNewValidCtxt:
5480 * @schema: a precompiled XML RelaxNGs
5481 *
5482 * Create an XML RelaxNGs validation context based on the given schema
5483 *
5484 * Returns the validation context or NULL in case of error
5485 */
5486xmlRelaxNGValidCtxtPtr
5487xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema) {
5488 xmlRelaxNGValidCtxtPtr ret;
5489
5490 ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt));
5491 if (ret == NULL) {
5492 xmlGenericError(xmlGenericErrorContext,
5493 "Failed to allocate new schama validation context\n");
5494 return (NULL);
5495 }
5496 memset(ret, 0, sizeof(xmlRelaxNGValidCtxt));
5497 ret->schema = schema;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005498 ret->error = xmlGenericError;
5499 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005500 return (ret);
5501}
5502
5503/**
5504 * xmlRelaxNGFreeValidCtxt:
5505 * @ctxt: the schema validation context
5506 *
5507 * Free the resources associated to the schema validation context
5508 */
5509void
5510xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt) {
5511 if (ctxt == NULL)
5512 return;
5513 xmlFree(ctxt);
5514}
5515
5516/**
5517 * xmlRelaxNGSetValidErrors:
5518 * @ctxt: a Relax-NG validation context
5519 * @err: the error function
5520 * @warn: the warning function
5521 * @ctx: the functions context
5522 *
5523 * Set the error and warning callback informations
5524 */
5525void
5526xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
5527 xmlRelaxNGValidityErrorFunc err,
5528 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
5529 if (ctxt == NULL)
5530 return;
5531 ctxt->error = err;
5532 ctxt->warning = warn;
5533 ctxt->userData = ctx;
5534}
5535
5536/**
5537 * xmlRelaxNGValidateDoc:
5538 * @ctxt: a Relax-NG validation context
5539 * @doc: a parsed document tree
5540 *
5541 * Validate a document tree in memory.
5542 *
5543 * Returns 0 if the document is valid, a positive error code
5544 * number otherwise and -1 in case of internal or API error.
5545 */
5546int
5547xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
5548 int ret;
5549
5550 if ((ctxt == NULL) || (doc == NULL))
5551 return(-1);
5552
5553 ctxt->doc = doc;
5554
5555 ret = xmlRelaxNGValidateDocument(ctxt, doc);
Daniel Veillard71531f32003-02-05 13:19:53 +00005556 /*
5557 * TODO: build error codes
5558 */
5559 if (ret == -1)
5560 return(1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005561 return(ret);
5562}
5563
5564#endif /* LIBXML_SCHEMAS_ENABLED */
5565